/* TCP control and status routines
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Mods by G1EMM
 * Mods by WG7J
 * Mods by WA3DSP
 * Mods by PA0GRI
 * Copyright 1992 Gerard J van der Grinten, PA0GRI
 */
#include "global.h"
#include "ctype.h"
#include "commands.h"
#ifdef MSDOS
#include "socket.h"
#else
#include "timer.h"
#include "mbuf.h"
#endif
#include "netuser.h"
#include "internet.h"
#include "iface.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: tcpcmd.c,v 1.23 2001/05/08 12:26:46 brian Exp $";
#endif

#ifdef  TCPACCESS
static int doaccess (int argc, char *argv[], void *p);
#endif

#if defined(TCPACCESS) || defined(UDPACCESS)
int do_tcpudp_access (int argc, char *argv[], void *p);
static void addtaccess (struct rtaccess **tpacc, uint32 target, unsigned int bits, int16 low, int16 high, int16 permit);
#endif

static int tcpirtt (int argc, char *argv[], void *p);
static int dotcpirtt (int argc, char *argv[], void *p);
static int tcpmss (int argc, char *argv[], void *p);
static int dotcpmss (int argc, char *argv[], void *p);
static int dortt (int argc, char *argv[], void *p);
static int dotcpclean (int argc, char *argv[], void *p);
static int dotcpkick (int argc, char *argv[], void *p);
static int dotcpreset (int argc, char *argv[], void *p);
static int dotcpretries (int argc, char *argv[], void *p);
static int tcpretries (int argc, char *argv[], void *p);
static int dotcpstat (int argc, char *argv[], void *p);
static int dotcpconnect (int argc, char *argv[], void *p);
static int dotcptimer (int argc, char *argv[], void *p);
static int tcptimer (int argc, char *argv[], void *p);
static int dotcptr (int argc, char *argv[], void *p);
static int dotcpwindow (int argc, char *argv[], void *p);
static int tcpwindow (int argc, char *argv[], void *p);
static int dotcpmaxwait (int argc, char *argv[], void *p);
static int tcpmaxwait (int argc, char *argv[], void *p);
static int dotcpsyndata (int argc, char *argv[], void *p);
static int tcpsyndata (int argc, char *argv[], void *p);
int dotcpview (int argc, char *argv[], void *p);
static int dotcpblimit (int argc, char *argv[], void *p);
static int tcpblimit (int argc, char *argv[], void *p);
static void rxtx_data_compute (struct tcb * tcb, int32 * sent, int32 * recvd);
static int tstat (int flag);
static int tstat2 (int flag);

#ifdef TCPACCESS
struct rtaccess *TCPaccess = NULLACCESS;	/* access list */
#endif
extern int tcptimertype;
extern long Tcp_maxwait;
extern int Tcp_blimit;


#ifdef CATALOG
#include "catalog.h"

#define CAT tcpcmd_catalog

#define tracingstr	__STR(0)
#define irttstr		__STR(1)
#define irttdisp	__STR(2)
#define mssstr		__STR(3)
#define windowstr	__STR(4)
#define syndatastr	__STR(5)
#define retriesstr	__STR(6)
#define maxwaitstr	__STR(7)
#define backoffstr	__STR(8)
#define cbheader	__STR(9)
#define reassembly	__STR(10)
#define reassstr	__STR(11)
#define retrying	__STR(12)
#define backoffval	__STR(13)
#define timerstopped	__STR(14)
#define timerrunning	__STR(15)
#define timerexp	__STR(16)
#define rttval		__STR(17)
#define viewsyntax	__STR(18)
#define viewhdr1	__STR(19)
#define viewhdr2	__STR(20)
#define timeris		__STR(21)
#define timertypesyntax	__STR(22)
#define accesshdr	__STR(23)
#define accesssyntax	__STR(24)
#define notfound	__STR(25)

