/*
 *  PPPCMD.C	-- PPP related user commands
 *
 *	This implementation of PPP is declared to be in the public domain.
 *
 *	Jan 91	Bill_Simpson@um.cc.umich.edu
 *		Computer Systems Consulting Services
 *
 *	Acknowledgements and correction history may be found in PPP.C
 */

#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "pktdrvr.h"
#include "ppp.h"
#include "pppfsm.h"
#include "ppplcp.h"
#include "ppppap.h"
#include "pppipcp.h"
#include "cmdparse.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: pppcmd.c,v 1.15 1996/12/23 20:37:36 root Exp $";
#endif


#ifdef PPP

static struct iface *ppp_lookup (char *ifname);
static int doppp_quick (int argc, char *argv[], void *p);
static int doppp_trace (int argc, char *argv[], void *p);
static int spot (int16 work, int16 want, int16 will, int16 mask);
static void genstat (struct ppp_s * ppp_p);
static void lcpstat (struct fsm_s * fsm_p);
static void papstat (struct fsm_s * fsm_p);
static void ipcpstat (struct fsm_s * fsm_p);
static int dotry_nak (int argc, char *argv[], void *p);
static int dotry_req (int argc, char *argv[], void *p);
static int dotry_terminate (int argc, char *argv[], void *p);



