/*
 *  PPPCMD.C	-- PPP related user commands
 *
 *	12-89	-- Katie Stevens (dkstevens@ucdavis.edu)
 *		   UC Davis, Computing Services
 *	PPP.07	04-90	[ks] new storage for peer addr for IPCP
 *	PPP.08  05-90	[ks] improve PPP trace reporting
 *	PPP.09	05-90	[ks] add commands to set PPP Auth protocol
 *			     (UPAP server) and UPAP peerID (UPAP client)
 *	PPP.10	6-90	[ks] improve keybd input of UPAP password
 *			     make ppp open/close/reset work properly
 *			     add peer IP lookup pool
 *	PPP.13	8-90	[ks] report PPP open timestamp with stats
 *	PPP.14	8-90	[ks] change UPAP to PAP for consistency with RFC1172
 *			     add RLSD physical link up/down handling
 *			     add autobaud handling to set link speed
 *			     make LCP/IPCP timeout configurable
 *	PPP.15	09-90	[ks] update to KA9Q NOS v900828
 */

#include <stdio.h>
#include <time.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "timer.h"
#include "pktdrvr.h"
#include "asy.h"
#include "slcompre.h"
#include "ppp.h"
#include "slip.h"
#include "commands.h"
#include "netuser.h"
/*#include "internet.h"*/
#include "cmdparse.h"
#include "config.h"

extern char Badhost[];

int16 ppptrace = 0;
int16 Npool = 0;		/* Number of pools for PPP interfaces */

#ifdef notdef /*MUXASY*/
struct ipcppool PPPpool[ASY_MAX*PORTS_PER_MUX];	/* For pooling peer IP addrs */
#else
struct ipcppool PPPpool[ASY_MAX];		/* For pooling peer IP addrs */
#endif

static int dopppaccomp __ARGS((int argc, char *argv[], void *p));
static int dopppactive __ARGS((int argc, char *argv[], void *p));
static int dopppauth __ARGS((int argc, char *argv[], void *p));
static int dopppclose __ARGS((int argc, char *argv[], void *p));
static int dopppctlmap __ARGS((int argc, char *argv[], void *p));
static int dopppmru __ARGS((int argc, char *argv[], void *p));
static int dopppipcomp __ARGS((int argc, char *argv[], void *p));
static int doppppassive __ARGS((int argc, char *argv[], void *p));
static int doppppeer __ARGS((int argc, char *argv[], void *p));
static int doppppool __ARGS((int argc, char *argv[], void *p));
static int dopppprotcomp __ARGS((int argc, char *argv[], void *p));
static int dopppreset __ARGS((int argc, char *argv[], void *p));
static int dopppstat __ARGS((int argc, char *argv[], void *p));
static int doppptrace __ARGS((int argc, char *argv[], void *p));
static int doppptimeout __ARGS((int argc, char *argv[], void *p));
static int dopapuser __ARGS((int argc, char *argv[], void *p));

static void genstat __ARGS((struct slip *sp));
static void pppstat __ARGS((struct slip *sp));
static void lcpstat __ARGS((struct lcpctl *lcpiop));
static void ipcpstat __ARGS((struct ipcpctl *ipcpiop));
static struct iface *ppplookup __ARGS((char *ifname));
static void dumppool __ARGS(());
static char *uptime __ARGS((long first, long second));

/* "ppp" subcommands */
static struct cmds Pppcmds[] = {
	"accompr",	dopppaccomp,	0,	2,
		"ppp accompr <iface> <int>",
	"activeopen",	dopppactive,	0,	2,
		"ppp activeopen <iface>",
	"authprot",	dopppauth,	0,	2,
		"ppp authprot <authtype>",
	"close",	dopppclose,	0,	2,
		"ppp close <iface>",
	"ctlmap",	dopppctlmap,	0,	2,
		"ppp ctlmap <iface> <int>",
	"ipcompr",	dopppipcomp,	0,	2,
		"ppp ipcompr <iface> <comprtype>",
	"mru",		dopppmru,	0,	2,
		"ppp mru <iface> <int>",
	"passiveopen",	doppppassive,	0,	2,
		"ppp passiveopen <iface>",
	"peeraddr",	doppppeer,	0,	2,
		"ppp peeraddr <iface> <addr>",
	"peerid",	dopapuser,	0,	2,
		"ppp peerid <iface> <peer ID code>",
	"pooladdr",	doppppool,	0,	1,
		"ppp pooladdr <addr>:<# addr in pool> [ <iface> ... ]",
	"protcompr",	dopppprotcomp,	0,	2,
		"ppp protcompr <iface> <int>",
	"reset",	dopppreset,	0,	2,
		"ppp reset <iface>",
	"status",	dopppstat,	0,	0,	NULLCHAR,
	"timeout",	doppptimeout,	0,	2,
		"ppp timeout <iface> <#sec>",
	"trace",	doppptrace,	0,	0,	NULLCHAR,
	NULLCHAR,
};