#else /* CATALOG */
static const char tracingstr[] = "TCP state tracing";
static const char irttstr[] = "TCP default irtt";
static const char irttdisp[] = "%s: srtt %lu mdev %lu\n";
static const char mssstr[] = "TCP MSS";
static const char windowstr[] = "TCP window";
static const char syndatastr[] = "TCP syn+data piggybacking";
static const char retriesstr[] = "max. retries";
static const char maxwaitstr[] = "max. retry wait (ms)";
static const char backoffstr[] = "backoff limit";
static const char cbheader[] = "      Init seq    Unack     Next Resent CWind Thrsh  Wind  MSS Queue      Total\nSend:";
static const char reassembly[] = "Reassembly queue:\n";
static const char reassstr[] = "  seq x%lx %u bytes\n";
static const char retrying[] = "Retrying ";
static const char backoffval[] = "Backoff %u ";
static const char timerstopped[] = "Timer stopped ";
static const char timerrunning[] = "Timer running (%ld/%ld ms) ";
static const char timerexp[] = "Timer expired ";
static const char rttval[] = "SRTT %ld ms Mean dev %ld ms\n";
static const char viewsyntax[] = "Use: tcp view <bytes|timers>\n";
static const char viewhdr1[] = "                                                 Send    Send   Receive Receive\nRemote Socket:Port:Local Port/State    TCB      Bytes Retries     Bytes Retries\n";
static const char viewhdr2[] = "Remote Socket:Port:Local Port/State    TCB   Boff State         Timer      SRTT\n";
static const char timeris[] = "Tcp timer type is %s\n";
static const char timertypesyntax[] = "use: tcp timertype [linear|exponential]\n";
static const char accesshdr[] = "IP Address      Mask  Low Port High Port State\n";
static const char accesssyntax[] = " Format: tcp access <permit|deny|delete> <dest addr>[/<bits>] [lowport [highport]]\n";
static const char notfound[] = "Not found.\n";

#endif /* CATALOG */

static const char tstat2hdr[] =
#if !defined(TNOS_68K) && !defined(UNIX)
"&TCB Rcv-Q Snd-Q  Local socket           Remote socket          State\n";
#else
"&TCB     Rcv-Q Snd-Q  Local socket           Remote socket          State\n";
#endif

static const char tstat2fmt[] =
#if !defined(TNOS_68K) && !defined(UNIX)
"%4.4x%6u%6u  ";
#else
"%-8.8lx%6u%6u  ";
#endif

static const char str23[] = "%-23s";
static const char tstatmib[] = "(%2u)%-20s%10lu";
static const char str9lx[] = "%9lx";
static const char str6u[] = "%6u";
static const char spaces6[] = "      ";
static const char spaces5[] = "     ";
static const char linearstr[] = "linear";
static const char exponentialstr[] = "exponential";
static const char permitstr[] = "permit";
static const char denystr[] = "deny";
static const char allstr[] = "all";
static const char deletestr[] = "delete";
static const char viewretrystr[] = " Retry ";
static const char viewtrystr[] = "  Try  ";
static const char viewstopped[] = "      Stopped";
static const char viewrun[] = " Run (";
static const char view9999[] = ">9999/9999)s";
static const char viewexp[] = "      Expired";


/* TCP subcommand table */
static struct cmds Tcpcmds[] =
{
#ifdef  TCPACCESS
	{ "access",		doaccess,	0, 0, NULLCHAR},
#endif
	{ "blimit",		dotcpblimit,	0, 0, NULLCHAR},
	{ "connections",	dotcpconnect,	0, 0, NULLCHAR},
	{ "clean",		dotcpclean,	0, 0, NULLCHAR},
	{ "irtt",		dotcpirtt,	0, 0, NULLCHAR},
	{ "kick",		dotcpkick,	0, 2, "tcp kick <tcb>"},
	{ "maxwait",		dotcpmaxwait,	0, 0, NULLCHAR},
	{ "mss",		dotcpmss,	0, 0, NULLCHAR},
	{ "reset",		dotcpreset,	0, 2, "tcp reset <tcb>"},
	{ "retries",		dotcpretries,	0, 0, NULLCHAR},
	{ "rtt",		dortt,		0, 3, "tcp rtt <tcb> <val>"},
	{ "status",		dotcpstat,	0, 0, NULLCHAR},
	{ "syndata",		dotcpsyndata,	0, 0, NULLCHAR},
	{ "timertype",		dotcptimer,	0, 0, NULLCHAR},
	{ "trace",		dotcptr,	0, 0, NULLCHAR},
	{ "view",		dotcpview,	0, 0, NULLCHAR},
	{ "window",		dotcpwindow,	0, 0, NULLCHAR},
	{ NULLCHAR,		NULL,		0, 0, NULLCHAR}
};


