/* * 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. * Send NAKs for unsent CIs. * Keep separate MTU, MRU. * Option tracing. * Extra data on authtype option. * Test restart. */ #include #include #include #include #include #include #include #include #include #ifdef STREAMS #include #include "ppp_str.h" #endif #include #include "pppd.h" #include "ppp.h" #include "fsm.h" #include "lcp.h" #include "magic.h" #include "chap.h" #include "upap.h" #include "ipcp.h" /* global vars */ fsm lcp_fsm[NPPP]; /* LCP fsm structure (global)*/ 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 */ /* local vars */ static void lcp_resetci __ARGS((fsm *)); /* Reset our Configuration Information */ static int lcp_cilen __ARGS((fsm *)); /* Return length of our CI */ static void lcp_addci __ARGS((fsm *, u_char *)); /* Add our CIs */ static int lcp_ackci __ARGS((fsm *, u_char *, int)); /* Ack some CIs */ static void lcp_nakci __ARGS((fsm *, u_char *, int)); /* Nak some CIs */ static void lcp_rejci __ARGS((fsm *, u_char *, int)); /* Reject some CIs */ static u_char lcp_reqci __ARGS((fsm *, u_char *, int *)); /* Check the requested CIs */ static void lcp_up __ARGS((fsm *)); /* We're UP */ static void lcp_down __ARGS((fsm *)); /* We're DOWN */ static void lcp_closed __ARGS((fsm *)); /* We're CLOSED */ static 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 */ }; #define DEFWARNLOOPS 10 /* XXX Move to lcp.h */ static 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->maxconfreqtransmits = DEFMAXCONFIGREQS; 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_chap = 0; /* Set to 1 on server */ wo->neg_upap = 0; /* Set to 1 on server */ wo->neg_magicnumber = 1; wo->neg_pcompression = 1; wo->neg_accompression = 1; ao->neg_mru = 1; ao->neg_asyncmap = 1; ao->neg_chap = 0; /* Set to 1 on client */ ao->chap_mdtype = CHAP_DIGEST_MD5; ao->chap_callback = CHAP_NOCALLBACK; ao->neg_upap = 0; /* Set to 1 on client */ ao->neg_magicnumber = 1; 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; u_char *p; int len; { fsm_input(&lcp_fsm[unit], p, len); } /* * lcp_protrej - A Protocol-Reject was received. */ /*ARGSUSED*/ void lcp_protrej(unit) int unit; { /* * Can't reject LCP! */ LCPDEBUG((LOG_WARNING, "lcp_protrej: Received Protocol-Reject for LCP!")) } /* * lcp_sprotrej - Send a Protocol-Reject for some protocol. */ void lcp_sprotrej(unit, p, len) int unit; u_char *p; int len; { /* this is marginal, as rejected-info should be full frame, * but at least we return the rejected-protocol */ p += 2; len -= 2; fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); } /* * lcp_resetci - Reset our CI. */ static 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. */ static int lcp_cilen(f) fsm *f; { lcp_options *go = &lcp_gotoptions[f->unit]; #define LENCIVOID(neg) (neg ? 2 : 0) #define LENCICHAP(neg) (neg ? 6 : 0) #define LENCISHORT(neg) (neg ? 4 : 0) #define LENCILONG(neg) (neg ? 6 : 0) return (LENCISHORT(go->neg_mru) + LENCILONG(go->neg_asyncmap) + LENCICHAP(go->neg_chap) + LENCISHORT(go->neg_upap) + LENCILONG(go->neg_magicnumber) + LENCIVOID(go->neg_pcompression) + LENCIVOID(go->neg_accompression)); } /* * lcp_addci - Add our desired CIs to a packet. */ static 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 ADDCICHAP(opt, neg, val, digest, callback) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(6, ucp); \ PUTSHORT(val, ucp); \ PUTCHAR(digest, ucp); \ PUTCHAR(callback, 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) ADDCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype, go->chap_callback) ADDCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP) ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber) 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. */ static int lcp_ackci(f, p, len) fsm *f; u_char *p; int len; { lcp_options *go = &lcp_gotoptions[f->unit]; u_char cilen, citype, cichar; 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 ACKCICHAP(opt, neg, val, digest, callback) \ if (neg) { \ if ((len -= 4 + sizeof (short)) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != 4 + sizeof (short) || \ citype != opt) \ goto bad; \ GETSHORT(cishort, p); \ if (cishort != val) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != digest) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != callback) \ 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) ACKCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype, go->chap_callback) ACKCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP) ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber) 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((LOG_WARNING, "lcp_acki: received bad Ack!")) return (0); } /* * lcp_nakci - NAK some of our CIs. */ static 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 NAKCICHAP(opt, neg, digest, callback, code) \ if (neg && \ len >= 4 + sizeof (short) && \ p[1] == 4 + sizeof (short) && \ p[0] == opt) { \ len -= 4 + sizeof (short); \ INCPTR(2, p); \ GETSHORT(cishort, p); \ 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; ) NAKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype, go->chap_callback, LCPDEBUG((LOG_WARNING, "Peer refuses to authenticate chap!")) ) NAKCISHORT(CI_AUTHTYPE, go->neg_upap, LCPDEBUG((LOG_WARNING, "Peer refuses to authenticate pap!")) ) NAKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber = magic(); if (++go->numloops % lcp_warnloops == 0) LCPDEBUG((LOG_INFO, "The line appears to be looped back.")); ) 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((LOG_WARNING, "lcp_nakci: received bad Nak!")) } /* * lcp_rejci - Reject some of our CIs. */ static 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((LOG_INFO,"lcp_rejci rejected void opt %d",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((LOG_INFO,"lcp_rejci rejected short opt %d", opt)) \ } #define REJCICHAP(opt, neg, val, digest, callback) \ myopt = opt; myval = val; \ if (neg && \ len >= 4 + sizeof (short) && \ p[1] == 4 + sizeof (short) && \ p[0] == opt) { \ len -= 4 + sizeof (short); \ INCPTR(2, p); \ GETSHORT(cishort, p); \ /* Check rejected value. */ \ xval = cishort; \ if (cishort != val) \ goto bad; \ neg = 0; \ INCPTR(2, p); \ LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", 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((LOG_INFO,"lcp_rejci rejected long opt %d", opt)) \ } REJCISHORT(CI_MRU, go->neg_mru, go->mru) REJCILONG(CI_ASYNCMAP, go->neg_asyncmap, go->asyncmap) REJCICHAP(CI_AUTHTYPE, go->neg_chap, CHAP, go->chap_mdtype, go->callback) REJCISHORT(CI_AUTHTYPE, go->neg_upap, UPAP) REJCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber) 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((LOG_WARNING, "lcp_rejci: received bad Reject!")) LCPDEBUG((LOG_WARNING, "lcp_rejci: plen %d len %d off %d, exp opt %d, found %d, val %d fval %d ", plen, len, p - start, myopt, p[0] &0xff, myval, xval )) } /* * lcp_reqci - Check the peer's requested CIs and send appropriate response. * * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified * appropriately. */ static 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, cichar;/* 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_chap = 0; ho->neg_upap = 0; ho->neg_magicnumber = 0; 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((LOG_WARNING, "lcp_reqci: bad CI length!")) 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((LOG_INFO, "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((LOG_INFO, "(%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((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP")) if (!ao->neg_asyncmap || cilen != sizeof (long)) { INCPTR(cilen, p); orc = CONFREJ; break; } GETLONG(cilong, p); LCPDEBUG((LOG_INFO, "(%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: if (cilen < sizeof (short) || (!ao->neg_upap && !ao->neg_chap)) { LCPDEBUG((LOG_WARNING, "lcp_reqci: rcvd AUTHTYPE, rejecting ...!")) INCPTR(cilen, p); orc = CONFREJ; break; } GETSHORT(cishort, p); LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE (%x)", cishort)) /* * Authtype must be UPAP or CHAP. */ if (cishort == UPAP) { INCPTR(cilen - sizeof (u_short), p); if (!ao->neg_upap) { /* we don't want to do PAP */ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")) orc = CONFREJ; break; } ho->neg_upap = 1; break; } else if (cishort == CHAP) { INCPTR(cilen - sizeof (u_short), p); if (!ao->neg_chap) { /* we don't want to do CHAP */ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")) orc = CONFREJ; break; } GETCHAR(cichar, p); /* get digest type*/ if (cichar != ao->chap_mdtype) { DECPTR(sizeof (u_char), p); orc = CONFNAK; PUTCHAR(ao->chap_mdtype, p); INCPTR(cilen - sizeof(u_char), p); break; } ho->chap_mdtype = cichar; /* save md type */ GETCHAR(cichar, p); /* get callback type*/ if (cichar != ao->chap_callback) { /* we don't callback yet */ DECPTR(sizeof (u_char), p); orc = CONFNAK; PUTCHAR(CHAP_NOCALLBACK, p); INCPTR(cilen - sizeof(u_char), p); break; } ho->chap_callback = cichar; /* save callback */ ho->neg_chap = 1; break; } else { DECPTR(sizeof (short), p); orc = CONFNAK; if (ao->neg_chap) { /* We prefer CHAP */ PUTSHORT(CHAP, p); } else if (ao->neg_upap) { PUTSHORT(CHAP, p); } else { syslog(LOG_ERR, "Coding botch in lcp_reqci authnak. This shouldn't happen."); exit(1); } INCPTR(cilen - sizeof (u_short), p); break; } case CI_MAGICNUMBER: LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER")) if (!ao->neg_magicnumber || cilen != sizeof (long)) { INCPTR(cilen, p); orc = CONFREJ; break; } GETLONG(cilong, p); LCPDEBUG((LOG_INFO, "(%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; case CI_PCOMPRESSION: LCPDEBUG((LOG_INFO, "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((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION")) if (!ao->neg_accompression || cilen != 0) { INCPTR(cilen, p); orc = CONFREJ; break; } ho->neg_accompression = 1; break; default: LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d", citype)) INCPTR(cilen, p); orc = CONFREJ; break; } cilen += 2; /* Adjust cilen whole CI */ endswitch: LCPDEBUG((LOG_INFO, " (%s)", 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((LOG_INFO, "lcp_reqci: returning %s.", rc == CONFACK ? "CONFACK" : rc == CONFNAK ? "CONFNAK" : "CONFREJ")) return (rc); /* Return final code */ } /* * lcp_up - LCP has come UP. * * Start UPAP, IPCP, etc. */ static 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) */ ChapLowerUp(f->unit); /* Enable CHAP */ upap_lowerup(f->unit); /* Enable UPAP */ ipcp_lowerup(f->unit); /* Enable IPCP */ if (go->neg_chap) { ChapAuthPeer(f->unit); auth = 1; } if (ho->neg_chap) { ChapAuthWithPeer(f->unit); auth = 1; } 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. */ static void lcp_down(f) fsm *f; { ipcp_lowerdown(f->unit); SIFDOWN(f->unit); SIFMTU(f->unit, MTU); SIFASYNCMAP(f->unit, 0xffffffff); CIFPCOMPRESSION(f->unit); CIFACCOMPRESSION(f->unit); ChapLowerDown(f->unit); upap_lowerdown(f->unit); } /* * lcp_closed - LCP has CLOSED. * * Alert other protocols. */ static 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); } }