/*
 *  PPPPAP.C	-- Password Authenticate Protocol for PPP
 *
 *	12-89	-- Katie Stevens (dkstevens@ucdavis.edu)
 *		   UC Davis, Computing Services
 *	PPP.09	05-90	[ks] add authentication phase
 *	PPP.10	06-90	[ks] improve keybd input of PAP password
 *			     make ppp open/close/reset work properly
 *	PPP.14	08-90	[ks] change PAP to PPP for consistency with RFC1172
 *	PPP.15	09-90	[ks] update to KA9Q NOS v900828
 */

#include <stdio.h>
#include "global.h"
#include "files.h"
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "ppp.h"
#include "slip.h"
#include "session.h"

/* Counter for PPP id field */
extern unsigned char pppid;

/* PPP tracing */
extern int ppptrace;

static void pap_open __ARGS((struct slip *sp));

static int pap_authen __ARGS((char *peerid, char *pass));
void pap_input __ARGS((int mustask, void *v1, void *v2));
static void pap_pwdlookup __ARGS((struct lcpctl *lcpiop));

static int pap_sendreq __ARGS((struct slip *sp));
static struct mbuf *pap_makereq __ARGS((struct lcpctl *lcpiop));

static void pap_rcvack __ARGS((struct slip *sp, struct cnfhdr *rcnf,
			struct mbuf *data));
static void pap_rcvnak __ARGS((struct slip *sp, struct cnfhdr *rcnf,
			struct mbuf *data));
static void pap_rcvreq __ARGS((struct slip *sp, struct cnfhdr *rcnf,
			struct mbuf *data));
static void pap_shutdown __ARGS((struct slip *sp));

static int pap_chkack __ARGS((struct slip *sp, struct cnfhdr *ackcnf,
			struct mbuf *data));
static int pap_chknak __ARGS((struct slip *sp, struct cnfhdr *nakcnf,
			struct mbuf *data));
static void pap_chkreq __ARGS((struct slip *sp, struct cnfhdr *reqcnf,
			struct mbuf *data));

static void pap_timeout __ARGS((void *vp));
static void pap_timer __ARGS((struct slip *sp));

static int pap_sendreply __ARGS((struct slip *sp, char code,
			unsigned char id, struct mbuf *data));

/* Possible PAP states */
static char *PAPStates[] = {
	"Closed",
	"Listen",
	"Req Sent",
	"Req Rcvd",
	"Open",
};

/* Possible PAP packet types */
static char *PAPCodes[] = {
	NULLCHAR,
	"Auth Req",
	"Auth Ack",
	"Auth Nak",
};

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

/* Initialize Password Auth Protocol state machine for config exchange */
int
pap_start(sp)
struct slip *sp;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if (ppptrace > 5)
		log(-1, "pap_start()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* Ready for PAP phase */
	pppiop->state = PPP_PAP;
	pap_reset(pppiop);

	/* Just finished LCP negotiation; prepare for PAP negotiation */
	if (lcpiop->lclparm.auth_type == PAP_AUTH_TYPE) {
		/* We asked remote to send AUTH_REQ; wait for that packet */
		lcpiop->pap_state = PAP_LISTEN;
		return 0;
	}

	/* Remote host asked us to send AUTH_REQ */
	lcpiop->pap_state = PAP_CLOSED;
	/* We need to send AUTH_REQ to remote host */
	/* Get the peer ID and password we will send */
	if ((lcpiop->pap_user == NULLCHAR)||(lcpiop->pap_pass == NULLCHAR)) {
		pap_getpass(sp,0);
		pwait(&(lcpiop->pap_user));
		if ((lcpiop->pap_user == NULLCHAR) ||
		    (lcpiop->pap_pass == NULLCHAR))
			return -1;
	}
	return(pap_sendreq(sp));
}

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

/* Initialize our PAP configuration options */
void
pap_init(sp)
struct slip *sp;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if (ppptrace > 5)
		log(-1, "pap_init()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* PAP layer closed; dont know local peerID/password values */
	lcpiop->pap_state = PAP_CLOSED;
	lcpiop->pap_user = NULLCHAR;
	lcpiop->pap_pass = NULLCHAR;
	return;
}

/* IP Control configuration negotiation complete */
static void
pap_open(sp)
struct slip *sp;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* Mark PAP as completed */
	if (ppptrace)
		log(-1,"%s: PPP/PAP: peer ID verified %s",
			sp->iface->name,lcpiop->pap_user);
	lcpiop->pap_state = PAP_OPEN;

	/* Ready for IPCP negotiation phase */
	ipcp_start(sp);
	return;
}