int
dotcp (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return subcmd (Tcpcmds, argc, argv, p);
}


static int
dotcptr (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setbool (&Tcp_trace, tracingstr, argc, argv);
}


/* Reset lingering FIN Wait 2 state connections */
static int
dotcpclean (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
struct tcb *tcb;

	for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb->next) {
		if (tcb->state == TCP_FINWAIT2) {
			reset_tcp (tcb);
			tcb = Tcbs;	/* start at the top again */
		}
	}
	return 0;
}


/* Eliminate a TCP connection */
static int
dotcpreset (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
register struct tcb *tcb;

	tcb = (struct tcb *) htoi (argv[1]);
	if (!tcpval (tcb)) {
		tputs (Notval);
		return 1;
	}
	reset_tcp (tcb);
	return 0;
}


/* Set initial round trip time for new connections */
static int
tcpirtt (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setlong ((long *) p, irttstr, argc, argv);
}


static int
dotcpirtt (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
struct tcp_rtt *tp;

	(void) tcpirtt (argc, argv, (void *) &Tcp_irtt);
	if (argc < 2) {
		for (tp = &Tcp_rtt[0]; tp < &Tcp_rtt[RTTCACHE]; tp++) {
			if (tp->addr != 0) {
				if (tprintf (irttdisp, inet_ntoa (tp->addr), tp->srtt, tp->mdev) == EOF)
					break;
			}
		}
	}
	return 0;
}


/* Set smoothed round trip time for specified TCB */
static int
dortt (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
register struct tcb *tcb;

	tcb = (struct tcb *) htoi (argv[1]);
	if (!tcpval (tcb)) {
		tputs (Notval);
		return 1;
	}
	tcb->srtt = atol (argv[2]);
	return 0;
}


/* Force a retransmission */
static int
dotcpkick (argc, argv, p)
int argc OPTIONAL;
char *argv[];
void *p OPTIONAL;
{
register struct tcb *tcb;

   
	if (strcmp (argv[1], "0") == 0) { /* If 'tcp kick 0' - kick everything */
		for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb->next) {
			switch (tcb->state) {
				case TCP_ESTABLISHED:
				case TCP_FINWAIT1:
				    kick_tcp (tcb);
				default:
				    continue;
			}
		}
		return 0;
	}
  
	tcb = (struct tcb *) htoi (argv[1]); /* Kick a single tcb */
	if (kick_tcp (tcb) == -1) {
		tputs (Notval);
		return 1;
	}
	return 0;
}


/* Set default maximum segment size */
static int
tcpmss (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setshort ((unsigned short *) p, mssstr, argc, argv);
}


static int
dotcpmss (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return tcpmss (argc, argv, (void *) &Tcp_mss);
}


/* Set default window size */
static int
tcpwindow (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setshort ((unsigned short *) p, windowstr, argc, argv);
}


static int
dotcpwindow (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return tcpwindow (argc, argv, (void *) &Tcp_window);
}


static int
tcpsyndata (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setbool ((int *) p, syndatastr, argc, argv);
}


static int
dotcpsyndata (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return tcpsyndata (argc, argv, (void *) &Tcp_syndata);
}


/* Set maximum number of backoffs before resetting the connection */
static int
tcpretries (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setint ((int *) p, retriesstr, argc, argv);
}


static int
dotcpretries (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return tcpretries (argc, argv, (void *) &Tcp_retries);
}


/* Set maximum retry waittime in ms. */
static int
tcpmaxwait (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setlong ((long *) p, maxwaitstr, argc, argv);
}


static int
dotcpmaxwait (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return tcpmaxwait (argc, argv, (void *) &Tcp_maxwait);
}


