inet.c   [plain text]


/* 
 Copyright (c) 2000  
 International Computer Science Institute
 All rights reserved.

 This file may contain software code originally developed for the
 Sting project. The Sting software carries the following copyright:

 Copyright (c) 1998, 1999
 Stefan Savage and the University of Washington.
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
 3. All advertising materials mentioning features or use of this software
    must display the following acknowledgment:
      This product includes software developed by ACIRI, the AT&T
      Center for Internet Research at ICSI (the International Computer
      Science Institute). This product may also include software developed
      by Stefan Savage at the University of Washington.  
 4. The names of ACIRI, ICSI, Stefan Savage and University of Washington
    may not be used to endorse or promote products derived from this software
    without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY ICSI AND CONTRIBUTORS ``AS IS'' AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED.  IN NO EVENT SHALL ICSI OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "base.h"
#include "inet.h"
#include "session.h"
#include "capture.h"
#include "support.h"
#include "history.h"

extern struct TcpSession session;
extern struct History history[];

/*
 * Deal with struct in_addr type agreement once and for all
 */
char *InetAddress(uint32 addr)
{

  struct in_addr s;
  s.s_addr = addr;

  //printf("In InetAddress:\n");
  //printf("addr = %s (%0x)\n", inet_ntoa(s), addr);

  return (inet_ntoa(s));
}

/*
 * Really slow implementation of ip checksum
 * ripped off from rfc1071
 */

uint16 InetChecksum(uint16 *ip, uint16 *tcp, uint16 ip_len, uint16 tcp_len) {

  uint32 sum = 0;

  uint32 ip_count = ip_len;
  uint32 tcp_count = tcp_len;
  uint16 *ip_addr = ip;
  uint16 *tcp_addr = tcp;

  if (session.debug >= SESSION_DEBUG_HIGH) {
    printf("In InetChecksum...\n");
    printf("iplen: %d, tcplen: %d\n", ip_len, tcp_len);
  }


  while(ip_count > 1) {
    //printf("ip[%d]: %x\n", ip_len - ip_count, htons(*ip_addr));
    sum += *ip_addr++;
    ip_count -= 2;
  }

  while(tcp_count > 1) {
    //printf("tcp[%d]: %x\n", tcp_len - tcp_count, htons(*tcp_addr));
    sum += *tcp_addr++;
    tcp_count -= 2;
  }

  if(ip_count > 0) {
    sum += *(uint8 *)ip_addr;
  }

  if(tcp_count > 0) {
    sum += *(uint8 *)tcp_addr;
  }

  while (sum >> 16) {
    sum = (sum & 0xffff) + (sum >> 16);
  }

  if (session.debug >= SESSION_DEBUG_HIGH) {
    printf("Out InetChecksum...\n");
  }

  return(~sum);

}


