ftp.nice.ch/pub/next/unix/communication/Alby.2.PPP.0.3.N.bs.tar.gz#/source/lcp.c

This is lcp.c in view mode; [Download] [Up]

/*
 * lcp.c - PPP Link Control Protocol.
 *
 * Copyright (c) 1989 Carnegie Mellon University.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by Carnegie Mellon University.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * TODO:
 * Keepalive.
 * Encryption.
 * Send NAKs for unsent CIs.
 * Keep separate MTU, MRU.
 * Option tracing.
 * Extra data on authtype option.
 * Test restart.
 */

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>

#include <net/if.h>
#include <netinet/in.h>

#ifdef STREAMS
#include <sys/stream.h>
#endif
#include "ppp.h"
#include "fsm.h"
#include "lcp.h"


void lcp_resetci();		/* Reset our Configuration Information */
int lcp_cilen();		/* Return length of our CI */
void lcp_addci();		/* Add our CIs */
int lcp_ackci();		/* Ack some CIs */
void lcp_nakci();		/* Nak some CIs */
void lcp_rejci();		/* Reject some CIs */
u_char lcp_reqci();		/* Check the requested CIs */
void lcp_up();			/* We're UP */
void lcp_down();		/* We're DOWN */
void lcp_closed();		/* We're CLOSED */


fsm lcp_fsm[NPPP];		/* LCP fsm structure */

fsm_callbacks lcp_callbacks = {	/* LCP callback routines */
    lcp_resetci,		/* Reset our Configuration Information */
    lcp_cilen,			/* Length of our Configuration Information */
    lcp_addci,			/* Add our Configuration Information */
    lcp_ackci,			/* ACK our Configuration Information */
    lcp_nakci,			/* NAK our Configuration Information */
    lcp_rejci,			/* Reject our Configuration Information */
    lcp_reqci,			/* Request peer's Configuration Information */
    lcp_up,			/* Called when fsm reaches OPEN state */
    lcp_down,			/* Called when fsm leaves OPEN state */
    lcp_closed,			/* Called when fsm reaches CLOSED state */
    NULL,			/* Called when Protocol-Reject received */
    NULL			/* Retransmission is necessary */
};


lcp_options lcp_wantoptions[NPPP]; /* Options that we want to request */
lcp_options lcp_gotoptions[NPPP]; /* Options that peer ack'd */
lcp_options lcp_allowoptions[NPPP]; /* Options that we allow peer to request */
lcp_options lcp_hisoptions[NPPP]; /* Options that we ack'd */


#define DEFWARNLOOPS	10	/* XXX Move to lcp.h */
int lcp_warnloops = DEFWARNLOOPS; /* Warn about a loopback this often */


/*
 * lcp_init - Initialize LCP.
 */
void lcp_init(unit)
    int unit;
{
    fsm *f = &lcp_fsm[unit];
    lcp_options *wo = &lcp_wantoptions[unit];
    lcp_options *ao = &lcp_allowoptions[unit];

    f->unit = unit;
    f->protocol = LCP;
    f->timeouttime = DEFTIMEOUT;
    f->maxtermtransmits = DEFMAXTERMTRANSMITS;
    f->maxnakloops = DEFMAXNAKLOOPS;
    f->callbacks = &lcp_callbacks;

    wo->passive = 0;
    wo->restart = 0;			/* Set to 1 in kernels or multi-line
					   implementations */

    wo->neg_mru = 1;
    wo->mru = DEFMRU;
    wo->neg_asyncmap = 1;
    wo->asyncmap = 0;
    wo->neg_upap = 0;			/* Set to 1 on server */
    wo->neg_encrypttype = 0;
    wo->encrypttype = 0;
    wo->neg_magicnumber = 1;
#if 0
    wo->neg_keepalive = 0;
#endif
    wo->neg_pcompression = 1;
    wo->neg_accompression = 1;

    ao->neg_mru = 1;
    ao->neg_asyncmap = 1;
    ao->neg_upap = 0;			/* Set to 1 on client */
    ao->neg_encrypttype = 0;
    ao->neg_magicnumber = 1;
#if 0
    ao->neg_keepalive = 0;
#endif
    ao->neg_pcompression = 1;
    ao->neg_accompression = 1;

    fsm_init(f);
}