/* Set backoff limit on the connection; from N1BEE */
static int
tcpblimit (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	return setint ((int *) p, backoffstr, argc, argv);
}


static int
dotcpblimit (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return tcpblimit (argc, argv, (void *) &Tcp_blimit);
}


/* Display status of TCBs */
static int
dotcpconnect (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
register struct tcb *tcb;

	if (argc < 2)
		(void) tstat2 (0);
	else {
		if (toupper (argv[1][0]) == 'A')
			(void) tstat (1);
		else {
			tcb = (struct tcb *) htoi (argv[1]);
			if (!tcpval (tcb))
				tputs (Notval);
			else
				st_tcp (tcb);
		}
	}
	return 0;
}


/* Display status of TCBs with MIB info*/
static int
dotcpstat (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
register struct tcb *tcb;

	if (argc < 2)
		(void) tstat (0);
	else {
		if (toupper (argv[1][0]) == 'A')
			(void) tstat (1);
		else {
			tcb = (struct tcb *) htoi (argv[1]);
			if (!tcpval (tcb))
				tputs (Notval);
			else
				st_tcp (tcb);
		}
	}
	return 0;
}


/* Dump TCP stats and summary of all TCBs
 *     &TCB Rcv-Q Snd-Q  Local socket           Remote socket          State
 *     1234     0     0  xxx.xxx.xxx.xxx:xxxxx  xxx.xxx.xxx.xxx:xxxxx  Established
 * Dump display for TNOS_68K and UNIX
 *     &TCB     Rcv-Q Snd-Q  Local socket           Remote socket          State
 *     123456       0     0  xxx.xxx.xxx.xxx:xxxxx  xxx.xxx.xxx.xxx:xxxxx  Established
 */
static int
tstat2 (int flag)
{
register struct tcb *tcb;

	tputs (tstat2hdr);
	for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb->next) {
		if (tcb->state == TCP_LISTEN && !flag)
			continue;
		tprintf (tstat2fmt, tcb, tcb->rcvcnt, tcb->sndcnt);
		tprintf (str23, pinet (&tcb->conn.local));
		tprintf (str23, pinet (&tcb->conn.remote));
		tprintf ("%-s", Tcpstates[(int) tcb->state]);
		if (tcb->state == TCP_LISTEN && tcb->flags.clone)
			tputs (" (S)");
		if (tputc ('\n') == EOF)
			return 0;
	}
	return 0;
}


static int
tstat (int flag)
{
register int i;
int j;

	for (j = i = 1; i <= NUMTCPMIB; i++) {
		if (Tcp_mib[i].name == NULLCHAR)
			continue;
		tprintf (tstatmib, i, Tcp_mib[i].name,
			 Tcp_mib[i].value.integer);
		if (j++ % 2)
			tputs (spaces5);
		else
			tputc ('\n');
	}
	if ((j % 2) == 0)
		tputc ('\n');

	return (tstat2 (flag));
}


/* Dump a TCP control block in detail */
void
st_tcp (tcb)
struct tcb *tcb;
{
int32 sent, recvd;

	if (tcb == NULLTCB)
		return;

	rxtx_data_compute (tcb, &sent, &recvd);

	tprintf ("Local: %s", pinet (&tcb->conn.local));
	tprintf (" Remote: %s", pinet (&tcb->conn.remote));
	tprintf (" State: %s\n", Tcpstates[(int) tcb->state]);
	tputs (cbheader);
	tprintf (str9lx, tcb->iss);
	tprintf (str9lx, tcb->snd.una);
	tprintf (str9lx, tcb->snd.nxt);
	tprintf ("%7lu", tcb->resent);
	tprintf (str6u, tcb->cwind);
	tprintf (str6u, tcb->ssthresh);
	tprintf (str6u, tcb->snd.wnd);
	tprintf ("%5u", tcb->mss);
	tprintf (str6u, tcb->sndcnt);
	tprintf ("%11lu\n", sent);

