/*- * Copyright (c) 1996 - 2001 Brian Somers * based on work by Toshiharu OHNO * Internet Initiative Japan, Inc (IIJ) * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "layer.h" #include "ua.h" #include "defs.h" #include "command.h" #include "mbuf.h" #include "log.h" #include "timer.h" #include "fsm.h" #include "iplist.h" #include "throughput.h" #include "proto.h" #include "descriptor.h" #include "lqr.h" #include "hdlc.h" #include "lcp.h" #include "ccp.h" #include "async.h" #include "link.h" #include "physical.h" #include "prompt.h" #include "slcompress.h" #include "ncpaddr.h" #include "ipcp.h" #include "filter.h" #include "mp.h" #include "chat.h" #include "auth.h" #include "chap.h" #include "cbcp.h" #include "datalink.h" #ifndef NORADIUS #include "radius.h" #endif #include "ipv6cp.h" #include "ncp.h" #include "bundle.h" /* for received LQRs */ struct lqrreq { struct fsm_opt_hdr hdr; u_short proto; /* Quality protocol */ u_int32_t period; /* Reporting interval */ }; static int LcpLayerUp(struct fsm *); static void LcpLayerDown(struct fsm *); static void LcpLayerStart(struct fsm *); static void LcpLayerFinish(struct fsm *); static void LcpInitRestartCounter(struct fsm *, int); static void LcpSendConfigReq(struct fsm *); static void LcpSentTerminateReq(struct fsm *); static void LcpSendTerminateAck(struct fsm *, u_char); static void LcpDecodeConfig(struct fsm *, u_char *, u_char *, int, struct fsm_decode *); static struct fsm_callbacks lcp_Callbacks = { LcpLayerUp, LcpLayerDown, LcpLayerStart, LcpLayerFinish, LcpInitRestartCounter, LcpSendConfigReq, LcpSentTerminateReq, LcpSendTerminateAck, LcpDecodeConfig, fsm_NullRecvResetReq, fsm_NullRecvResetAck }; static const char * const lcp_TimerNames[] = {"LCP restart", "LCP openmode", "LCP stopped"}; static const char * protoname(unsigned proto) { static const char * const cftypes[] = { /* Check out the latest ``Assigned numbers'' rfc (1700) */ NULL, "MRU", /* 1: Maximum-Receive-Unit */ "ACCMAP", /* 2: Async-Control-Character-Map */ "AUTHPROTO", /* 3: Authentication-Protocol */ "QUALPROTO", /* 4: Quality-Protocol */ "MAGICNUM", /* 5: Magic-Number */ "RESERVED", /* 6: RESERVED */ "PROTOCOMP", /* 7: Protocol-Field-Compression */ "ACFCOMP", /* 8: Address-and-Control-Field-Compression */ "FCSALT", /* 9: FCS-Alternatives */ "SDP", /* 10: Self-Describing-Pad */ "NUMMODE", /* 11: Numbered-Mode */ "MULTIPROC", /* 12: Multi-Link-Procedure */ "CALLBACK", /* 13: Callback */ "CONTIME", /* 14: Connect-Time */ "COMPFRAME", /* 15: Compound-Frames */ "NDE", /* 16: Nominal-Data-Encapsulation */ "MRRU", /* 17: Multilink-MRRU */ "SHORTSEQ", /* 18: Multilink-Short-Sequence-Number-Header */ "ENDDISC", /* 19: Multilink-Endpoint-Discriminator */ "PROPRIETRY", /* 20: Proprietary */ "DCEID", /* 21: DCE-Identifier */ "MULTIPP", /* 22: Multi-Link-Plus-Procedure */ "LDBACP", /* 23: Link Discriminator for BACP */ }; if (proto > sizeof cftypes / sizeof *cftypes || cftypes[proto] == NULL) return HexStr(proto, NULL, 0); return cftypes[proto]; } int lcp_ReportStatus(struct cmdargs const *arg) { struct link *l; struct lcp *lcp; l = command_ChooseLink(arg); lcp = &l->lcp; prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, lcp->fsm.name, State2Nam(lcp->fsm.state)); prompt_Printf(arg->prompt, " his side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n" " MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n", lcp->his_mru, (u_long)lcp->his_accmap, lcp->his_protocomp ? "on" : "off", lcp->his_acfcomp ? "on" : "off", (u_long)lcp->his_magic, lcp->his_mrru, lcp->his_shortseq ? "on" : "off", lcp->his_reject); prompt_Printf(arg->prompt, " my side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n" " MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n", lcp->want_mru, (u_long)lcp->want_accmap, lcp->want_protocomp ? "on" : "off", lcp->want_acfcomp ? "on" : "off", (u_long)lcp->want_magic, lcp->want_mrru, lcp->want_shortseq ? "on" : "off", lcp->my_reject); if (lcp->cfg.mru) prompt_Printf(arg->prompt, "\n Defaults: MRU = %d (max %d), ", lcp->cfg.mru, lcp->cfg.max_mru); else prompt_Printf(arg->prompt, "\n Defaults: MRU = any (max %d), ", lcp->cfg.max_mru); if (lcp->cfg.mtu) prompt_Printf(arg->prompt, "MTU = %d (max %d), ", lcp->cfg.mtu, lcp->cfg.max_mtu); else prompt_Printf(arg->prompt, "MTU = any (max %d), ", lcp->cfg.max_mtu); prompt_Printf(arg->prompt, "ACCMAP = %08lx\n", (u_long)lcp->cfg.accmap); prompt_Printf(arg->prompt, " LQR period = %us, ", lcp->cfg.lqrperiod); prompt_Printf(arg->prompt, "Open Mode = %s", lcp->cfg.openmode == OPEN_PASSIVE ? "passive" : "active"); if (lcp->cfg.openmode > 0) prompt_Printf(arg->prompt, " (delay %ds)", lcp->cfg.openmode); prompt_Printf(arg->prompt, "\n FSM retry = %us, max %u Config" " REQ%s, %u Term REQ%s\n", lcp->cfg.fsm.timeout, lcp->cfg.fsm.maxreq, lcp->cfg.fsm.maxreq == 1 ? "" : "s", lcp->cfg.fsm.maxtrm, lcp->cfg.fsm.maxtrm == 1 ? "" : "s"); prompt_Printf(arg->prompt, " Ident: %s\n", lcp->cfg.ident); prompt_Printf(arg->prompt, "\n Negotiation:\n"); prompt_Printf(arg->prompt, " ACFCOMP = %s\n", command_ShowNegval(lcp->cfg.acfcomp)); prompt_Printf(arg->prompt, " CHAP = %s\n", command_ShowNegval(lcp->cfg.chap05)); #ifndef NODES prompt_Printf(arg->prompt, " CHAP80 = %s\n", command_ShowNegval(lcp->cfg.chap80nt)); prompt_Printf(arg->prompt, " LANMan = %s\n", command_ShowNegval(lcp->cfg.chap80lm)); prompt_Printf(arg->prompt, " CHAP81 = %s\n", command_ShowNegval(lcp->cfg.chap81)); #endif prompt_Printf(arg->prompt, " LQR = %s\n", command_ShowNegval(lcp->cfg.lqr)); prompt_Printf(arg->prompt, " LCP ECHO = %s\n", lcp->cfg.echo ? "enabled" : "disabled"); prompt_Printf(arg->prompt, " PAP = %s\n", command_ShowNegval(lcp->cfg.pap)); prompt_Printf(arg->prompt, " PROTOCOMP = %s\n", command_ShowNegval(lcp->cfg.protocomp)); return 0; } static u_int32_t GenerateMagic(void) { /* Generate random number which will be used as magic number */ randinit(); return random(); } void lcp_SetupCallbacks(struct lcp *lcp) { lcp->fsm.fn = &lcp_Callbacks; lcp->fsm.FsmTimer.name = lcp_TimerNames[0]; lcp->fsm.OpenTimer.name = lcp_TimerNames[1]; lcp->fsm.StoppedTimer.name = lcp_TimerNames[2]; } void lcp_Init(struct lcp *lcp, struct bundle *bundle, struct link *l, const struct fsm_parent *parent) { /* Initialise ourselves */ int mincode = parent ? 1 : LCP_MINMPCODE; fsm_Init(&lcp->fsm, "LCP", PROTO_LCP, mincode, LCP_MAXCODE, LogLCP, bundle, l, parent, &lcp_Callbacks, lcp_TimerNames); lcp->cfg.mru = 0; lcp->cfg.max_mru = MAX_MRU; lcp->cfg.mtu = 0; lcp->cfg.max_mtu = MAX_MTU; lcp->cfg.accmap = 0; lcp->cfg.openmode = 1; lcp->cfg.lqrperiod = DEF_LQRPERIOD; lcp->cfg.fsm.timeout = DEF_FSMRETRY; lcp->cfg.fsm.maxreq = DEF_FSMTRIES; lcp->cfg.fsm.maxtrm = DEF_FSMTRIES; lcp->cfg.acfcomp = NEG_ENABLED|NEG_ACCEPTED; lcp->cfg.chap05 = NEG_ACCEPTED; #ifndef NODES lcp->cfg.chap80nt = NEG_ACCEPTED; lcp->cfg.chap80lm = 0; lcp->cfg.chap81 = NEG_ACCEPTED; #endif lcp->cfg.lqr = NEG_ACCEPTED; lcp->cfg.echo = 0; lcp->cfg.pap = NEG_ACCEPTED; lcp->cfg.protocomp = NEG_ENABLED|NEG_ACCEPTED; *lcp->cfg.ident = '\0'; lcp_Setup(lcp, lcp->cfg.openmode); } void lcp_Setup(struct lcp *lcp, int openmode) { struct physical *p = link2physical(lcp->fsm.link); lcp->fsm.open_mode = openmode; lcp->his_mru = DEF_MRU; lcp->his_mrru = 0; lcp->his_magic = 0; lcp->his_lqrperiod = 0; lcp->his_acfcomp = 0; lcp->his_auth = 0; lcp->his_authtype = 0; lcp->his_callback.opmask = 0; lcp->his_shortseq = 0; lcp->mru_req = 0; if ((lcp->want_mru = lcp->cfg.mru) == 0) lcp->want_mru = DEF_MRU; lcp->want_mrru = lcp->fsm.bundle->ncp.mp.cfg.mrru; lcp->want_shortseq = IsEnabled(lcp->fsm.bundle->ncp.mp.cfg.shortseq) ? 1 : 0; lcp->want_acfcomp = IsEnabled(lcp->cfg.acfcomp) ? 1 : 0; if (lcp->fsm.parent) { lcp->his_accmap = 0xffffffff; lcp->want_accmap = lcp->cfg.accmap; lcp->his_protocomp = 0; lcp->want_protocomp = IsEnabled(lcp->cfg.protocomp) ? 1 : 0; lcp->want_magic = GenerateMagic(); if (IsEnabled(lcp->cfg.chap05)) { lcp->want_auth = PROTO_CHAP; lcp->want_authtype = 0x05; #ifndef NODES } else if (IsEnabled(lcp->cfg.chap80nt) || IsEnabled(lcp->cfg.chap80lm)) { lcp->want_auth = PROTO_CHAP; lcp->want_authtype = 0x80; } else if (IsEnabled(lcp->cfg.chap81)) { lcp->want_auth = PROTO_CHAP; lcp->want_authtype = 0x81; #endif } else if (IsEnabled(lcp->cfg.pap)) { lcp->want_auth = PROTO_PAP; lcp->want_authtype = 0; } else { lcp->want_auth = 0; lcp->want_authtype = 0; } if (p->type != PHYS_DIRECT) memcpy(&lcp->want_callback, &p->dl->cfg.callback, sizeof(struct callback)); else lcp->want_callback.opmask = 0; lcp->want_lqrperiod = IsEnabled(lcp->cfg.lqr) ? lcp->cfg.lqrperiod * 100 : 0; } else { lcp->his_accmap = lcp->want_accmap = 0; lcp->his_protocomp = lcp->want_protocomp = 1; lcp->want_magic = 0; lcp->want_auth = 0; lcp->want_authtype = 0; lcp->want_callback.opmask = 0; lcp->want_lqrperiod = 0; } lcp->his_reject = lcp->my_reject = 0; lcp->auth_iwait = lcp->auth_ineed = 0; lcp->LcpFailedMagic = 0; } static void LcpInitRestartCounter(struct fsm *fp, int what) { /* Set fsm timer load */ struct lcp *lcp = fsm2lcp(fp); fp->FsmTimer.load = lcp->cfg.fsm.timeout * SECTICKS; switch (what) { case FSM_REQ_TIMER: fp->restart = lcp->cfg.fsm.maxreq; break; case FSM_TRM_TIMER: fp->restart = lcp->cfg.fsm.maxtrm; break; default: fp->restart = 1; break; } } static void LcpSendConfigReq(struct fsm *fp) { /* Send config REQ please */ struct physical *p = link2physical(fp->link); struct lcp *lcp = fsm2lcp(fp); u_char buff[200]; struct fsm_opt *o; struct mp *mp; u_int16_t proto; u_short maxmru; if (!p) { log_Printf(LogERROR, "%s: LcpSendConfigReq: Not a physical link !\n", fp->link->name); return; } o = (struct fsm_opt *)buff; if (!physical_IsSync(p)) { if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP)) INC_FSM_OPT(TY_ACFCOMP, 2, o); if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP)) INC_FSM_OPT(TY_PROTOCOMP, 2, o); if (!REJECTED(lcp, TY_ACCMAP)) { ua_htonl(&lcp->want_accmap, o->data); INC_FSM_OPT(TY_ACCMAP, 6, o); } } maxmru = p ? physical_DeviceMTU(p) : 0; if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru)) maxmru = lcp->cfg.max_mru; if (maxmru && lcp->want_mru > maxmru) { log_Printf(LogWARN, "%s: Reducing configured MRU from %u to %u\n", fp->link->name, lcp->want_mru, maxmru); lcp->want_mru = maxmru; } if (!REJECTED(lcp, TY_MRU)) { ua_htons(&lcp->want_mru, o->data); INC_FSM_OPT(TY_MRU, 4, o); } if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM)) { ua_htonl(&lcp->want_magic, o->data); INC_FSM_OPT(TY_MAGICNUM, 6, o); } if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) { proto = PROTO_LQR; ua_htons(&proto, o->data); ua_htonl(&lcp->want_lqrperiod, o->data + 2); INC_FSM_OPT(TY_QUALPROTO, 8, o); } switch (lcp->want_auth) { case PROTO_PAP: proto = PROTO_PAP; ua_htons(&proto, o->data); INC_FSM_OPT(TY_AUTHPROTO, 4, o); break; case PROTO_CHAP: proto = PROTO_CHAP; ua_htons(&proto, o->data); o->data[2] = lcp->want_authtype; INC_FSM_OPT(TY_AUTHPROTO, 5, o); break; } if (!REJECTED(lcp, TY_CALLBACK)) { if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) { *o->data = CALLBACK_AUTH; INC_FSM_OPT(TY_CALLBACK, 3, o); } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) { *o->data = CALLBACK_CBCP; INC_FSM_OPT(TY_CALLBACK, 3, o); } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164)) { size_t sz = strlen(lcp->want_callback.msg); if (sz > sizeof o->data - 1) { sz = sizeof o->data - 1; log_Printf(LogWARN, "Truncating E164 data to %zu octets (oops!)\n", sz); } *o->data = CALLBACK_E164; memcpy(o->data + 1, lcp->want_callback.msg, sz); INC_FSM_OPT(TY_CALLBACK, sz + 3, o); } } if (lcp->want_mrru && !REJECTED(lcp, TY_MRRU)) { ua_htons(&lcp->want_mrru, o->data); INC_FSM_OPT(TY_MRRU, 4, o); if (lcp->want_shortseq && !REJECTED(lcp, TY_SHORTSEQ)) INC_FSM_OPT(TY_SHORTSEQ, 2, o); } mp = &lcp->fsm.bundle->ncp.mp; if (mp->cfg.enddisc.class != 0 && IsEnabled(mp->cfg.negenddisc) && !REJECTED(lcp, TY_ENDDISC)) { *o->data = mp->cfg.enddisc.class; memcpy(o->data+1, mp->cfg.enddisc.address, mp->cfg.enddisc.len); INC_FSM_OPT(TY_ENDDISC, mp->cfg.enddisc.len + 3, o); } fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff, MB_LCPOUT); } void lcp_SendProtoRej(struct lcp *lcp, u_char *option, int count) { /* Don't understand `option' */ fsm_Output(&lcp->fsm, CODE_PROTOREJ, lcp->fsm.reqid, option, count, MB_LCPOUT); } int lcp_SendIdentification(struct lcp *lcp) { static u_char id; /* Use a private id */ u_char msg[DEF_MRU - 3]; const char *argv[2]; char *exp[2]; if (*lcp->cfg.ident == '\0') return 0; argv[0] = lcp->cfg.ident; argv[1] = NULL; command_Expand(exp, 1, argv, lcp->fsm.bundle, 1, getpid()); ua_htonl(&lcp->want_magic, msg); strncpy(msg + 4, exp[0], sizeof msg - 5); msg[sizeof msg - 1] = '\0'; fsm_Output(&lcp->fsm, CODE_IDENT, id++, msg, 4 + strlen(msg + 4), MB_LCPOUT); log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->want_magic); log_Printf(LogLCP, " TEXT %s\n", msg + 4); command_Free(1, exp); return 1; } void lcp_RecvIdentification(struct lcp *lcp, char *data) { log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->his_magic); log_Printf(LogLCP, " TEXT %s\n", data); } static void LcpSentTerminateReq(struct fsm *fp __unused) { /* Term REQ just sent by FSM */ } static void LcpSendTerminateAck(struct fsm *fp, u_char id) { /* Send Term ACK please */ struct physical *p = link2physical(fp->link); if (p && p->dl->state == DATALINK_CBCP) cbcp_ReceiveTerminateReq(p); fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_LCPOUT); } static void LcpLayerStart(struct fsm *fp) { /* We're about to start up ! */ struct lcp *lcp = fsm2lcp(fp); log_Printf(LogLCP, "%s: LayerStart\n", fp->link->name); lcp->LcpFailedMagic = 0; fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3; lcp->mru_req = 0; } static void LcpLayerFinish(struct fsm *fp) { /* We're now down */ log_Printf(LogLCP, "%s: LayerFinish\n", fp->link->name); } static int LcpLayerUp(struct fsm *fp) { /* We're now up */ struct physical *p = link2physical(fp->link); struct lcp *lcp = fsm2lcp(fp); log_Printf(LogLCP, "%s: LayerUp\n", fp->link->name); physical_SetAsyncParams(p, lcp->want_accmap, lcp->his_accmap); lqr_Start(lcp); hdlc_StartTimer(&p->hdlc); fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3; lcp_SendIdentification(lcp); return 1; } static void LcpLayerDown(struct fsm *fp) { /* About to come down */ struct physical *p = link2physical(fp->link); log_Printf(LogLCP, "%s: LayerDown\n", fp->link->name); hdlc_StopTimer(&p->hdlc); lqr_StopTimer(p); lcp_Setup(fsm2lcp(fp), 0); } static int E164ok(struct callback *cb, char *req, int sz) { char list[sizeof cb->msg], *next; int len; if (!strcmp(cb->msg, "*")) return 1; strncpy(list, cb->msg, sizeof list - 1); list[sizeof list - 1] = '\0'; for (next = strtok(list, ","); next; next = strtok(NULL, ",")) { len = strlen(next); if (sz == len && !memcmp(list, req, sz)) return 1; } return 0; } static int lcp_auth_nak(struct lcp *lcp, struct fsm_decode *dec) { struct fsm_opt nak; nak.hdr.id = TY_AUTHPROTO; if (IsAccepted(lcp->cfg.pap)) { nak.hdr.len = 4; nak.data[0] = (unsigned char)(PROTO_PAP >> 8); nak.data[1] = (unsigned char)PROTO_PAP; fsm_nak(dec, &nak); return 1; } nak.hdr.len = 5; nak.data[0] = (unsigned char)(PROTO_CHAP >> 8); nak.data[1] = (unsigned char)PROTO_CHAP; if (IsAccepted(lcp->cfg.chap05)) { nak.data[2] = 0x05; fsm_nak(dec, &nak); #ifndef NODES } else if (IsAccepted(lcp->cfg.chap80nt) || IsAccepted(lcp->cfg.chap80lm)) { nak.data[2] = 0x80; fsm_nak(dec, &nak); } else if (IsAccepted(lcp->cfg.chap81)) { nak.data[2] = 0x81; fsm_nak(dec, &nak); #endif } else { return 0; } return 1; } static void LcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type, struct fsm_decode *dec) { /* Deal with incoming PROTO_LCP */ struct lcp *lcp = fsm2lcp(fp); int pos, op, callback_req, chap_type; size_t sz; u_int32_t magic, accmap; u_short mru, phmtu, maxmtu, maxmru, wantmtu, wantmru, proto; struct lqrreq req; char request[20], desc[22]; struct mp *mp; struct physical *p = link2physical(fp->link); struct fsm_opt *opt, nak; sz = 0; op = callback_req = 0; while (end - cp >= (int)sizeof(opt->hdr)) { if ((opt = fsm_readopt(&cp)) == NULL) break; snprintf(request, sizeof request, " %s[%d]", protoname(opt->hdr.id), opt->hdr.len); switch (opt->hdr.id) { case TY_MRRU: mp = &lcp->fsm.bundle->ncp.mp; ua_ntohs(opt->data, &mru); log_Printf(LogLCP, "%s %u\n", request, mru); switch (mode_type) { case MODE_REQ: if (mp->cfg.mrru) { if (REJECTED(lcp, TY_MRRU)) /* Ignore his previous reject so that we REQ next time */ lcp->his_reject &= ~(1 << opt->hdr.id); if (mru > MAX_MRU) { /* Push him down to MAX_MRU */ lcp->his_mrru = MAX_MRU; nak.hdr.id = TY_MRRU; nak.hdr.len = 4; ua_htons(&lcp->his_mrru, nak.data); fsm_nak(dec, &nak); } else if (mru < MIN_MRU) { /* Push him up to MIN_MRU */ lcp->his_mrru = MIN_MRU; nak.hdr.id = TY_MRRU; nak.hdr.len = 4; ua_htons(&lcp->his_mrru, nak.data); fsm_nak(dec, &nak); } else { lcp->his_mrru = mru; fsm_ack(dec, opt); } break; } else { fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } break; case MODE_NAK: if (mp->cfg.mrru) { if (REJECTED(lcp, TY_MRRU)) /* Must have changed his mind ! */ lcp->his_reject &= ~(1 << opt->hdr.id); if (mru > MAX_MRU) lcp->want_mrru = MAX_MRU; else if (mru < MIN_MRU) lcp->want_mrru = MIN_MRU; else lcp->want_mrru = mru; } /* else we honour our config and don't send the suggested REQ */ break; case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); lcp->want_mrru = 0; /* Ah well, no multilink :-( */ break; } break; case TY_MRU: lcp->mru_req = 1; ua_ntohs(opt->data, &mru); log_Printf(LogLCP, "%s %d\n", request, mru); switch (mode_type) { case MODE_REQ: maxmtu = p ? physical_DeviceMTU(p) : 0; if (lcp->cfg.max_mtu && (!maxmtu || maxmtu > lcp->cfg.max_mtu)) maxmtu = lcp->cfg.max_mtu; wantmtu = lcp->cfg.mtu; if (maxmtu && wantmtu > maxmtu) { log_Printf(LogWARN, "%s: Reducing configured MTU from %u to %u\n", fp->link->name, wantmtu, maxmtu); wantmtu = maxmtu; } if (maxmtu && mru > maxmtu) { lcp->his_mru = maxmtu; nak.hdr.id = TY_MRU; nak.hdr.len = 4; ua_htons(&lcp->his_mru, nak.data); fsm_nak(dec, &nak); } else if (wantmtu && mru < wantmtu) { /* Push him up to MTU or MIN_MRU */ lcp->his_mru = wantmtu; nak.hdr.id = TY_MRU; nak.hdr.len = 4; ua_htons(&lcp->his_mru, nak.data); fsm_nak(dec, &nak); } else { lcp->his_mru = mru; fsm_ack(dec, opt); } break; case MODE_NAK: maxmru = p ? physical_DeviceMTU(p) : 0; if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru)) maxmru = lcp->cfg.max_mru; wantmru = lcp->cfg.mru > maxmru ? maxmru : lcp->cfg.mru; if (wantmru && mru > wantmru) lcp->want_mru = wantmru; else if (mru > maxmru) lcp->want_mru = maxmru; else if (mru < MIN_MRU) lcp->want_mru = MIN_MRU; else lcp->want_mru = mru; break; case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); /* Set the MTU to what we want anyway - the peer won't care! */ if (lcp->his_mru > lcp->want_mru) lcp->his_mru = lcp->want_mru; break; } break; case TY_ACCMAP: ua_ntohl(opt->data, &accmap); log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)accmap); switch (mode_type) { case MODE_REQ: lcp->his_accmap = accmap; fsm_ack(dec, opt); break; case MODE_NAK: lcp->want_accmap = accmap; break; case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); break; } break; case TY_AUTHPROTO: ua_ntohs(opt->data, &proto); chap_type = opt->hdr.len == 5 ? opt->data[2] : 0; log_Printf(LogLCP, "%s 0x%04x (%s)\n", request, proto, Auth2Nam(proto, chap_type)); switch (mode_type) { case MODE_REQ: switch (proto) { case PROTO_PAP: if (opt->hdr.len == 4 && IsAccepted(lcp->cfg.pap)) { lcp->his_auth = proto; lcp->his_authtype = 0; fsm_ack(dec, opt); } else if (!lcp_auth_nak(lcp, dec)) { lcp->my_reject |= (1 << opt->hdr.id); fsm_rej(dec, opt); } break; case PROTO_CHAP: if ((chap_type == 0x05 && IsAccepted(lcp->cfg.chap05)) #ifndef NODES || (chap_type == 0x80 && (IsAccepted(lcp->cfg.chap80nt) || (IsAccepted(lcp->cfg.chap80lm)))) || (chap_type == 0x81 && IsAccepted(lcp->cfg.chap81)) #endif ) { lcp->his_auth = proto; lcp->his_authtype = chap_type; fsm_ack(dec, opt); } else { #ifdef NODES if (chap_type == 0x80) { log_Printf(LogWARN, "CHAP 0x80 not available without DES\n"); } else if (chap_type == 0x81) { log_Printf(LogWARN, "CHAP 0x81 not available without DES\n"); } else #endif if (chap_type != 0x05) log_Printf(LogWARN, "%s not supported\n", Auth2Nam(PROTO_CHAP, chap_type)); if (!lcp_auth_nak(lcp, dec)) { lcp->my_reject |= (1 << opt->hdr.id); fsm_rej(dec, opt); } } break; default: log_Printf(LogLCP, "%s 0x%04x - not recognised\n", request, proto); if (!lcp_auth_nak(lcp, dec)) { lcp->my_reject |= (1 << opt->hdr.id); fsm_rej(dec, opt); } break; } break; case MODE_NAK: switch (proto) { case PROTO_PAP: if (IsEnabled(lcp->cfg.pap)) { lcp->want_auth = PROTO_PAP; lcp->want_authtype = 0; } else { log_Printf(LogLCP, "Peer will only send PAP (not enabled)\n"); lcp->his_reject |= (1 << opt->hdr.id); } break; case PROTO_CHAP: if (chap_type == 0x05 && IsEnabled(lcp->cfg.chap05)) { lcp->want_auth = PROTO_CHAP; lcp->want_authtype = 0x05; #ifndef NODES } else if (chap_type == 0x80 && (IsEnabled(lcp->cfg.chap80nt) || IsEnabled(lcp->cfg.chap80lm))) { lcp->want_auth = PROTO_CHAP; lcp->want_authtype = 0x80; } else if (chap_type == 0x81 && IsEnabled(lcp->cfg.chap81)) { lcp->want_auth = PROTO_CHAP; lcp->want_authtype = 0x81; #endif } else { #ifdef NODES if (chap_type == 0x80) { log_Printf(LogLCP, "Peer will only send MSCHAP (not available" " without DES)\n"); } else if (chap_type == 0x81) { log_Printf(LogLCP, "Peer will only send MSCHAPV2 (not available" " without DES)\n"); } else #endif log_Printf(LogLCP, "Peer will only send %s (not %s)\n", Auth2Nam(PROTO_CHAP, chap_type), #ifndef NODES (chap_type == 0x80 || chap_type == 0x81) ? "configured" : #endif "supported"); lcp->his_reject |= (1 << opt->hdr.id); } break; default: /* We've been NAK'd with something we don't understand :-( */ lcp->his_reject |= (1 << opt->hdr.id); break; } break; case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); break; } break; case TY_QUALPROTO: memcpy(&req, opt, sizeof req); log_Printf(LogLCP, "%s proto %x, interval %lums\n", request, ntohs(req.proto), (u_long)ntohl(req.period) * 10); switch (mode_type) { case MODE_REQ: if (ntohs(req.proto) != PROTO_LQR || !IsAccepted(lcp->cfg.lqr)) { fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } else { lcp->his_lqrperiod = ntohl(req.period); if (lcp->his_lqrperiod < MIN_LQRPERIOD * 100) lcp->his_lqrperiod = MIN_LQRPERIOD * 100; req.period = htonl(lcp->his_lqrperiod); fsm_ack(dec, opt); } break; case MODE_NAK: lcp->want_lqrperiod = ntohl(req.period); break; case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); break; } break; case TY_MAGICNUM: ua_ntohl(opt->data, &magic); log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)magic); switch (mode_type) { case MODE_REQ: if (lcp->want_magic) { /* Validate magic number */ if (magic == lcp->want_magic) { sigset_t emptyset; log_Printf(LogLCP, "Magic is same (%08lx) - %d times\n", (u_long)magic, ++lcp->LcpFailedMagic); lcp->want_magic = GenerateMagic(); fsm_nak(dec, opt); ualarm(TICKUNIT * (4 + 4 * lcp->LcpFailedMagic), 0); sigemptyset(&emptyset); sigsuspend(&emptyset); } else { lcp->his_magic = magic; lcp->LcpFailedMagic = 0; fsm_ack(dec, opt); } } else { lcp->my_reject |= (1 << opt->hdr.id); fsm_rej(dec, opt); } break; case MODE_NAK: log_Printf(LogLCP, " Magic 0x%08lx is NAKed!\n", (u_long)magic); lcp->want_magic = GenerateMagic(); break; case MODE_REJ: log_Printf(LogLCP, " Magic 0x%08x is REJected!\n", magic); lcp->want_magic = 0; lcp->his_reject |= (1 << opt->hdr.id); break; } break; case TY_PROTOCOMP: log_Printf(LogLCP, "%s\n", request); switch (mode_type) { case MODE_REQ: if (IsAccepted(lcp->cfg.protocomp)) { lcp->his_protocomp = 1; fsm_ack(dec, opt); } else { #ifdef OLDMST /* MorningStar before v1.3 needs NAK */ fsm_nak(dec, opt); #else fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); #endif } break; case MODE_NAK: case MODE_REJ: lcp->want_protocomp = 0; lcp->his_reject |= (1 << opt->hdr.id); break; } break; case TY_ACFCOMP: log_Printf(LogLCP, "%s\n", request); switch (mode_type) { case MODE_REQ: if (IsAccepted(lcp->cfg.acfcomp)) { lcp->his_acfcomp = 1; fsm_ack(dec, opt); } else { #ifdef OLDMST /* MorningStar before v1.3 needs NAK */ fsm_nak(dec, opt); #else fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); #endif } break; case MODE_NAK: case MODE_REJ: lcp->want_acfcomp = 0; lcp->his_reject |= (1 << opt->hdr.id); break; } break; case TY_SDP: log_Printf(LogLCP, "%s\n", request); switch (mode_type) { case MODE_REQ: case MODE_NAK: case MODE_REJ: break; } break; case TY_CALLBACK: if (opt->hdr.len == 2) { op = CALLBACK_NONE; sz = 0; } else { op = (int)opt->data[0]; sz = opt->hdr.len - 3; } switch (op) { case CALLBACK_AUTH: log_Printf(LogLCP, "%s Auth\n", request); break; case CALLBACK_DIALSTRING: log_Printf(LogLCP, "%s Dialstring %.*s\n", request, (int)sz, opt->data + 1); break; case CALLBACK_LOCATION: log_Printf(LogLCP, "%s Location %.*s\n", request, (int)sz, opt->data + 1); break; case CALLBACK_E164: log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, (int)sz, opt->data + 1); break; case CALLBACK_NAME: log_Printf(LogLCP, "%s Name %.*s\n", request, (int)sz, opt->data + 1); break; case CALLBACK_CBCP: log_Printf(LogLCP, "%s CBCP\n", request); break; default: log_Printf(LogLCP, "%s ???\n", request); break; } switch (mode_type) { case MODE_REQ: callback_req = 1; if (p->type != PHYS_DIRECT) { fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } nak.hdr.id = opt->hdr.id; nak.hdr.len = 3; if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(op)) && (op != CALLBACK_AUTH || p->link.lcp.want_auth) && (op != CALLBACK_E164 || E164ok(&p->dl->cfg.callback, opt->data + 1, sz))) { lcp->his_callback.opmask = CALLBACK_BIT(op); if (sz > sizeof lcp->his_callback.msg - 1) { sz = sizeof lcp->his_callback.msg - 1; log_Printf(LogWARN, "Truncating option arg to %zu octets\n", sz); } memcpy(lcp->his_callback.msg, opt->data + 1, sz); lcp->his_callback.msg[sz] = '\0'; fsm_ack(dec, opt); } else if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) && p->link.lcp.auth_ineed) { nak.data[0] = CALLBACK_AUTH; fsm_nak(dec, &nak); } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) { nak.data[0] = CALLBACK_CBCP; fsm_nak(dec, &nak); } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) { nak.data[0] = CALLBACK_E164; fsm_nak(dec, &nak); } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) { log_Printf(LogWARN, "Cannot insist on auth callback without" " PAP or CHAP enabled !\n"); nak.data[0] = 2; fsm_nak(dec, &nak); } else { lcp->my_reject |= (1 << opt->hdr.id); fsm_rej(dec, opt); } break; case MODE_NAK: /* We don't do what he NAKs with, we do things in our preferred order */ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_AUTH); else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_CBCP); else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164)) lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_E164); if (lcp->want_callback.opmask == CALLBACK_BIT(CALLBACK_NONE)) { log_Printf(LogPHASE, "Peer NAKd all callbacks, trying none\n"); lcp->want_callback.opmask = 0; } else if (!lcp->want_callback.opmask) { log_Printf(LogPHASE, "Peer NAKd last configured callback\n"); fsm_Close(&lcp->fsm); } break; case MODE_REJ: if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) { lcp->his_reject |= (1 << opt->hdr.id); lcp->want_callback.opmask = 0; } else { log_Printf(LogPHASE, "Peer rejected *required* callback\n"); fsm_Close(&lcp->fsm); } break; } break; case TY_SHORTSEQ: mp = &lcp->fsm.bundle->ncp.mp; log_Printf(LogLCP, "%s\n", request); switch (mode_type) { case MODE_REQ: if (lcp->want_mrru && IsAccepted(mp->cfg.shortseq)) { lcp->his_shortseq = 1; fsm_ack(dec, opt); } else { fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } break; case MODE_NAK: /* * He's trying to get us to ask for short sequence numbers. * We ignore the NAK and honour our configuration file instead. */ break; case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); lcp->want_shortseq = 0; /* For when we hit MP */ break; } break; case TY_ENDDISC: mp = &lcp->fsm.bundle->ncp.mp; log_Printf(LogLCP, "%s %s\n", request, mp_Enddisc(opt->data[0], opt->data + 1, opt->hdr.len - 3)); switch (mode_type) { case MODE_REQ: if (!p) { log_Printf(LogLCP, " ENDDISC rejected - not a physical link\n"); fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } else if (!IsAccepted(mp->cfg.negenddisc)) { lcp->my_reject |= (1 << opt->hdr.id); fsm_rej(dec, opt); } else if (opt->hdr.len < sizeof p->dl->peer.enddisc.address + 3 && opt->data[0] <= MAX_ENDDISC_CLASS) { p->dl->peer.enddisc.class = opt->data[0]; p->dl->peer.enddisc.len = opt->hdr.len - 3; memcpy(p->dl->peer.enddisc.address, opt->data + 1, opt->hdr.len - 3); p->dl->peer.enddisc.address[opt->hdr.len - 3] = '\0'; /* XXX: If mp->active, compare and NAK with mp->peer ? */ fsm_ack(dec, opt); } else { if (opt->data[0] > MAX_ENDDISC_CLASS) log_Printf(LogLCP, " ENDDISC rejected - unrecognised class %d\n", opt->data[0]); else log_Printf(LogLCP, " ENDDISC rejected - local max length is %ld\n", (long)(sizeof p->dl->peer.enddisc.address - 1)); fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } break; case MODE_NAK: /* Treat this as a REJ, we don't vary our disc (yet) */ case MODE_REJ: lcp->his_reject |= (1 << opt->hdr.id); break; } break; default: sz = (sizeof desc - 2) / 2; if (sz + 2 > opt->hdr.len) sz = opt->hdr.len - 2; pos = 0; desc[0] = sz ? ' ' : '\0'; for (pos = 0; sz--; pos++) sprintf(desc+(pos<<1)+1, "%02x", opt->data[pos]); log_Printf(LogLCP, "%s%s\n", request, desc); if (mode_type == MODE_REQ) { fsm_rej(dec, opt); lcp->my_reject |= (1 << opt->hdr.id); } break; } } if (mode_type != MODE_NOP) { if (mode_type == MODE_REQ && p && p->type == PHYS_DIRECT && p->dl->cfg.callback.opmask && !callback_req && !(p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))) { /* We *REQUIRE* that the peer requests callback */ nak.hdr.id = TY_CALLBACK; nak.hdr.len = 3; if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) && p->link.lcp.want_auth) nak.data[0] = CALLBACK_AUTH; else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) nak.data[0] = CALLBACK_CBCP; else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) nak.data[0] = CALLBACK_E164; else { log_Printf(LogWARN, "Cannot insist on auth callback without" " PAP or CHAP enabled !\n"); nak.hdr.len = 2; /* XXX: Silly ! */ } fsm_nak(dec, &nak); } if (mode_type == MODE_REQ && !lcp->mru_req) { mru = DEF_MRU; phmtu = p ? physical_DeviceMTU(p) : 0; if (phmtu && mru > phmtu) mru = phmtu; if (mru > lcp->cfg.max_mtu) mru = lcp->cfg.max_mtu; if (mru < DEF_MRU) { /* Don't let the peer use the default MRU */ lcp->his_mru = lcp->cfg.mtu && lcp->cfg.mtu < mru ? lcp->cfg.mtu : mru; nak.hdr.id = TY_MRU; nak.hdr.len = 4; ua_htons(&lcp->his_mru, nak.data); fsm_nak(dec, &nak); lcp->mru_req = 1; /* Don't keep NAK'ing this */ } } fsm_opt_normalise(dec); } } extern struct mbuf * lcp_Input(struct bundle *bundle __unused, struct link *l, struct mbuf *bp) { /* Got PROTO_LCP from link */ m_settype(bp, MB_LCPIN); fsm_Input(&l->lcp.fsm, bp); return NULL; }