/*
 * lcp_activeopen - Actively open LCP.
 */
void lcp_activeopen(unit)
    int unit;
{
    fsm_activeopen(&lcp_fsm[unit]);
}


/*
 * lcp_passiveopen - Passively open LCP.
 */
void lcp_passiveopen(unit)
    int unit;
{
    fsm_passiveopen(&lcp_fsm[unit]);
}


/*
 * lcp_close - Close LCP.
 */
void lcp_close(unit)
    int unit;
{
    fsm_close(&lcp_fsm[unit]);
}


/*
 * lcp_lowerup - The lower layer is up.
 */
void lcp_lowerup(unit)
    int unit;
{
    SIFDOWN(unit);
    SIFMTU(unit, MTU);
    SIFASYNCMAP(unit, 0xffffffff);
    CIFPCOMPRESSION(unit);
    CIFACCOMPRESSION(unit);

    fsm_lowerup(&lcp_fsm[unit]);
}


/*
 * lcp_lowerdown - The lower layer is down.
 */
void lcp_lowerdown(unit)
    int unit;
{
    fsm_lowerdown(&lcp_fsm[unit]);
}


/*
 * lcp_input - Input LCP packet.
 */
void lcp_input(unit, p, len)
    int unit;
    PACKET *p;
    int len;
{
    fsm_input(&lcp_fsm[unit], p, len);
}


/*
 * lcp_protrej - A Protocol-Reject was received.
 */
void lcp_protrej(unit)
    int unit;
{
    /*
     * Can't reject LCP!
     */
    FSMDEBUG((stderr,
	      "ppp: lcp_protrej: Received Protocol-Reject for LCP!\n"));
}


/*
 * lcp_sprotrej - Send a Protocol-Reject for some protocol.
 */
void lcp_sprotrej(unit, p, len)
    int unit;
    PACKET *p;
    int len;
{
    fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
	      PACKET_DATA(p) + 2, len);
    PACKET_FREE(p + DLLHEADERLEN);
}


/*
 * lcp_resetci - Reset our CI.
 */
void lcp_resetci(f)
    fsm *f;
{
    lcp_wantoptions[f->unit].magicnumber = magic();
    lcp_wantoptions[f->unit].numloops = 0;
    lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
}


/*
 * lcp_cilen - Return length of our CI.
 */
int lcp_cilen(f)
    fsm *f;
{
    lcp_options *go = &lcp_gotoptions[f->unit];

#define LENCIVOID(neg) (neg ? 2 : 0)
#define LENCISHORT(neg)  (neg ? 4 : 0)
#define LENCILONG(neg)  (neg ? 6 : 0)

    return (LENCISHORT(go->neg_mru) +
	    LENCILONG(go->neg_asyncmap) +
	    LENCISHORT(go->neg_upap) +
	    LENCISHORT(go->neg_encrypttype) +
	    LENCILONG(go->neg_magicnumber) +
#if 0
	    LENCI(go->neg_keepalive) +
#endif
	    LENCIVOID(go->neg_pcompression) +
	    LENCIVOID(go->neg_accompression));
}


/*
 * lcp_addci - Add our desired CIs to a packet.
 */
void lcp_addci(f, ucp)
    fsm *f;
    u_char *ucp;
{
    lcp_options *go = &lcp_gotoptions[f->unit];

#define ADDCIVOID(opt, neg) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(2, ucp); \
    }
#define ADDCISHORT(opt, neg, val) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(2 + sizeof (short), ucp); \
	PUTSHORT(val, ucp); \
    }
#define ADDCILONG(opt, neg, val) \
    if (neg) { \
	PUTCHAR(opt, ucp); \
	PUTCHAR(2 + sizeof (long), ucp); \
	PUTLONG(val, ucp); \
    }

    ADDCISHORT(CI_MRU, go->neg_mru, go->mru)
    ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap)
    ADDCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP)
    ADDCISHORT(CI_ENCRYPTTYPE, go->neg_encrypttype, go->encrypttype)
    ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber)