/* Reset PAP configuration options for initial request */
int
pap_reset(pppiop)
struct pppctl *pppiop;
{
	struct lcpctl *lcpiop;

	if (ppptrace > 5)
		log(-1, "pap_reset()");

	lcpiop = &(pppiop->lcpio);
	lcpiop->pap_state = PAP_CLOSED;
	lcpiop->ack_retry = 0;

	return 0;
}

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

/* Verify peer ID and password sent by remote host with PAP AUTH_REQ */
static int
pap_authen(peerid,pass)
char *peerid;
char *pass;
{
	int privs;
	char *path;
	int anony = 0;

	/* Use same login as FTP server */
	path = mallocw(128);
	privs = userlogin(peerid,pass,&path,128,&anony);
	free(path);

	/* Check privs for this peer ID */
	if (privs == -1) {
		if (ppptrace > 2)
			log(-1,
			   "PAP peerID/password incorrect or not found: %s",
			   peerid);
		return -1;
	}
	if ((privs & PPP_ACCESS_PRIV) == 0) {
		if (ppptrace > 2)
			log(-1,"PAP no permission for PPP access: %s",peerid);
		return -1;
	}
	return 0;
}

/* Get peerID and password to send to remote host with PAP AUTH_REQ */
int
pap_getpass(sp,mustask)
struct slip *sp;
int mustask;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);
	if (mustask) {
		if (lcpiop->pap_user != NULLCHAR) {
			free(lcpiop->pap_user);
			lcpiop->pap_user = NULLCHAR;
		}
		if (lcpiop->pap_pass != NULLCHAR) {
			free(lcpiop->pap_pass);
			lcpiop->pap_pass = NULLCHAR;
		}
	} else if ((lcpiop->pap_user != NULLCHAR)
			&&(lcpiop->pap_pass == NULLCHAR)) {
		pap_pwdlookup(lcpiop);
	}
	if ((lcpiop->pap_user == NULLCHAR)||(lcpiop->pap_pass == NULLCHAR)) {
		newproc("PAP Input",256,pap_input,mustask,
			 (void *)sp->iface,
			 (void *)lcpiop);
	}
	return 0;
}

void
pap_input(mustask, v1, v2)
int mustask;
void *v1;
void *v2;
{
	struct iface *iface;
	struct lcpctl *lcpiop;
	char buf[21];
	struct session *sp;

	iface = (struct iface *)v1;
	lcpiop = (struct lcpctl *)v2;

	/* Allocate a session control block */
	if((sp = newsession("PPP/PAP Auth",PPPPASS)) == NULLSESSION){
		tprintf("Too many sessions\n");
		return;
	}

	if (mustask)
		tprintf("\n%s: PPP/PAP Password Authentication Failed; enter peer ID and password again\n",
			iface->name);
	else
		tprintf("\n%s: PPP/PAP Password Authentication Required\n",
			iface->name);
	tprintf("%s: PPP/PAP  Peer ID: ",iface->name);
	usflush(sp->output);

	/* Only ask for the peer ID if it is unknown */
	if (lcpiop->pap_user == NULLCHAR) {
		recvline(sp->input,buf,20);
		/* Send the command only if the user response
		 * was non-null
		 */
		if (buf[0] == '\n')
			lcpiop->pap_user = NULLCHAR;
		else {
			rip(buf);
			lcpiop->pap_user = mallocw(strlen(buf)+1);
			strcpy(lcpiop->pap_user,buf);
		}
	} else {
		tprintf("%s\n",lcpiop->pap_user);
	}

	/* Always ask for the password */
	/* turn off echo */
	sp->ttystate.echo = 0;
	tprintf("%s: PPP/PAP Password: ",iface->name);
	usflush(sp->output);
	recvline(sp->input,buf,20);
	tprintf("\n");
	/* Turn echo back on */
	sp->ttystate.echo = 1;
	/* Save the password only if the user response was non-null */
	if (buf[0] == '\n')
		lcpiop->pap_pass = NULLCHAR;
	else {
		rip(buf);
		lcpiop->pap_pass = mallocw(strlen(buf)+1);
		strcpy(lcpiop->pap_pass,buf);
	}

	if (sp != NULLSESSION)
		freesession(sp);

	psignal(&(lcpiop->pap_user),0);
	return;	
}

