freebsd-nq/usr.sbin/ppp/lcp.c
Brian Somers ff360cc91b Make the way FSM options are processed easier to read by using structures
instead of u_char *.

The changes are cosmetic except:

  RecvConfigAck() now displays the options that are being ACK'd
  Huge (bogus) options sent from the peer won't cause an infinite loop
  SendIdent and ReceiveIdent are displayed consistenlty with other FSM data
  LCP AUTHPROTO options that aren't understood are NAK'd, not REJ'd
2002-04-16 23:57:09 +00:00

1292 lines
39 KiB
C

/*-
* 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 <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 "ip.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(int 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 < 0 || 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, " 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.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)) {
int sz = strlen(lcp->want_callback.msg);
if (sz > sizeof o->data - 1) {
sz = sizeof o->data - 1;
log_Printf(LogWARN, "Truncating E164 data to %d 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)
{
/* 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 sz, pos, op, callback_req, chap_type;
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 = op = callback_req = 0;
while (end - cp >= 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);
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:
req = (struct lqrreq *)opt;
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:
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;
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, sz,
opt->data + 1);
break;
case CALLBACK_LOCATION:
log_Printf(LogLCP, "%s Location %.*s\n", request, sz, opt->data + 1);
break;
case CALLBACK_E164:
log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, sz, opt->data + 1);
break;
case CALLBACK_NAME:
log_Printf(LogLCP, "%s Name %.*s\n", request, 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 %d 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 - 3 < sizeof p->dl->peer.enddisc.address &&
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 > opt->hdr.len - 2)
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, struct link *l, struct mbuf *bp)
{
/* Got PROTO_LCP from link */
m_settype(bp, MB_LCPIN);
fsm_Input(&l->lcp.fsm, bp);
return NULL;
}