/*
** pat_ip.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 06:42:50 1999 
** Last update Thu Oct 28 20:19:55 1999 
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "pat.h"
#include "pat_ip.h"
#include "pat_icmp.h"
#include "pat_udp.h"
#include "pat_tcp.h"
#include "pat_igmp.h"
#include "pat_data.h"
#include "pat_8.h"
#include "pat_16.h"
#include "pat_32.h"
#include "pat_inaddr.h"
#include "pat_ipproto.h"
#include "pat_time.h"

t_id			*pat_ip_sub_id = NULL;

/* initializes pat_ip_sub_id (see ip_sub(3)).
   Returns 0 if OK. Might return various errors. */
t_status		pat_ip_init(VOID_DECL)
{
  t_status		status;

  if ((pat_ip_sub_id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  return (0);
}

/* deletes pat_ip_sub_id */
VOID_FUNC		pat_ip_destroy(VOID_DECL)
{
  id_delete(pat_ip_sub_id);
}

char				*inaddrtime_itmpl = "\n\
<table _name=inaddrtime[%i%] bgcolor=\"%%inaddrtimeColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<small>\n\
<a href=\"set(inaddrtime[%i%].Inaddr)\">%%inaddrtime[%i%].Inaddr%%</a>(<a href=\"set(inaddrtime[%i%].inaddr)\">%%inaddrtime[%i%].inaddr%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<small>\n\
<a href=\"set(inaddrtime[%i%].Time)\">%%inaddrtime[%i%].Time%%</a>(<a href=\"set(inaddrtime[%i%].time)\">%%inaddrtime[%i%].time%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

t_field				inaddrtime_fields[] = 
{
  {"inaddr",	0,	&inaddr_pat,		NULL},
  {"Inaddr",	0,	&inaddr_resolved_pat,	NULL},
  {"time",	4,	&u32_pat,		NULL},
  {"Time",	4,	&time_pat,		NULL},
  NULL_FIELD
};

int		inaddrtime_chan;

PAT_NAME_GENERIC(inaddrtime_pat_name,
		 (VOID_PTR)&inaddrtime_chan,
		 "inaddrtime")

PAT_GET_FIELD_GENERIC(inaddrtime_pat_get_field,
		      inaddrtime_fields)

PAT_SET_FIELD_GENERIC(inaddrtime_pat_set_field,
		      inaddrtime_fields)

PAT_GET_FIELDS_GENERIC(inaddrtime_pat_get_fields,
		       inaddrtime_fields)

PAT_GET_FIELD_PAT_GENERIC(inaddrtime_pat_get_field_pat,
			  inaddrtime_fields)

PAT_GET_TMPL_GENERIC(inaddrtime_pat_get_tmpl,
		     (VOID_PTR)&inaddrtime_chan,
		     inaddrtime_itmpl)

PAT_OFF_GENERIC(inaddrtime_pat_off,
		8)
    
PAT_SUB_GENERIC(inaddrtime_pat_sub,
		&inaddrtime_pat)
    
t_pat				inaddrtime_pat = 
{
  inaddrtime_pat_name,		/* t_pat_name_proc		*/
  inaddrtime_pat_off,		/* t_pat_off_proc		*/
  inaddrtime_pat_sub,		/* t_pat_sub_proc		*/
  NULL,				/* t_pat_sum_proc		*/
  inaddrtime_pat_get_field,	/* t_pat_get_field_proc		*/
  inaddrtime_pat_set_field,	/* t_pat_set_field_proc		*/
  inaddrtime_pat_get_fields,	/* t_pat_get_fields_proc	*/
  inaddrtime_pat_get_tmpl,	/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  NULL,				/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  inaddrtime_pat_get_field_pat,	/* t_pat_get_field_pat_proc	*/
  NULL,				/* t_pat_extract_proc		*/
  NULL,				/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};

char				*ipoptdata_itmpl = "\n\
<table _name=ipoptdata[%i%] width=100%%%% bgcolor=\"%%ipoptdataColor%%\">\n\
<tr>\n\
<td width=100%%%%>\n\
<code>\n\
%%ipoptdata[%i%].cookedhtmlbuf%%\n\
</code>\n\
</tr>\n\
</table>\n\
<br>\n\
";

int	ipoptdata_chan;

PAT_NAME_GENERIC(ipoptdata_pat_name,
		 (VOID_PTR)&ipoptdata_chan,
		 "ipoptdata")

PAT_GET_TMPL_GENERIC(ipoptdata_pat_get_tmpl,
		     (VOID_PTR)&ipoptdata_chan,
		     ipoptdata_itmpl)

t_pat				ipoptdata_pat = 
{
  ipoptdata_pat_name,		/* t_pat_name_proc		*/
  data_pat_off,			/* t_pat_off_proc		*/
  NULL,				/* t_pat_sub_proc		*/
  NULL,				/* t_pat_sum_proc		*/
  data_pat_get_field,		/* t_pat_get_field_proc		*/
  NULL,				/* t_pat_set_field_proc		*/
  data_pat_get_fields,		/* t_pat_get_fields_proc	*/
  ipoptdata_pat_get_tmpl,	/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  NULL,				/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  NULL,				/* t_pat_get_field_pat_proc	*/
  NULL,				/* t_pat_extract_proc		*/
  NULL,				/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};

/*
 Copy Class Number Value Name                            Reference
---- ----- ------ ----- ------------------------------- ---------
   0     0      0     0 EOOL   - End of Options List    [RFC791,JBP]
   0     0      1     1 NOP    - No Operation           [RFC791,JBP]
   1     0      2   130 SEC    - Security                  [RFC1108]
   1     0      3   131 LSR    - Loose Source Route     [RFC791,JBP]
   0     2      4    68 TS     - Time Stamp             [RFC791,JBP]
   1     0      5   133 E-SEC  - Extended Security         [RFC1108]
   1     0      6   134 CIPSO  - Commercial Security           [???]
   0     0      7     7 RR     - Record Route           [RFC791,JBP]
   1     0      8   136 SID    - Stream ID              [RFC791,JBP]
   1     0      9   137 SSR    - Strict Source Route    [RFC791,JBP]
   0     0     10    10 ZSU    - Experimental Measurement      [ZSu]
   0     0     11    11 MTUP   - MTU Probe                 [RFC1191]
   0     0     12    12 MTUR   - MTU Reply                 [RFC1191]
   1     2     13   205 FINN   - Experimental Flow Control    [Finn]
   1     0     14   142 VISA   - Expermental Access Control [Estrin]
   0     0     15    15 ENCODE - ???                      [VerSteeg]
   1     0     16   144 IMITD  - IMI Traffic Descriptor        [Lee]
   1     0     17   145 EIP    - ???                       [RFC1358]
   0     2     18    82 TR     - Traceroute                [RFC1393]
   1     0     19   147 ADDEXT - Address Extension    [Ullmann IPv7] 
   */

t_assoc			ipopt_assocs[] = 
{
  {"eool",		(VOID_PTR)IPOPT_EOOL},
  {"nop",		(VOID_PTR)IPOPT_NOP},
  {"sec",		(VOID_PTR)130},
  {"lsr",		(VOID_PTR)IPOPT_LSR},
  {"ts",		(VOID_PTR)IPOPT_TS},
  {"e-sec",		(VOID_PTR)133},
  {"cipso",		(VOID_PTR)134},
  {"rr",		(VOID_PTR)IPOPT_RR},
  {"sid",		(VOID_PTR)136},
  {"ssr",		(VOID_PTR)IPOPT_SSR},
  {"zsu",		(VOID_PTR)10},
  {"mtup",		(VOID_PTR)11},
  {"mtur",		(VOID_PTR)12},
  {"finn",		(VOID_PTR)205},
  {"visa",		(VOID_PTR)142},
  {"encode",		(VOID_PTR)15},
  {"imitd",		(VOID_PTR)144},
  {"eip",		(VOID_PTR)145},
  {"tr",		(VOID_PTR)82},
  {"addext",		(VOID_PTR)147},
  {NULL,		0},
};

t_field				ipopt_simple_fields[] = 
{
  {"code",	0,	&u8_pat,		NULL},
  {"Code",	0,	&u8assoc_pat,	(VOID_PTR)ipopt_assocs},
  NULL_FIELD
};

t_field				ipopt_double_fields[] = 
{
  {"code",	0,	&u8_pat,		NULL},
  {"Code",	0,	&u8assoc_pat,	(VOID_PTR)ipopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  NULL_FIELD
};

t_field				ipopt_rr_fields[] = 
{
  {"code",	0,	&u8_pat,		NULL},
  {"Code",	0,	&u8assoc_pat,	(VOID_PTR)ipopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  {"ptr",	2,	&u8_pat,		NULL},
  NULL_FIELD
};

t_bit_field		ipopt_ts_of_bit_field =
{
  0,3
};	

t_bit_field		ipopt_ts_fl_bit_field =
{
  4,7
};	

t_assoc			ipopt_ts_fl_assocs[] = 
{
  {"ts_only",		(VOID_PTR)0},
  {"both",		(VOID_PTR)1},
  {"match",		(VOID_PTR)2},
  {NULL,		NULL},	
};

t_bit_field_assocs_data	ipopt_ts_fl_bfad = 
{
  &ipopt_ts_fl_bit_field,
  ipopt_ts_fl_assocs
};

t_field				ipopt_ts_fields[] = 
{
  {"code",	0,	&u8_pat,		NULL},
  {"Code",	0,	&u8assoc_pat,	(VOID_PTR)ipopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  {"ptr",	2,	&u8_pat,		NULL},
  {"of",	3,&u8bitfield_pat,	(VOID_PTR)(&ipopt_ts_of_bit_field)},
  {"fl",	3,&u8bitfield_pat,	(VOID_PTR)(&ipopt_ts_fl_bit_field)},
  {"Fl",	3,&u8bitfieldassoc_pat,(VOID_PTR)(&ipopt_ts_fl_bfad)},
  NULL_FIELD
};

t_field			*ipopt_get_fields(buf,len,status)
char			*buf;
int			len;
t_status		*status;
{
  if (len < 1)
    {
      *status = ERR_PAT_TRUNC_PAT;
      return (NULL);
    }
  switch ((t_u8)(buf[0]))
    {
    case IPOPT_EOOL:
    case IPOPT_NOP:
      return (ipopt_simple_fields);
    case IPOPT_RR:
    case IPOPT_LSR:
    case IPOPT_SSR:
      return (ipopt_rr_fields);
    case IPOPT_TS:
      return (ipopt_ts_fields);
    default:
      return (ipopt_double_fields);
    }
}

char				*ipopt_simple_itmpl = "\n\
<table _name=ipopt[%i%] bgcolor=\"%%ipoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].Code)\">%%ipopt[%i%].Code%%</a>\n\
(<a href=\"set(ipopt[%i%].code)\">%%ipopt[%i%].code%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*ipopt_double_itmpl = "\n\
<table _name=ipopt[%i%] bgcolor=\"%%ipoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].Code)\">%%ipopt[%i%].Code%%</a>\n\
(<a href=\"set(ipopt[%i%].code)\">%%ipopt[%i%].code%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=50%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].len)\">%%ipopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*ipopt_rr_itmpl = "\n\
<table _name=ipopt[%i%] bgcolor=\"%%ipoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].Code)\">%%ipopt[%i%].Code%%</a>\n\
(<a href=\"set(ipopt[%i%].code)\">%%ipopt[%i%].code%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].len)\">%%ipopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].ptr)\">%%ipopt[%i%].ptr%%</a>\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*ipopt_ts_itmpl = "\n\
<table _name=ipopt[%i%] bgcolor=\"%%ipoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=25%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].Code)\">%%ipopt[%i%].Code%%</a>\n\
(<a href=\"set(ipopt[%i%].code)\">%%ipopt[%i%].code%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=25%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].len)\">%%ipopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
<td align=center width=25%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].ptr)\">%%ipopt[%i%].ptr%%</a>\n\
</small>\n\
</td>\n\
<td align=center width=12%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].of)\">%%ipopt[%i%].of%%</a>\n\
</small>\n\
</td>\n\
<td align=center width=13%%%%>\n\
<small>\n\
<a href=\"set(ipopt[%i%].Fl)\">%%ipopt[%i%].Fl%%</a>\n\
(<a href=\"set(ipopt[%i%].fl)\">%%ipopt[%i%].fl%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char			*ipopt_get_itmpl(buf,len,status)
char			*buf;
int			len;
t_status		*status;
{
  if (len < 1)
    {
      *status = ERR_PAT_TRUNC_PAT;
      return (NULL);
    }
  switch ((t_u8)(buf[0]))
    {
    case IPOPT_EOOL:
    case IPOPT_NOP:
      return (ipopt_simple_itmpl);
    case IPOPT_RR:
    case IPOPT_LSR:
    case IPOPT_SSR:
      return (ipopt_rr_itmpl);
    case IPOPT_TS:
      return (ipopt_ts_itmpl);
    default:
      return (ipopt_double_itmpl);
    }
}

int	ipopt_chan;

PAT_NAME_GENERIC(ipopt_pat_name,
		 (VOID_PTR)&ipopt_chan,
		 "ipopt")

PAT_HAS_OPT_DECL(ipopt_pat_has_opt)
{
  if (len < 1)
    return (ERR_PAT_TRUNC_PAT);
  switch ((t_u8)(buf[0]))
    {
    case IPOPT_RR:
    case IPOPT_LSR:
    case IPOPT_SSR:
      {
	if (len < 2)
	  return (ERR_PAT_TRUNC_PAT);
	(*has_opt_return) = TRUE;
	(*opt_off_return) = 3;
	if ((t_u8)(buf[1]) < 3)
	  {
	    (*has_opt_return) = FALSE;
	    return (0);
	  }
	(*opt_len_return) = (t_u8)(buf[1]) - 3;
	(*opt_pat_return) = &inaddr_pat;
	return (0);
      }
    case IPOPT_TS:
      {
	t_u8		fl;

	if (len < 4)
	  return (ERR_PAT_TRUNC_PAT);
	(*has_opt_return) = TRUE;
	(*opt_off_return) = 4;
	if ((t_u8)(buf[1]) < 4)
	  {
	    (*has_opt_return) = FALSE;
	    return (0);
	  }
	(*opt_len_return) = (t_u8)(buf[1]) - 4;
	bit_field_u8_get((t_u8 *)(buf + 3),
			 &fl,
			 ipopt_ts_fl_bit_field.from,
			 ipopt_ts_fl_bit_field.to);
	if (fl == 0)
	  (*opt_pat_return) = &time_pat;
	else
	  (*opt_pat_return) = &inaddrtime_pat;
	return (0);
      }
    default:
      {
	(*has_opt_return) = FALSE;
	return (0);
      }
    }
}

PAT_OFF_DECL(ipopt_pat_off)
{
  if (len < 1)
    return (ERR_PAT_TRUNC_PAT);
  switch ((t_u8)(buf[0]))
    {
    case IPOPT_EOOL:
    case IPOPT_NOP:
      (*off_return) = 1;
      return (0);
    }
  if (len < 2)
    return (ERR_PAT_TRUNC_PAT);
  (*off_return) = (t_off)(t_u8)(buf[1]);
  return (0);
}

PAT_SUB_DECL(ipopt_pat_sub)
{
  if (len < 1)
    return (ERR_PAT_TRUNC_PAT);
  switch ((t_u8)(buf[0]))
    {
    case IPOPT_EOOL:
      (*pat_return) = &ipoptdata_pat;
      return (0);
    }
  (*pat_return) = &ipopt_pat;
  return (0);
}

PAT_GET_FIELD_DECL(ipopt_pat_get_field)
{
  t_field			*fields;
  t_field			*fieldptr;
  t_status			status;

  if ((fields = ipopt_get_fields(buf,
				 len,
				 &status)) == NULL)
    return (status);
  fieldptr = NULL;
  while (fields->name)
    {
      if (!strcmp(fields->name,field))
	fieldptr = fields;
      fields++;
    }
  if (!fieldptr)
    return (ERR_PAT_NO_SUCH_FIELD);
  return (get_field_to_str(buf,
			   len,
			   fieldptr,
			   str,
			   max_len));
}

PAT_SET_FIELD_DECL(ipopt_pat_set_field)
{
  t_field			*fields;
  t_field			*fieldptr;
  t_status			status;

  if ((fields = ipopt_get_fields(buf,
				 len,
				 &status)) == NULL)
    return (status);
  fieldptr = NULL;
  while (fields->name)
    {
      if (!strcmp(fields->name,field))
	fieldptr = fields;
      fields++;
    }
  if (!fieldptr)
    return (ERR_PAT_NO_SUCH_FIELD);
  return (set_field_from_str(buf,
			     len,
			     fieldptr,
			     value));
}

PAT_GET_FIELD_PAT_DECL(ipopt_pat_get_field_pat)
{
  t_field			*fields;	
  t_status			status;
  
  if ((fields = ipopt_get_fields(buf,
				 len,
				 &status)) == NULL)
    return (status);
  while (fields->name)
    {
      if (!strcmp(field,fields->name))
	{
	  (*pat_return) = fields->pat;
	  (*data_return) = fields->data;
	  return (0);
	}
      fields++;
    }
  return (ERR_PAT_NO_SUCH_FIELD);
}

PAT_GET_TMPL_DECL(ipopt_pat_get_tmpl)
{
  char		*itmpl;
  t_hash_elt	*he;
  int		i;
  t_status	status;

  if ((itmpl = ipopt_get_itmpl(buf,
			       len,
			       &status)) == NULL)
    return (status);
  if (he = id_get(id,
		  (VOID_PTR)&ipopt_chan))
    i = (int)(he->value);
  else
    i = 0;
  if ((status = itmpl_format(i,
			     itmpl,
			     str,
			     max_len)) != 0)
    return (status);
  if ((status = id_override(id,
			    (VOID_PTR)&ipopt_chan,
			    (VOID_PTR)(++i))) != 0)
    return (status);
  return (0);
}
    
t_pat				ipopt_pat = 
{
  ipopt_pat_name,		/* t_pat_name_proc		*/
  ipopt_pat_off,		/* t_pat_off_proc		*/
  ipopt_pat_sub,		/* t_pat_sub_proc		*/
  NULL,				/* t_pat_sum_proc		*/
  ipopt_pat_get_field,		/* t_pat_get_field_proc		*/
  ipopt_pat_set_field,		/* t_pat_set_field_proc		*/
  NULL,				/* t_pat_get_fields_proc	*/
  ipopt_pat_get_tmpl,		/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  ipopt_pat_has_opt,		/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  ipopt_pat_get_field_pat,	/* t_pat_get_field_pat_proc	*/
  NULL,				/* t_pat_extract_proc		*/
  NULL,				/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};

t_assoc			ipfrag_assocs[] =
{
  {"mf",		(VOID_PTR)IPFRAG_MF},
  {"df",		(VOID_PTR)IPFRAG_DF},
  {NULL,		NULL},
};

/*
TOS Value       Description                             Reference
---------       --------------------------              ---------
  0000          Default                                 [RFC1349]
  0001          Minimize Monetary Cost                  [RFC1349]
  0010          Maximize Reliability                    [RFC1349]
  0100          Maximize Throughput                     [RFC1349]
  1000          Minimize Delay                          [RFC1349]
  1111          Maximize Security                       [RFC1455]
  */

t_assoc			iptos_assocs[] = 
{
  {"default",			(VOID_PTR)0x0},
  {"minimize_monetary_cost",	(VOID_PTR)0x1},
  {"maximize_reliability",	(VOID_PTR)0x2},
  {"maximize_throughput",	(VOID_PTR)0x4},
  {"minimize_delay",		(VOID_PTR)0x8},
  {"maximize_security",		(VOID_PTR)0x17},
  {NULL,		NULL}
};

t_bit_field		ip_v_bit_field =
{
  0,3
};	

t_bit_field		ip_hl_bit_field =
{
  4,7
};	

t_bit_field		ip_frag_bit_field = 
{
  0,2
};

t_bit_field_assocs_data	ip_frag_bfad = 
{
  &ip_frag_bit_field,
  ipfrag_assocs
};

t_bit_field		ip_off_bit_field =
{
  3,15
};

/* computes an internet checksum.
   It is portable and deals with alignment.
   Returns the checksum. */
t_u16			in_cksum(buf,nwords)
t_u16			*buf;
int			nwords;
{
  unsigned long		sum;
   
  sum = 0;
  while (nwords > 0)
    {
      t_u16		u16;
      
      FBCOPY(buf,&u16,sizeof (u16)); /* alignment */
      sum += UNSAFE_HTONS(u16);
      buf++;
      nwords--;
    }
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ((t_u16)(~sum));
}

/* computes ip checksum. 
   It deals with alignment.
   It calls in_cksum(3). */
VOID_FUNC		ip_compute_sum(ip)
t_ip			*ip;
{
  t_u16			zero;

  zero = 0;
  FBCOPY(&zero,&(ip->sum),sizeof (zero)); /* alignment */
  safe_htons(in_cksum((t_u16 *)ip,ip_get_hl(ip) * 2),&(ip->sum));
}

/* sets ip version.
   Note: deals with alignment */
VOID_FUNC		ip_set_v(ip,v)
t_ip			*ip;
int			v;
{
  t_u8			u8;

  FBCOPY(&ip->vhl,&u8,sizeof (u8));
  bit_field_u8_set(&u8,
		   ip_v_bit_field.from,
		   ip_v_bit_field.to,
		   v);
  FBCOPY(&u8,&ip->vhl,sizeof (u8));
}

/* gets ip version.
   Note: deals with alignment.
   Returns version. */
int			ip_get_v(ip)
t_ip			*ip;
{
  t_u8			vp;
  t_u8			u8;

  FBCOPY(&ip->vhl,&u8,sizeof (u8));
  bit_field_u8_get(&u8,
		   &vp,
		   ip_v_bit_field.from,
		   ip_v_bit_field.to);
  return ((int)vp);
}

/* sets ip header length.
   Note: deals with alignment. */
VOID_FUNC		ip_set_hl(ip,hl)
t_ip			*ip;
int			hl;
{
  t_u8			u8;
  
  FBCOPY(&ip->vhl,&u8,sizeof (u8));
  bit_field_u8_set(&u8,
		   ip_hl_bit_field.from,
		   ip_hl_bit_field.to,
		   hl);
  FBCOPY(&u8,&ip->vhl,sizeof (u8));
}

/* gets ip header length.
   Note: deals with alignment.
   Returns ip header length */
int			ip_get_hl(ip)
t_ip			*ip;
{
  t_u8			hl;
  t_u8			u8;

  FBCOPY(&ip->vhl,&u8,sizeof (u8));
  bit_field_u8_get(&u8,
		   &hl,
		   ip_hl_bit_field.from,
		   ip_hl_bit_field.to);
  return ((int)hl);
}

/* sets ip tos.
   Note: deals with alignment */
VOID_FUNC		ip_set_tos(ip,tos)
t_ip			*ip;
int			tos;
{
  t_u8			u8;

  u8 = (t_u8)tos;
  FBCOPY(&u8,&ip->tos,sizeof (char)); /* alignment */ 
}

/* returns ip tos.
   Note: deals with alignment */
int			ip_get_tos(ip)
t_ip			*ip;
{
  int			tos;

  FBCOPY(&ip->tos,&tos,sizeof (char)); /* alignment */
  return ((int)tos);
}

/* sets ip length.
   Note: deals with alignment */
VOID_FUNC		ip_set_len(ip,len)
t_ip			*ip;
int			len;
{
  safe_htons(len,&(ip->len));
}

/* gets ip length.
   Note: deals with alignment.
   Returns ip length. */
int			ip_get_len(ip)
t_ip			*ip;
{
  return (safe_ntohs(&ip->len));
}

/* sets ip id.
   Note: deals with alignment */
VOID_FUNC		ip_set_id(ip,id)
t_ip			*ip;
int			id;
{
  safe_htons(id,&ip->id);
}

/* gets ip id.
   Note: deals with alignment. 
   Returns ip id */
int			ip_get_id(ip)
t_ip			*ip;
{
  return (safe_ntohs(&ip->id));
}

/* sets ip off.
   Note: deals with alignment. */ 
VOID_FUNC		ip_set_off(ip,off)
t_ip			*ip;
int			off;
{
  t_u16			u16;

  FBCOPY(&ip->fragoff,&u16,sizeof (t_u16)); /* alignment */
  bit_field_u16_set(&u16,
		    ip_off_bit_field.from,
		    ip_off_bit_field.to,
		    off);
  FBCOPY(&u16,&ip->fragoff,sizeof (t_u16)); /* alignment */
}

/* gets ip off.
   Note: deals with alignement.
   Return ip off. */
int			ip_get_off(ip)
t_ip			*ip;
{
  t_u16			off;
  t_u16			u16;

  FBCOPY(&ip->fragoff,&u16,sizeof (t_u16)); /* alignment */
  bit_field_u16_get(&u16,
		    &off,
		    ip_off_bit_field.from,
		    ip_off_bit_field.to);
  return ((int)off);
}

/* sets ip ttl.
   Note: deals with alignment */
VOID_FUNC		ip_set_ttl(ip,ttl)
t_ip			*ip;
int			ttl;
{
  t_u8			u8;

  u8 = (t_u8)ttl;
  FBCOPY(&u8,&ip->ttl,sizeof (char)); /* alignment */ 
}

/* gets ip ttl.
   Note: deals with alignment */
int			ip_get_ttl(ip)
t_ip			*ip;
{
  int			ttl;

  FBCOPY(&ip->ttl,&ttl,sizeof (char)); /* alignment */
  return ((int)ttl);
}

/* sets ip proto.
   Note: deals with alignment */
VOID_FUNC		ip_set_p(ip,p)
t_ip			*ip;
int			p;
{
  t_u8			u8;

  u8 = (t_u8)p;
  FBCOPY(&u8,&ip->p,sizeof (char)); /* alignment */ 
}

/* gets ip proto.
   Note: deqls with alignment */
int			ip_get_p(ip)
t_ip			*ip;
{
  t_u8			p;

  FBCOPY(&ip->p,&p,sizeof (char)); /* alignment */
  return ((int)p);
}

/* sets ip sum.
   Note: deals with alignment */
VOID_FUNC		ip_set_sum(ip,sum)
t_ip			*ip;
int			sum;
{
  safe_htons(sum,&ip->sum);
}

/* gets ip sum.
   Note: deals with alignement */
int			ip_get_sum(ip)
t_ip			*ip;
{
  return (safe_ntohs(&ip->sum));
}

/* sets ip src.
   Note: deals with alignment */
VOID_FUNC		ip_set_src(ip,src)
t_ip			*ip;
t_in_addr		*src;
{
  FBCOPY(src,&(ip->src),sizeof (struct in_addr));
}

/* gets ip dst.
   Note: deals with alignment */
VOID_FUNC		ip_get_src(ip,src)
t_ip			*ip;
t_in_addr		*src;
{
  FBCOPY(&(ip->src),src,sizeof (struct in_addr));
}

/* sets ip dst.
   Note: deals with alignment */
VOID_FUNC		ip_set_dst(ip,dst)
t_ip			*ip;
t_in_addr		*dst;
{
  FBCOPY(dst,&(ip->dst),sizeof (struct in_addr));
}

/* gets ip dst.
   Note: deals with alignment */
VOID_FUNC		ip_get_dst(ip,dst)
t_ip			*ip;
t_in_addr		*dst;
{
  FBCOPY(&(ip->dst),dst,sizeof (struct in_addr));
}

t_field				ip_fields[] = 
{
  {"hl",	0,	         &u8bitfield_pat,(VOID_PTR)(&ip_hl_bit_field)},
  {"v",		0,	          &u8bitfield_pat,(VOID_PTR)(&ip_v_bit_field)},
  {"tos",	OFFSET(t_ip *,tos),	&u8_pat,		NULL},
  {"Tos",	OFFSET(t_ip *,tos),	&u8assoc_pat,	iptos_assocs},
  {"len",	OFFSET(t_ip *,len),	&nu16_pat,		NULL},
  {"id",	OFFSET(t_ip *,id),	&nu16_pat,		NULL},
  {"frag",	OFFSET(t_ip *,fragoff),
   &nu16bitfield_pat,	(VOID_PTR)(&ip_frag_bit_field)},
  {"Frag",	OFFSET(t_ip *,fragoff),
   &nu16bitfieldassoc_pat,	(VOID_PTR)(&ip_frag_bfad)},
  {"off",	OFFSET(t_ip *,fragoff),
   &nu16bitfield_pat,	(VOID_PTR)(&ip_off_bit_field)},
  {"ttl",	OFFSET(t_ip *,ttl),	&u8_pat,		NULL},
  {"p",		OFFSET(t_ip *,p),	&u8_pat,		NULL},
  {"P",		OFFSET(t_ip *,p),	&ipproto_pat,	NULL},
  {"sum",	OFFSET(t_ip *,sum),	&nu16_pat,		NULL},
  {"src",	OFFSET(t_ip *,src),	&inaddr_pat,		NULL},
  {"Src",	OFFSET(t_ip *,src),	&inaddr_resolved_pat,NULL},
  {"dst",	OFFSET(t_ip *,dst),	&inaddr_pat,		NULL},
  {"Dst",	OFFSET(t_ip *,dst),	&inaddr_resolved_pat,NULL},
  NULL_FIELD
};

char				*ip_itmpl = "\n\
<table _name=ip[%i%] width=100%%%% bgcolor=\"%%ipColor%%\">\n\
<tr>\n\
<td align=center width=12%%%%>\n\
<a href=\"set(ip[%i%].v)\">%%ip[%i%].v%%</a>\n\
</td>\n\
<td align=center width=13%%%%>\n\
<a href=\"set(ip[%i%].hl)\">%%ip[%i%].hl%%</a>\n\
</td>\n\
<td align=center width=25%%%%>\n\
<_tiny>\n\
<a href=\"set(ip[%i%].Tos)\">%%ip[%i%].Tos%%</a>\n\
</_tiny>\n\
(<a href=\"set(ip[%i%].tos)\">%%ip[%i%].tos%%</a>)\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(ip[%i%].len)\">%%ip[%i%].len%%</a>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(ip[%i%].id)\">%%ip[%i%].id%%</a>\n\
</td>\n\
<td align=center width=25%%%%>\n\
<a href=\"set(ip[%i%].Frag)\">%%ip[%i%].Frag%%</a>\n\
(<a href=\"set(ip[%i%].frag)\">%%ip[%i%].frag%%</a>)\n\
</td>\n\
<td align=center width=25%%%%>\n\
<a href=\"set(ip[%i%].off)\">%%ip[%i%].off%%</a>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=25%%%%>\n\
<a href=\"set(ip[%i%].ttl)\">%%ip[%i%].ttl%%</a>\n\
</td>\n\
<td align=center width=25%%%%>\n\
<a href=\"set(ip[%i%].P)\">%%ip[%i%].P%%</a>\n\
(<a href=\"set(ip[%i%].p)\">%%ip[%i%].p%%</a>)\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(ip[%i%].sum)\">%%ip[%i%].sum%%</a>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<a href=\"set(ip[%i%].Src)\">%%ip[%i%].Src%%</a>\n\
(<a href=\"set(ip[%i%].src)\">%%ip[%i%].src%%</a>)\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<a href=\"set(ip[%i%].Dst)\">%%ip[%i%].Dst%%</a>\n\
(<a href=\"set(ip[%i%].dst)\">%%ip[%i%].dst%%</a>)\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

int	pat_ip_src_chan;
int	pat_ip_dst_chan;
int	ip_chan;

PAT_NAME_GENERIC(ip_pat_name,
		 (VOID_PTR)&ip_chan,
		 "ip")

PAT_GET_FIELD_GENERIC(ip_pat_get_field,
		      ip_fields)

PAT_SET_FIELD_GENERIC(ip_pat_set_field,
		      ip_fields)

PAT_GET_FIELDS_GENERIC(ip_pat_get_fields,
		       ip_fields)

PAT_GET_FIELD_PAT_GENERIC(ip_pat_get_field_pat,
			  ip_fields)

PAT_GET_TMPL_GENERIC(ip_pat_get_tmpl,
		     (VOID_PTR)&ip_chan,
		     ip_itmpl)

PAT_OFF_DECL(ip_pat_off)
{
  t_ip			*ip;

  ip = (t_ip *)buf;
  PAT_IP_CHECK(ip,buf,len);
  (*off_return) = ip_get_hl(ip) * 4;
  return (0);
}

PAT_SUB_DECL(ip_pat_sub)
{
  t_ip			*ip;
  int			p;
  t_status		status;	
  
  ip = (t_ip *)buf;
  PAT_IP_CHECK(ip,buf,len);
  if (optional_id)
    {
      t_hash_elt	*he;
      t_u32		inaddr;
      
      FBCOPY(&(ip->src),&inaddr,sizeof (inaddr));
      if ((status = id_override(optional_id,
				(VOID_PTR)&pat_ip_src_chan,
				(VOID_PTR)inaddr)) != 0)
	return (status);
      FBCOPY(&(ip->dst),&inaddr,sizeof (inaddr));
      if ((status = id_override(optional_id,
				(VOID_PTR)&pat_ip_dst_chan,
				(VOID_PTR)inaddr)) != 0)
	return (status);
    }
  switch (p = ip_get_p(ip))
    {
    case IP_PROTO_ICMP:
      (*pat_return) = &icmp_pat;
      return (0);
    case IP_PROTO_UDP:
      (*pat_return) = &udp_pat;
      return (0);
    case IP_PROTO_TCP:
      (*pat_return) = &tcp_pat;
      return (0);
    case IP_PROTO_IGMP:
      (*pat_return) = &igmp_pat;
      return (0);
    case 0:
      (*pat_return) = &data_pat;
      return (0);
    }
  if (pat_ip_sub_id)
    {
      t_hash_elt	*he;
      
      if (he = id_get(pat_ip_sub_id,
		      (VOID_PTR)(t_u32)p))
	{
	  (*pat_return) = (t_pat *)(he->value);
	  return (0);
	}
    }
  (*pat_return) = &data_pat;
  return (0);
}

PAT_SUM_DECL(ip_pat_sum)
{
  t_ip			*ip;

  PAT_IP_CHECK(ip,buf,len);
  if (ip_get_len(ip) != len)
    return (ERR_PAT_BAD_LEN);
  if (ip_get_hl(ip) * 4 < IP_MINHLEN)
    return (ERR_PAT_BAD_HEADER);
  ip_compute_sum(ip);
  return (0);
}

PAT_ADAPT_LEN_DECL(ip_pat_adapt_len)
{
  t_ip			*ip;

  PAT_IP_CHECK(ip,buf,len);
  ip_set_len(ip,len);
  return (0);
}

PAT_HAS_OPT_DECL(ip_pat_has_opt)
{
  t_ip			*ip;

  PAT_IP_CHECK(ip,buf,len);
  if (ip_get_hl(ip) * 4 > IP_MINHLEN)
    {
      (*has_opt_return) = TRUE;
      (*opt_off_return) = IP_MINHLEN;
      (*opt_len_return) = ip_get_hl(ip) * 4 - IP_MINHLEN;
      (*opt_pat_return) = &ipopt_pat;
    }
  else
    {
      (*has_opt_return) = FALSE;
    }
  return (0);
}

t_pat				ip_pat = 
{
  ip_pat_name,			/* t_pat_name_proc		*/
  ip_pat_off,			/* t_pat_off_proc		*/
  ip_pat_sub,			/* t_pat_sub_proc		*/
  ip_pat_sum,			/* t_pat_sum_proc		*/
  ip_pat_get_field,		/* t_pat_get_field_proc		*/
  ip_pat_set_field,		/* t_pat_set_field_proc		*/
  ip_pat_get_fields,		/* t_pat_get_fields_proc	*/
  ip_pat_get_tmpl,		/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  ip_pat_has_opt,		/* t_pat_has_opt_proc		*/
  ip_pat_adapt_len,		/* t_pat_adapt_len_proc		*/
  ip_pat_get_field_pat,		/* t_pat_get_field_pat_proc	*/
  NULL,				/* t_pat_extract_proc		*/
  NULL,				/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};
