| /*- |
| * 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: src/usr.sbin/ppp/lcp.c,v 1.110.14.1 2010/12/21 17:10:29 kensmith Exp $ |
| */ |
| |
| #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; |
| } |