/*
** pat_tcp.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 06:41:04 1999 
** Last update Thu Oct 28 20:19:30 1999 
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "pat_tcp.h"
#include "pat_ip.h"
#include "pat_data.h"
#include "pat_8.h"
#include "pat_16.h"
#include "pat_32.h"
#include "pat_port.h"
#include "pat_seqack.h"
#include "pat_time.h"

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

int	tcpoptdata_chan;

PAT_NAME_GENERIC(tcpoptdata_pat_name,
		 (VOID_PTR)&tcpoptdata_chan,
		 "tcpoptdata")

PAT_GET_TMPL_GENERIC(tcpoptdata_pat_get_tmpl,
		     (VOID_PTR)&tcpoptdata_chan,
		     tcpoptdata_itmpl)

t_pat				tcpoptdata_pat = 
{
  tcpoptdata_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	*/
  tcpoptdata_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	*/
};

/*
Kind   Length   Meaning                           Reference
----   ------   -------------------------------   ---------
  0        -    End of Option List                 [RFC793]
  1        -    No-Operation                       [RFC793]
  2        4    Maximum Segment Lifetime           [RFC793]
  3        3    WSOPT - Window Scale              [RFC1323]
  4        2    SACK Permitted                    [RFC1072]
  5        N    SACK                              [RFC1072]
  6        6    Echo (obsoleted by option 8)      [RFC1072]
  7        6    Echo Reply (obsoleted by option 8)[RFC1072]
  8       10    TSOPT - Time Stamp Option         [RFC1323]
  9        2    Partial Order Connection Permitted[RFC1693]
 10        5    Partial Order Service Profile     [RFC1693]
 11             CC                                 [Braden]
 12             CC.NEW                             [Braden]
 13             CC.ECHO                            [Braden]
 14         3   TCP Alternate Checksum Request    [RFC1146]
 15         N   TCP Alternate Checksum Data       [RFC1146]
 16             Skeeter                           [Knowles]
 17             Bubba                             [Knowles]
 18         3   Trailer Checksum Option    [Subbu & Monroe]


TCP ALTERNATE CHECKSUM NUMBERS


Number  Description                     Reference
------- ------------------------------- ----------
   0    TCP Checksum                    [RFC-1146]
   1    8-bit Fletchers's algorithm     [RFC-1146]
   2    16-bit Fletchers's algorithm    [RFC-1146]
   3    Redundant Checksum Avoidance    [Kay]
*/

t_assoc				tcpopt_assocs[] = 
{
  {"eool",			(VOID_PTR)TCPOPT_EOOL},
  {"nop",			(VOID_PTR)TCPOPT_NOP},
  {"mss",			(VOID_PTR)TCPOPT_MSS},
  {"win",			(VOID_PTR)TCPOPT_WIN},
  {"ts",			(VOID_PTR)TCPOPT_TS},
  {NULL,			0},
};

t_field				tcpopt_simple_fields[] = 
{
  {"kind",	0,	&u8_pat,		NULL},
  {"Kind",	0,	&u8assoc_pat,	(VOID_PTR)tcpopt_assocs},
  NULL_FIELD
};