#if 0
    ADDCI(CI_KEEPALIVE, go->neg_keepalive, go->keepalive)
#endif
    ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression)
    ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression)
}


/*
 * lcp_ackci - Ack our CIs.
 *
 * Returns:
 *	0 - Ack was bad.
 *	1 - Ack was good.
 */
int lcp_ackci(f, p, len)
    fsm *f;
    u_char *p;
    int len;
{
    lcp_options *go = &lcp_gotoptions[f->unit];
    u_char cilen, citype;
    u_short cishort;
    u_long cilong;

    /*
     * CIs must be in exactly the same order that we sent.
     * Check packet length and CI length at each step.
     * If we find any deviations, then this packet is bad.
     */
#define ACKCIVOID(opt, neg) \
    if (neg) { \
	if ((len -= 2) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != 2 || \
	    citype != opt) \
	    goto bad; \
    }
#define ACKCISHORT(opt, neg, val) \
    if (neg) { \
	if ((len -= 2 + sizeof (short)) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != 2 + sizeof (short) || \
	    citype != opt) \
	    goto bad; \
	GETSHORT(cishort, p); \
	if (cishort != val) \
	    goto bad; \
    }
#define ACKCILONG(opt, neg, val) \
    if (neg) { \
	if ((len -= 2 + sizeof (long)) < 0) \
	    goto bad; \
	GETCHAR(citype, p); \
	GETCHAR(cilen, p); \
	if (cilen != 2 + sizeof (long) || \
	    citype != opt) \
	    goto bad; \
	GETLONG(cilong, p); \
	if (cilong != val) \
	    goto bad; \
    }

    ACKCISHORT(CI_MRU, go->neg_mru, go->mru)
    ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap)
    ACKCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP)
    ACKCISHORT(CI_ENCRYPTTYPE, go->neg_encrypttype, go->encrypttype)
    ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber)
#if 0
    ACKCI(CI_KEEPALIVE, go->neg_keepalive, go->keepalive)
#endif
    ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression)
    ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression)

    /*
     * If there are any remaining CIs, then this packet is bad.
     */
    if (len != 0)
	goto bad;
    return (1);
bad:
    LCPDEBUG((stderr, "ppp: lcp_acki: received bad Ack!\n"));
    return (0);
}


/*
 * lcp_nakci - NAK some of our CIs.
 */