static char *PPPStatus[] = {
	"physical layer down",
	"waiting for link speed assignment message",
	"Closed",
	"waiting for LCP negotiation",
	"waiting for PAP verfication",
	"waiting for IPCP negotiation",
	"Open"
};
static char *LCPStatus[] = {
	"Closed",
	"Listen; waiting for remote host to attempt open",
	"Req Sent: Attempting to start configuration exchange",
	"Ack Rcvd: Remote host accepted our request; waiting for remote request",
	"Ack Sent: Waiting for reply to our request; accepted remote request",
	"Open; configuration negotiation complete",
	"Terminate request sent to remote host"
};

/****************************************************************************/

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

/****************************************************************************/

/* Initiate active open on a PPP interface */
static int
dopppactive(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	/* Look for the PPP interface */
	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	/* Start LCP configuration negotiation on interface */
	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	lcpiop->active = 1;
	if ((pppiop->state == PPP_PL_DOWN) || (pppiop->state == PPP_AUTOBAUD))
		return 0;
	else
		return(lcp_start(&Slip[ifp->xdev]));
}

/* Close connection on PPP interface */
static int
dopppclose(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;

	/* Look for the PPP interface */
	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	/* Close PPP connection */
	return(ppp_close(&Slip[ifp->xdev],1));
}

/* Set PPP interface to passive open; will revert to LISTEN on next close */
static int
doppppassive(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	/* Look for the PPP interface */
	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	/* Start LCP configuration negotiation on interface */
	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	lcpiop->active = 0;
	return 0;
}

/* Close and reopen connection on PPP interface */
static int
dopppreset(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct iface *ifp;
	struct slip *sp;
	struct pppctl *pp;
	struct slcompress *slp;

	/* Look for the PPP interface */
	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;
	sp = &Slip[ifp->xdev];
	pp = sp->pppio;

	/* Close PPP connection */
	ppp_close(&Slip[ifp->xdev],0);

#ifndef PPP_NO_STATS
	/* Zero PPP packet counters */
	pp->sndpkt = 0L;		/* # packets sent on PPP interface */
	pp->snderr = 0;			/* # pkts with PPP error on send */
	pp->rcvpkt = 0L;		/* # packets rcvd on PPP interface */
	pp->rcverr = 0;			/* # pkts with error */
	pp->csumerr = 0;		/* # rcv pkts with bad PPP checksum */

	pp->sndlcp = 0;			/* # LCP packets sent */
	pp->sndpap = 0;			/* # PAP packets sent */
	pp->sndipcp = 0;		/* # IPCP packets sent */
	pp->sndip = 0L;			/* # IP packets sent */
	pp->rcvlcp = 0;			/* # LCP packets received */
	pp->rcvpap = 0;			/* # PAP packets received */
	pp->rcvipcp = 0;		/* # IPCP packets received */
	pp->rcvip = 0L;			/* # IP packets received */
	pp->rcvunk = 0;			/* # unknown packets received */
#endif

#ifndef SL_NO_STATS
	slp = sp->slcomp;
	slp->sls_nontcp = 0L;		/* outbound non-TCP packets */
	slp->sls_asistcp = 0L;		/* outbound TCP packets */
	slp->sls_compressed = 0L;	/* outbound compressed packets */
	slp->sls_uncompressed = 0L;	/* outbound uncompressed packets */
	slp->sls_searches = 0L;		/* searches for connection state */
	slp->sls_misses = 0L;		/* times couldn't find conn. state */
	slp->sls_nontcpin = 0L;		/* inbound non-TCP packets */
	slp->sls_tcpin = 0L;		/* inbound TCP packets */
	slp->sls_uncompressedin = 0L;	/* inbound uncompressed packets */
	slp->sls_compressedin = 0L;	/* inbound compressed packets */
	slp->sls_errorin = 0L;		/* inbound unknown type packets */
	slp->sls_tossed = 0L;		/* inbound tossed because of error */
#endif

	/* Always restart as active open when PPP reset */
	pp->lcpio.active = 1;
	tprintf("Attempting to reopen PPP interface  %s\n",ifp->name);
	return(lcp_start(&Slip[ifp->xdev]));
}