	tputs ("Recv:");
	tprintf (str9lx, tcb->irs);
	tputs ("         ");
	tprintf (str9lx, tcb->rcv.nxt);
	tprintf ("%7lu", tcb->rerecv);
	tputs (spaces6);
	tputs (spaces6);
	tprintf (str6u, tcb->rcv.wnd);
	tputs (spaces5);
	tprintf (str6u, tcb->rcvcnt);
	tprintf ("%11lu\n", recvd);

	if (tcb->reseq != (struct reseq *) NULL) {
		register struct reseq *rp;

		tputs (reassembly);
		for (rp = tcb->reseq; rp != (struct reseq *) NULL; rp = rp->next) {
			if (tprintf (reassstr, rp->seg.seq, rp->length) == EOF)
				return;
		}
	}
	if (tcb->backoff > 0)
		tprintf (backoffval, tcb->backoff);
	if (tcb->flags.retran)
		tputs (retrying);
	switch (tcb->timer.state) {
		case TIMER_STOP:
			tputs (timerstopped);
			break;
		case TIMER_RUN:
			tprintf (timerrunning,
				 (long) read_timer (&tcb->timer),
				 (long) dur_timer (&tcb->timer));
			break;
		case TIMER_EXPIRE:
			tputs (timerexp);
			break;
		default:
			break;
	}
	tprintf (rttval, tcb->srtt, tcb->mdev);
}


static void
rxtx_data_compute (tcb, sent, recvd)
struct tcb *tcb;
int32 *sent;
int32 *recvd;
{

	/* Compute total data sent and received; take out SYN and FIN */
	*sent = tcb->snd.una - tcb->iss;	/* Acknowledged data only */
	*recvd = tcb->rcv.nxt - tcb->irs;
	switch (tcb->state) {
		case TCP_LISTEN:
		case TCP_SYN_SENT:	/* Nothing received or acked yet */
			*sent = *recvd = 0;
			break;
		case TCP_SYN_RECEIVED:
			(*recvd)--;	/* Got SYN, no data acked yet */
			*sent = 0;
			break;
		case TCP_ESTABLISHED:	/* Got and sent SYN */
		case TCP_FINWAIT1:	/* FIN not acked yet */
			(*sent)--;
			(*recvd)--;
			break;
		case TCP_FINWAIT2:	/* Our SYN and FIN both acked */
			*sent -= 2;
			(*recvd)--;
			break;
		case TCP_CLOSE_WAIT:	/* Got SYN and FIN, our FIN not yet acked */
		case TCP_CLOSING:
		case TCP_LAST_ACK:
			(*sent)--;
			*recvd -= 2;
			break;
		case TCP_TIME_WAIT:	/* Sent and received SYN/FIN, all acked */
			*sent -= 2;
			*recvd -= 2;
			break;
		default:
			break;
	}
}


/* TCP View Command - D. Crompton 1/92 */
/* Modified for sorted display and     */
/* two views - tcp view b|t - 3/92     */