/* Check the FTP userfile for this peer ID; get password if available */
static void
pap_pwdlookup(lcpiop)
struct lcpctl *lcpiop;
{
	char *buf;
	char *cp, *cp1;
	char *svp;
	FILE *fp;
	int perm;

	if((fp = fopen(Userfile,READ_TEXT)) == NULLFILE)
		/* Userfile doesn't exist */
		return;

	/* Locate user name in password file */
	buf = mallocw(128);
	while(fgets(buf,128,fp),!feof(fp)){
		if(buf[0] == '#')
			continue;	/* Comment */
		if((cp = strchr(buf,' ')) == NULLCHAR)
			/* Bogus entry */
			continue;
		*cp++ = '\0';		/* Now points to password */
		svp = cp;		/* Save ptr to password */
		if(stricmp(lcpiop->pap_user,buf) == 0)
			break;		/* Found peer ID */
	}
	if(feof(fp)){
		/* Peer ID not found in file */
		fclose(fp);
		free(buf);
		return;
	}
	fclose(fp);
	/* Look for space after password field in file */
	if((cp1 = strchr(cp,' ')) == NULLCHAR) {
		/* Invalid file entry */
		free(buf);
		return;
	}
	*cp1++ = '\0';	/* Now points to path field */
	if((cp = strchr(cp1,' ')) == NULLCHAR) {
		/* Permission field missing */
		free(buf);
		return;
	}
	*cp++ = '\0';	/* now points to permission field */
	perm = atoi(cp);

	/* Check permissions for this peer ID */
	if ((perm & PPP_PWD_LOOKUP) == 0) {
		/* Not in ftpuser file for password lookup */
		free(buf);
		return;
	}

	/* Save the password from this userfile record */
	lcpiop->pap_pass = malloc(strlen(svp)+1);
	strcpy(lcpiop->pap_pass,svp);
	return;
}

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

/* Send our PAP configuration request */
static int
pap_sendreq(sp)
struct slip *sp;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct mbuf *bp;

	if (ppptrace > 5)
		log(-1,"pap_sendreq()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* Get a packet with our configuration request */
	bp = pap_makereq(lcpiop);

	/* Start timer against wait for reply to our config request */
	pap_timer(sp);

	/* Send PAP configuration request to remote host */
	pppiop->state = PPP_PAP;
	if (lcpiop->pap_state != PAP_REQ_SENT)
		lcpiop->pap_state = PAP_REQ_SENT;
	return(pap_sendreply(sp, AUTH_REQ, 0, bp));
}

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

static struct mbuf *
pap_makereq(lcpiop)
struct lcpctl *lcpiop;
{
	register unsigned char *cp;
	struct mbuf *req_bp = NULLBUF;
	int len;

	if (ppptrace > 5)
		log(-1,"    pap_makereq()   peer ID: %s",lcpiop->pap_user);

	/* Get buffer for authenticate request packet */
	len = strlen(lcpiop->pap_user)+strlen(lcpiop->pap_pass)+2;
	if ((req_bp = alloc_mbuf(len)) == NULLBUF)
		return NULLBUF;

	/* Load peer ID and password for authenticate packet */
	cp = req_bp->data;
	*cp++ = (char)strlen(lcpiop->pap_user);
	memcpy(cp, lcpiop->pap_user, strlen(lcpiop->pap_user));
	cp += strlen(lcpiop->pap_user);
	*cp++ = (char)strlen(lcpiop->pap_pass);
	memcpy(cp, lcpiop->pap_pass, strlen(lcpiop->pap_pass));
	req_bp->cnt += len;

	/* Return our config request */
	return(req_bp);
}

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

/* Remote host ACKed our configuration request */
static void
pap_rcvack(sp, rcnf, data)
struct slip *sp;
struct cnfhdr *rcnf;
struct mbuf *data;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if (ppptrace > 5)
		log(-1, "pap_rcvack()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);
	stop_timer(&lcpiop->lcp_tm);

	switch(lcpiop->pap_state) {
	case PAP_REQ_SENT:
		/* Make sure ACK is proper */
		if (pap_chkack(sp, rcnf, data) != -1) {
			/* Remote host accepted our request */
			pap_open(sp);
			break;
		}

		/* Still need to settle request from remote host */
		pap_timer(sp);
		break;

	case PAP_REQ_RCVD:
	case PAP_CLOSED:
	case PAP_LISTEN:
	case PAP_OPEN:
	default:
		/* Confusion; shutdown the connection */
		free_p(data);
		pap_shutdown(sp);
		break;
	}
	return;
}