void lcp_nakci(f, p, len)
    fsm *f;
    u_char *p;
    int len;
{
    lcp_options *go = &lcp_gotoptions[f->unit];
    lcp_options *wo = &lcp_wantoptions[f->unit];
    u_short cishort;
    u_long cilong;

    /*
     * Any Nak'd CIs must be in exactly the same order that we sent.
     * Check packet length and CI length at each step.
     * If we find any deviations, then this packet is bad.
     */
#define NAKCIVOID(opt, neg, code) \
    if (neg && \
	len >= 2 && \
	p[1] == 2 && \
	p[0] == opt) { \
	len -= 2; \
	INCPTR(2, p); \
	code \
    }
#define NAKCISHORT(opt, neg, code) \
    if (neg && \
	len >= 2 + sizeof (short) && \
	p[1] == 2 + sizeof (short) && \
	p[0] == opt) { \
	len -= 2 + sizeof (short); \
	INCPTR(2, p); \
	GETSHORT(cishort, p); \
	code \
    }
#define NAKCILONG(opt, neg, code) \
    if (neg && \
	len >= 2 + sizeof (long) && \
	p[1] == 2 + sizeof (long) && \
	p[0] == opt) { \
	len -= 2 + sizeof (long); \
	INCPTR(2, p); \
	GETLONG(cilong, p); \
	code \
    }

    /*
     * We don't care if they want to send us smaller packets than
     * we want.  Therefore, accept any MRU less than what we asked for,
     * but then ignore the new value when setting the MRU in the kernel.
     * If they send us a bigger MRU than what we asked, reject it and
     * let him decide to accept our value.
     */
    NAKCISHORT(CI_MRU, go->neg_mru,
	       if (cishort <= wo->mru)
		    go->mru = cishort;
	       else
		    goto bad;
	       )
    NAKCILONG(CI_ASYNCMAP, go->neg_asyncmap,
	      go->asyncmap |= cilong;
	      )
    NAKCISHORT(CI_AUTHTYPE, go->neg_upap,
	       fprintf(stderr, "ppp: Peer refuses to authenticate!\n");
	       )
    NAKCISHORT(CI_ENCRYPTTYPE, go->neg_encrypttype,
	       go->neg_encrypttype = 0;
	       )
    NAKCILONG(CI_MAGICNUMBER, go->neg_magicnumber,
	      go->magicnumber = magic();
	      if (++go->numloops % lcp_warnloops == 0)
		  fprintf(stderr, "ppp: The line appears to be looped back.\n");
	      )
#if 0
    NAKCI(CI_KEEPALIVE, go->neg_keepalive,
	  )
#endif
    NAKCIVOID(CI_PCOMPRESSION, go->neg_pcompression,
	       go->neg_pcompression = 0;
	      )
    NAKCIVOID(CI_ACCOMPRESSION, go->neg_accompression,
	       go->neg_accompression = 0;
	      )

    /*
     * If there are any remaining CIs, then this packet is bad.
     */
    if (len == 0)
	return;
bad:
    LCPDEBUG((stderr, "ppp: lcp_nakci: received bad Nak!\n"));
}


/*
 * lcp_rejci - Reject some of our CIs.
 */
void lcp_rejci(f, p, len)
    fsm *f;
    u_char *p;
    int len;
{
    lcp_options *go = &lcp_gotoptions[f->unit];
    u_short cishort;
    u_long cilong;
    u_char *start = p;
    int myopt, myval, xval, plen = len;
    /*
     * Any Rejected CIs must be in exactly the same order that we sent.
     * Check packet length and CI length at each step.
     * If we find any deviations, then this packet is bad.
     */
#define REJCIVOID(opt, neg) \
    myopt = opt; \
    if (neg && \
	len >= 2 && \
	p[1] == 2 && \
	p[0] == opt) { \
	len -= 2; \
	INCPTR(2, p); \
	neg = 0; \
	LCPDEBUG((stderr,"ppp: lcp_rejci rejected void opt %d\n",opt)); \
    }
#define REJCISHORT(opt, neg, val) \
    myopt = opt; myval = val; \
    if (neg && \
	len >= 2 + sizeof (short) && \
	p[1] == 2 + sizeof (short) && \
	p[0] == opt) { \
	len -= 2 + sizeof (short); \
	INCPTR(2, p); \
	GETSHORT(cishort, p); \
	/* Check rejected value. */ \
  	xval = cishort; \
	if (cishort != val) \
	    goto bad; \
	neg = 0; \
	LCPDEBUG((stderr,"ppp: lcp_rejci rejected short opt %d\n", opt)); \
    }
#define REJCILONG(opt, neg, val) \
    myopt = opt; myval = val; \
    if (neg && \
	len >= 2 + sizeof (long) && \
	p[1] == 2 + sizeof (long) && \
	p[0] == opt) { \
	len -= 2 + sizeof (long); \
	INCPTR(2, p); \
	GETLONG(cilong, p); \
        xval = cilong; \
	/* Check rejected value. */ \
	if (cilong != val) \
	    goto bad; \
	neg = 0; \
	LCPDEBUG((stderr,"ppp: lcp_rejci rejected long opt %d\n", opt)); \
    }

    REJCISHORT(CI_MRU, go->neg_mru, go->mru)
    REJCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap)
    REJCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP)
    REJCISHORT(CI_ENCRYPTTYPE, go->neg_encrypttype, go->encrypttype)
    REJCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber)
#if 0
    REJCI(CI_KEEPALIVE, go->neg_keepalive, go->keepalive)