int
dotcpview (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
register struct tcb *tcb;
int32 sent, recvd;
int i, j, k = 0, vtype;
char temp[80];
char *buf, *cp;

	if (argc == 1)
		vtype = 1;
	else {
		switch (argv[1][0]) {
			case 'b':
				vtype = 1;
				break;
			case 't':
				vtype = 0;
				break;
			default:
				tputs (viewsyntax);
				return 0;
		}
	}

	for (tcb = Tcbs, i = 0; tcb != NULLTCB; tcb = tcb->next) {
		if (tcb->state == TCP_LISTEN)
			continue;
		i++;
	}

	if (i) {
		buf = mallocw ((unsigned) (i * 80));

		if (vtype)
			tputs (viewhdr1);
		else
			tputs (viewhdr2);

		for (tcb = Tcbs, j = 0; tcb != NULLTCB; tcb = tcb->next) {
			if (tcb->state == TCP_LISTEN)
				continue;

			strncpy (temp, pinet (&tcb->conn.remote), 80);
			cp = strstr (pinet (&tcb->conn.local), ":");
			if (cp)
				strcat (temp, cp);
			strcat (temp, "/");
			strcat (temp, Tcpstates[(int) tcb->state]);
			temp[37] = 0;
			k = SPRINTF ((&buf[j], "%-35.35s", temp));
			sprintf (temp, "%8lx", ptol (tcb));
#if 0
			temp[4] = 0;
			k += SPRINTF ((&buf[j + k], " %4s", temp));
#else
			k += SPRINTF ((&buf[j + k], " %8s", temp));
#endif
			if (vtype) {
				rxtx_data_compute (tcb, &sent, &recvd);
				k += SPRINTF ((&buf[j + k], "%9lu ", sent));
				k += SPRINTF ((&buf[j + k], "%7lu ", tcb->resent));
				k += SPRINTF ((&buf[j + k], "%9lu ", recvd));
				sprintf (&buf[j + k], "%7lu", tcb->rerecv);
			} else {
				k += SPRINTF ((&buf[j + k], " %4u", tcb->backoff));
				if (tcb->flags.retran)
					k += SPRINTF ((&buf[j + k], viewretrystr));
				else
					k += SPRINTF ((&buf[j + k], viewtrystr));
				switch (tcb->timer.state) {
					case TIMER_STOP:
						k += SPRINTF ((&buf[j + k], viewstopped));
						break;
					case TIMER_RUN:
						k += SPRINTF ((&buf[j + k], viewrun));
						if ((long) dur_timer (&tcb->timer) < 10000) {
							k += SPRINTF ((&buf[j + k], "%ld/%ld)ms",
								       (long) read_timer (&tcb->timer),
								       (long) dur_timer (&tcb->timer)));
						} else {
							if (((long) read_timer (&tcb->timer) / 1000) > 9999) {
								k += SPRINTF ((&buf[j + k], view9999));
							} else {
								k += SPRINTF ((&buf[j + k], "%ld/%ld)s",
									       (long) read_timer (&tcb->timer) / 1000,
									       (long) dur_timer (&tcb->timer) / 1000));
							}
						}
						break;
					case TIMER_EXPIRE:
						k += SPRINTF ((&buf[j + k], viewexp));
						break;
					default:
						break;
				}
				for (; k < 73; k++)
					buf[j + k] = ' ';
				if ((tcb->srtt) < 10000)
					sprintf (&buf[j + 73], "%4ldms", tcb->srtt);
				else {
					if ((tcb->srtt / 1000) > 9999) {
						sprintf (&buf[j + 73], ">9999s");
					} else {
						sprintf (&buf[j + 73], "%4lds", tcb->srtt / 1000);
					}
				}
			}
			j += 80;
		}

		qsort (buf, (size_t) i, 80, (int (*)(const void *, const void *)) strcmp);
		for (j = 0, k = 0; j < i; j++, k += 80) {
			tputs (&buf[k]);
			if (tputc ('\n') == EOF)
				return 0;
		}
		free (buf);
	}
	return 0;
}


/* tcp timers type - linear v exponential */
static int
tcptimer (argc, argv, p)
int argc;
char *argv[];
void *p;
{
	if (argc < 2) {
		tprintf (timeris, *(int *) p ? linearstr : exponentialstr);
		return 0;
	}
	switch (argv[1][0]) {
		case 'l':
		case 'L':
			*(int *) p = 1;
			break;
		case 'e':
		case 'E':
			*(int *) p = 0;
			break;
		default:
			tputs (timertypesyntax);
			return -1;
	}

	return 0;
}