void WriteIPPacket(struct IPPacket *p,
		   uint32 src, 
		   uint32 dst, 
		   uint16 sport, 
		   uint16 dport,
		   uint32 seq, 
		   uint32 ack, 
		   uint8 flags, 
		   uint16 win,
		   uint16 urp, 
		   uint16 datalen, 
		   uint16 ip_optlen,
		   uint16 optlen, 
		   uint8  iptos, 
		   uint8  u4tf) 
{

  struct IpHeader *ip = p->ip;
  struct TcpHeader *tcp = p->tcp;

  if (session.debug >= SESSION_DEBUG_HIGH) {
    printf("In WriteIPPacket...\n");
  }

  /* Zero out IpHeader to ensure proper checksum computation */
  bzero((char *)(p->ip), sizeof(struct IpHeader));
 
  ip->ip_src = src;
  ip->ip_dst = dst;
  ip->ip_p = IPPROTOCOL_TCP;
  ip->ip_xsum =
    htons((uint16)(sizeof(struct TcpHeader) + datalen + optlen)); /* pseudo hdr */

  tcp->tcp_sport = htons(sport);
  tcp->tcp_dport = htons(dport);
  tcp->tcp_seq = htonl(seq);
  tcp->tcp_ack = htonl(ack);
  tcp->tcp_hl = (sizeof(struct TcpHeader) + optlen) << 2;
  tcp->tcp_hl = tcp->tcp_hl | u4tf;
  tcp->tcp_flags = flags;

  tcp->tcp_win = htons(win);
  tcp->tcp_urp = htons(urp);
	
  tcp->tcp_xsum = 0;
  tcp->tcp_xsum = InetChecksum((uint16 *)ip, (uint16 *)tcp, 
			       (uint16)sizeof(struct IpHeader), /* IP Options should aren't included */
			       (uint16)(sizeof(struct TcpHeader) + datalen + optlen));

  /* Fill in real ip header */
  if (session.curr_ttl != 0) {
    ip->ip_ttl = session.curr_ttl;
  }else {
    ip->ip_ttl = 60;
  }

  //printf("TTL: %d\n", ip->ip_ttl);
    
  ip->ip_tos = iptos;

  /* IP  Version and Header len field */
  ip->ip_vhl = 0x40 + 0x5 + (int)(ip_optlen/4);
  ip->ip_p = IPPROTOCOL_TCP;

  ip->ip_off = IP_DF;
  ip->ip_len = (uint16)(sizeof(struct IpHeader) + ip_optlen + sizeof(struct TcpHeader) + optlen + datalen);

  ip->ip_xsum = 0;
  ip->ip_xsum = InetChecksum((uint16 *)ip, NULL,
			       (uint16)sizeof(struct IpHeader) + ip_optlen, /* IP Options should aren't included */
			       0);

  if (session.debug >= SESSION_DEBUG_HIGH) {
    printf("Out WriteIPPacket...\n");
  }

}

void ReadIPPacket(struct IPPacket *p,
		  uint32 *src, 
		  uint32 *dst, 
		  uint16 *sport, 
		  uint16 *dport,
		  uint32 *seq, 
		  uint32 *ack, 
		  uint8 *flags, 
		  uint16 *win,
		  uint16 *urp, 
		  uint16 *datalen, 
		  uint16 *ip_optlen,
		  uint16 *optlen) 
{

  /* TODO: Add reading of IP options, if any */

  struct IpHeader *ip = p->ip;
  struct TcpHeader *tcp = p->tcp;

  uint16 ip_len;
  uint16 ip_hl;
  uint16 tcp_hl;

  /* XXX do checksum check? */
  if (ip->ip_p != IPPROTOCOL_TCP && ip->ip_p != IPPROTOCOL_ICMP) {
    printf("Unexpected protocol packet: %u\n", ip->ip_p);
    Quit(ERR_CHECKSUM);
  }

  *src = ip->ip_src;
  *dst = ip->ip_dst;
  *sport = ntohs(tcp->tcp_sport);
  *dport = ntohs(tcp->tcp_dport);
  *seq = ntohl(tcp->tcp_seq);
  *ack = ntohl(tcp->tcp_ack);
  *flags = tcp->tcp_flags;
  *win = ntohs(tcp->tcp_win);
  *urp = ntohs(tcp->tcp_urp);

  tcp_hl = tcp->tcp_hl >> 2;
  ip_len = ntohs(ip->ip_len);
  ip_hl = (ip->ip_vhl & 0x0f) << 2;
  *datalen = (ip_len - ip_hl) - tcp_hl;
  *ip_optlen = ip_hl - (unsigned int)sizeof(struct IpHeader); /* added to support IP Options */
  *optlen = tcp_hl - (unsigned int)sizeof(struct TcpHeader);

}

void PrintICMPUnreachableErrorPacket(struct ICMPUnreachableErrorPacket *p)
{

  struct IpHeader *ip = &p->ip;
  struct IcmpHeader *icmp = &p->icmp;
  struct IpHeader *off_ip = &p->off_ip;

  printf("IPHdr: ");
  printf("%s > ", InetAddress(ip->ip_src));
  printf("%s ", InetAddress(ip->ip_dst));
  printf(" datalen: %u\n", ip->ip_len);
  printf("ICMPHdr: ");
  printf("Type: %u  Code: %u MTU next hop: %u xsum: %x\n",
	 icmp->icmp_type, 
	 icmp->icmp_code, 
	 ntohs(icmp->icmp_mtu),
	 icmp->icmp_xsum);
  printf("Off IPHdr: ");
  printf("%s > ", InetAddress(off_ip->ip_src));
  printf("%s ", InetAddress(off_ip->ip_dst));
  printf(" datalen: %u ",   off_ip->ip_len);
  printf("tcp sport: %u ",  ntohs(p->tcp_sport));
  printf("tcp dport: %u ",  ntohs(p->tcp_dport));
  printf("tcp seqno: %u\n", (uint32)ntohl(p->tcp_seqno));

}