/*******************************************/

/* Set Address/Control Compression for a PPP interface */
static int
dopppaccomp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int x;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct lcpparm *localp;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;
	--argc;++argv;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	localp = &(lcpiop->initparm);
	localp->neg_ac_compress = 1;
	return setbool(&(localp->ac_compress),
			"Addr/Ctl Field Compression",argc,argv);
}

/* Set authentication type for PPP interface */
static int
dopppauth(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int x;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct lcpparm *localp;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	localp = &(lcpiop->initparm);
	if (argc < 3) {
		tprintf("%s\n",((localp->auth_type == PAP_AUTH_TYPE) ? "PAP" : "None"));
	} else if ((strnicmp("pap",argv[2],2) == 0)
		    || ((int16)strtol(argv[2],NULLCHARP,0)==PAP_AUTH_TYPE)) {
		localp->neg_auth_type = 1;
		localp->auth_type = PAP_AUTH_TYPE;
	} else if ((stricmp("none",argv[2]) == 0)
		    || ((int16)strtol(argv[2],NULLCHARP,0) == 0)) {
		localp->neg_auth_type = 0;
		localp->auth_type = DEF_AUTH_TYPE;
	} else {
		tprintf("unknown Authentication Protocol type; command ignored\n");
		return 1;
	}
	return 0;
}

/* Set Async Control Map for a PPP interface */
static int
dopppctlmap(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	long lx;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct lcpparm *localp;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	localp = &(lcpiop->initparm);
	if (argc < 3) {
		tprintf("0x%08lx\n",localp->ctl_map);
	} else {
		lx = strtol(argv[2], NULLCHARP, 0);
		localp->neg_ctl_map = 1;
		localp->ctl_map = lx;
	}
	return 0;
}

/* Set preferred Maximum Receive Unit for a PPP interface */
static int
dopppmru(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int x;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct lcpparm *localp;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	localp = &(lcpiop->initparm);
	if (argc < 3) {
		tprintf("%d\n",localp->mru);
	} else {
		x = atoi(argv[2]);
		if ((x < MIN_MRU)||(x > DEF_MRU)) {
			tprintf("Command rejected; MRU out of range: Min MRU: %d  Max MRU: %d\n",
				MIN_MRU,DEF_MRU);
			return -1;
		} else {
			localp->neg_mru = 1;
			localp->mru = x;
		}
	}
	return 0;
}

/* Set Protocol Compression for a PPP interface */
static int
dopppprotcomp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int x;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct lcpparm *localp;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;
	--argc;++argv;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	localp = &(lcpiop->initparm);
	localp->neg_prot_compress = 1;
	return setbool(&(localp->prot_compress),
			"Protocol Field Compression",argc,argv);
}

/* Set timeout interval when waiting for response from remote peer */
static int
doppptimeout(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int x;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct ipcpctl *ipcpiop;
	struct timer *t;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	t = &(lcpiop->lcp_tm);
	if (argc < 3) {
		tprintf("%d\n",(t->start/(1000L/MSPTICK)));
	} else {
		x = atoi(argv[2]);
		if (x <= 0) {
			tprintf("Command rejected; timeout value must be > 0\n");
			return -1;
		} else {
			t->start = x*(1000L/MSPTICK);
			ipcpiop = &(pppiop->ipcpio);
			t = &(ipcpiop->ipcp_tm);
			t->start = x*(1000L/MSPTICK);
		}
	}
	return 0;
}