static int
dotcptimer (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{

	return tcptimer (argc, argv, (void *) &tcptimertype);
}


#if defined(TCPACCESS) || defined(UDPACCESS)

int
do_tcpudp_access (argc, argv, p)
int argc;
char *argv[];
void *p;
{
uint32 target;
unsigned bits;
char *bitp;
int16 lport, hport, state;
char const *cp;		/* for printing the table */
struct rtaccess *tpacc;
struct rtaccess **headptr = (struct rtaccess **)p;
struct rtaccess *head;
struct rtaccess *prev;

	if (argc == 1) {	/* print out the table */
		tputs (accesshdr);
		for (tpacc = *headptr; tpacc != NULLACCESS; tpacc = tpacc->nxtbits) {
			if (tpacc->target != 0)
				cp = inet_ntoa (tpacc->target);
			else
				cp = allstr;
			tprintf ("%-16s", cp);
			tprintf ("%4u ", tpacc->bits);
			tprintf ("%9u", tpacc->lowport);
			tprintf ("%10u ", tpacc->highport);
			if (tpacc->status)
				cp = denystr;
			else
				cp = permitstr;
			tprintf ("%-6s\n", cp);
		}
		return 0;
	}
	if (strcmp (argv[1], permitstr) == 0)
		state = 0;
	else {
		if ((strcmp (argv[1], "deny") == 0) || (strcmp (argv[1], "delete") == 0))
			state = (int16) - 1;
		else {
			tputs (accesssyntax);
			return 1;
		}
	}
	if (strcmp (argv[2], allstr) == 0) {
		target = 0;
		bits = 0;
	} else {
		/* If IP address is followed by an optional slash and
		 * a length field, (e.g., 128.96/16) get it;
		 * otherwise assume a full 32-bit address
		 */
		if ((bitp = strchr (argv[2], '/')) != NULLCHAR) {
			/* Terminate address token for resolve() call */
			*bitp++ = '\0';
			bits = (unsigned) atoi (bitp);
		} else
			bits = 32;

		if ((target = resolve (argv[2])) == 0) {
			tprintf (Badhost, argv[2]);
			return 1;
		}
	}

	if (argc > 3) {
		if (strcmp (argv[3], allstr) == 0) {
			lport = 1;
			hport = 65534L;
		} else {
			lport = (int16) atoi (argv[3]);
			hport = lport;
		}
	} else {
		lport = 0;
		hport = 0;
	}
	if (argc > 4)
		hport = (int16) atoi (argv[4]);

	if (strcmp (argv[1], deletestr) == 0) {
		prev = NULLACCESS;
		head = tpacc = *headptr;
		while (tpacc != NULLACCESS) {
			head = tpacc;
			tpacc = tpacc->nxtbits;
			if ((head->target == target) && (head->bits == bits) &&
			    (head->lowport == lport) && (head->highport == hport)) {	/*match*/
				/*now delete. watch for special cases*/
				if (head == *headptr)	/* first in chain */
					*headptr = head->nxtbits;
				else	{
					/* sanity check: we cant get here with prev == NULLACCESS !! */
					if (prev)
						prev->nxtbits = tpacc;
				}
				free (head);
				return 0;
			}
			prev = head;
		}
		tputs (notfound);
		return 1;
	}
	/* add the access */
	addtaccess (headptr, target, bits, lport, hport, state);
	return 0;
}



static int
doaccess (argc, argv, p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (do_tcpudp_access (argc, argv, &TCPaccess));
}



/*lint -esym(18,addtaccess) */
/* add an entry to the access control list */
/* not a lot of error checking 8-) */
static void
addtaccess (headacc, target, bits, low, high, permit)
struct rtaccess **headacc;
uint32 target;			/* Target IP address prefix */
unsigned int bits;		/* Size of target address prefix in bits (0-32) */
int16 low;
int16 high;
int16 permit;
{
struct rtaccess *tpacc;		/*temporary*/
struct rtaccess *holder;	/*for the new record*/

	holder = (struct rtaccess *) callocw (1, sizeof (struct rtaccess));

	holder->nxtiface = NULLACCESS;
	holder->nxtbits = NULLACCESS;
	holder->target = target;
	holder->bits = bits;
	holder->lowport = low;
	holder->highport = high;
	holder->status = permit;

	if ((low || high) && (*headacc == NULLACCESS || tcp_check (*headacc, target, low) == -1))
		addtaccess (headacc, target, bits, 0, 0, ~permit);

	if ((tpacc = *headacc) == NULLACCESS)
		*headacc = holder;
	else {
		while (tpacc->nxtbits != NULLACCESS)
			tpacc = tpacc->nxtbits;
		tpacc->nxtbits = holder;
	}
}



/*lint -esym(18,tcp_check) */
/* check to see if port is "authorized".  Returns 0 if matching permit record
   is found or no access records exists, -1 if not found or deny record found */
int
tcp_check (accptr, src, port)
struct rtaccess *accptr;
uint32 src;
int16 port;
{
unsigned long mask;
unsigned short tmp;
int retval = -1;

	if (accptr == NULLACCESS)
		return 0;	/* no access control */
	for (; accptr != NULLACCESS; accptr = accptr->nxtbits) {
		tmp = (int16) (32 - accptr->bits);
		if (tmp == 32)	/* accptr == 0 !! */
			mask = 0;		/* GCC bug?? */
		else
			mask = ~0L << tmp;	/*lint !e703 */
		if ((accptr->target == (uint32) (mask & src)) &&
		  (((port >= accptr->lowport) && (port <= accptr->highport))
		   || (!accptr->lowport))) {
		   	/* if this is a default entry, save the status,
		   	   but look in case there is a more specific entry
		   	   later in the list. If not, upon exit, we will
		   	   return the proper status
		   	 */
		   	if (!accptr->lowport && !accptr->highport)
		   		retval = accptr->status;
		   	else
				return (accptr->status);
		}
	}
	return retval;		/* fall through to here if specific entry not found */
}

#endif


/* These are the interface dependent tcp parameters */
static int doiftcpblimit (int argc, char *argv[], void *p);
static int doiftcpirtt (int argc, char *argv[], void *p);
static int doiftcpmaxwait (int argc, char *argv[], void *p);
static int doiftcpretries (int argc, char *argv[], void *p);
static int doiftcptimertype (int argc, char *argv[], void *p);
static int doiftcpwindow (int argc, char *argv[], void *p);
static int doiftcpsyndata (int argc, char *argv[], void *p);
static int doiftcpmss (int argc, char *argv[], void *p);


static struct cmds Iftcpcmds[] =
{
	{ "blimit",	doiftcpblimit,		0, 0, NULLCHAR},
	{ "irtt",	doiftcpirtt,		0, 0, NULLCHAR},
	{ "maxwait",	doiftcpmaxwait,		0, 0, NULLCHAR},
	{ "mss",	doiftcpmss,		0, 0, NULLCHAR},
	{ "retries",	doiftcpretries,		0, 0, NULLCHAR},
	{ "syndata",	doiftcpsyndata,		0, 0, NULLCHAR},
	{ "timertype",	doiftcptimertype,	0, 0, NULLCHAR},
	{ "window",	doiftcpwindow,		0, 0, NULLCHAR},
	{ NULLCHAR,	NULL,			0, 0, NULLCHAR}
};


int 
doiftcp (int argc, char *argv[], void *p)
{
	if (!p)
		return 0;
	return subcmd (Iftcpcmds, argc, argv, p);
}


static int 
doiftcpblimit (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpblimit (argc, argv, (void *) &ifp->tcp->blimit);
}


static int 
doiftcpirtt (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpirtt (argc, argv, (void *) &ifp->tcp->irtt);
}


static int 
doiftcpmaxwait (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpmaxwait (argc, argv, (void *) &ifp->tcp->maxwait);
}


static int 
doiftcpmss (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpmss (argc, argv, (void *) &ifp->tcp->mss);
}


static int 
doiftcpretries (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpretries (argc, argv, (void *) &ifp->tcp->retries);
}


static int 
doiftcptimertype (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcptimer (argc, argv, (void *) &ifp->tcp->timertype);
}


static int 
doiftcpwindow (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpwindow (argc, argv, (void *) &ifp->tcp->window);
}


static int 
doiftcpsyndata (int argc, char *argv[], void *p)
{
struct iface *ifp = p;

	return tcpsyndata (argc, argv, (void *) &ifp->tcp->syndata);
}


void 
init_iftcp (struct iftcp *tcp)
{
	tcp->blimit = Tcp_blimit;
	tcp->maxwait = Tcp_maxwait;
	tcp->window = Tcp_window;
	tcp->mss = Tcp_mss;
	tcp->irtt = Tcp_irtt;
	tcp->retries = Tcp_retries;
	tcp->timertype = tcptimertype;
	tcp->syndata = Tcp_syndata;
}
