www.pudn.com > src.rar > ip_options.c


/* 
  This file is taken from Linux 2.0.36 kernel source. 
  Modified in Jun 99 by Nergal. 
*/ 
 
#include  
#ifdef WIN32 
	#include "nids.h" 
#endif 
 
#define __u8 unsigned char 
#define __u16 unsigned short 
#define __u32 unsigned int 
 
#define IPOPT_END	0 
#define IPOPT_NOOP	1 
#define IPOPT_SEC	130 
#define IPOPT_LSRR	131 
#define IPOPT_SSRR	137 
#define IPOPT_RR	7 
#define IPOPT_SID	136 
#define IPOPT_TIMESTAMP	68 
 
#define MAXTTL		255 
 
struct timestamp { 
  __u8 len; 
  __u8 ptr; 
#if defined(LIBNET_LIL_ENDIAN) 
  __u8 flags:4, overflow:4; 
#elif defined(LIBNET_BIG_ENDIAN) 
  __u8 overflow:4, flags:4; 
#else 
#error	"unknown byte ordering" 
#endif 
  __u32 data[9]; 
}; 
 
#define MAX_ROUTE	16 
 
struct route { 
  char route_size; 
  char pointer; 
  unsigned long route[MAX_ROUTE]; 
}; 
 
#define IPOPT_OPTVAL 0 
#define IPOPT_OLEN   1 
#define IPOPT_OFFSET 2 
#define IPOPT_MINOFF 4 
#define MAX_IPOPTLEN 40 
#define IPOPT_NOP IPOPT_NOOP 
#define IPOPT_EOL IPOPT_END 
#define IPOPT_TS  IPOPT_TIMESTAMP 
 
#define	IPOPT_TS_TSONLY		0	/* timestamps only */ 
#define	IPOPT_TS_TSANDADDR	1	/* timestamps and addresses */ 
#define	IPOPT_TS_PRESPEC	3	/* specified modules only */ 
 
struct options { 
  __u32 faddr;			/* Saved first hop address */ 
  unsigned char optlen; 
  unsigned char srr; 
  unsigned char rr; 
  unsigned char ts; 
  unsigned char is_setbyuser:1,	/* Set by setsockopt?			 */ 
       is_data:1,		/* Options in __data, rather than skb	 */ 
       is_strictroute:1,	/* Strict source route			 */ 
       srr_is_hit:1,		/* Packet destination addr was our one	 */ 
       is_changed:1,		/* IP checksum more not valid		 */ 
       rr_needaddr:1,		/* Need to record addr of outgoing dev	 */ 
       ts_needtime:1,		/* Need to record timestamp		 */ 
       ts_needaddr:1;		/* Need to record addr of outgoing dev  */ 
  unsigned char __pad1; 
  unsigned char __pad2; 
  unsigned char __pad3; 
  unsigned char __data[0]; 
}; 
 
struct iphdr { 
#if defined(LIBNET_LIL_ENDIAN) 
  __u8 ihl:4, version:4; 
#elif defined (LIBNET_BIG_ENDIAN) 
  __u8 version:4, ihl:4; 
#else 
#error	"unknown byte ordering" 
#endif 
  __u8 tos; 
  __u16 tot_len; 
  __u16 id; 
  __u16 frag_off; 
  __u8 ttl; 
  __u8 protocol; 
  __u16 check; 
  __u32 saddr; 
  __u32 daddr; 
  /* The options start here. */ 
}; 
 
#define ip_chk_addr(x) 0 
 