void PrintTcpPacket(struct IPPacket *p)
{

  struct IpHeader *ip = p->ip;
  struct TcpHeader *tcp = p->tcp;

  char *opt; 
  int optlen; 
  char *ip_opt;
  int ip_optlen;
  int i;
 
  printf("%s.%u > ", InetAddress(ip->ip_src), ntohs(tcp->tcp_sport));
  printf("%s.%u ", InetAddress(ip->ip_dst), ntohs(tcp->tcp_dport));

  if (tcp->tcp_flags & TCPFLAGS_SYN) {
    printf("S");
  }

  if (tcp->tcp_flags & TCPFLAGS_ACK) {
    printf("A");
  }

  if (tcp->tcp_flags & TCPFLAGS_FIN) {
    printf("F");
  }

  if (tcp->tcp_flags & TCPFLAGS_ECN_ECHO) {
    printf("E");
  }

  if (tcp->tcp_flags & TCPFLAGS_CWR) {
    printf("W");
  }

  if (tcp->tcp_flags & TCPFLAGS_RST) {
    printf("R");
  }
  if (tcp->tcp_flags & TCPFLAGS_PSH) {
    printf("P");
  }

  if (tcp->tcp_flags & TCPFLAGS_URG) {
    printf("U");
  }

  if (INSESSION(p,session.src,session.sport,session.dst,session.dport)) {
    printf(" seq: %u, ack: %u", (uint32)ntohl(tcp->tcp_seq) - session.iss, (uint32)ntohl(tcp->tcp_ack) - session.irs);
  } else {
    printf(" seq: %u, ack: %u", (uint32)ntohl(tcp->tcp_seq) - session.irs, (uint32)ntohl(tcp->tcp_ack) - session.iss);
  }

  /* IP Options */
  ip_optlen = ((ip->ip_vhl & 0x0f) << 2) - sizeof(struct IpHeader);
  ip_opt = (char *)ip + sizeof(struct IpHeader);

  i = 0;
  while (i < ip_optlen) {
    
    switch ((unsigned char)ip_opt[i]) {
    case IPOPT_NOP:
      printf(" ipopt%d: %s ", i + 1, "IPOPT_NOP");
      i = i + 1;
      break;

    case IPOPT_EOL:
      printf(" ipopt%d: %s ", i + 1, "IPOPT_EOL");
      i = ip_optlen + 1;
      break;
      
    case IPOPT_RR:
      printf(" ipopt%d: %s ", i + 1, "IPOPT_RR");
      i = i + IPOLEN_RR;
      break;

    default:
      printf("ip_opt%d: UNKNOWN ", i + 1);
      i = i + (uint8)ip_opt[i+1] ;
    }
  }

  printf(" win: %u, urg: %u, ttl: %d", ntohs(tcp->tcp_win), ntohs(tcp->tcp_urp), ip->ip_ttl);
  printf(" datalen: %u, optlen: %u ", 
	 ip->ip_len - ((ip->ip_vhl &0x0f) << 2) - (tcp->tcp_hl >> 2),
	 (tcp->tcp_hl >> 2) - (unsigned int)sizeof(struct TcpHeader));  


  /* TCP Options */
  optlen = (tcp->tcp_hl >> 2) - (unsigned int)sizeof (struct TcpHeader) ;
  opt = (char *)tcp + sizeof(struct TcpHeader);

  i = 0 ;

  while (i < optlen) {

    switch ((unsigned char)opt[i]) {

    case TCPOPT_EOL: 
      printf (" opt%d: %s ", i + 1, "TCPOPT_EOL");
      i = optlen + 1; 
      break ; 

    case TCPOPT_NOP:
      printf (" opt%d: %s ", i + 1, "TCPOPT_NOP");
      i++ ; 
      break ;

    case TCPOPT_MAXSEG:
      printf (" opt%d: %s: %d ", i + 1, "TCPOPT_MAXSEG", ntohs(*(uint16 *)((char *)opt+2)));
      i = i + TCPOLEN_MAXSEG ; 
      break ;

    case TCPOPT_WINDOW:
      printf (" opt%d: %s ", i + 1, "TCPOPT_WINDOW");
      i = i + TCPOLEN_WINDOW ;
      break ; 

    case TCPOPT_SACK_PERMITTED:
      printf (" opt%d: %s ", i + 1, "TCPOPT_SACK_PERMITTED");
      i = i + TCPOLEN_SACK_PERMITTED ; 
      break ; 

    case TCPOPT_TIMESTAMP:
      printf (" opt%d: %s ", i + 1, "TCPOPT_TIMESTAMP");
      i = i + TCPOLEN_TIMESTAMP ; 
      break ; 

    default: 
      printf (" opt%d c:%d l:%d: UNKNOWN ", i + 1, (uint8)opt[i], (uint8)opt[i+1]);
      if ((uint8)opt[i+1] > 0) {
	i = i + (uint8)opt[i+1] ;
      } else {
	Quit(20); 
      }
      break ;
    } 
  }
  printf ("\n");
}