/* "ppp" subcommands */
static struct cmds Pppcmds[] =
{
	{ "ipcp",	doppp_ipcp,	0, 0, NULLCHAR },
	{ "lcp",	doppp_lcp,	0, 0, NULLCHAR },
	{ "pap",	doppp_pap,	0, 0, NULLCHAR },
	{ "quick",	doppp_quick,	0, 0, NULLCHAR },
	{ "trace",	doppp_trace,	0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};


/* { "ppp <iface> <ncp> try" subcommands */
static struct cmds PppTrycmds[] =
{
	{ "configure",	dotry_req,	0, 0, NULLCHAR },
	{ "failure",	dotry_nak,	0, 0, NULLCHAR },
	{ "terminate",	dotry_terminate,0, 0, NULLCHAR },
	{ NULLCHAR,	NULL,		0, 0, NULLCHAR }
};


static const char *PPPStatus[] =
{
	"Physical Line Dead",
	"Establishment Phase",
	"Authentication Phase",
	"Network Protocol Phase",
	"Termination Phase"
};


static const char *NCPStatus[] =
{
	"Closed",
	"Listening -- waiting for remote host to attempt open",
	"Starting configuration exchange",
	"Remote host accepted our request; waiting for remote request",
	"We accepted remote request; waiting for reply to our request",
	"Opened",
	"Terminate request sent to remote host"
};


#ifdef CATALOG
#include "catalog.h"

#define CAT pppcmd_catalog

#define ifaceunk	__STR(0)
#define notPPP		__STR(1)
#define syntaxPPP	__STR(2)
#define PPPtracing	__STR(3)

#else /* CATALOG */

static const char ifaceunk[] = "%s: Interface unknown\n";
static const char notPPP[] = "%s: not a PPP interface\n";
static const char syntaxPPP[] = "ppp <iface> required\n";
static const char PPPtracing[] = "PPP tracing";

#endif /* CATALOG */

static const char stat1[] = "%10lu In,  %10lu Flags,%6u ME, %6u FE, %6u CSE, %6u other\n";
static const char stat2[] = "\t\t%6u Lcp,%6u Pap,%6u IPcp,%6u Unknown\n";
static const char stat3[] = "%10lu Out, %10lu Flags,%6u ME, %6u Fail\n";
static const char stat4[] = "\t\t%6u Lcp,%6u Pap,%6u IPcp\n";
static const char PAPstr[] = "PAP %s\n";
static const char LCPstr[] = "LCP %s\n";
static const char header[] = "\t\t MRU\t ACCM\t\t AP\t PFC  ACFC Magic\n\tLocal:\t";
static const char yes[] = "Yes ";
static const char no[] = "No  ";
static const char unused[] = "unused\n";
static const char none[] = "None\t";
static const char none2[] = "none";
static const char papmessage[] = "\tMessage: '%s'\n";
static const char IPCPstr[] = "IPCP %s\n";
static const char localaddr[] = "\tlocal IP address: %s";
static const char remoteaddr[] = "  remote IP address: %s\n";
static const char ipcpstatstr[] = "   %s\tTCP header compression enabled: slots = %d, flag = 0x%02x\n";
static const char badval[] = "Value %s (%d) must be > 0\n";
static const char intstr[] = "%d\n";
static const char longstr[] = "%d\n";
static const char openfor[] = "\t(open for %s)";
static const char hexstr[] = "0x%08lx%c";
static const char hexstr2[] = "0x%04x\t";
static const char paptab[] = "Pap\t";
static const char remotestr[] = "\tRemote:\t";
static const char int4str[] = "%4d\t";


int PPPtrace;
struct iface *PPPiface;		/* iface for trace */


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

static struct iface *
ppp_lookup (char *ifname)
{
register struct iface *ifp;

	if ((ifp = if_lookup (ifname)) == NULLIF) {
		tprintf (ifaceunk, ifname);
		return (NULLIF);
	}
	if (ifp->type != CL_PPP) {
		tprintf (notPPP, ifp->name);
		return (NULLIF);
	}
	return (ifp);
}



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

int
doppp_commands (int argc, char *argv[], void *p OPTIONAL)
{
register struct iface *ifp;

	if (argc < 2) {
		tputs (syntaxPPP);
		return -1;
	}
	if ((ifp = ppp_lookup (argv[1])) == NULLIF)
		return -1;

	if (argc == 2) {
		ppp_show (ifp);
		return 0;
	}
	return subcmd (Pppcmds, argc - 1, &argv[1], ifp);
}



/* Close connection on PPP interface */
int
doppp_close (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
register struct fsm_s *fsm_p = p;

	fsm_p->flags &= ~(FSM_ACTIVE | FSM_PASSIVE);

	fsm_close (fsm_p);
	return 0;
}



int
doppp_passive (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
register struct fsm_s *fsm_p = p;

	fsm_p->flags &= ~FSM_ACTIVE;
	fsm_p->flags |= FSM_PASSIVE;

	fsm_start (fsm_p);
	return 0;
}



int
doppp_active (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
register struct fsm_s *fsm_p = p;

	fsm_p->flags &= ~FSM_PASSIVE;
	fsm_p->flags |= FSM_ACTIVE;

	if (fsm_p->state < fsmLISTEN)
		fsm_p->state = fsmLISTEN;
	return 0;
}



static int
doppp_quick (int argc OPTIONAL, char *argv[] OPTIONAL, void *p)
{
register struct iface *ifp = p;
register struct ppp_s *ppp_p = ifp->edv;
struct lcp_s *lcp_p = ppp_p->fsm[Lcp].pdv;
struct ipcp_s *ipcp_p = ppp_p->fsm[IPcp].pdv;

	lcp_p->local.want.accm = 0L;
	lcp_p->local.want.negotiate |= LCP_N_ACCM;
	lcp_p->local.want.magic_number += (uint32) &lcp_p->local.want.magic_number;
	lcp_p->local.want.negotiate |= LCP_N_MAGIC;
	lcp_p->local.want.negotiate |= LCP_N_ACFC;
	lcp_p->local.want.negotiate |= LCP_N_PFC;

	ipcp_p->local.want.compression = PPP_COMPR_PROTOCOL;
	ipcp_p->local.want.slots = 16;
	ipcp_p->local.want.slot_compress = 1;
	ipcp_p->local.want.negotiate |= IPCP_N_COMPRESS;
	(void) doppp_active (0, NULL, &(ppp_p->fsm[IPcp]));

	return 0;
}



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

void
ppp_show (struct iface *ifp)
{
register struct ppp_s *ppp_p = ifp->edv;

	genstat (ppp_p);
	if (ppp_p->fsm[Lcp].pdv != NULL)
		lcpstat (&(ppp_p->fsm[Lcp]));
	if (ppp_p->fsm[Pap].pdv != NULL)
		papstat (&(ppp_p->fsm[Pap]));
	if (ppp_p->fsm[IPcp].pdv != NULL)
		ipcpstat (&(ppp_p->fsm[IPcp]));
}



static void
genstat (register struct ppp_s *ppp_p)
{

	tputs (PPPStatus[ppp_p->phase]);

	if (ppp_p->phase == pppREADY)
		tprintf (openfor, tformat (secclock () - ppp_p->upsince));

	tputc ('\n');

	tprintf (stat1, ppp_p->InRxOctetCount, ppp_p->InOpenFlag, ppp_p->InMemory,
		 ppp_p->InFrame, ppp_p->InChecksum, ppp_p->InError);
	tprintf (stat2, ppp_p->InNCP[Lcp], ppp_p->InNCP[Pap],
		 ppp_p->InNCP[IPcp], ppp_p->InUnknown);
	tprintf (stat3, ppp_p->OutTxOctetCount, ppp_p->OutOpenFlag,
		 ppp_p->OutMemory, ppp_p->OutError);
	tprintf (stat4, ppp_p->OutNCP[Lcp], ppp_p->OutNCP[Pap], ppp_p->OutNCP[IPcp]);
}



static int
spot (int16 work, int16 want, int16 will, int16 mask)
{
	char blot = ' ';
	int result = (work & mask);

	if (!(will & mask))
		blot = '*';
	else if ((want ^ work) & mask)
		blot = (result ? '+' : '-');

	tputc (uchar (blot));
	return result;
}



static void
lcpstat (struct fsm_s *fsm_p)
{
struct lcp_s *lcp_p = fsm_p->pdv;
struct lcp_value_s *localp = &(lcp_p->local.work);
int16 localwork = lcp_p->local.work.negotiate;
int16 localwant = lcp_p->local.want.negotiate;
int16 localwill = lcp_p->local.will_negotiate;
struct lcp_value_s *remotep = &(lcp_p->remote.work);
int16 remotework = lcp_p->remote.work.negotiate;
int16 remotewant = lcp_p->remote.want.negotiate;
int16 remotewill = lcp_p->remote.will_negotiate;

	tprintf (LCPstr, NCPStatus[fsm_p->state]);

	tputs (header);

	/*lint -esym(534,spot) */
	spot (localwork, localwant, localwill, LCP_N_MRU);
	tprintf (int4str, localp->mru);

	spot (localwork, localwant, localwill, LCP_N_ACCM);
	tprintf (hexstr, localp->accm, '\t');

	if (spot (localwork, localwant, localwill, LCP_N_AUTHENT)) {
		switch (localp->authentication) {
			case PPP_PAP_PROTOCOL:
				tputs (paptab);
				break;
			default:
				tprintf (hexstr2, localp->authentication);
				break;
		}
	} else
		tputs (none);

	tprintf (spot (localwork, localwant, localwill, LCP_N_PFC) ? yes : no);
	tprintf (spot (localwork, localwant, localwill, LCP_N_ACFC) ? yes : no);

	spot (localwork, localwant, localwill, LCP_N_MAGIC);
	if (localp->magic_number != 0L)
		tprintf (hexstr, localp->magic_number, '\n');
	else
		tputs (unused);

	tputs (remotestr);

	spot (remotework, remotewant, remotewill, LCP_N_MRU);
	tprintf (int4str, remotep->mru);

	spot (remotework, remotewant, remotewill, LCP_N_ACCM);
	tprintf (hexstr, remotep->accm, '\t');

	if (spot (remotework, remotewant, remotewill, LCP_N_AUTHENT)) {
		switch (remotep->authentication) {
			case PPP_PAP_PROTOCOL:
				tputs (paptab);
				break;
			default:
				tprintf (hexstr2, remotep->authentication);
				break;
		}
	} else
		tputs (none);

	tprintf (spot (remotework, remotewant, remotewill, LCP_N_PFC) ? yes : no);
	tprintf (spot (remotework, remotewant, remotewill, LCP_N_ACFC) ? yes : no);

	spot (remotework, remotewant, remotewill, LCP_N_MAGIC);
	if (remotep->magic_number != 0L)
		tprintf (hexstr, remotep->magic_number, '\n');
	else
		tputs (unused);
}



static void
papstat (struct fsm_s *fsm_p)
{
struct pap_s *pap_p = fsm_p->pdv;

	tprintf (PAPstr, NCPStatus[fsm_p->state]);
	tprintf (papmessage, (pap_p->message == NULL) ? none2 : pap_p->message);
}



static void
ipcpstat (struct fsm_s *fsm_p)
{
struct ipcp_s *ipcp_p = fsm_p->pdv;
struct ipcp_value_s *localp = &(ipcp_p->local.work);
int16 localwork = ipcp_p->local.work.negotiate;
struct ipcp_value_s *remotep = &(ipcp_p->remote.work);
int16 remotework = ipcp_p->remote.work.negotiate;

	tprintf (IPCPstr, NCPStatus[fsm_p->state]);
	tprintf (localaddr, inet_ntoa (localp->address));
	tprintf (remoteaddr, inet_ntoa (localp->other));

	if (localwork & IPCP_N_COMPRESS) {
		tprintf (ipcpstatstr, " In", localp->slots, localp->slot_compress);
		slhc_i_status (ipcp_p->slhcp);
	}
	if (remotework & IPCP_N_COMPRESS) {
		tprintf (ipcpstatstr, "Out", remotep->slots, remotep->slot_compress);
		slhc_o_status (ipcp_p->slhcp);
	}
}



/****************************************************************************/
/* Set timeout interval when waiting for response from remote peer */
int
doppp_timeout (int argc, char *argv[], void *p)
{
struct fsm_s *fsm_p = p;
struct timer *t = &(fsm_p->timer);

	if (argc < 2)
		tprintf (longstr, dur_timer (t) / 1000L);
	else {
		int x = (int) strtol (argv[1], NULLCHARP, 0);

		if (x <= 0) {
			tprintf (badval, argv[1], x);
			return -1;
		} else
			set_timer (t, x * 1000L);
	}
	return 0;
}



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



static int
dotry_nak (int argc, char *argv[], void *p)
{
struct fsm_s *fsm_p = p;

	if (argc < 2)
		tprintf (intstr, fsm_p->try_nak);
	else {
		int x = (int) strtol (argv[1], NULLCHARP, 0);

		if (x <= 0) {
			tprintf (badval, argv[1], x);
			return -1;
		} else
			fsm_p->try_nak = uchar (x);
	}
	return 0;
}



static int
dotry_req (int argc, char *argv[], void *p)
{
struct fsm_s *fsm_p = p;

	if (argc < 2)
		tprintf (intstr, fsm_p->try_req);
	else {
		int x = (int) strtol (argv[1], NULLCHARP, 0);

		if (x <= 0) {
			tprintf (badval, argv[1], x);
			return -1;
		} else
			fsm_p->try_req = uchar (x);
	}
	return 0;
}



static int
dotry_terminate (int argc, char *argv[], void *p)
{
struct fsm_s *fsm_p = p;

	if (argc < 2)
		tprintf (intstr, fsm_p->try_terminate);
	else {
		int x = (int) strtol (argv[1], NULLCHARP, 0);

		if (x <= 0) {
			tprintf (badval, argv[1], x);
			return -1;
		} else
			fsm_p->try_terminate = uchar (x);
	}
	return 0;
}



static int
doppp_trace (int argc, char *argv[], void *p)
{
register struct iface *ifp = p;
register struct ppp_s *ppp_p = ifp->edv;
int tracing = ppp_p->trace;
int result = setint (&tracing, PPPtracing, argc, argv);

	ppp_p->trace = uchar (tracing);
	return result;
}


#endif /* PPP */
