freebsd-skq/usr.sbin/ppp/lcp.c
2005-01-10 09:48:51 +00:00

1303 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 <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);
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;
}