#endif
    REJCIVOID(CI_PCOMPRESSION, go->neg_pcompression)
    REJCIVOID(CI_ACCOMPRESSION, go->neg_accompression)

    /*
     * If there are any remaining CIs, then this packet is bad.
     */
    if (len == 0)
	return;
bad:
    LCPDEBUG((stderr, "ppp: lcp_rejci: received bad Reject!\n"));
    LCPDEBUG((stderr, "ppp: lcp_rejci: plen %d len %d off %d, exp opt %d, found %d, val %d fval %d \n",
	plen, len, p - start, myopt, p[0] &0xff, myval, xval ));
    for(len=0; len < plen; len++) {
	if(!(len % 16))
		LCPDEBUG((stderr,"\nppp: lcp_rejci: "));
	LCPDEBUG((stderr,"%0x ",*start++));
    }
    LCPDEBUG((stderr,"\n"));
}


/*
 * lcp_reqci - Check the peer's requested CIs and send appropriate response.
 *
 * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
 * appropriately.
 */
u_char lcp_reqci(f, inp, len)
    fsm *f;
    u_char *inp;		/* Requested CIs */
    int *len;			/* Length of requested CIs */
{
    lcp_options *go = &lcp_gotoptions[f->unit];
    lcp_options *ho = &lcp_hisoptions[f->unit];
    lcp_options *ao = &lcp_allowoptions[f->unit];
    u_char *cip;		/* Pointer to Current CI */
    u_char cilen, citype;	/* Parsed len, type, char value */
    u_short cishort;		/* Parsed short value */
    u_long cilong;		/* Parse long value */
    int rc = CONFACK;		/* Final packet return code */
    int orc;			/* Individual option return code */
    u_char *p = inp;		/* Pointer to next char to parse */
    u_char *ucp = inp;		/* Pointer to current output char */
    int l = *len;		/* Length left */

    /*
     * Reset all his options.
     */
    ho->neg_mru = 0;
    ho->neg_asyncmap = 0;
    ho->neg_upap = 0;
    ho->neg_encrypttype = 0;
    ho->neg_magicnumber = 0;
#if 0
    ho->neg_keepalive = 0;
#endif
    ho->neg_pcompression = 0;
    ho->neg_accompression = 0;

    /*
     * Process all his options.
     */
    while (l) {
	orc = CONFACK;			/* Assume success */
	cip = p;			/* Remember begining of CI */
	if (l < 2 ||			/* Not enough data for CI header or */
	    p[1] < 2 ||			/*  CI length too small or */
	    p[1] > l) {			/*  CI length too big? */
	    LCPDEBUG((stderr, "ppp: lcp_reqci: bad CI length!\n"));
	    orc = CONFREJ;		/* Reject bad CI */
	    cilen = l;			/* Reject till end of packet */
	    l = 0;			/* Don't loop again */
	    goto endswitch;
	}
	GETCHAR(citype, p);		/* Parse CI type */
	GETCHAR(cilen, p);		/* Parse CI length */
	l -= cilen;			/* Adjust remaining length */
	cilen -= 2;			/* Adjust cilen to just data */

	switch (citype) {		/* Check CI type */
	  case CI_MRU:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd MRU"));
	    if (!ao->neg_mru ||		/* Allow option? */
		cilen != sizeof (short)) { /* Check CI length */
		INCPTR(cilen, p); 	/* Skip rest of CI */
		orc = CONFREJ;		/* Reject CI */
		break;
	    }
	    GETSHORT(cishort, p);	/* Parse MRU */
	    LCPDEBUG((stderr, "(%d)", cishort));

	    /*
	     * He must be able to receive at least our minimum.
	     * No need to check a maximum.  If he sends a large number,
	     * we'll just ignore it.
	     */
	    if (cishort < MINMRU) {
		orc = CONFNAK;		/* Nak CI */
		DECPTR(sizeof (short), p); /* Backup */
		PUTSHORT(MINMRU, p);	/* Give him a hint */
		break;
	    }
	    ho->neg_mru = 1;		/* Remember he sent and MRU */
	    ho->mru = cishort;		/* And remember value */
	    break;

	  case CI_ASYNCMAP:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd ASYNCMAP"));
	    if (!ao->neg_asyncmap ||
		cilen != sizeof (long)) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    GETLONG(cilong, p);
	    LCPDEBUG((stderr, "(%lx)", cilong));

	    /* XXX Accept anything he says */
#if 0
	    /*
	     * Asyncmap must be OR of two maps.
	     */
	    if ((lcp_wantoptions[f->unit].neg_asyncmap &&
		 cilong != (lcp_wantoptions[f->unit].asyncmap | cilong)) ||
		(!lcp_wantoptions[f->unit].neg_asyncmap &&
		 cilong != 0xffffffff)) {
		orc = CONFNAK;
		DECPTR(sizeof (long), p);
		PUTLONG(lcp_wantoptions[f->unit].neg_asyncmap ?
			lcp_wantoptions[f->unit].asyncmap | cilong :
			0xffffffff, p);
		break;
	    }
#endif
	    ho->neg_asyncmap = 1;
	    ho->asyncmap = cilong;
	    break;

	  case CI_AUTHTYPE:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd AUTHTYPE"));
	    if (!ao->neg_upap ||
		cilen < sizeof (short)) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    GETSHORT(cishort, p);
	    LCPDEBUG((stderr, "(%x)", cishort));

	    /*
	     * Authtype must be UPAP.
	     */
	    if (cishort != UPAP) {
		DECPTR(sizeof (short), p);
		orc = CONFNAK;
		PUTSHORT(UPAP, p);
		INCPTR(cilen - sizeof (u_short), p);
		break;
	    }
	    INCPTR(cilen - sizeof (u_short), p);
	    ho->neg_upap = 1;
	    break;

#if 0
/* XXX */
	  case CI_ENCRYPTTYPE:
	    if (!ao->neg_encrypttype ||
		cilen != sizeof (short)) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    GETSHORT(cishort, p);
	    LCPDEBUG((stderr, "(%d)", cishort));

	    /*
	     * Encrypttype must be XXX.
	     */
	    if (XXX) {
		DECPTR(sizeof (short), p);
		orc = CONFNAK;
		PUTSHORT(XXX, p);
		break;
	    }
	    ho->neg_encrypttype = 1;
	    ho->encrypttype = cishort;
	    break;
#endif

	  case CI_MAGICNUMBER:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd MAGICNUMBER"));
	    if (!ao->neg_magicnumber ||
		cilen != sizeof (long)) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    GETLONG(cilong, p);
	    LCPDEBUG((stderr, "(%lx)", cilong));

	    /*
	     * He must have a different magic number.
	     */
	    if (go->neg_magicnumber &&
		cilong == go->magicnumber) {
		orc = CONFNAK;
		DECPTR(sizeof (long), p);
		cilong = magic();	/* Don't put magic() inside macro! */
		PUTLONG(cilong, p);
		break;
	    }
	    ho->neg_magicnumber = 1;
	    ho->magicnumber = cilong;
	    break;

#if 0
	  case CI_KEEPALIVE:
	    if (!ao->neg_keepalive ||
		cilen != )) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    GET(ci, p);
	    LCPDEBUG((stderr, "(%d)", cishort));

	    /*
	     * He must XXX
	     */
	    if () {
		orc = CONFNAK;
		DECPTR(sizeof (), p);
		PUT(, p);
		break;
	    }
	    ho->neg_keepalive = 1;
	    ho->keepalive = ;
	    break;
#endif

	  case CI_PCOMPRESSION:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd PCOMPRESSION"));
	    if (!ao->neg_pcompression ||
		cilen != 0) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    ho->neg_pcompression = 1;
	    break;

	  case CI_ACCOMPRESSION:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd ACCOMPRESSION"));
	    if (!ao->neg_accompression ||
		cilen != 0) {
		INCPTR(cilen, p);
		orc = CONFREJ;
		break;
	    }
	    ho->neg_accompression = 1;
	    break;

	  default:
	    LCPDEBUG((stderr, "ppp: lcp_reqci: rcvd unknown option %d",
		      citype));
	    INCPTR(cilen, p);
	    orc = CONFREJ;
	    break;
	}
	cilen += 2;			/* Adjust cilen whole CI */

endswitch:
	LCPDEBUG((stderr, " (%s)\n",
		  orc == CONFACK ? "ACK" : (orc == CONFNAK ? "NAK" : "REJ")));
	if (orc == CONFACK &&		/* Good CI */
	    rc != CONFACK)		/*  but prior CI wasnt? */
	    continue;			/* Don't send this one */

	if (orc == CONFNAK) {		/* Nak this CI? */
	    if (rc == CONFREJ)		/* Rejecting prior CI? */
		continue;		/* Don't send this one */
	    if (rc == CONFACK) {	/* Ack'd all prior CIs? */
		rc = CONFNAK;		/* Not anymore... */
		ucp = inp;		/* Backup */
	    }
	}
	if (orc == CONFREJ &&		/* Reject this CI */
	    rc != CONFREJ) {		/*  but no prior ones? */
	    rc = CONFREJ;
	    ucp = inp;			/* Backup */
	}
	if (ucp != cip)			/* Need to move CI? */
	    BCOPY(cip, ucp, cilen);	/* Move it */
	INCPTR(cilen, ucp);		/* Update output pointer */
    }

    /*
     * XXX If we wanted to send additional NAKs (for unsent CIs), the
     * code would go here.  This must be done with care since it might
     * require a longer packet than we received.
     */

    *len = ucp - inp;			/* Compute output length */
    LCPDEBUG((stderr, "ppp: lcp_reqci: returning %s.\n",
	      rc == CONFACK ? "CONFACK" :
	      rc == CONFNAK ? "CONFNAK" : "CONFREJ"));
    return (rc);			/* Return final code */
}


