diff options
Diffstat (limited to 'usr.sbin/ppp/lcp.c')
-rw-r--r-- | usr.sbin/ppp/lcp.c | 1305 |
1 files changed, 1305 insertions, 0 deletions
diff --git a/usr.sbin/ppp/lcp.c b/usr.sbin/ppp/lcp.c new file mode 100644 index 0000000..cf75718 --- /dev/null +++ b/usr.sbin/ppp/lcp.c @@ -0,0 +1,1305 @@ +/*- + * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> + * based on work by Toshiharu OHNO <tony-o@iij.ad.jp> + * 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 <sys/param.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#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; +} |