struct IPPacket *FindHeaderBoundaries(char *p) {

  struct IPPacket *packet;
  uint16 ip_hl;

  if ((packet = (struct IPPacket *)calloc(1, sizeof(struct IPPacket))) == NULL) { 
    printf("FindHeaderBoundaries: Cannot allocate memory for read packet\nRETURN CODE: %d\n", ERR_MEM_ALLOC);
    Quit(ERR_MEM_ALLOC);
  }

  packet->ip = (struct IpHeader *)p;

  if (packet->ip->ip_p != IPPROTOCOL_TCP &&
    packet->ip->ip_p != IPPROTOCOL_ICMP) {
    printf("Error: Unexpected protocol packet: %u \n",  packet->ip->ip_p);
    Quit(ERR_CHECKSUM);
  }

  ip_hl = (packet->ip->ip_vhl & 0x0f) << 2;

  packet->tcp = (struct TcpHeader *)((char *)p + ip_hl);
  return packet;

}


struct IPPacket *
AllocateIPPacket(int ip_optlen, int tcp_optlen, int datalen, char *str)
{
	struct IPPacket *p;

	if (session.debug >= SESSION_DEBUG_HIGH) {
		printf("In AllocateIPPacket: %s...\n", str);
	}

	if ((p = (struct IPPacket *)calloc(1, sizeof(struct IPPacket)))
	    == NULL) {
		printf("%s ERROR: No space for packet\nRETURN CODE: %d",
		    str, ERR_MEM_ALLOC);
		Quit(ERR_MEM_ALLOC);
	}

	if ((p->ip = (struct IpHeader *)calloc(1, 
	    sizeof(struct IpHeader) + ip_optlen)) == NULL) {
		printf("%s ERROR: No IpHeader space for packet\n"
		    "RETURN CODE: %d", str, ERR_MEM_ALLOC);
		Quit(ERR_MEM_ALLOC);
	}

	if ((p->tcp = (struct TcpHeader *)calloc(1,
	    sizeof(struct TcpHeader) + tcp_optlen + datalen)) == NULL) {
		printf("%s ERROR: No TcpHeader space for packet\n"
		    "RETURN CODE: %d", str, ERR_MEM_ALLOC);
		Quit(ERR_MEM_ALLOC);
	}

	if (session.debug >= SESSION_DEBUG_HIGH) {
		printf("Out of AllocateIPPacket: %s...\n", str);
	}
	return(p);
}

void
FreeIPPacket(struct IPPacket **pkt_p)
{
	struct IPPacket *pkt;
	if (pkt_p == NULL)
		return;
	if ((pkt = *pkt_p) == NULL)
		return;
	if (pkt->ip != NULL) {
		free(pkt->ip);
		pkt->ip = NULL;
	}
	if (pkt->tcp != NULL) {
		free(pkt->tcp);
		pkt->tcp = NULL;
	}
	free(pkt);
	*pkt_p = NULL;
}