/*
 * lcp_up - LCP has come UP.
 *
 * Start UPAP, IPCP, etc.
 */
void lcp_up(f)
    fsm *f;
{
    lcp_options *ho = &lcp_hisoptions[f->unit];
    lcp_options *go = &lcp_gotoptions[f->unit];
    int auth = 0;

    if (ho->neg_mru)
	SIFMTU(f->unit, ho->mru);
    if (ho->neg_asyncmap)
	SIFASYNCMAP(f->unit, ho->asyncmap);
    if (ho->neg_pcompression)
	SIFPCOMPRESSION(f->unit);
    if (ho->neg_accompression)
	SIFACCOMPRESSION(f->unit);
    SIFUP(f->unit);		/* Bring the interface up (set IFF_UP) */
    upap_lowerup(f->unit);	/* Enable UPAP */
    ipcp_lowerup(f->unit);	/* Enable IPCP */
    if (go->neg_upap) {
	upap_authpeer(f->unit);
	auth = 1;
    }
    if (ho->neg_upap) {
	upap_authwithpeer(f->unit);
	auth = 1;
    }
    if (!auth)
	ipcp_activeopen(f->unit);
}


/*
 * lcp_down - LCP has gone DOWN.
 *
 * Alert other protocols.
 */
void lcp_down(f)
    fsm *f;
{
    SIFDOWN(f->unit);
    SIFMTU(f->unit, MTU);
    SIFASYNCMAP(f->unit, 0xffffffff);
    CIFPCOMPRESSION(f->unit);
    CIFACCOMPRESSION(f->unit);
    upap_lowerdown(f->unit);
    ipcp_lowerdown(f->unit);
}


/*
 * lcp_closed - LCP has CLOSED.
 *
 * Alert other protocols.
 */
void lcp_closed(f)
    fsm *f;
{
    if (lcp_wantoptions[f->unit].restart) {
	if (lcp_wantoptions[f->unit].passive)
	    lcp_passiveopen(f->unit);	/* Start protocol in passive mode */
	else
	    lcp_activeopen(f->unit);	/* Start protocol in active mode */
    }
    else {
	EXIT(f->unit);
    }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.