/* Remote host NAKed our configuration request */
static void
pap_rcvnak(sp, rcnf, data)
struct slip *sp;
struct cnfhdr *rcnf;
struct mbuf *data;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if (ppptrace > 5)
		log(-1, "pap_rcvnak()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);
	stop_timer(&lcpiop->lcp_tm);

	switch(lcpiop->pap_state) {
	case PAP_REQ_SENT:
		/* Remote host NAKd our AUTH_REQ */
		if (pap_chknak(sp, rcnf, data) == -1) {
			/* Bad NAK packet */
			/* Wait for another; resend request on timeout */
			pap_timer(sp);
			break;
		}
		/* Must have sent a bad peer ID or password */
		/* Get the password again */
		pap_getpass(sp,1);
		pwait(&(lcpiop->pap_user));
		if (lcpiop->pap_pass == NULLCHAR)
			/* No password entered, close PPP link */
			pap_shutdown(sp);
		else
			/* Send AUTH_REQ again with new password */
			pap_sendreq(sp);
		break;
	case PAP_REQ_RCVD:
	case PAP_OPEN:
	case PAP_CLOSED:
	case PAP_LISTEN:
	default:
		/* Confusion; shutdown the connection */
		free_p(data);
		pap_shutdown(sp);
		break;
	}
	return;
}

/* Process configuration request sent by remote host */
static void
pap_rcvreq(sp, rcnf, data)
struct slip *sp;
struct cnfhdr *rcnf;
struct mbuf *data;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if (ppptrace > 5)
		log(-1, "pap_rcvreq()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	switch(lcpiop->pap_state) {
	case PAP_LISTEN:	/* Normal event */
	case PAP_REQ_RCVD:	/* Normal event */
		/* Evaluate configuration request from remote host */
		pap_chkreq(sp, rcnf, data);
		break;

	case PAP_CLOSED:
	case PAP_REQ_SENT:
	case PAP_OPEN:
	default:
		/* We are closed; dont accept any connections */
		free_p(data);
		pap_shutdown(sp);
		break;
	}
	return;
}

/* PAP failure, abandon PAP attempt; shutdown LCP layer */
static void
pap_shutdown(sp)
struct slip *sp;
{
	struct pppctl *pppiop;

	if (ppptrace > 5)
		log(-1, "pap_shutdown()");

	pppiop = sp->pppio;

	if (ppptrace > 1)
		log(-1,"%s: PPP/PAP: Failed; close PPP connection",sp->iface->name);
	pap_reset(pppiop);

	/* Shut the link down completely */
	lcp_shutdown(sp);
	return;
}

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

/* Process configuration ACK send by remote host */
static int
pap_chkack(sp, ackcnf, data)
struct slip *sp;
struct cnfhdr *ackcnf;
struct mbuf *data;
{
	int ackerr = 0;
	struct opthdr ackopt;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	char *pap_msg;
	int len;

	if (ppptrace > 5)
		log(-1,"pap_chkack()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* PAP ID field must match last request we sent */
	if (ackcnf->id != lcpiop->lastid) {
		if (ppptrace > 5)
			log(-1,"improper PAP ACK; bad ID");
		free_p(data);
		return -1;
	}

	if ((ppptrace > 1) && (len_p(data) != 0)) {
		/* Log ASCII message from remote host, if any */
		len = pullchar(&data);
		pap_msg = mallocw(len+1);
		len = dqdata(data,pap_msg,len);
		pap_msg[len] = '\0';
		log(-1,"%s: PPP/PAP: ACK msg: %s",sp->iface->name,pap_msg);
		free(pap_msg);
	} else {
		free_p(data);
	}

	if (ackerr) {
		/* Error in configuration ACK */
		if (ppptrace > 5)
			log(-1,"improper PAP ACK");
		return -1;
	}

	/* ACK is acceptable */
	if (ppptrace > 5)
		log(-1,"valid PAP ACK");
	return 0;
}

/* Process configuration NAK send by remote host */
static int
pap_chknak(sp, nakcnf, data)
struct slip *sp;
struct cnfhdr *nakcnf;
struct mbuf *data;
{
	int nakerr = 0;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct opthdr nakopt;
	int len;
	char *pap_msg;