t_field				tcpopt_double_fields[] = 
{
  {"kind",	0,	&u8_pat,		NULL},
  {"Kind",	0,	&u8assoc_pat,	(VOID_PTR)tcpopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  NULL_FIELD
};

t_field				tcpopt_mss_fields[] = 
{
  {"kind",	0,	&u8_pat,		NULL},
  {"Kind",	0,	&u8assoc_pat,	(VOID_PTR)tcpopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  {"mss",	2,	&nu16_pat,		NULL},
  NULL_FIELD
};

t_field				tcpopt_win_fields[] = 
{
  {"kind",	0,	&u8_pat,		NULL},
  {"Kind",	0,	&u8assoc_pat,	(VOID_PTR)tcpopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  {"shift",	2,	&u8_pat,		NULL},
  NULL_FIELD
};

t_field				tcpopt_ts_fields[] = 
{
  {"kind",	0,	&u8_pat,		NULL},
  {"Kind",	0,	&u8assoc_pat,	(VOID_PTR)tcpopt_assocs},
  {"len",	1,	&u8_pat,		NULL},
  {"ts",	2,	&nu32_pat,		NULL},
  {"Ts",	2,	&time_pat,		NULL},
  {"tsreply",	6,	&nu32_pat,		NULL},
  {"Tsreply",	6,	&time_pat,		NULL},
  NULL_FIELD
};

t_field			*tcpopt_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 TCPOPT_EOOL:
    case TCPOPT_NOP:
      return (tcpopt_simple_fields);
    case TCPOPT_MSS:
      return (tcpopt_mss_fields);
    case TCPOPT_WIN:
      return (tcpopt_win_fields);
    case TCPOPT_TS:
      return (tcpopt_ts_fields);
    default:
      return (tcpopt_double_fields);
    }
}

char				*tcpopt_simple_itmpl = "\n\
<table _name=tcpopt[%i%] bgcolor=\"%%tcpoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Kind)\">%%tcpopt[%i%].Kind%%</a>\n\
(<a href=\"set(tcpopt[%i%].kind)\">%%tcpopt[%i%].kind%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*tcpopt_double_itmpl = "\n\
<table _name=tcpopt[%i%] bgcolor=\"%%tcpoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Kind)\">%%tcpopt[%i%].Kind%%</a>\n\
(<a href=\"set(tcpopt[%i%].kind)\">%%tcpopt[%i%].kind%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=50%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].len)\">%%tcpopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*tcpopt_mss_itmpl = "\n\
<table _name=tcpopt[%i%] bgcolor=\"%%tcpoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Kind)\">%%tcpopt[%i%].Kind%%</a>\n\
(<a href=\"set(tcpopt[%i%].kind)\">%%tcpopt[%i%].kind%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].len)\">%%tcpopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].mss)\">%%tcpopt[%i%].mss%%</a>\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*tcpopt_win_itmpl = "\n\
<table _name=tcpopt[%i%] bgcolor=\"%%tcpoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Kind)\">%%tcpopt[%i%].Kind%%</a>\n\
(<a href=\"set(tcpopt[%i%].kind)\">%%tcpopt[%i%].kind%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].len)\">%%tcpopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
<td align=center width=33%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].shift)\">%%tcpopt[%i%].shift%%</a>\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char				*tcpopt_ts_itmpl = "\n\
<table _name=tcpopt[%i%] bgcolor=\"%%tcpoptColor%%\" width=100%%%%>\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Kind)\">%%tcpopt[%i%].Kind%%</a>\n\
(<a href=\"set(tcpopt[%i%].kind)\">%%tcpopt[%i%].kind%%</a>)\n\
</small>\n\
</td>\n\
<td align=center width=50%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].len)\">%%tcpopt[%i%].len%%</a>\n\
</small>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Ts)\">%%tcpopt[%i%].Ts%%</a>(<a href=\"set(tcpopt[%i%].ts)\">%%tcpopt[%i%].ts%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<small>\n\
<a href=\"set(tcpopt[%i%].Tsreply)\">%%tcpopt[%i%].Tsreply%%</a>\n\
(<a href=\"set(tcpopt[%i%].tsreply)\">%%tcpopt[%i%].tsreply%%</a>)\n\
</small>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

char			*tcpopt_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 TCPOPT_EOOL:
    case TCPOPT_NOP:
      return (tcpopt_simple_itmpl);
    case TCPOPT_MSS:
      return (tcpopt_mss_itmpl);
    case TCPOPT_WIN:
      return (tcpopt_win_itmpl);
    case TCPOPT_TS:
      return (tcpopt_ts_itmpl);
    default:
      return (tcpopt_double_itmpl);
    }
}

int	tcpopt_chan;

PAT_NAME_GENERIC(tcpopt_pat_name,
		 (VOID_PTR)&tcpopt_chan,
		 "tcpopt")

