/**************************************************************************** 
** File: ip.c
**
** Author: Mike Borella
**
** Comments: Dump IP header information
**
** $Log: ip.c,v $
** Revision 1.2  1998/06/12 21:01:06  mborella
** Added log tag
**
*****************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "config.h"
#include "ip.h"

extern struct arg_t *my_args;

/*----------------------------------------------------------------------------
**
** dump_ip()
**
** Parse IP header and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_ip(u_char *bp, int length)
{
  IPHdr *ip, ip2;
  u_int hlen, len, off;
  u_char *cp = NULL;
  u_int frag_off;
  u_char tos;
  u_int16_t csum;
  u_int16_t my_csum;
  void dump_udp(u_char *, int);
  void dump_tcp(u_char *, int);
  void dump_icmp(u_char *, u_char *);
  u_int16_t in_cksum(u_int16_t *, int);

  /*
   * Dump header announcement
   */

  printf("-----------------------------------------------------------------\n");
  printf("                        IP Header\n");
  printf("-----------------------------------------------------------------\n");

  /*
   * View the packet as an IP header
   */

  ip = (IPHdr *) bp;

  /*
   * Check for truncated header and truncated packet
   */
  
  if (length < sizeof(IPHdr))
    {
      printf("Truncated header, length = %d bytes\n", length);
      return;
    }

  len = ntohs(ip->ip_len);
  if (length < len)
    {
      printf("Truncated packet: length field = %d, actual length = %d\n", 
	     len, length);
      return;
    }

  /*
   * Dump header fields
   */
  
  csum = ntohs(ip->ip_csum);
  hlen = ip->ip_hl*4;

  if (!my_args->n)
    {
      printf("Version:                %d\n", ip->ip_v);
      printf("Header length:          %d\n", hlen);
      
      tos = ip->ip_tos;
      printf("Type of service:        %d", tos);
      printf(" (precedence=%d, D=%d, T=%d, R=%d, U=%d)\n",
	     tos >> 5, (tos & 0x10) >> 4, (tos & 0x08) >> 3,
	     (tos & 0x04) >> 2, tos & 0x03);

      printf("Total length:           %d\n", ntohs(ip->ip_len));
      printf("Identification #:       %d\n", ntohs(ip->ip_id));
      
      frag_off = ntohs(ip->ip_off);
      printf("Fragmentation offset:   %d", (frag_off & 0x1fff) * 8);
      frag_off &= 0xe000;
      printf(" (U=%d, DF=%d, MF=%d)\n", (frag_off & 0x8000) >> 15, 
	     (frag_off & 0x4000) >> 14, (frag_off & 0x2000) >> 13);
      
      printf("Time to live:           %d\n", ip->ip_ttl);
      printf("Protocol:               %d\n", ip->ip_p);
      
      printf("Header checksum:        %d ", csum);
      memcpy((void *) &ip2, (void *) ip, sizeof(IPHdr));
      ip2.ip_csum = 0;
      my_csum = ntohs(in_cksum((u_int16_t *) &ip2, sizeof(IPHdr)));
      if (my_csum != csum) printf("(error: should be %d)", my_csum);
      printf("\n");
      
      printf("Source address          %s\n", inet_ntoa(ip->ip_src));
      printf("Destination address     %s\n", inet_ntoa(ip->ip_dst));
    }


  /*
   * If this is fragment zero, hand it to the next higher
   * level protocol.
   */

  len -= hlen;
  off = ntohs(ip->ip_off);
  if ((off & 0x1fff) == 0) 
    {
      cp = (u_char *) ip + hlen;
      switch (ip->ip_p) 
	{
	case IPPROTO_TCP:
	  dump_tcp(cp, len);
	  break;
       
	case IPPROTO_UDP:
	  dump_udp(cp, len);
	  break;
	  
	case IPPROTO_ICMP:
	  dump_icmp(cp, (u_char *) ip);
	  break; 
	  
	default:
	  break;
	}
    }
}