/* Set peer ID string to send with PAP AUTH_REQ */
static int
dopapuser(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int x;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	lcpiop = &(pppiop->lcpio);
	if (argc < 3) {
		tprintf("%s\n",((lcpiop->pap_user==NULLCHAR)?"None":lcpiop->pap_user));
	} else if (strnicmp("none",argv[2],2) == 0) {
		if (lcpiop->pap_user != NULLCHAR)
			free(lcpiop->pap_user);
	} else {
		if (lcpiop->pap_user != NULLCHAR)
			free(lcpiop->pap_user);
		if (lcpiop->pap_pass != NULLCHAR)
			free(lcpiop->pap_pass);
		lcpiop->pap_user = mallocw(strlen(argv[2])+1);
		strcpy(lcpiop->pap_user,argv[2]);
		pap_getpass(&Slip[ifp->xdev],0);
	}
	return 0;
}


/*******************************************/

/* Set IP compression type for PPP interface */
static int
dopppipcomp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int32 x32;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct ipcpctl *ipcpiop;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	ipcpiop = &(pppiop->ipcpio);
	if (argc < 3) {
		if (ipcpiop->ip_compr_type == IPCP_VJCOMPR)
			tprintf("Van Jacobson TCP header compression enabled\n");
		else
			tprintf("IP compression disabled\n");
	} else if ((strnicmp("vj",argv[2],2) == 0)
		    || ((int16)strtol(argv[2],NULLCHARP,0) == IPCP_VJCOMPR)) {
		ipcpiop->neg_ip_compr = 1;
		ipcpiop->ip_compr_type = IPCP_VJCOMPR;
	} else if ((int16)strtol(argv[2],NULLCHARP,0) == 0) {
		ipcpiop->neg_ip_compr = 1;
		ipcpiop->ip_compr_type = 0;
	} else {
		tprintf("unknown IP compression type; command ignored\n");
		return 1;
	}
	return 0;
}

/* Set peer address for PPP interface */
static int
doppppeer(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int32 x32;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct ipcpctl *ipcpiop;

	if ((ifp = ppplookup(argv[1])) == NULLIF)
		return -1;

	pppiop = Slip[ifp->xdev].pppio;
	ipcpiop = &(pppiop->ipcpio);
	if (argc < 3) {
		tprintf("%s\n",inet_ntoa(ipcpiop->peer_addr));
	} else if ((x32 = resolve(argv[2])) == 0L) {
		tprintf(Badhost,argv[2]);
		return 1;
	} else {
		ipcpiop->peer_addr = x32;
	}
	return 0;
}

/* Set a pool of peer addresses for PPP interface */
static int
doppppool(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int pool;
	int pool_cnt;
	char *bitp;
	int32 pool_addr;
	int32 pool_mask;
	struct ipcppool *poolp;
	struct iface *ifp;
	struct pppctl *pppiop;
	struct ipcpctl *ipcpiop;

	if (argc < 2) {
		dumppool();
		return 0;
	}

	if (Npool > ASY_MAX) {
		tprintf("Too many PPP address pools -- command rejected\n");
		return -1;
	}

	/* Need at least one IP address for the pool */
	if((pool_addr = resolve(argv[1])) == 0){
		tprintf("Must specify an IP address pool\n");
		return -1;
	}
	/* May specify a consecutive range of addresses; otherwise assume 1 */
	if((bitp = strchr(argv[1],':')) != NULLCHAR){
		bitp++;
		pool_cnt = atoi(bitp);
	} else
		pool_cnt = 1;
	if (pool_cnt < 0) {
		tprintf("Unreasonable address range  --  command rejected\n");
		return -1;
	}

	if (argc < 3) {
		printf("Must specify a PPP interface\n");
		return -1;
	}

	pool = Npool++;
	poolp = &(PPPpool[pool]);
	poolp->peer_min = pool_addr;
	poolp->peer_max = pool_addr + pool_cnt - 1;
	poolp->peer_next = pool_addr;

	argc -= 2;argv++;
	while (argc--) {
		argv++;
		if ((ifp = ppplookup(*argv)) == NULLIF)
			continue;
		pppiop = Slip[ifp->xdev].pppio;
		pppiop->ipcpio.peer_pool = &(PPPpool[pool]);
	}
	return 0;
}