	if (ppptrace > 5)
		log(-1,"pap_chknak()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* PAP ID field must match last request we sent */
	if (nakcnf->id != lcpiop->lastid) {
		if (ppptrace > 1)
			log(-1,"improper PAP NAK; bad ID");
		free_p(data);
		return -1;
	}

	if ((ppptrace > 1) && (len_p(data) != 0)) {
		/* Log ASCII message from remote host, if any */
		len = pullchar(&data);
		pap_msg = mallocw(len+1);
		len = dqdata(data,pap_msg,len);
		pap_msg[len] = '\0';
		log(-1,"%s: PPP/PAP: NAK msg: %s",sp->iface->name,pap_msg);
		free(pap_msg);
	} else {
		free_p(data);
	}

	if (nakerr) {
		/* Error in configuration NAK */
		if (ppptrace > 5)
			log(-1,"improper PAP NAK");
		return -1;
	}

	/* NAK packet was properly constructed */
	if (ppptrace > 5)
		log(-1,"valid PAP NAK");
	return 0;
}

/* Check IP Control options requested by the remote host */
static void
pap_chkreq(sp, reqcnf, data)
struct slip *sp;
struct cnfhdr *reqcnf;
struct mbuf *data;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	int ilen,ulen,plen;
	register unsigned char *cp;
	char cnf_accept = CONFIG_ACK;		/* Overall reply to request */
	struct opthdr reqopt;			/* Per option header storage */
	struct opthdr replyopt;			/* For building reply */
	struct mbuf *reply_bp = NULLBUF;	/* Actual reply packet */
	char userpass[48];			/* Storage for peerID/pass */
	char *pap_msg = NULLCHAR;		/* Message for remote host */

	if (ppptrace > 5)
		log(-1, "pap_chkreq()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);
	lcpiop->pap_state = PAP_REQ_RCVD;

	/* Make sure length in PAP config header is realistic */
	ilen = len_p(data);
	if (ilen < reqcnf->len)
		reqcnf->len = ilen;

	/* Extract peerID/password sent by remote host */
	plen = dqdata(data,userpass,48);
	ulen = (int)userpass[0];
	if (ulen >= 48) {
		strcpy(userpass,"foo");
	} else {
		++ulen;
		plen = (int)userpass[ulen];
		userpass[ulen] = '\0';
		++ulen;
		plen += ulen;
		if (plen >= 48)
			plen = 47;
		userpass[plen] = '\0';
	}
	if (ppptrace > 5)
		log(-1,"PAP peer ID: %s",&(userpass[1]));

	/* Verify peerID/pass sent by remote host */
	if (lcpiop->pap_user != NULLCHAR) {
		free(lcpiop->pap_user);
		lcpiop->pap_user = NULLCHAR;
	}
	if (pap_authen(&(userpass[1]),&(userpass[ulen])) == -1) {
		cnf_accept = AUTH_NAK;
		pap_msg = " Invalid peer ID or password";
	} else {
		cnf_accept = AUTH_ACK;
		pap_msg = " Welcome to the Internet";
		lcpiop->pap_user = mallocw(strlen(&(userpass[1]))+1);
		strcpy(lcpiop->pap_user,&(userpass[1]));
	}
	ilen = strlen(pap_msg);
	reply_bp = qdata(pap_msg,ilen);
	reply_bp->data[0] = (char)(ilen - 1);

	/* Send ACK/NAK to remote host */
	if (cnf_accept == AUTH_ACK) {
		if (ppptrace > 1)
			log(-1, "%s: PPP/PAP: peerID (%s) from remote host has been verified",
				sp->iface->name,&(userpass[1]));

		/* Accept configuration requested by remote host */
		pap_sendreply(sp, AUTH_ACK, reqcnf->id, reply_bp);
		/* PPP data link now ready for next phase */
		pap_open(sp);
	} else {
		/* NAK config request made by remote host */
		if (ppptrace)
			log(-1,"%s: PPP/PAP: invalid peerID (%s) from remote host",
				sp->iface->name,&(userpass[1]));
		pap_sendreply(sp, AUTH_NAK, reqcnf->id, reply_bp);
		if (++lcpiop->ack_retry > PAP_FAIL_MAX) {
			/* Shut the link down after too many failed auth */
			if (ppptrace)
				log(-1,"%s: PPP/PAP: Failed; too many failed attempts; close PPP link",
					sp->iface->name);
			pap_shutdown(sp);
		} else {
			if (ppptrace > 1)
				log(-1,"%s: PPP/PAP: not verified; wait for another attempt",
					sp->iface->name);
		}
	}
	return;
}

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

/* Timeout while waiting for reply from remote host */
static void
pap_timeout(vp)
void *vp;
{
	struct slip *sp;
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;

	if (ppptrace > 1)
		log(-1, "pap_timeout()");

	/* Load pointers to interface that timed-out */
	sp = (struct slip *)vp;
	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);