PAT_OFF_DECL(tcpopt_pat_off)
{
  if (len < 1)
    return (ERR_PAT_TRUNC_PAT);
  switch ((t_u8)(buf[0]))
    {
    case TCPOPT_EOOL:
    case TCPOPT_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(tcpopt_pat_sub)
{
  if (len < 1)
    return (ERR_PAT_TRUNC_PAT);
  switch ((t_u8)(buf[0]))
    {
    case TCPOPT_EOOL:
      (*pat_return) = &tcpoptdata_pat;
      return (0);
    }
  (*pat_return) = &tcpopt_pat;
  return (0);
}

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

  if ((fields = tcpopt_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(tcpopt_pat_set_field)
{
  t_field			*fields;
  t_field			*fieldptr;
  t_status			status;

  if ((fields = tcpopt_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(tcpopt_pat_get_field_pat)
{
  t_field			*fields;	
  t_status			status;

  if ((fields = tcpopt_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(tcpopt_pat_get_tmpl)
{
  char		*itmpl;
  t_hash_elt	*he;
  int		i;
  t_status	status;

  if ((itmpl = tcpopt_get_itmpl(buf,
				len,
				&status)) == NULL)
    return (status);
  if (he = id_get(id,
		  (VOID_PTR)&tcpopt_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)&tcpopt_chan,
			    (VOID_PTR)(++i))) != 0)
    return (status);
  return (0);
}

PAT_HAS_OPT_DECL(tcpopt_pat_has_opt)
{
  if (len < 1)
    return (ERR_PAT_TRUNC_PAT);
  (*has_opt_return) = FALSE;
  return (0);
}

t_pat				tcpopt_pat = 
{
  tcpopt_pat_name,		/* t_pat_name_proc		*/
  tcpopt_pat_off,		/* t_pat_off_proc		*/
  tcpopt_pat_sub,		/* t_pat_sub_proc		*/
  NULL,				/* t_pat_sum_proc		*/
  tcpopt_pat_get_field,		/* t_pat_get_field_proc		*/
  tcpopt_pat_set_field,		/* t_pat_set_field_proc		*/
  NULL,				/* t_pat_get_fields_proc	*/
  tcpopt_pat_get_tmpl,		/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  tcpopt_pat_has_opt,		/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  tcpopt_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_mask_def		tcpflags_mask_defs[] = 
{
  {"fin",		TCP_FIN},
  {"syn",		TCP_SYN},
  {"rst",		TCP_RST},
  {"push",		TCP_PUSH},
  {"ack",		TCP_ACK},
  {"urg",		TCP_URG},
  NULL_MASK_DEF
};

t_bit_field		tcp_off_bit_field =
{
  0,3
};	

t_bit_field		tcp_x2_bit_field =
{
  4,7
};	

/* computes a tcp checksum.
   It is portable and deals with alignment.
   Returns checksum. */
t_u16			tcp_cksum(ip)
t_ip			*ip;
{
   t_tcp		*tcp;
   t_u16		*sptr;
   t_u16		len;
   unsigned long	sum;
   int			i;
   
   tcp = (t_tcp *)(((char *)ip) + ip_get_hl(ip) * 4);
   sum = 0;
   sptr = (t_u16 *)(&(ip->src));
   i = 0;
   while (i < IP_ALEN)
     {
       t_u16		u16;

       FBCOPY(sptr,&u16,sizeof (u16)); /* alignment */
       sum += UNSAFE_HTONS(u16);
       sptr++;
       i++;
     }
   sptr = (t_u16 *)tcp;
   len = UNSAFE_NTOHS(ip->len) - ip_get_hl(ip) * 4;
   sum += IP_PROTO_TCP + len;
   if (len % 2)
   {
      ((char *)tcp)[len] = 0;
      len += 1;
   }
   len >>= 1;
   i = 0;
   while (i < len)
     {
       t_u16		u16;

       FBCOPY(sptr,&u16,sizeof (u16)); /* alignment */
       sum += UNSAFE_HTONS(u16);
       sptr++;
       i++;
     }
   sum = (sum >> 16) + (sum & 0xffff);
   sum += (sum >> 16);
   return ((t_u16)(~sum & 0xffff));
}

VOID_FUNC		tcp_compute_sum(ip)
t_ip			*ip;
{
   t_tcp		*tcp;
   t_u16		zero;

   tcp = (t_tcp *)(((char *)ip) + ip_get_hl(ip) * 4);
   zero = 0;
   FBCOPY(&zero,&tcp->sum,sizeof (zero)); /* alignment */
   safe_htons(tcp_cksum(ip),&tcp->sum);
}

VOID_FUNC		tcp_set_proto(ip)
t_ip			*ip;
{
  ip->p = IP_PROTO_TCP;
}

VOID_FUNC		tcp_set_sport(tcp,sport)
t_tcp			*tcp;
int			sport;
{
  safe_htons(sport,&tcp->sport);
}

int			tcp_get_sport(tcp)
t_tcp			*tcp;
{
  return (safe_ntohs(&tcp->sport));
}	

VOID_FUNC		tcp_set_dport(tcp,dport)
t_tcp			*tcp;
int			dport;
{
  safe_htons(dport,&tcp->dport);
}

int			tcp_get_dport(tcp)
t_tcp			*tcp;
{
  return (safe_ntohs(&tcp->dport));
}	

VOID_FUNC		tcp_set_seq(tcp,seq)
t_tcp			*tcp;
t_tcp_seq		seq;
{
  safe_htonl(seq,&(tcp->seq));
}

t_tcp_seq		tcp_get_seq(tcp)
t_tcp			*tcp;
{
  return (safe_ntohl(&(tcp->seq)));
}

VOID_FUNC		tcp_set_ack(tcp,ack)
t_tcp			*tcp;
t_tcp_seq		ack;
{
  safe_htonl(ack,&(tcp->ack));
}

t_tcp_seq		tcp_get_ack(tcp)
t_tcp			*tcp;
{
  return (safe_ntohl(&(tcp->ack)));
}

VOID_FUNC		tcp_set_off(tcp,off)
t_tcp			*tcp;
int			off;
{
  bit_field_u8_set(&(tcp->offx2),
		   tcp_off_bit_field.from,
		   tcp_off_bit_field.to,
		   off);
}

int			tcp_get_off(tcp)
t_tcp			*tcp;
{
  t_u8			off;
  
  bit_field_u8_get(&(tcp->offx2),
		   &off,
		   tcp_off_bit_field.from,
		   tcp_off_bit_field.to);
  return ((int)off);
}

VOID_FUNC		tcp_set_x2(tcp,x2)
t_tcp			*tcp;
int			x2;
{
  bit_field_u8_set(&(tcp->offx2),
		   tcp_x2_bit_field.from,
		   tcp_x2_bit_field.to,
		   x2);
}

int			tcp_get_x2(tcp)
t_tcp			*tcp;
{
  t_u8			x2;
  
  bit_field_u8_get(&(tcp->offx2),
		   &x2,
		   tcp_x2_bit_field.from,
		   tcp_x2_bit_field.to);
  return ((int)x2);
}

VOID_FUNC		tcp_set_flags(tcp,flags)
t_tcp			*tcp;
int			flags;
{
  tcp->flags = (u_char)flags;
}

int			tcp_get_flags(tcp)
t_tcp			*tcp;
{
  return (tcp->flags);
}

VOID_FUNC		tcp_set_win(tcp,win)
t_tcp			*tcp;
int			win;
{
  safe_htons(win,&tcp->win);
}

int			tcp_get_win(tcp)
t_tcp			*tcp;
{
  return (safe_ntohs(&tcp->win));
}	

VOID_FUNC		tcp_set_sum(tcp,sum)
t_tcp			*tcp;
int			sum;
{
  safe_htons(sum,&tcp->sum);
}

int			tcp_get_sum(tcp)
t_tcp			*tcp;
{
  return (safe_ntohs(&tcp->sum));
}

VOID_FUNC		tcp_set_urp(tcp,urp)
t_tcp			*tcp;
int			urp;
{
  safe_htons(urp,&tcp->urp);
}

int			tcp_get_urp(tcp)
t_tcp			*tcp;
{
  return (safe_ntohs(&tcp->urp));
}	

t_field				tcp_fields[] = 
{
  {"sport",	OFFSET(t_tcp *,sport),		&nu16_pat,		NULL},
  {"Sport",	OFFSET(t_tcp *,sport),		&port_resolved_pat,	"tcp"},
  {"dport",	OFFSET(t_tcp *,dport),		&nu16_pat,		NULL},
  {"Dport",	OFFSET(t_tcp *,dport),		&port_resolved_pat,	"tcp"},
  {"seq",	OFFSET(t_tcp *,seq),		&nu32_pat,		NULL},
  {"ack",	OFFSET(t_tcp *,ack),		&nu32_pat,		NULL},
  {"x2",	OFFSET(t_tcp *,offx2),
   &u8bitfield_pat,	(VOID_PTR)(&tcp_x2_bit_field)},
  {"off",	OFFSET(t_tcp *,offx2),
   &u8bitfield_pat,	(VOID_PTR)(&tcp_off_bit_field)},
  {"flags",	OFFSET(t_tcp *,flags),		&u8_pat,		NULL},
  {"Flags",	OFFSET(t_tcp *,flags),	 &u8mask_pat,  tcpflags_mask_defs},
  {"win",	OFFSET(t_tcp *,win),		&nu16_pat,		NULL},
  {"sum",	OFFSET(t_tcp *,sum),		&nu16_pat,		NULL},
  {"urp",	OFFSET(t_tcp *,urp),		&nu16_pat,		NULL},
  NULL_FIELD
};

char				*tcp_itmpl = "\n\
<table _name=tcp[%i%] width=100%%%% bgcolor=\"%%tcpColor%%\">\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(tcp[%i%].Sport)\">%%tcp[%i%].Sport%%</a>\n\
(<a href=\"set(tcp[%i%].sport)\">%%tcp[%i%].sport%%</a>)\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(tcp[%i%].Dport)\">%%tcp[%i%].Dport%%</a>\n\
(<a href=\"set(tcp[%i%].dport)\">%%tcp[%i%].dport%%</a>)\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<a href=\"set(tcp[%i%].seq)\">%%tcp[%i%].seq%%</a>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=100%%%%>\n\
<a href=\"set(tcp[%i%].ack)\">%%tcp[%i%].ack%%</a>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=12%%%%>\n\
<a href=\"set(tcp[%i%].x2)\">%%tcp[%i%].x2%%</a>\n\
</td>\n\
<td align=center width=13%%%%>\n\
<a href=\"set(tcp[%i%].off)\">%%tcp[%i%].off%%</a>\n\
</td>\n\
<td align=center width=25%%%%>\n\
<a href=\"set(tcp[%i%].Flags)\">%%tcp[%i%].Flags%%</a>\n\
(<a href=\"set(tcp[%i%].flags)\">%%tcp[%i%].flags%%</a>)\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(tcp[%i%].win)\">%%tcp[%i%].win%%</a>\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(tcp[%i%].sum)\">%%tcp[%i%].sum%%</a>\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(tcp[%i%].urp)\">%%tcp[%i%].urp%%</a>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

int	tcp_chan;

PAT_NAME_GENERIC(tcp_pat_name,
		 (VOID_PTR)&tcp_chan,
		 "tcp")

PAT_GET_FIELDS_GENERIC(tcp_pat_get_fields,
		       tcp_fields)

PAT_GET_FIELD_PAT_GENERIC(tcp_pat_get_field_pat,
			  tcp_fields)

PAT_GET_TMPL_GENERIC(tcp_pat_get_tmpl,
		     (VOID_PTR)&tcp_chan,
		     tcp_itmpl)

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

  fieldptr = NULL;
  fields = tcp_fields;
  while (fields->name)
    {
      if (!strcmp(fields->name,field))
	fieldptr = fields;
      fields++;
    }
  if (!strcmp(field,"Seq") || !strcmp(field,"Ack"))
    {
      char			*buf2;
      int			len2;
      VOID_PTR			data2;
      
      buf2 = buf + TCP_SEQACK_OFF;
      len2 = len - TCP_SEQACK_OFF;
      if (optional_id)
	{
	  t_seq_ack_data	sad;
	  t_hash_elt		*he;
	  t_tcp			*tcp;
	  t_off			off;
	  
	  PAT_TCP_CHECK(tcp,buf,len);
	  if ((status = tcp_pat_off(NULL,
				    buf,
				    len,
				    &off)) != 0)
	    return (status);
	  if (he = id_get(optional_id,
			  &pat_ip_src_chan))
	    FBCOPY(&he->value,&sad.src,sizeof (sad.src));
	  else
	    goto noipadvice;
	  if (he = id_get(optional_id,
			  &pat_ip_dst_chan))
	    FBCOPY(&he->value,&sad.dst,sizeof (sad.dst));
	  else
	    goto noipadvice;
	  sad.sport = safe_ntohs(&tcp->sport);
	  sad.dport = safe_ntohs(&tcp->dport);
	  sad.flags = tcp->flags;
	  sad.datalen = len - off;
	  sad.seq_wanted = !strcmp(field,"Seq");
	  data2 = &sad;
	}
      else
	{
	noipadvice:
	  data2 = NULL;
	}
      assert(seqack_pat.extract_proc);
      return (seqack_pat.extract_proc(data2,
				      buf2,
				      len2,
				      str,
				      max_len));
    }
  if (!fieldptr)
    return (ERR_PAT_NO_SUCH_FIELD);
  return (get_field_to_str(buf,
			   len,
			   fieldptr,
			   str,
			   max_len));
}

PAT_SET_FIELD_DECL(tcp_pat_set_field)
{
  t_field			*fieldptr;
  t_field			*fields;
	
  fieldptr = NULL;
  fields = tcp_fields;
  while (fields->name)
    {
      if (!strcmp(fields->name,field))
	fieldptr = fields; 
      fields++;
    }
  if (!strcmp(field,"Seq") || !strcmp(field,"Ack"))
    return (ERR_PAT_NI);
  if (!fieldptr)
    return (ERR_PAT_NO_SUCH_FIELD);
  return (set_field_from_str(buf,
			     len,
			     fieldptr,
			     value));
}

PAT_OFF_DECL(tcp_pat_off)
{
  t_tcp			*tcp;
  
  PAT_TCP_CHECK(tcp,buf,len);
  (*off_return) = tcp_get_off(tcp) * 4;
  return (0);
}

PAT_SUB_DECL(tcp_pat_sub)
{
  t_tcp			*tcp;

  PAT_TCP_CHECK(tcp,buf,len);
  (*pat_return) = &data_pat;
  return (0);
}

PAT_SUM_DECL(tcp_pat_sum)
{
  t_tcp			*tcp;
  t_ip			*ip;

  PAT_TCP_CHECK(tcp,buf,len);
  PAT_IP_CHECK(ip,up_buf,up_len);
  if (ip_get_len(ip) != up_len)
    return (ERR_PAT_BAD_LEN);
  if (ip_get_hl(ip) * 4 < IP_MINHLEN)
    return (ERR_PAT_BAD_HEADER);
  tcp_compute_sum(ip);
  return (0);
}

PAT_HAS_OPT_DECL(tcp_pat_has_opt)
{
  t_tcp			*tcp;

  PAT_TCP_CHECK(tcp,buf,len);
  if (tcp_get_off(tcp) * 4 > TCP_MINHLEN)
    {
      (*has_opt_return) = TRUE;
      (*opt_off_return) = TCP_MINHLEN;
      (*opt_len_return) = tcp_get_off(tcp) * 4 - TCP_MINHLEN;
      (*opt_pat_return) = &tcpopt_pat;
    }
  else
    {
      (*has_opt_return) = FALSE;
    }
  return (0);
}

t_pat				tcp_pat = 
{
  tcp_pat_name,			/* t_pat_name_proc		*/
  tcp_pat_off,			/* t_pat_off_proc		*/
  tcp_pat_sub,			/* t_pat_sub_proc		*/
  tcp_pat_sum,			/* t_pat_sum_proc		*/
  tcp_pat_get_field,		/* t_pat_get_field_proc		*/
  tcp_pat_set_field,		/* t_pat_set_field_proc		*/
  tcp_pat_get_fields,		/* t_pat_get_fields_proc	*/
  tcp_pat_get_tmpl,		/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  tcp_pat_has_opt,		/* t_pat_has_opt_proc		*/
  NULL,				/* t_pat_adapt_len_proc		*/
  tcp_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	*/
};