/****************************************************************************/

static int
doppptrace(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setshort(&ppptrace,"PPP tracing",argc,argv);
}

/****************************************************************************/

static int
dopppstat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct slip *sp;
	struct iface *ifp;

	if (argc < 2) {
		for (sp = Slip;sp < &Slip[Nasy];sp++) {
			if (sp->type != CL_PPP)
				continue;
			genstat(sp);
		}
	} else {
		if ((ifp = ppplookup(argv[1])) == NULLIF)
			return -1;
		sp = &Slip[ifp->xdev];
		genstat(sp);
		pppstat(sp);
		lcpstat(&(sp->pppio->lcpio));
		ipcpstat(&(sp->pppio->ipcpio));
	}
	return 0;
}

static void
genstat(sp)
struct slip *sp;
{
	register struct pppctl *pp;

	pp = sp->pppio;
	tprintf("%s:\t",sp->iface->name);
	tprintf("SndPkt: %ld  SndErr: %d  RcvPkt: %ld  RcvErr: %d  ChksumErr: %d\n",
		pp->sndpkt,pp->snderr,pp->rcvpkt,pp->rcverr,pp->csumerr);
	return;
}

static void
pppstat(sp)
struct slip *sp;
{
	char *cp;
	register struct pppctl *pp;
	register struct slcompress *scp;

	pp = sp->pppio;
	scp = sp->slcomp;
	tprintf("\tOverall PPP state: %s",
		PPPStatus[pp->state]);
	if (pp->state == PPP_OPEN) {
		tprintf("   (open for %s)\n",uptime(pp->upsince,time(0L)));
	} else {
		tprintf("\n");
	}
	tprintf("\t    SndIP: %ld  SndLCP: %d  SndPAP: %d  SndIPCP: %d\n",
		pp->sndip,pp->sndlcp,pp->sndpap,pp->sndipcp);
	if (sp->escaped & PPP_XMT_VJCOMPR) {
		tprintf("\t      (IP) NotTCP: %ld  AsIsTCP: %ld  CmpTCP: %ld  UncmpTCP: %ld\n",
			scp->sls_nontcp,scp->sls_asistcp,scp->sls_compressed,scp->sls_uncompressed);
	}
	tprintf("\t    RcvIP: %ld  RcvLCP: %d  RcvPAP: %d  RcvIPCP: %d  RcvUnknown: %d\n",
		pp->rcvip,pp->rcvlcp,pp->rcvpap,pp->rcvipcp,pp->rcvunk);
	if (sp->escaped & PPP_RCV_VJCOMPR) {
		tprintf("\t      (IP) NotTCP: %ld  AsIsTCP: %ld  CmpTCP: %ld  UncmpTCP: %ld\n",
			scp->sls_nontcpin,scp->sls_tcpin,scp->sls_compressedin,scp->sls_uncompressedin);
		tprintf("\t           Unknown: %ld  CmpError: %ld\n",
			scp->sls_errorin,scp->sls_tossed);
	}
	return;
}

static void
lcpstat(lcpiop)
struct lcpctl *lcpiop;
{
	struct lcpparm *localp, *remotep;

	localp = &(lcpiop->lclparm);
	remotep = &(lcpiop->remparm);

	tprintf("\tLCP state: %s\n",LCPStatus[lcpiop->lcp_state]);
	tprintf("\t\t\t\tLocal options\tRemote options\n");
	tprintf("\t    MRU:\t\t%d\t\t%d\n",
		localp->mru,remotep->mru);
	tprintf("\t    Ctl Map:\t\t0x%08lx\t0x%08lx\n",
		localp->ctl_map,remotep->ctl_map);
	tprintf("\t    Auth Prot:\t\t%s\t\t%s\n",
		((localp->auth_type == PAP_AUTH_TYPE) ? "PAP" : "None"),
		((remotep->auth_type == PAP_AUTH_TYPE) ? "PAP" : "None"));
	tprintf("\t    Protocol Compr:\t%s\t\t%s\n",
		(localp->prot_compress ? "ON" : "OFF"),
		(remotep->prot_compress ? "ON" : "OFF"));
	tprintf("\t    Addr/Ctl Compr:\t%s\t\t%s\n",
		(localp->ac_compress ? "ON" : "OFF"),
		(remotep->ac_compress ? "ON" : "OFF"));
}