	/* Attempt to get things going again */
	switch(lcpiop->pap_state) {
	case PAP_REQ_SENT:
		/* Timeout waiting for ACK to our request */
		if (++lcpiop->ack_retry > PAP_RETRY_MAX) {
			/* Remote host doesnt seem to be listening */
			pap_shutdown(sp);
			break;
		}
		/* Resend the request */
		pap_sendreq(sp);
		break;

	case PAP_REQ_RCVD:
	case PAP_CLOSED:
	case PAP_LISTEN:
	case PAP_OPEN:
	default:
		/* Confusion; shutdown the connection */
		pap_shutdown(sp);
		break;
	}
	return;
}

/* Set a timer in case an expected event does not occur */
static void
pap_timer(sp)
struct slip *sp;
{
	struct pppctl *pppiop;
	struct lcpctl *lcpiop;
	struct timer *t;

	if (ppptrace > 5)
		log(-1,"pap_timer()");

	pppiop = sp->pppio;
	lcpiop = &(pppiop->lcpio);
	t = &(lcpiop->lcp_tm);
	t->func = (void (*)())pap_timeout;
	t->arg = (void *)sp;
	start_timer(t);
	return;
}

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

/* Send an PAP packet to the remote host */
static int
pap_sendreply(sp,code,id,data)
struct slip *sp;
char code;
unsigned char id;
struct mbuf *data;
{
	struct iface *iface;
	struct cnfhdr hdr;

	/* Load PAP header values */
	hdr.code = code;
	switch(code) {
	case AUTH_REQ:
		/* Save ID field for match aginst replies from remote host */
		sp->pppio->lcpio.lastid = pppid;
		/* Use a unique ID field value */
		hdr.id = pppid++;
		break;

	case AUTH_ACK:
	case AUTH_NAK:
		/* Use ID sent by remote host */
		hdr.id = id;
		break;

	default:
		/* Shouldnt happen */
		if (ppptrace)
			log(-1, "%s: PPP/PAP: bogus code: %x\n",
				sp->iface->name, code);
		return -1;
	}
	hdr.len = len_p(data) + CNF_HDRLEN;

	/* Prepend PAP header to packet data */
	if ((data = htoncnf(&hdr,data)) == NULLBUF)
		return -1;

	if (ppptrace > 1)
		log(-1, "%s: PPP/PAP Send: current state: %s   PAP option: %s  id: %d  len: %d",
			sp->iface->name,
			PAPStates[sp->pppio->lcpio.pap_state],
			PAPCodes[code],hdr.id,hdr.len);

	/* Send PAP packet to remote host */
	sp->pppio->sndpap++;
	iface = sp->iface;
	return( (*iface->output)
		(iface, NULLCHAR, NULLCHAR, PPP_PAP_TYPE, data) );
}

/* Process incoming PAP packet */
void
papproc(iface,bp)
struct iface *iface;
struct mbuf *bp;
{
	struct slip *sp;
	struct lcpctl *lcpiop;
	struct cnfhdr hdr;

	sp = &Slip[iface->xdev];

	/* Extract PAP header */
	ntohcnf(&hdr, &bp);
	hdr.len -= CNF_HDRLEN;			/* Length includes envelope */
	trim_mbuf(&bp, hdr.len);		/* Trim off FCS bytes */

	if (ppptrace > 1)
		log(-1,	"%s: PPP/PAP Recv: current state: %s   PAP option: %s    id: %d   len: %d",
			iface->name,
			PAPStates[sp->pppio->lcpio.pap_state],
			PAPCodes[hdr.code], hdr.id, hdr.len);

	/* Process PAP packet data */
	switch(hdr.code) {
	case AUTH_REQ:				/* Request of remote host */
		pap_rcvreq(sp, &hdr, bp);
		break;
	case AUTH_ACK:				/* Remote accepted our req */
		pap_rcvack(sp, &hdr, bp);
		break;
	case AUTH_NAK:				/* Remote declined our req */
		pap_rcvnak(sp, &hdr, bp);
		break;
	default:
		if (ppptrace)
			log(-1,"%s: PPP/PAP: Unknown packet type: %x; dropping packet",
				sp->iface->name,hdr.code);
		free_p(bp);
		break;
	}
	return;
}