int  
ip_options_compile(unsigned char *iph) 
{ 
  int l; 
  unsigned char *optptr; 
  int optlen; 
  unsigned char *pp_ptr = 0; 
  char optholder[16]; 
  struct options *opt; 
  int skb = 1; 
  int skb_pa_addr = 314159; 
 
  opt = (struct options *) optholder; 
  memset(opt, 0, sizeof(struct options)); 
  opt->optlen = ((struct iphdr *) iph)->ihl * 4 - sizeof(struct iphdr); 
  optptr = iph + sizeof(struct iphdr); 
  opt->is_data = 0; 
 
  for (l = opt->optlen; l > 0;) { 
    switch (*optptr) { 
    case IPOPT_END: 
      for (optptr++, l--; l > 0; l--) { 
	if (*optptr != IPOPT_END) { 
	  *optptr = IPOPT_END; 
	  opt->is_changed = 1; 
	} 
      } 
      goto eol; 
    case IPOPT_NOOP: 
      l--; 
      optptr++; 
      continue; 
    } 
    optlen = optptr[1]; 
    if (optlen < 2 || optlen > l) { 
      pp_ptr = optptr; 
      goto error; 
    } 
    switch (*optptr) { 
    case IPOPT_SSRR: 
    case IPOPT_LSRR: 
      if (optlen < 3) { 
	pp_ptr = optptr + 1; 
	goto error; 
      } 
      if (optptr[2] < 4) { 
	pp_ptr = optptr + 2; 
	goto error; 
      } 
      /* NB: cf RFC-1812 5.2.4.1 */ 
      if (opt->srr) { 
	pp_ptr = optptr; 
	goto error; 
      } 
      if (!skb) { 
	if (optptr[2] != 4 || optlen < 7 || ((optlen - 3) & 3)) { 
	  pp_ptr = optptr + 1; 
	  goto error; 
	} 
	memcpy(&opt->faddr, &optptr[3], 4); 
	if (optlen > 7) 
	  memmove(&optptr[3], &optptr[7], optlen - 7); 
      } 
      opt->is_strictroute = (optptr[0] == IPOPT_SSRR); 
      opt->srr = optptr - iph; 
      break; 
    case IPOPT_RR: 
      if (opt->rr) { 
	pp_ptr = optptr; 
	goto error; 
      } 
      if (optlen < 3) { 
	pp_ptr = optptr + 1; 
	goto error; 
      } 
      if (optptr[2] < 4) { 
	pp_ptr = optptr + 2; 
	goto error; 
      } 
      if (optptr[2] <= optlen) { 
	if (optptr[2] + 3 > optlen) { 
	  pp_ptr = optptr + 2; 
	  goto error; 
	} 
	if (skb) { 
	  memcpy(&optptr[optptr[2] - 1], &skb_pa_addr, 4); 
	  opt->is_changed = 1; 
	} 
	optptr[2] += 4; 
	opt->rr_needaddr = 1; 
      } 
      opt->rr = optptr - iph; 
      break; 
    case IPOPT_TIMESTAMP: 
      if (opt->ts) { 
	pp_ptr = optptr; 
	goto error; 
      } 
      if (optlen < 4) { 
	pp_ptr = optptr + 1; 
	goto error; 
      } 
      if (optptr[2] < 5) { 
	pp_ptr = optptr + 2; 
	goto error; 
      } 
      if (optptr[2] <= optlen) { 
	struct timestamp *ts = (struct timestamp *) (optptr + 1); 
	__u32 *timeptr = 0; 
 
	if (ts->ptr + 3 > ts->len) { 
	  pp_ptr = optptr + 2; 
	  goto error; 
	} 
	switch (ts->flags) { 
	case IPOPT_TS_TSONLY: 
	  opt->ts = optptr - iph; 
	  if (skb) 
	    timeptr = (__u32 *) & optptr[ts->ptr - 1]; 
	  opt->ts_needtime = 1; 
	  ts->ptr += 4; 
	  break; 
	case IPOPT_TS_TSANDADDR: 
	  if (ts->ptr + 7 > ts->len) { 
	    pp_ptr = optptr + 2; 
	    goto error; 
	  } 
	  opt->ts = optptr - iph; 
	  if (skb) { 
	    memcpy(&optptr[ts->ptr - 1], &skb_pa_addr, 4); 
	    timeptr = (__u32 *) & optptr[ts->ptr + 3]; 
	  } 
	  opt->ts_needaddr = 1; 
	  opt->ts_needtime = 1; 
	  ts->ptr += 8; 
	  break; 
	case IPOPT_TS_PRESPEC: 
	  if (ts->ptr + 7 > ts->len) { 
	    pp_ptr = optptr + 2; 
	    goto error; 
	  } 
	  opt->ts = optptr - iph; 
	  { 
	    __u32 addr; 
 
	    memcpy(&addr, &optptr[ts->ptr - 1], 4); 
	    if (ip_chk_addr(addr) == 0) 
	      break; 
	    if (skb) 
	      timeptr = (__u32 *) & optptr[ts->ptr + 3]; 
	  } 
	  opt->ts_needaddr = 1; 
	  opt->ts_needtime = 1; 
	  ts->ptr += 8; 
	  break; 
	default: 
	  pp_ptr = optptr + 3; 
	  goto error; 
	} 
	if (timeptr) { 
	  //struct timeval tv; 
	  __u32 midtime = 1; 
 
	  //do_gettimeofday(&tv); 
	  //midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000); 
	  memcpy(timeptr, &midtime, sizeof(__u32)); 
	  opt->is_changed = 1; 
	} 
      } 
      else { 
	struct timestamp *ts = (struct timestamp *) (optptr + 1); 
 
	if (ts->overflow == 15) { 
	  pp_ptr = optptr + 3; 
	  goto error; 
	} 
	opt->ts = optptr - iph; 
	if (skb) { 
	  ts->overflow++; 
	  opt->is_changed = 1; 
	} 
      } 
      break; 
    case IPOPT_SEC: 
    case IPOPT_SID: 
    default: 
      if (!skb) { 
	pp_ptr = optptr; 
	goto error; 
      } 
      break; 
    } 
    l -= optlen; 
    optptr += optlen; 
  } 
 
eol: 
  if (!pp_ptr) 
    if (!((struct options *) optholder)->srr) 
      return 0; 
 
error: 
  return -1; 
}