static void
ipcpstat(ipcpiop)
struct ipcpctl *ipcpiop;
{
	tprintf("\tIPCP state: %s\n",LCPStatus[ipcpiop->ipcp_state]);
	tprintf("\t    IPCP local IP address:\t%s\n",
		inet_ntoa(ipcpiop->attempt_src));
	tprintf("\t    IPCP remote IP address:\t%s\n",
		inet_ntoa(ipcpiop->attempt_dest));
	if (ipcpiop->lcl_ip_compr == IPCP_VJCOMPR)
		tprintf("\t    Rcv: Van Jacobson TCP header compression enabled\n");
	else
		tprintf("\t    Rcv: IP compression disabled\n");
	if (ipcpiop->rem_ip_compr == IPCP_VJCOMPR)
		tprintf("\t    Snd: Van Jacobson TCP header compression enabled\n");
	else
		tprintf("\t    Snd: IP compression disabled\n");

	return;
}

/****************************************************************************/

static void
dumppool()
{
	struct iface *ifp;
	struct pppctl *pppiop;
	struct ipcppool *poolp;

	tprintf("Interface        Pool Base/Count\tNext Address\n");
	for (ifp=Ifaces; ifp != NULLIF; ifp = ifp->next) {
		if ((Slip[ifp->xdev].iface == ifp) &&
		    (Slip[ifp->xdev].type == CL_PPP)) {
			pppiop = Slip[ifp->xdev].pppio;
			poolp = pppiop->ipcpio.peer_pool;
			if (poolp == NULLPOOL)
				continue;
			tprintf("%-11s%19s/%-2d",
				ifp->name,inet_ntoa(poolp->peer_min),
				(poolp->peer_max - poolp->peer_min + 1));
			tprintf("\t%s\n",inet_ntoa(poolp->peer_next));
		}
	}
	return;
}

static struct iface *
ppplookup(ifname)
char *ifname;
{
	struct iface *ifp;

	for (ifp=Ifaces;ifp != NULLIF;ifp = ifp->next) {
		if (!strcmp(ifname,ifp->name))
			break;
	}
	if (ifp == NULLIF) {
		tprintf("Interface %s unknown\n",ifname);
		return(NULLIF);
	} else if (ifp->type != CL_PPP) {
		tprintf("Interface %s not a PPP interface\n",ifp->name);
		return(NULLIF);
	} else {
		return(ifp);
	}
}


/* Break a time differential, measured in seconds, into weeks, days      */
/* hours, minutes, and seconds. Store ASCII description in static buffer */
#define SECS_MIN	60L
#define SECS_HR		3600L
#define SECS_DAY	86400L
#define SECS_WEEK	604800L

static char utbuf[128];

static char *
uptime(first, second)
long first;
long second;
{
	int found = 0;
	long diff;
	long part;

	utbuf[0] = '\0';
	diff = second - first;
	if ((diff > SECS_DAY)||(found)) {
		part = diff / SECS_DAY;
		sprintf(&(utbuf[strlen(utbuf)]),
			"%ld day%s ",part,((part==1)?",":"s,"));
		diff -= (part * SECS_DAY);
		found = 100;
	}
	if ((diff > SECS_HR)||(found)) {
		part = diff / SECS_HR;
		sprintf(&(utbuf[strlen(utbuf)]),
			"%ld hr%s ",part,((part==1)?",":"s,"));
		diff -= (part * SECS_HR);
		++found;
	}
	if ((diff > SECS_MIN)||(found)) {
		part = diff / SECS_MIN;
		sprintf(&(utbuf[strlen(utbuf)]),"%ld mi%s",part,
			((found < 100)?"n, ":"n"));
		diff -= (part * SECS_MIN);
	}
	if (found < 100)
		sprintf(&(utbuf[strlen(utbuf)]),"%ld sec",diff);
	return(utbuf);
}
