/* * lcp.c - PPP Link Control Protocol. * * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "netif/ppp/ppp_opts.h" #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ /* * @todo: */ #if 0 /* UNUSED */ #include #include #include #endif /* UNUSED */ #include "netif/ppp/ppp_impl.h" #include "netif/ppp/fsm.h" #include "netif/ppp/lcp.h" #if CHAP_SUPPORT #include "netif/ppp/chap-new.h" #endif /* CHAP_SUPPORT */ #include "netif/ppp/magic.h" /* * When the link comes up we want to be able to wait for a short while, * or until seeing some input from the peer, before starting to send * configure-requests. We do this by delaying the fsm_lowerup call. */ /* steal a bit in fsm flags word */ #define DELAYED_UP 0x80 static void lcp_delayed_up(void *arg); /* * LCP-related command-line options. */ #if 0 /* UNUSED */ int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ #endif /* UNUSED */ #if 0 /* UNUSED */ /* options */ static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ #endif /* UNUSED */ #if 0 /* UNUSED */ #if PPP_LCP_ADAPTIVE bool lcp_echo_adaptive = 0; /* request echo only if the link was idle */ #endif bool lax_recv = 0; /* accept control chars in asyncmap */ bool noendpoint = 0; /* don't send/accept endpoint discriminator */ #endif /* UNUSED */ #if PPP_OPTIONS static int noopt (char **); #endif /* PPP_OPTIONS */ #ifdef HAVE_MULTILINK static int setendpoint (char **); static void printendpoint (option_t *, void (*)(void *, char *, ...), void *); #endif /* HAVE_MULTILINK */ #if PPP_OPTIONS static option_t lcp_option_list[] = { /* LCP options */ { "-all", o_special_noarg, (void *)noopt, "Don't request/allow any LCP options" }, { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression, "Disable address/control compression", OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression, "Disable address/control compression", OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, "Set asyncmap (for received packets)", OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, { "-as", o_uint32, &lcp_wantoptions[0].asyncmap, "Set asyncmap (for received packets)", OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, "Disable asyncmap negotiation", OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, &lcp_allowoptions[0].neg_asyncmap }, { "-am", o_uint32, &lcp_wantoptions[0].asyncmap, "Disable asyncmap negotiation", OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, &lcp_allowoptions[0].neg_asyncmap }, { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber, "Disable magic number negotiation (looped-back line detection)", OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber, "Disable magic number negotiation (looped-back line detection)", OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, { "mru", o_int, &lcp_wantoptions[0].mru, "Set MRU (maximum received packet size) for negotiation", OPT_PRIO, &lcp_wantoptions[0].neg_mru }, { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru, "Disable MRU negotiation (use default 1500)", OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, { "-mru", o_bool, &lcp_wantoptions[0].neg_mru, "Disable MRU negotiation (use default 1500)", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, { "mtu", o_int, &lcp_allowoptions[0].mru, "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU }, { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression, "Disable protocol field compression", OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression, "Disable protocol field compression", OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, { "passive", o_bool, &lcp_wantoptions[0].passive, "Set passive mode", 1 }, { "-p", o_bool, &lcp_wantoptions[0].passive, "Set passive mode", OPT_ALIAS | 1 }, { "silent", o_bool, &lcp_wantoptions[0].silent, "Set silent mode", 1 }, { "lcp-echo-failure", o_int, &lcp_echo_fails, "Set number of consecutive echo failures to indicate link failure", OPT_PRIO }, { "lcp-echo-interval", o_int, &lcp_echo_interval, "Set time in seconds between LCP echo requests", OPT_PRIO }, #if PPP_LCP_ADAPTIVE { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive, "Suppress LCP echo requests if traffic was received", 1 }, #endif { "lcp-restart", o_int, &lcp_fsm[0].timeouttime, "Set time in seconds between LCP retransmissions", OPT_PRIO }, { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits, "Set maximum number of LCP terminate-request transmissions", OPT_PRIO }, { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits, "Set maximum number of LCP configure-request transmissions", OPT_PRIO }, { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops, "Set limit on number of LCP configure-naks", OPT_PRIO }, { "receive-all", o_bool, &lax_recv, "Accept all received control characters", 1 }, #ifdef HAVE_MULTILINK { "mrru", o_int, &lcp_wantoptions[0].mrru, "Maximum received packet size for multilink bundle", OPT_PRIO, &lcp_wantoptions[0].neg_mrru }, { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, "Use short sequence numbers in multilink headers", OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf }, { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, "Don't use short sequence numbers in multilink headers", OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf }, { "endpoint", o_special, (void *) setendpoint, "Endpoint discriminator for multilink", OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint }, #endif /* HAVE_MULTILINK */ { "noendpoint", o_bool, &noendpoint, "Don't send or accept multilink endpoint discriminator", 1 }, {NULL} }; #endif /* PPP_OPTIONS */ /* * Callbacks for fsm code. (CI = Configuration Information) */ static void lcp_resetci(fsm *f); /* Reset our CI */ static int lcp_cilen(fsm *f); /* Return length of our CI */ static void lcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI to pkt */ static int lcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */ static int lcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree); /* Rcv peer CI */ static void lcp_up(fsm *f); /* We're UP */ static void lcp_down(fsm *f); /* We're DOWN */ static void lcp_starting (fsm *); /* We need lower layer up */ static void lcp_finished (fsm *); /* We need lower layer down */ static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len); static void lcp_rprotrej(fsm *f, u_char *inp, int len); /* * routines to send LCP echos to peer */ static void lcp_echo_lowerup(ppp_pcb *pcb); static void lcp_echo_lowerdown(ppp_pcb *pcb); static void LcpEchoTimeout(void *arg); static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len); static void LcpSendEchoRequest(fsm *f); static void LcpLinkFailure(fsm *f); static void LcpEchoCheck(fsm *f); static const 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 OPENED state */ lcp_down, /* Called when fsm leaves OPENED state */ lcp_starting, /* Called when we want the lower layer up */ lcp_finished, /* Called when we want the lower layer down */ NULL, /* Called when Protocol-Reject received */ NULL, /* Retransmission is necessary */ lcp_extcode, /* Called to handle LCP-specific codes */ "LCP" /* String name of protocol */ }; /* * Protocol entry points. * Some of these are called directly. */ static void lcp_init(ppp_pcb *pcb); static void lcp_input(ppp_pcb *pcb, u_char *p, int len); static void lcp_protrej(ppp_pcb *pcb); #if PRINTPKT_SUPPORT static int lcp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg); #endif /* PRINTPKT_SUPPORT */ const struct protent lcp_protent = { PPP_LCP, lcp_init, lcp_input, lcp_protrej, lcp_lowerup, lcp_lowerdown, lcp_open, lcp_close, #if PRINTPKT_SUPPORT lcp_printpkt, #endif /* PRINTPKT_SUPPORT */ #if PPP_DATAINPUT NULL, #endif /* PPP_DATAINPUT */ #if PRINTPKT_SUPPORT "LCP", NULL, #endif /* PRINTPKT_SUPPORT */ #if PPP_OPTIONS lcp_option_list, NULL, #endif /* PPP_OPTIONS */ #if DEMAND_SUPPORT NULL, NULL #endif /* DEMAND_SUPPORT */ }; /* * Length of each type of configuration option (in octets) */ #define CILEN_VOID 2 #define CILEN_CHAR 3 #define CILEN_SHORT 4 /* CILEN_VOID + 2 */ #if CHAP_SUPPORT #define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */ #endif /* CHAP_SUPPORT */ #define CILEN_LONG 6 /* CILEN_VOID + 4 */ #if LQR_SUPPORT #define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */ #endif /* LQR_SUPPORT */ #define CILEN_CBCP 3 #define CODENAME(x) ((x) == CONFACK ? "ACK" : \ (x) == CONFNAK ? "NAK" : "REJ") #if PPP_OPTIONS /* * noopt - Disable all options (why?). */ static int noopt(argv) char **argv; { BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); return (1); } #endif /* PPP_OPTIONS */ #ifdef HAVE_MULTILINK static int setendpoint(argv) char **argv; { if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) { lcp_wantoptions[0].neg_endpoint = 1; return 1; } option_error("Can't parse '%s' as an endpoint discriminator", *argv); return 0; } static void printendpoint(opt, printer, arg) option_t *opt; void (*printer) (void *, char *, ...); void *arg; { printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint)); } #endif /* HAVE_MULTILINK */ /* * lcp_init - Initialize LCP. */ static void lcp_init(ppp_pcb *pcb) { fsm *f = &pcb->lcp_fsm; lcp_options *wo = &pcb->lcp_wantoptions; lcp_options *ao = &pcb->lcp_allowoptions; f->pcb = pcb; f->protocol = PPP_LCP; f->callbacks = &lcp_callbacks; fsm_init(f); BZERO(wo, sizeof(*wo)); wo->neg_mru = 1; wo->mru = PPP_DEFMRU; wo->neg_asyncmap = 1; wo->neg_magicnumber = 1; wo->neg_pcompression = 1; wo->neg_accompression = 1; BZERO(ao, sizeof(*ao)); ao->neg_mru = 1; ao->mru = PPP_MAXMRU; ao->neg_asyncmap = 1; #if CHAP_SUPPORT ao->neg_chap = 1; ao->chap_mdtype = CHAP_MDTYPE_SUPPORTED; #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT ao->neg_upap = 1; #endif /* PAP_SUPPORT */ #if EAP_SUPPORT ao->neg_eap = 1; #endif /* EAP_SUPPORT */ ao->neg_magicnumber = 1; ao->neg_pcompression = 1; ao->neg_accompression = 1; ao->neg_endpoint = 1; } /* * lcp_open - LCP is allowed to come up. */ void lcp_open(ppp_pcb *pcb) { fsm *f = &pcb->lcp_fsm; lcp_options *wo = &pcb->lcp_wantoptions; f->flags &= ~(OPT_PASSIVE | OPT_SILENT); if (wo->passive) f->flags |= OPT_PASSIVE; if (wo->silent) f->flags |= OPT_SILENT; fsm_open(f); } /* * lcp_close - Take LCP down. */ void lcp_close(ppp_pcb *pcb, const char *reason) { fsm *f = &pcb->lcp_fsm; int oldstate; if (pcb->phase != PPP_PHASE_DEAD #ifdef HAVE_MULTILINK && pcb->phase != PPP_PHASE_MASTER #endif /* HAVE_MULTILINK */ ) new_phase(pcb, PPP_PHASE_TERMINATE); if (f->flags & DELAYED_UP) { UNTIMEOUT(lcp_delayed_up, f); f->state = PPP_FSM_STOPPED; } oldstate = f->state; fsm_close(f, reason); if (oldstate == PPP_FSM_STOPPED && (f->flags & (OPT_PASSIVE|OPT_SILENT|DELAYED_UP))) { /* * This action is not strictly according to the FSM in RFC1548, * but it does mean that the program terminates if you do a * lcp_close() when a connection hasn't been established * because we are in passive/silent mode or because we have * delayed the fsm_lowerup() call and it hasn't happened yet. */ f->flags &= ~DELAYED_UP; lcp_finished(f); } } /* * lcp_lowerup - The lower layer is up. */ void lcp_lowerup(ppp_pcb *pcb) { lcp_options *wo = &pcb->lcp_wantoptions; fsm *f = &pcb->lcp_fsm; /* * Don't use A/C or protocol compression on transmission, * but accept A/C and protocol compressed packets * if we are going to ask for A/C and protocol compression. */ if (ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0) < 0 || ppp_recv_config(pcb, PPP_MRU, (pcb->settings.lax_recv? 0: 0xffffffff), wo->neg_pcompression, wo->neg_accompression) < 0) return; pcb->peer_mru = PPP_MRU; if (pcb->settings.listen_time != 0) { f->flags |= DELAYED_UP; TIMEOUTMS(lcp_delayed_up, f, pcb->settings.listen_time); } else fsm_lowerup(f); } /* * lcp_lowerdown - The lower layer is down. */ void lcp_lowerdown(ppp_pcb *pcb) { fsm *f = &pcb->lcp_fsm; if (f->flags & DELAYED_UP) { f->flags &= ~DELAYED_UP; UNTIMEOUT(lcp_delayed_up, f); } else fsm_lowerdown(f); } /* * lcp_delayed_up - Bring the lower layer up now. */ static void lcp_delayed_up(void *arg) { fsm *f = (fsm*)arg; if (f->flags & DELAYED_UP) { f->flags &= ~DELAYED_UP; fsm_lowerup(f); } } /* * lcp_input - Input LCP packet. */ static void lcp_input(ppp_pcb *pcb, u_char *p, int len) { fsm *f = &pcb->lcp_fsm; if (f->flags & DELAYED_UP) { f->flags &= ~DELAYED_UP; UNTIMEOUT(lcp_delayed_up, f); fsm_lowerup(f); } fsm_input(f, p, len); } /* * lcp_extcode - Handle a LCP-specific code. */ static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; u_char *magp; switch( code ){ case PROTREJ: lcp_rprotrej(f, inp, len); break; case ECHOREQ: if (f->state != PPP_FSM_OPENED) break; magp = inp; PUTLONG(go->magicnumber, magp); fsm_sdata(f, ECHOREP, id, inp, len); break; case ECHOREP: lcp_received_echo_reply(f, id, inp, len); break; case DISCREQ: case IDENTIF: case TIMEREM: break; default: return 0; } return 1; } /* * lcp_rprotrej - Receive an Protocol-Reject. * * Figure out which protocol is rejected and inform it. */ static void lcp_rprotrej(fsm *f, u_char *inp, int len) { int i; const struct protent *protp; u_short prot; #if PPP_PROTOCOLNAME const char *pname; #endif /* PPP_PROTOCOLNAME */ if (len < 2) { LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!")); return; } GETSHORT(prot, inp); /* * Protocol-Reject packets received in any state other than the LCP * OPENED state SHOULD be silently discarded. */ if( f->state != PPP_FSM_OPENED ){ LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state)); return; } #if PPP_PROTOCOLNAME pname = protocol_name(prot); #endif /* PPP_PROTOCOLNAME */ /* * Upcall the proper Protocol-Reject routine. */ for (i = 0; (protp = protocols[i]) != NULL; ++i) if (protp->protocol == prot) { #if PPP_PROTOCOLNAME if (pname != NULL) ppp_dbglog("Protocol-Reject for '%s' (0x%x) received", pname, prot); else #endif /* PPP_PROTOCOLNAME */ ppp_dbglog("Protocol-Reject for 0x%x received", prot); (*protp->protrej)(f->pcb); return; } #if PPP_PROTOCOLNAME if (pname != NULL) ppp_warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname, prot); else #endif /* #if PPP_PROTOCOLNAME */ ppp_warn("Protocol-Reject for unsupported protocol 0x%x", prot); } /* * lcp_protrej - A Protocol-Reject was received. */ /*ARGSUSED*/ static void lcp_protrej(ppp_pcb *pcb) { /* * Can't reject LCP! */ ppp_error("Received Protocol-Reject for LCP!"); fsm_protreject(&pcb->lcp_fsm); } /* * lcp_sprotrej - Send a Protocol-Reject for some protocol. */ void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len) { fsm *f = &pcb->lcp_fsm; /* * Send back the protocol and the information field of the * rejected packet. We only get here if LCP is in the OPENED state. */ #if 0 p += 2; len -= 2; #endif fsm_sdata(f, PROTREJ, ++f->id, p, len); } /* * lcp_resetci - Reset our CI. */ static void lcp_resetci(fsm *f) { ppp_pcb *pcb = f->pcb; lcp_options *wo = &pcb->lcp_wantoptions; lcp_options *go = &pcb->lcp_gotoptions; lcp_options *ao = &pcb->lcp_allowoptions; #if PPP_AUTH_SUPPORT /* note: default value is true for allow options */ if (pcb->settings.user && pcb->settings.passwd) { #if PAP_SUPPORT if (pcb->settings.refuse_pap) { ao->neg_upap = 0; } #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT if (pcb->settings.refuse_chap) { ao->chap_mdtype &= ~MDTYPE_MD5; } #if MSCHAP_SUPPORT if (pcb->settings.refuse_mschap) { ao->chap_mdtype &= ~MDTYPE_MICROSOFT; } if (pcb->settings.refuse_mschap_v2) { ao->chap_mdtype &= ~MDTYPE_MICROSOFT_V2; } #endif /* MSCHAP_SUPPORT */ ao->neg_chap = (ao->chap_mdtype != MDTYPE_NONE); #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT if (pcb->settings.refuse_eap) { ao->neg_eap = 0; } #endif /* EAP_SUPPORT */ #if PPP_SERVER /* note: default value is false for wanted options */ if (pcb->settings.auth_required) { #if PAP_SUPPORT if (!pcb->settings.refuse_pap) { wo->neg_upap = 1; } #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT if (!pcb->settings.refuse_chap) { wo->chap_mdtype |= MDTYPE_MD5; } #if MSCHAP_SUPPORT if (!pcb->settings.refuse_mschap) { wo->chap_mdtype |= MDTYPE_MICROSOFT; } if (!pcb->settings.refuse_mschap_v2) { wo->chap_mdtype |= MDTYPE_MICROSOFT_V2; } #endif /* MSCHAP_SUPPORT */ wo->neg_chap = (wo->chap_mdtype != MDTYPE_NONE); #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT if (!pcb->settings.refuse_eap) { wo->neg_eap = 1; } #endif /* EAP_SUPPORT */ } #endif /* PPP_SERVER */ } else { #if PAP_SUPPORT ao->neg_upap = 0; #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT ao->neg_chap = 0; ao->chap_mdtype = MDTYPE_NONE; #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT ao->neg_eap = 0; #endif /* EAP_SUPPORT */ } PPPDEBUG(LOG_DEBUG, ("ppp: auth protocols:")); #if PAP_SUPPORT PPPDEBUG(LOG_DEBUG, (" PAP=%d", ao->neg_upap)); #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT PPPDEBUG(LOG_DEBUG, (" CHAP=%d CHAP_MD5=%d", ao->neg_chap, !!(ao->chap_mdtype&MDTYPE_MD5))); #if MSCHAP_SUPPORT PPPDEBUG(LOG_DEBUG, (" CHAP_MS=%d CHAP_MS2=%d", !!(ao->chap_mdtype&MDTYPE_MICROSOFT), !!(ao->chap_mdtype&MDTYPE_MICROSOFT_V2))); #endif /* MSCHAP_SUPPORT */ #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT PPPDEBUG(LOG_DEBUG, (" EAP=%d", ao->neg_eap)); #endif /* EAP_SUPPORT */ PPPDEBUG(LOG_DEBUG, ("\n")); #endif /* PPP_AUTH_SUPPORT */ wo->magicnumber = magic(); wo->numloops = 0; *go = *wo; #ifdef HAVE_MULTILINK if (!multilink) { go->neg_mrru = 0; #endif /* HAVE_MULTILINK */ go->neg_ssnhf = 0; go->neg_endpoint = 0; #ifdef HAVE_MULTILINK } #endif /* HAVE_MULTILINK */ if (pcb->settings.noendpoint) ao->neg_endpoint = 0; pcb->peer_mru = PPP_MRU; #if 0 /* UNUSED */ auth_reset(pcb); #endif /* UNUSED */ } /* * lcp_cilen - Return length of our CI. */ static int lcp_cilen(fsm *f) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; #define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) #if CHAP_SUPPORT #define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) #endif /* CHAP_SUPPORT */ #define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) #define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) #if LQR_SUPPORT #define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) #endif /* LQR_SUPPORT */ #define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) /* * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will * accept more than one. We prefer EAP first, then CHAP, then * PAP. */ return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + #if EAP_SUPPORT LENCISHORT(go->neg_eap) + #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ #if EAP_SUPPORT LENCICHAP(!go->neg_eap && go->neg_chap) + #endif /* EAP_SUPPORT */ #if !EAP_SUPPORT LENCICHAP(go->neg_chap) + #endif /* !EAP_SUPPORT */ #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ #if EAP_SUPPORT && CHAP_SUPPORT LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) + #endif /* EAP_SUPPORT && CHAP_SUPPORT */ #if EAP_SUPPORT && !CHAP_SUPPORT LENCISHORT(!go->neg_eap && go->neg_upap) + #endif /* EAP_SUPPORT && !CHAP_SUPPORT */ #if !EAP_SUPPORT && CHAP_SUPPORT LENCISHORT(!go->neg_chap && go->neg_upap) + #endif /* !EAP_SUPPORT && CHAP_SUPPORT */ #if !EAP_SUPPORT && !CHAP_SUPPORT LENCISHORT(go->neg_upap) + #endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ #endif /* PAP_SUPPORT */ #if LQR_SUPPORT LENCILQR(go->neg_lqr) + #endif /* LQR_SUPPORT */ LENCICBCP(go->neg_cbcp) + LENCILONG(go->neg_magicnumber) + LENCIVOID(go->neg_pcompression) + LENCIVOID(go->neg_accompression) + #ifdef HAVE_MULTILINK LENCISHORT(go->neg_mrru) + #endif /* HAVE_MULTILINK */ LENCIVOID(go->neg_ssnhf) + (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0)); } /* * lcp_addci - Add our desired CIs to a packet. */ static void lcp_addci(fsm *f, u_char *ucp, int *lenp) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; u_char *start_ucp = ucp; #define ADDCIVOID(opt, neg) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_VOID, ucp); \ } #define ADDCISHORT(opt, neg, val) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_SHORT, ucp); \ PUTSHORT(val, ucp); \ } #if CHAP_SUPPORT #define ADDCICHAP(opt, neg, val) \ if (neg) { \ PUTCHAR((opt), ucp); \ PUTCHAR(CILEN_CHAP, ucp); \ PUTSHORT(PPP_CHAP, ucp); \ PUTCHAR((CHAP_DIGEST(val)), ucp); \ } #endif /* CHAP_SUPPORT */ #define ADDCILONG(opt, neg, val) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_LONG, ucp); \ PUTLONG(val, ucp); \ } #if LQR_SUPPORT #define ADDCILQR(opt, neg, val) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_LQR, ucp); \ PUTSHORT(PPP_LQR, ucp); \ PUTLONG(val, ucp); \ } #endif /* LQR_SUPPORT */ #define ADDCICHAR(opt, neg, val) \ if (neg) { \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_CHAR, ucp); \ PUTCHAR(val, ucp); \ } #define ADDCIENDP(opt, neg, class, val, len) \ if (neg) { \ int i; \ PUTCHAR(opt, ucp); \ PUTCHAR(CILEN_CHAR + len, ucp); \ PUTCHAR(class, ucp); \ for (i = 0; i < len; ++i) \ PUTCHAR(val[i], ucp); \ } ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, go->asyncmap); #if EAP_SUPPORT ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ #if EAP_SUPPORT ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); #endif /* EAP_SUPPORT */ #if !EAP_SUPPORT ADDCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype); #endif /* !EAP_SUPPORT */ #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ #if EAP_SUPPORT && CHAP_SUPPORT ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); #endif /* EAP_SUPPORT && CHAP_SUPPORT */ #if EAP_SUPPORT && !CHAP_SUPPORT ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP); #endif /* EAP_SUPPORT && !CHAP_SUPPORT */ #if !EAP_SUPPORT && CHAP_SUPPORT ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); #endif /* !EAP_SUPPORT && CHAP_SUPPORT */ #if !EAP_SUPPORT && !CHAP_SUPPORT ADDCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP); #endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ #endif /* PAP_SUPPORT */ #if LQR_SUPPORT ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); #endif /* LQR_SUPPORT */ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); #ifdef HAVE_MULTILINK ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru); #endif ADDCIVOID(CI_SSNHF, go->neg_ssnhf); ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_, go->endpoint.value, go->endpoint.length); if (ucp - start_ucp != *lenp) { /* this should never happen, because peer_mtu should be 1500 */ ppp_error("Bug in lcp_addci: wrong length"); } } /* * lcp_ackci - Ack our CIs. * This should not modify any state if the Ack is bad. * * Returns: * 0 - Ack was bad. * 1 - Ack was good. */ static int lcp_ackci(fsm *f, u_char *p, int len) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; u_char cilen, citype, cichar; u_short cishort; u32_t 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 -= CILEN_VOID) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_VOID || \ citype != opt) \ goto bad; \ } #define ACKCISHORT(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_SHORT) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_SHORT || \ citype != opt) \ goto bad; \ GETSHORT(cishort, p); \ if (cishort != val) \ goto bad; \ } #define ACKCICHAR(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_CHAR) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_CHAR || \ citype != opt) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != val) \ goto bad; \ } #if CHAP_SUPPORT #define ACKCICHAP(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_CHAP) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_CHAP || \ citype != (opt)) \ goto bad; \ GETSHORT(cishort, p); \ if (cishort != PPP_CHAP) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != (CHAP_DIGEST(val))) \ goto bad; \ } #endif /* CHAP_SUPPORT */ #define ACKCILONG(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_LONG) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_LONG || \ citype != opt) \ goto bad; \ GETLONG(cilong, p); \ if (cilong != val) \ goto bad; \ } #if LQR_SUPPORT #define ACKCILQR(opt, neg, val) \ if (neg) { \ if ((len -= CILEN_LQR) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_LQR || \ citype != opt) \ goto bad; \ GETSHORT(cishort, p); \ if (cishort != PPP_LQR) \ goto bad; \ GETLONG(cilong, p); \ if (cilong != val) \ goto bad; \ } #endif /* LQR_SUPPORT */ #define ACKCIENDP(opt, neg, class, val, vlen) \ if (neg) { \ int i; \ if ((len -= CILEN_CHAR + vlen) < 0) \ goto bad; \ GETCHAR(citype, p); \ GETCHAR(cilen, p); \ if (cilen != CILEN_CHAR + vlen || \ citype != opt) \ goto bad; \ GETCHAR(cichar, p); \ if (cichar != class) \ goto bad; \ for (i = 0; i < vlen; ++i) { \ GETCHAR(cichar, p); \ if (cichar != val[i]) \ goto bad; \ } \ } ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, go->asyncmap); #if EAP_SUPPORT ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ #if EAP_SUPPORT ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); #endif /* EAP_SUPPORT */ #if !EAP_SUPPORT ACKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype); #endif /* !EAP_SUPPORT */ #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ #if EAP_SUPPORT && CHAP_SUPPORT ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); #endif /* EAP_SUPPORT && CHAP_SUPPORT */ #if EAP_SUPPORT && !CHAP_SUPPORT ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP); #endif /* EAP_SUPPORT && !CHAP_SUPPORT */ #if !EAP_SUPPORT && CHAP_SUPPORT ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); #endif /* !EAP_SUPPORT && CHAP_SUPPORT */ #if !EAP_SUPPORT && !CHAP_SUPPORT ACKCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP); #endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ #endif /* PAP_SUPPORT */ #if LQR_SUPPORT ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); #endif /* LQR_SUPPORT */ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); #ifdef HAVE_MULTILINK ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru); #endif /* HAVE_MULTILINK */ ACKCIVOID(CI_SSNHF, go->neg_ssnhf); ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_, go->endpoint.value, go->endpoint.length); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; return (1); bad: LCPDEBUG(("lcp_acki: received bad Ack!")); return (0); } /* * lcp_nakci - Peer has sent a NAK for some of our CIs. * This should not modify any state if the Nak is bad * or if LCP is in the OPENED state. * * Returns: * 0 - Nak was bad. * 1 - Nak was good. */ static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; lcp_options *wo = &pcb->lcp_wantoptions; u_char citype, cichar, *next; u_short cishort; u32_t cilong; lcp_options no; /* options we've seen Naks for */ lcp_options try_; /* options to request next time */ int looped_back = 0; int cilen; BZERO(&no, sizeof(no)); try_ = *go; /* * 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) \ if (go->neg && \ len >= CILEN_VOID && \ p[1] == CILEN_VOID && \ p[0] == opt) { \ len -= CILEN_VOID; \ INCPTR(CILEN_VOID, p); \ no.neg = 1; \ try_.neg = 0; \ } #if CHAP_SUPPORT #define NAKCICHAP(opt, neg, code) \ if (go->neg && \ len >= CILEN_CHAP && \ p[1] == CILEN_CHAP && \ p[0] == opt) { \ len -= CILEN_CHAP; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETCHAR(cichar, p); \ no.neg = 1; \ code \ } #endif /* CHAP_SUPPORT */ #define NAKCICHAR(opt, neg, code) \ if (go->neg && \ len >= CILEN_CHAR && \ p[1] == CILEN_CHAR && \ p[0] == opt) { \ len -= CILEN_CHAR; \ INCPTR(2, p); \ GETCHAR(cichar, p); \ no.neg = 1; \ code \ } #define NAKCISHORT(opt, neg, code) \ if (go->neg && \ len >= CILEN_SHORT && \ p[1] == CILEN_SHORT && \ p[0] == opt) { \ len -= CILEN_SHORT; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ no.neg = 1; \ code \ } #define NAKCILONG(opt, neg, code) \ if (go->neg && \ len >= CILEN_LONG && \ p[1] == CILEN_LONG && \ p[0] == opt) { \ len -= CILEN_LONG; \ INCPTR(2, p); \ GETLONG(cilong, p); \ no.neg = 1; \ code \ } #if LQR_SUPPORT #define NAKCILQR(opt, neg, code) \ if (go->neg && \ len >= CILEN_LQR && \ p[1] == CILEN_LQR && \ p[0] == opt) { \ len -= CILEN_LQR; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETLONG(cilong, p); \ no.neg = 1; \ code \ } #endif /* LQR_SUPPORT */ #define NAKCIENDP(opt, neg) \ if (go->neg && \ len >= CILEN_CHAR && \ p[0] == opt && \ p[1] >= CILEN_CHAR && \ p[1] <= len) { \ len -= p[1]; \ INCPTR(p[1], p); \ no.neg = 1; \ try_.neg = 0; \ } /* * NOTE! There must be no assignments to individual fields of *go in * the code below. Any such assignment is a BUG! */ /* * 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, accept it, up to * the limit of the default MRU we'd get if we didn't negotiate. */ if (go->neg_mru && go->mru != PPP_DEFMRU) { NAKCISHORT(CI_MRU, neg_mru, if (cishort <= wo->mru || cishort <= PPP_DEFMRU) try_.mru = cishort; ); } /* * Add any characters they want to our (receive-side) asyncmap. */ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) { NAKCILONG(CI_ASYNCMAP, neg_asyncmap, try_.asyncmap = go->asyncmap | cilong; ); } /* * If they've nak'd our authentication-protocol, check whether * they are proposing a different protocol, or a different * hash algorithm for CHAP. */ if ((0 #if CHAP_SUPPORT || go->neg_chap #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT || go->neg_upap #endif /* PAP_SUPPORT */ #if EAP_SUPPORT || go->neg_eap #endif /* EAP_SUPPORT */ ) && len >= CILEN_SHORT && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { cilen = p[1]; len -= cilen; #if CHAP_SUPPORT no.neg_chap = go->neg_chap; #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT no.neg_upap = go->neg_upap; #endif /* PAP_SUPPORT */ #if EAP_SUPPORT no.neg_eap = go->neg_eap; #endif /* EAP_SUPPORT */ INCPTR(2, p); GETSHORT(cishort, p); #if PAP_SUPPORT if (cishort == PPP_PAP && cilen == CILEN_SHORT) { #if EAP_SUPPORT /* If we were asking for EAP, then we need to stop that. */ if (go->neg_eap) try_.neg_eap = 0; else #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT /* If we were asking for CHAP, then we need to stop that. */ if (go->neg_chap) try_.neg_chap = 0; else #endif /* CHAP_SUPPORT */ /* * If we weren't asking for CHAP or EAP, then we were asking for * PAP, in which case this Nak is bad. */ goto bad; } else #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { GETCHAR(cichar, p); #if EAP_SUPPORT /* Stop asking for EAP, if we were. */ if (go->neg_eap) { try_.neg_eap = 0; /* Try to set up to use their suggestion, if possible */ if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) try_.chap_mdtype = CHAP_MDTYPE_D(cichar); } else #endif /* EAP_SUPPORT */ if (go->neg_chap) { /* * We were asking for our preferred algorithm, they must * want something different. */ if (cichar != CHAP_DIGEST(go->chap_mdtype)) { if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) { /* Use their suggestion if we support it ... */ try_.chap_mdtype = CHAP_MDTYPE_D(cichar); } else { /* ... otherwise, try our next-preferred algorithm. */ try_.chap_mdtype &= ~(CHAP_MDTYPE(try_.chap_mdtype)); if (try_.chap_mdtype == MDTYPE_NONE) /* out of algos */ try_.neg_chap = 0; } } else { /* * Whoops, they Nak'd our algorithm of choice * but then suggested it back to us. */ goto bad; } } else { /* * Stop asking for PAP if we were asking for it. */ #if PAP_SUPPORT try_.neg_upap = 0; #endif /* PAP_SUPPORT */ } } else #endif /* CHAP_SUPPORT */ { #if EAP_SUPPORT /* * If we were asking for EAP, and they're Conf-Naking EAP, * well, that's just strange. Nobody should do that. */ if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap) ppp_dbglog("Unexpected Conf-Nak for EAP"); /* * We don't recognize what they're suggesting. * Stop asking for what we were asking for. */ if (go->neg_eap) try_.neg_eap = 0; else #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT if (go->neg_chap) try_.neg_chap = 0; else #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT if(1) try_.neg_upap = 0; else #endif /* PAP_SUPPORT */ {} p += cilen - CILEN_SHORT; } } #if LQR_SUPPORT /* * If they can't cope with our link quality protocol, we'll have * to stop asking for LQR. We haven't got any other protocol. * If they Nak the reporting period, take their value XXX ? */ NAKCILQR(CI_QUALITY, neg_lqr, if (cishort != PPP_LQR) try_.neg_lqr = 0; else try_.lqr_period = cilong; ); #endif /* LQR_SUPPORT */ /* * Only implementing CBCP...not the rest of the callback options */ NAKCICHAR(CI_CALLBACK, neg_cbcp, try_.neg_cbcp = 0; (void)cichar; /* if CHAP support is not compiled, cichar is set but not used, which makes some compilers complaining */ ); /* * Check for a looped-back line. */ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, try_.magicnumber = magic(); looped_back = 1; ); /* * Peer shouldn't send Nak for protocol compression or * address/control compression requests; they should send * a Reject instead. If they send a Nak, treat it as a Reject. */ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression); NAKCIVOID(CI_ACCOMPRESSION, neg_accompression); #ifdef HAVE_MULTILINK /* * Nak for MRRU option - accept their value if it is smaller * than the one we want. */ if (go->neg_mrru) { NAKCISHORT(CI_MRRU, neg_mrru, if (treat_as_reject) try_.neg_mrru = 0; else if (cishort <= wo->mrru) try_.mrru = cishort; ); } #else /* HAVE_MULTILINK */ LWIP_UNUSED_ARG(treat_as_reject); #endif /* HAVE_MULTILINK */ /* * Nak for short sequence numbers shouldn't be sent, treat it * like a reject. */ NAKCIVOID(CI_SSNHF, neg_ssnhf); /* * Nak of the endpoint discriminator option is not permitted, * treat it like a reject. */ NAKCIENDP(CI_EPDISC, neg_endpoint); /* * There may be remaining CIs, if the peer is requesting negotiation * on an option that we didn't include in our request packet. * If we see an option that we requested, or one we've already seen * in this packet, then this packet is bad. * If we wanted to respond by starting to negotiate on the requested * option(s), we could, but we don't, because except for the * authentication type and quality protocol, if we are not negotiating * an option, it is because we were told not to. * For the authentication type, the Nak from the peer means * `let me authenticate myself with you' which is a bit pointless. * For the quality protocol, the Nak means `ask me to send you quality * reports', but if we didn't ask for them, we don't want them. * An option we don't recognize represents the peer asking to * negotiate some option we don't support, so ignore it. */ while (len >= CILEN_VOID) { GETCHAR(citype, p); GETCHAR(cilen, p); if (cilen < CILEN_VOID || (len -= cilen) < 0) goto bad; next = p + cilen - 2; switch (citype) { case CI_MRU: if ((go->neg_mru && go->mru != PPP_DEFMRU) || no.neg_mru || cilen != CILEN_SHORT) goto bad; GETSHORT(cishort, p); if (cishort < PPP_DEFMRU) { try_.neg_mru = 1; try_.mru = cishort; } break; case CI_ASYNCMAP: if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) || no.neg_asyncmap || cilen != CILEN_LONG) goto bad; break; case CI_AUTHTYPE: if (0 #if CHAP_SUPPORT || go->neg_chap || no.neg_chap #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT || go->neg_upap || no.neg_upap #endif /* PAP_SUPPORT */ #if EAP_SUPPORT || go->neg_eap || no.neg_eap #endif /* EAP_SUPPORT */ ) goto bad; break; case CI_MAGICNUMBER: if (go->neg_magicnumber || no.neg_magicnumber || cilen != CILEN_LONG) goto bad; break; case CI_PCOMPRESSION: if (go->neg_pcompression || no.neg_pcompression || cilen != CILEN_VOID) goto bad; break; case CI_ACCOMPRESSION: if (go->neg_accompression || no.neg_accompression || cilen != CILEN_VOID) goto bad; break; #if LQR_SUPPORT case CI_QUALITY: if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) goto bad; break; #endif /* LQR_SUPPORT */ #ifdef HAVE_MULTILINK case CI_MRRU: if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT) goto bad; break; #endif /* HAVE_MULTILINK */ case CI_SSNHF: if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID) goto bad; try_.neg_ssnhf = 1; break; case CI_EPDISC: if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR) goto bad; break; default: break; } p = next; } /* * OK, the Nak is good. Now we can update state. * If there are any options left we ignore them. */ if (f->state != PPP_FSM_OPENED) { if (looped_back) { if (++try_.numloops >= pcb->settings.lcp_loopbackfail) { ppp_notice("Serial line is looped back."); pcb->err_code = PPPERR_LOOPBACK; lcp_close(f->pcb, "Loopback detected"); } } else try_.numloops = 0; *go = try_; } return 1; bad: LCPDEBUG(("lcp_nakci: received bad Nak!")); return 0; } /* * lcp_rejci - Peer has Rejected some of our CIs. * This should not modify any state if the Reject is bad * or if LCP is in the OPENED state. * * Returns: * 0 - Reject was bad. * 1 - Reject was good. */ static int lcp_rejci(fsm *f, u_char *p, int len) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; u_char cichar; u_short cishort; u32_t cilong; lcp_options try_; /* options to request next time */ try_ = *go; /* * 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) \ if (go->neg && \ len >= CILEN_VOID && \ p[1] == CILEN_VOID && \ p[0] == opt) { \ len -= CILEN_VOID; \ INCPTR(CILEN_VOID, p); \ try_.neg = 0; \ } #define REJCISHORT(opt, neg, val) \ if (go->neg && \ len >= CILEN_SHORT && \ p[1] == CILEN_SHORT && \ p[0] == opt) { \ len -= CILEN_SHORT; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ /* Check rejected value. */ \ if (cishort != val) \ goto bad; \ try_.neg = 0; \ } #if CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT #define REJCICHAP(opt, neg, val) \ if (go->neg && \ len >= CILEN_CHAP && \ p[1] == CILEN_CHAP && \ p[0] == opt) { \ len -= CILEN_CHAP; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ goto bad; \ try_.neg = 0; \ try_.neg_eap = try_.neg_upap = 0; \ } #endif /* CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT */ #if CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT #define REJCICHAP(opt, neg, val) \ if (go->neg && \ len >= CILEN_CHAP && \ p[1] == CILEN_CHAP && \ p[0] == opt) { \ len -= CILEN_CHAP; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ goto bad; \ try_.neg = 0; \ try_.neg_upap = 0; \ } #endif /* CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT */ #if CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT #define REJCICHAP(opt, neg, val) \ if (go->neg && \ len >= CILEN_CHAP && \ p[1] == CILEN_CHAP && \ p[0] == opt) { \ len -= CILEN_CHAP; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ goto bad; \ try_.neg = 0; \ try_.neg_eap = 0; \ } #endif /* CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT */ #if CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT #define REJCICHAP(opt, neg, val) \ if (go->neg && \ len >= CILEN_CHAP && \ p[1] == CILEN_CHAP && \ p[0] == opt) { \ len -= CILEN_CHAP; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ goto bad; \ try_.neg = 0; \ } #endif /* CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT */ #define REJCILONG(opt, neg, val) \ if (go->neg && \ len >= CILEN_LONG && \ p[1] == CILEN_LONG && \ p[0] == opt) { \ len -= CILEN_LONG; \ INCPTR(2, p); \ GETLONG(cilong, p); \ /* Check rejected value. */ \ if (cilong != val) \ goto bad; \ try_.neg = 0; \ } #if LQR_SUPPORT #define REJCILQR(opt, neg, val) \ if (go->neg && \ len >= CILEN_LQR && \ p[1] == CILEN_LQR && \ p[0] == opt) { \ len -= CILEN_LQR; \ INCPTR(2, p); \ GETSHORT(cishort, p); \ GETLONG(cilong, p); \ /* Check rejected value. */ \ if (cishort != PPP_LQR || cilong != val) \ goto bad; \ try_.neg = 0; \ } #endif /* LQR_SUPPORT */ #define REJCICBCP(opt, neg, val) \ if (go->neg && \ len >= CILEN_CBCP && \ p[1] == CILEN_CBCP && \ p[0] == opt) { \ len -= CILEN_CBCP; \ INCPTR(2, p); \ GETCHAR(cichar, p); \ /* Check rejected value. */ \ if (cichar != val) \ goto bad; \ try_.neg = 0; \ } #define REJCIENDP(opt, neg, class, val, vlen) \ if (go->neg && \ len >= CILEN_CHAR + vlen && \ p[0] == opt && \ p[1] == CILEN_CHAR + vlen) { \ int i; \ len -= CILEN_CHAR + vlen; \ INCPTR(2, p); \ GETCHAR(cichar, p); \ if (cichar != class) \ goto bad; \ for (i = 0; i < vlen; ++i) { \ GETCHAR(cichar, p); \ if (cichar != val[i]) \ goto bad; \ } \ try_.neg = 0; \ } REJCISHORT(CI_MRU, neg_mru, go->mru); REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); #if EAP_SUPPORT REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP); if (!go->neg_eap) { #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype); if (!go->neg_chap) { #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT } #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT } #endif /* EAP_SUPPORT */ #if LQR_SUPPORT REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); #endif /* LQR_SUPPORT */ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); REJCIVOID(CI_PCOMPRESSION, neg_pcompression); REJCIVOID(CI_ACCOMPRESSION, neg_accompression); #ifdef HAVE_MULTILINK REJCISHORT(CI_MRRU, neg_mrru, go->mrru); #endif /* HAVE_MULTILINK */ REJCIVOID(CI_SSNHF, neg_ssnhf); REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class_, go->endpoint.value, go->endpoint.length); /* * If there are any remaining CIs, then this packet is bad. */ if (len != 0) goto bad; /* * Now we can update state. */ if (f->state != PPP_FSM_OPENED) *go = try_; return 1; bad: LCPDEBUG(("lcp_rejci: received bad Reject!")); return 0; } /* * lcp_reqci - Check the peer's requested CIs and send appropriate response. * * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified * appropriately. If reject_if_disagree is non-zero, doesn't return * CONFNAK; returns CONFREJ if it can't return CONFACK. * * inp = Requested CIs * lenp = Length of requested CIs */ static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; lcp_options *ho = &pcb->lcp_hisoptions; lcp_options *ao = &pcb->lcp_allowoptions; u_char *cip, *next; /* Pointer to current and next CIs */ int cilen, citype, cichar; /* Parsed len, type, char value */ u_short cishort; /* Parsed short value */ u32_t cilong; /* Parse long value */ int rc = CONFACK; /* Final packet return code */ int orc; /* Individual option return code */ u_char *p; /* Pointer to next char to parse */ u_char *rejp; /* Pointer to next char in reject frame */ struct pbuf *nakp; /* Nak buffer */ u_char *nakoutp; /* Pointer to next char in Nak frame */ int l = *lenp; /* Length left */ /* * Reset all his options. */ BZERO(ho, sizeof(*ho)); /* * Process all his options. */ next = inp; nakp = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE); if(NULL == nakp) return 0; if(nakp->tot_len != nakp->len) { pbuf_free(nakp); return 0; } nakoutp = (u_char*)nakp->payload; rejp = inp; while (l) { orc = CONFACK; /* Assume success */ cip = p = next; /* 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(("lcp_reqci: bad CI length!")); orc = CONFREJ; /* Reject bad CI */ cilen = l; /* Reject till end of packet */ l = 0; /* Don't loop again */ citype = 0; goto endswitch; } GETCHAR(citype, p); /* Parse CI type */ GETCHAR(cilen, p); /* Parse CI length */ l -= cilen; /* Adjust remaining length */ next += cilen; /* Step to next CI */ switch (citype) { /* Check CI type */ case CI_MRU: if (!ao->neg_mru || /* Allow option? */ cilen != CILEN_SHORT) { /* Check CI length */ orc = CONFREJ; /* Reject CI */ break; } GETSHORT(cishort, p); /* Parse MRU */ /* * 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 < PPP_MINMRU) { orc = CONFNAK; /* Nak CI */ PUTCHAR(CI_MRU, nakoutp); PUTCHAR(CILEN_SHORT, nakoutp); PUTSHORT(PPP_MINMRU, nakoutp); /* Give him a hint */ break; } ho->neg_mru = 1; /* Remember he sent MRU */ ho->mru = cishort; /* And remember value */ break; case CI_ASYNCMAP: if (!ao->neg_asyncmap || cilen != CILEN_LONG) { orc = CONFREJ; break; } GETLONG(cilong, p); /* * Asyncmap must have set at least the bits * which are set in lcp_allowoptions[unit].asyncmap. */ if ((ao->asyncmap & ~cilong) != 0) { orc = CONFNAK; PUTCHAR(CI_ASYNCMAP, nakoutp); PUTCHAR(CILEN_LONG, nakoutp); PUTLONG(ao->asyncmap | cilong, nakoutp); break; } ho->neg_asyncmap = 1; ho->asyncmap = cilong; break; case CI_AUTHTYPE: if (cilen < CILEN_SHORT || !(0 #if PAP_SUPPORT || ao->neg_upap #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT || ao->neg_chap #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT || ao->neg_eap #endif /* EAP_SUPPORT */ )) { /* * Reject the option if we're not willing to authenticate. */ ppp_dbglog("No auth is possible"); orc = CONFREJ; break; } GETSHORT(cishort, p); /* * Authtype must be PAP, CHAP, or EAP. * * Note: if more than one of ao->neg_upap, ao->neg_chap, and * ao->neg_eap are set, and the peer sends a Configure-Request * with two or more authenticate-protocol requests, then we will * reject the second request. * Whether we end up doing CHAP, UPAP, or EAP depends then on * the ordering of the CIs in the peer's Configure-Request. */ #if PAP_SUPPORT if (cishort == PPP_PAP) { /* we've already accepted CHAP or EAP */ if (0 #if CHAP_SUPPORT || ho->neg_chap #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT || ho->neg_eap #endif /* EAP_SUPPORT */ || cilen != CILEN_SHORT) { LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); orc = CONFREJ; break; } if (!ao->neg_upap) { /* we don't want to do PAP */ orc = CONFNAK; /* NAK it and suggest CHAP or EAP */ PUTCHAR(CI_AUTHTYPE, nakoutp); #if EAP_SUPPORT if (ao->neg_eap) { PUTCHAR(CILEN_SHORT, nakoutp); PUTSHORT(PPP_EAP, nakoutp); } else { #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT PUTCHAR(CILEN_CHAP, nakoutp); PUTSHORT(PPP_CHAP, nakoutp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT } #endif /* EAP_SUPPORT */ break; } ho->neg_upap = 1; break; } #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT if (cishort == PPP_CHAP) { /* we've already accepted PAP or EAP */ if ( #if PAP_SUPPORT ho->neg_upap || #endif /* PAP_SUPPORT */ #if EAP_SUPPORT ho->neg_eap || #endif /* EAP_SUPPORT */ cilen != CILEN_CHAP) { LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); orc = CONFREJ; break; } if (!ao->neg_chap) { /* we don't want to do CHAP */ orc = CONFNAK; /* NAK it and suggest EAP or PAP */ PUTCHAR(CI_AUTHTYPE, nakoutp); PUTCHAR(CILEN_SHORT, nakoutp); #if EAP_SUPPORT if (ao->neg_eap) { PUTSHORT(PPP_EAP, nakoutp); } else #endif /* EAP_SUPPORT */ #if PAP_SUPPORT if(1) { PUTSHORT(PPP_PAP, nakoutp); } else #endif /* PAP_SUPPORT */ {} break; } GETCHAR(cichar, p); /* get digest type */ if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) { /* * We can't/won't do the requested type, * suggest something else. */ orc = CONFNAK; PUTCHAR(CI_AUTHTYPE, nakoutp); PUTCHAR(CILEN_CHAP, nakoutp); PUTSHORT(PPP_CHAP, nakoutp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); break; } ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */ ho->neg_chap = 1; break; } #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT if (cishort == PPP_EAP) { /* we've already accepted CHAP or PAP */ if ( #if CHAP_SUPPORT ho->neg_chap || #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT ho->neg_upap || #endif /* PAP_SUPPORT */ cilen != CILEN_SHORT) { LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting...")); orc = CONFREJ; break; } if (!ao->neg_eap) { /* we don't want to do EAP */ orc = CONFNAK; /* NAK it and suggest CHAP or PAP */ PUTCHAR(CI_AUTHTYPE, nakoutp); #if CHAP_SUPPORT if (ao->neg_chap) { PUTCHAR(CILEN_CHAP, nakoutp); PUTSHORT(PPP_CHAP, nakoutp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); } else #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT if(1) { PUTCHAR(CILEN_SHORT, nakoutp); PUTSHORT(PPP_PAP, nakoutp); } else #endif /* PAP_SUPPORT */ {} break; } ho->neg_eap = 1; break; } #endif /* EAP_SUPPORT */ /* * We don't recognize the protocol they're asking for. * Nak it with something we're willing to do. * (At this point we know ao->neg_upap || ao->neg_chap || * ao->neg_eap.) */ orc = CONFNAK; PUTCHAR(CI_AUTHTYPE, nakoutp); #if EAP_SUPPORT if (ao->neg_eap) { PUTCHAR(CILEN_SHORT, nakoutp); PUTSHORT(PPP_EAP, nakoutp); } else #endif /* EAP_SUPPORT */ #if CHAP_SUPPORT if (ao->neg_chap) { PUTCHAR(CILEN_CHAP, nakoutp); PUTSHORT(PPP_CHAP, nakoutp); PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); } else #endif /* CHAP_SUPPORT */ #if PAP_SUPPORT if(1) { PUTCHAR(CILEN_SHORT, nakoutp); PUTSHORT(PPP_PAP, nakoutp); } else #endif /* PAP_SUPPORT */ {} break; #if LQR_SUPPORT case CI_QUALITY: if (!ao->neg_lqr || cilen != CILEN_LQR) { orc = CONFREJ; break; } GETSHORT(cishort, p); GETLONG(cilong, p); /* * Check the protocol and the reporting period. * XXX When should we Nak this, and what with? */ if (cishort != PPP_LQR) { orc = CONFNAK; PUTCHAR(CI_QUALITY, nakoutp); PUTCHAR(CILEN_LQR, nakoutp); PUTSHORT(PPP_LQR, nakoutp); PUTLONG(ao->lqr_period, nakoutp); break; } break; #endif /* LQR_SUPPORT */ case CI_MAGICNUMBER: if (!(ao->neg_magicnumber || go->neg_magicnumber) || cilen != CILEN_LONG) { orc = CONFREJ; break; } GETLONG(cilong, p); /* * He must have a different magic number. */ if (go->neg_magicnumber && cilong == go->magicnumber) { cilong = magic(); /* Don't put magic() inside macro! */ orc = CONFNAK; PUTCHAR(CI_MAGICNUMBER, nakoutp); PUTCHAR(CILEN_LONG, nakoutp); PUTLONG(cilong, nakoutp); break; } ho->neg_magicnumber = 1; ho->magicnumber = cilong; break; case CI_PCOMPRESSION: if (!ao->neg_pcompression || cilen != CILEN_VOID) { orc = CONFREJ; break; } ho->neg_pcompression = 1; break; case CI_ACCOMPRESSION: if (!ao->neg_accompression || cilen != CILEN_VOID) { orc = CONFREJ; break; } ho->neg_accompression = 1; break; #ifdef HAVE_MULTILINK case CI_MRRU: if (!ao->neg_mrru || !multilink || cilen != CILEN_SHORT) { orc = CONFREJ; break; } GETSHORT(cishort, p); /* possibly should insist on a minimum/maximum MRRU here */ ho->neg_mrru = 1; ho->mrru = cishort; break; #endif /* HAVE_MULTILINK */ case CI_SSNHF: if (!ao->neg_ssnhf #ifdef HAVE_MULTILINK || !multilink #endif /* HAVE_MULTILINK */ || cilen != CILEN_VOID) { orc = CONFREJ; break; } ho->neg_ssnhf = 1; break; case CI_EPDISC: if (!ao->neg_endpoint || cilen < CILEN_CHAR || cilen > CILEN_CHAR + MAX_ENDP_LEN) { orc = CONFREJ; break; } GETCHAR(cichar, p); cilen -= CILEN_CHAR; ho->neg_endpoint = 1; ho->endpoint.class_ = cichar; ho->endpoint.length = cilen; MEMCPY(ho->endpoint.value, p, cilen); INCPTR(cilen, p); break; default: LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype)); orc = CONFREJ; break; } endswitch: if (orc == CONFACK && /* Good CI */ rc != CONFACK) /* but prior CI wasnt? */ continue; /* Don't send this one */ if (orc == CONFNAK) { /* Nak this CI? */ if (reject_if_disagree /* Getting fed up with sending NAKs? */ && citype != CI_MAGICNUMBER) { orc = CONFREJ; /* Get tough if so */ } else { if (rc == CONFREJ) /* Rejecting prior CI? */ continue; /* Don't send this one */ rc = CONFNAK; } } if (orc == CONFREJ) { /* Reject this CI */ rc = CONFREJ; if (cip != rejp) /* Need to move rejected CI? */ MEMCPY(rejp, cip, cilen); /* Move it */ INCPTR(cilen, rejp); /* Update output pointer */ } } /* * If we wanted to send additional NAKs (for unsent CIs), the * code would go here. The extra NAKs would go at *nakoutp. * At present there are no cases where we want to ask the * peer to negotiate an option. */ switch (rc) { case CONFACK: *lenp = next - inp; break; case CONFNAK: /* * Copy the Nak'd options from the nak buffer to the caller's buffer. */ *lenp = nakoutp - (u_char*)nakp->payload; MEMCPY(inp, nakp->payload, *lenp); break; case CONFREJ: *lenp = rejp - inp; break; default: break; } pbuf_free(nakp); LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc))); return (rc); /* Return final code */ } /* * lcp_up - LCP has come UP. */ static void lcp_up(fsm *f) { ppp_pcb *pcb = f->pcb; lcp_options *wo = &pcb->lcp_wantoptions; lcp_options *ho = &pcb->lcp_hisoptions; lcp_options *go = &pcb->lcp_gotoptions; lcp_options *ao = &pcb->lcp_allowoptions; int mtu, mru; if (!go->neg_magicnumber) go->magicnumber = 0; if (!ho->neg_magicnumber) ho->magicnumber = 0; /* * Set our MTU to the smaller of the MTU we wanted and * the MRU our peer wanted. If we negotiated an MRU, * set our MRU to the larger of value we wanted and * the value we got in the negotiation. * Note on the MTU: the link MTU can be the MRU the peer wanted, * the interface MTU is set to the lowest of that, the * MTU we want to use, and our link MRU. */ mtu = ho->neg_mru? ho->mru: PPP_MRU; mru = go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU; #ifdef HAVE_MULTILINK if (!(multilink && go->neg_mrru && ho->neg_mrru)) #endif /* HAVE_MULTILINK */ netif_set_mtu(pcb, LWIP_MIN(LWIP_MIN(mtu, mru), ao->mru)); ppp_send_config(pcb, mtu, (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), ho->neg_pcompression, ho->neg_accompression); ppp_recv_config(pcb, mru, (pcb->settings.lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff), go->neg_pcompression, go->neg_accompression); if (ho->neg_mru) pcb->peer_mru = ho->mru; lcp_echo_lowerup(f->pcb); /* Enable echo messages */ link_established(pcb); } /* * lcp_down - LCP has gone DOWN. * * Alert other protocols. */ static void lcp_down(fsm *f) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; lcp_echo_lowerdown(f->pcb); link_down(pcb); ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0); ppp_recv_config(pcb, PPP_MRU, (go->neg_asyncmap? go->asyncmap: 0xffffffff), go->neg_pcompression, go->neg_accompression); pcb->peer_mru = PPP_MRU; } /* * lcp_starting - LCP needs the lower layer up. */ static void lcp_starting(fsm *f) { ppp_pcb *pcb = f->pcb; link_required(pcb); } /* * lcp_finished - LCP has finished with the lower layer. */ static void lcp_finished(fsm *f) { ppp_pcb *pcb = f->pcb; link_terminated(pcb); } #if PRINTPKT_SUPPORT /* * lcp_printpkt - print the contents of an LCP packet. */ static const char* const lcp_codenames[] = { "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej", "ProtRej", "EchoReq", "EchoRep", "DiscReq", "Ident", "TimeRem" }; static int lcp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) { int code, id, len, olen, i; const u_char *pstart, *optend; u_short cishort; u32_t cilong; if (plen < HEADERLEN) return 0; pstart = p; GETCHAR(code, p); GETCHAR(id, p); GETSHORT(len, p); if (len < HEADERLEN || len > plen) return 0; if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(lcp_codenames)) printer(arg, " %s", lcp_codenames[code-1]); else printer(arg, " code=0x%x", code); printer(arg, " id=0x%x", id); len -= HEADERLEN; switch (code) { case CONFREQ: case CONFACK: case CONFNAK: case CONFREJ: /* print option list */ while (len >= 2) { GETCHAR(code, p); GETCHAR(olen, p); p -= 2; if (olen < 2 || olen > len) { break; } printer(arg, " <"); len -= olen; optend = p + olen; switch (code) { case CI_MRU: if (olen == CILEN_SHORT) { p += 2; GETSHORT(cishort, p); printer(arg, "mru %d", cishort); } break; case CI_ASYNCMAP: if (olen == CILEN_LONG) { p += 2; GETLONG(cilong, p); printer(arg, "asyncmap 0x%x", cilong); } break; case CI_AUTHTYPE: if (olen >= CILEN_SHORT) { p += 2; printer(arg, "auth "); GETSHORT(cishort, p); switch (cishort) { #if PAP_SUPPORT case PPP_PAP: printer(arg, "pap"); break; #endif /* PAP_SUPPORT */ #if CHAP_SUPPORT case PPP_CHAP: printer(arg, "chap"); if (p < optend) { switch (*p) { case CHAP_MD5: printer(arg, " MD5"); ++p; break; #if MSCHAP_SUPPORT case CHAP_MICROSOFT: printer(arg, " MS"); ++p; break; case CHAP_MICROSOFT_V2: printer(arg, " MS-v2"); ++p; break; #endif /* MSCHAP_SUPPORT */ default: break; } } break; #endif /* CHAP_SUPPORT */ #if EAP_SUPPORT case PPP_EAP: printer(arg, "eap"); break; #endif /* EAP_SUPPORT */ default: printer(arg, "0x%x", cishort); } } break; #if LQR_SUPPORT case CI_QUALITY: if (olen >= CILEN_SHORT) { p += 2; printer(arg, "quality "); GETSHORT(cishort, p); switch (cishort) { case PPP_LQR: printer(arg, "lqr"); break; default: printer(arg, "0x%x", cishort); } } break; #endif /* LQR_SUPPORT */ case CI_CALLBACK: if (olen >= CILEN_CHAR) { p += 2; printer(arg, "callback "); GETCHAR(cishort, p); switch (cishort) { case CBCP_OPT: printer(arg, "CBCP"); break; default: printer(arg, "0x%x", cishort); } } break; case CI_MAGICNUMBER: if (olen == CILEN_LONG) { p += 2; GETLONG(cilong, p); printer(arg, "magic 0x%x", cilong); } break; case CI_PCOMPRESSION: if (olen == CILEN_VOID) { p += 2; printer(arg, "pcomp"); } break; case CI_ACCOMPRESSION: if (olen == CILEN_VOID) { p += 2; printer(arg, "accomp"); } break; case CI_MRRU: if (olen == CILEN_SHORT) { p += 2; GETSHORT(cishort, p); printer(arg, "mrru %d", cishort); } break; case CI_SSNHF: if (olen == CILEN_VOID) { p += 2; printer(arg, "ssnhf"); } break; case CI_EPDISC: #ifdef HAVE_MULTILINK if (olen >= CILEN_CHAR) { struct epdisc epd; p += 2; GETCHAR(epd.class, p); epd.length = olen - CILEN_CHAR; if (epd.length > MAX_ENDP_LEN) epd.length = MAX_ENDP_LEN; if (epd.length > 0) { MEMCPY(epd.value, p, epd.length); p += epd.length; } printer(arg, "endpoint [%s]", epdisc_to_str(&epd)); } #else printer(arg, "endpoint"); #endif break; default: break; } while (p < optend) { GETCHAR(code, p); printer(arg, " %.2x", code); } printer(arg, ">"); } break; case TERMACK: case TERMREQ: if (len > 0 && *p >= ' ' && *p < 0x7f) { printer(arg, " "); ppp_print_string(p, len, printer, arg); p += len; len = 0; } break; case ECHOREQ: case ECHOREP: case DISCREQ: if (len >= 4) { GETLONG(cilong, p); printer(arg, " magic=0x%x", cilong); len -= 4; } break; case IDENTIF: case TIMEREM: if (len >= 4) { GETLONG(cilong, p); printer(arg, " magic=0x%x", cilong); len -= 4; } if (code == TIMEREM) { if (len < 4) break; GETLONG(cilong, p); printer(arg, " seconds=%u", cilong); len -= 4; } if (len > 0) { printer(arg, " "); ppp_print_string(p, len, printer, arg); p += len; len = 0; } break; default: break; } /* print the rest of the bytes in the packet */ for (i = 0; i < len && i < 32; ++i) { GETCHAR(code, p); printer(arg, " %.2x", code); } if (i < len) { printer(arg, " ..."); p += len - i; } return p - pstart; } #endif /* PRINTPKT_SUPPORT */ /* * Time to shut down the link because there is nothing out there. */ static void LcpLinkFailure(fsm *f) { ppp_pcb *pcb = f->pcb; if (f->state == PPP_FSM_OPENED) { ppp_info("No response to %d echo-requests", pcb->lcp_echos_pending); ppp_notice("Serial link appears to be disconnected."); pcb->err_code = PPPERR_PEERDEAD; lcp_close(pcb, "Peer not responding"); } } /* * Timer expired for the LCP echo requests from this process. */ static void LcpEchoCheck(fsm *f) { ppp_pcb *pcb = f->pcb; LcpSendEchoRequest (f); if (f->state != PPP_FSM_OPENED) return; /* * Start the timer for the next interval. */ if (pcb->lcp_echo_timer_running) ppp_warn("assertion lcp_echo_timer_running==0 failed"); TIMEOUT (LcpEchoTimeout, f, pcb->settings.lcp_echo_interval); pcb->lcp_echo_timer_running = 1; } /* * LcpEchoTimeout - Timer expired on the LCP echo */ static void LcpEchoTimeout(void *arg) { fsm *f = (fsm*)arg; ppp_pcb *pcb = f->pcb; if (pcb->lcp_echo_timer_running != 0) { pcb->lcp_echo_timer_running = 0; LcpEchoCheck ((fsm *) arg); } } /* * LcpEchoReply - LCP has received a reply to the echo */ static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; u32_t magic_val; LWIP_UNUSED_ARG(id); /* Check the magic number - don't count replies from ourselves. */ if (len < 4) { ppp_dbglog("lcp: received short Echo-Reply, length %d", len); return; } GETLONG(magic_val, inp); if (go->neg_magicnumber && magic_val == go->magicnumber) { ppp_warn("appear to have received our own echo-reply!"); return; } /* Reset the number of outstanding echo frames */ pcb->lcp_echos_pending = 0; } /* * LcpSendEchoRequest - Send an echo request frame to the peer */ static void LcpSendEchoRequest(fsm *f) { ppp_pcb *pcb = f->pcb; lcp_options *go = &pcb->lcp_gotoptions; u32_t lcp_magic; u_char pkt[4], *pktp; /* * Detect the failure of the peer at this point. */ if (pcb->settings.lcp_echo_fails != 0) { if (pcb->lcp_echos_pending >= pcb->settings.lcp_echo_fails) { LcpLinkFailure(f); pcb->lcp_echos_pending = 0; } } #if PPP_LCP_ADAPTIVE /* * If adaptive echos have been enabled, only send the echo request if * no traffic was received since the last one. */ if (pcb->settings.lcp_echo_adaptive) { static unsigned int last_pkts_in = 0; #if PPP_STATS_SUPPORT update_link_stats(f->unit); link_stats_valid = 0; #endif /* PPP_STATS_SUPPORT */ if (link_stats.pkts_in != last_pkts_in) { last_pkts_in = link_stats.pkts_in; return; } } #endif /* * Make and send the echo request frame. */ if (f->state == PPP_FSM_OPENED) { lcp_magic = go->magicnumber; pktp = pkt; PUTLONG(lcp_magic, pktp); fsm_sdata(f, ECHOREQ, pcb->lcp_echo_number++, pkt, pktp - pkt); ++pcb->lcp_echos_pending; } } /* * lcp_echo_lowerup - Start the timer for the LCP frame */ static void lcp_echo_lowerup(ppp_pcb *pcb) { fsm *f = &pcb->lcp_fsm; /* Clear the parameters for generating echo frames */ pcb->lcp_echos_pending = 0; pcb->lcp_echo_number = 0; pcb->lcp_echo_timer_running = 0; /* If a timeout interval is specified then start the timer */ if (pcb->settings.lcp_echo_interval != 0) LcpEchoCheck (f); } /* * lcp_echo_lowerdown - Stop the timer for the LCP frame */ static void lcp_echo_lowerdown(ppp_pcb *pcb) { fsm *f = &pcb->lcp_fsm; if (pcb->lcp_echo_timer_running != 0) { UNTIMEOUT (LcpEchoTimeout, f); pcb->lcp_echo_timer_running = 0; } } #endif /* PPP_SUPPORT */