/*
* input.c
*
* Written by Archie Cobbs <archie@freebsd.org>
* Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
* See ``COPYRIGHT.whistle''
*/
#include "ppp.h"
#include "input.h"
#include "ipcp.h"
#include "chap.h"
#include "pap.h"
#include "eap.h"
#include "lcp.h"
#include "ip.h"
#include "ccp.h"
#include "ecp.h"
#include "ngfunc.h"
/*
* INTERNAL FUNCTIONS
*/
static int InputLinkCheck(Link l, int proto);
static void InputMPLink(Bund b, int proto, Mbuf pkt);
static int InputDispatch(Bund b, Link l, int proto, Mbuf bp);
/*
* InputFrame()
*
* Input a PPP frame having protocol "proto" from link "linkNum",
* which may be either a link number or NG_PPP_BUNDLE_LINKNUM.
* This always consumes the mbuf.
*/
void
InputFrame(Bund b, Link l, int proto, Mbuf bp)
{
Mbuf protoRej;
u_int16_t nprot;
/* Check the link */
if (l == NULL) {
/* Only limited link-layer stuff allowed over the MP bundle */
if (PROT_LINK_LAYER(proto)) {
InputMPLink(b, proto, bp);
return;
}
} else {
/* Check protocol vs. link state */
if (!InputLinkCheck(l, proto)) {
mbfree(bp);
return;
}
}
/* Dispatch frame to the appropriate protocol engine */
if (InputDispatch(b, l, proto, bp) >= 0)
return;
/* Unknown protocol, so find a link to send protocol reject on */
if (l == NULL) {
int k;
for (k = 0; k < NG_PPP_MAX_LINKS &&
(!b->links[k] || b->links[k]->lcp.phase != PHASE_NETWORK);
k++);
if (k == NG_PPP_MAX_LINKS) {
mbfree(bp);
return;
}
l = b->links[k];
}
/* Send a protocol reject on the chosen link */
nprot = htons((u_int16_t) proto);
protoRej = mbcopyback(bp, -2, (u_char *) &nprot, 2);
FsmOutputMbuf(&l->lcp.fsm, CODE_PROTOREJ, l->lcp.fsm.rejid++, protoRej);
}
/*
* InputDispatch()
*
* Given an unwrapped PPP frame of type "proto", dispatch to wherever.
* Returns negative if protocol was unknown, otherwise returns zero
* and consumes packet. Any packets we expect the peer to send but
* shouldn't be received by this daemon are logged and dropped.
*/
static int
InputDispatch(Bund b, Link l, int proto, Mbuf bp)
{
int reject = 0;
/* link level protos */
if (l) {
switch (proto) {
case PROTO_LCP:
LcpInput(l, bp);
return(0);
case PROTO_PAP:
case PROTO_CHAP:
case PROTO_EAP:
AuthInput(l, proto, bp);
return(0);
case PROTO_MP:
if (!Enabled(&l->conf.options, LINK_CONF_MULTILINK))
reject = 1;
goto done;
}
}
/* bundle level protos */
if (b) {
switch (proto) {
case PROTO_IPCP:
case PROTO_IP:
case PROTO_VJUNCOMP:
case PROTO_VJCOMP:
if (!Enabled(&b->conf.options, BUND_CONF_IPCP))
reject = 1;
else if (proto == PROTO_IPCP) {
IpcpInput(b, bp);
return(0);
}
break;
case PROTO_IPV6CP:
case PROTO_IPV6:
if (!Enabled(&b->conf.options, BUND_CONF_IPV6CP))
reject = 1;
else if (proto == PROTO_IPV6CP) {
Ipv6cpInput(b, bp);
return(0);
}
break;
case PROTO_CCP:
case PROTO_COMPD:
if (!Enabled(&b->conf.options, BUND_CONF_COMPRESSION))
reject = 1;
else if (proto == PROTO_CCP) {
CcpInput(b, bp);
return(0);
}
break;
case PROTO_ECP:
case PROTO_CRYPT:
if (!Enabled(&b->conf.options, BUND_CONF_ENCRYPTION))
reject = 1;
else if (proto == PROTO_ECP) {
EcpInput(b, bp);
return(0);
}
break;
default: /* completely unknown protocol, reject it */
reject = 1;
break;
}
}
done:
/* Protocol unexpected, so either reject or drop */
Log(LG_LINK|LG_BUND, ("[%s] rec'd unexpected protocol %s%s",
(l ? l->name : b->name), ProtoName(proto), reject ? ", rejecting" : ""));
if (!reject)
mbfree(bp);
return (reject ? -1 : 0);
}
/*
* InputLinkCheck()
*
* Make sure this protocol is acceptable and makes sense on this link.
* Returns TRUE if so and the frame should be handled further.
*/
static int
InputLinkCheck(Link l, int proto)
{
/* Check link LCP state */
switch (l->lcp.phase) {
case PHASE_DEAD:
Log(LG_LINK, ("[%s] rec'd proto %s while dead",
l->name, ProtoName(proto)));
return(FALSE);
case PHASE_ESTABLISH:
if (proto != PROTO_LCP) {
Log(LG_LINK, ("[%s] rec'd proto %s during establishment phase",
l->name, ProtoName(proto)));
return(FALSE);
}
break;
case PHASE_AUTHENTICATE:
if (!PROT_LINK_LAYER(proto)) {
Log(LG_LINK, ("[%s] rec'd proto %s during authenticate phase",
l->name, ProtoName(proto)));
return(FALSE);
}
break;
case PHASE_NETWORK:
break;
case PHASE_TERMINATE:
if (proto != PROTO_LCP) {
Log(LG_LINK, ("[%s] rec'd proto %s during terminate phase",
l->name, ProtoName(proto)));
return(FALSE);
}
break;
default:
assert(0);
}
/* OK */
return(TRUE);
}
/*
* InputMPLink()
*
* Deal with an incoming link-level packet on the virtual link (!)
* Only certain link-level packets make sense coming over the bundle.
* In any case, this consumes the mbuf.
*/
static void
InputMPLink(Bund b, int proto, Mbuf pkt)
{
struct fsmheader *hdr;
int k;
switch (proto) {
case PROTO_LCP:
if (MBLEN(pkt) < sizeof(hdr))
break;
hdr = (struct fsmheader *)(void *)MBDATA(pkt);
switch (hdr->code) {
case CODE_CODEREJ: /* these two are OK */
case CODE_PROTOREJ:
for (k = 0; k < NG_PPP_MAX_LINKS && !b->links[k]; k++)
if (k < NG_PPP_MAX_LINKS) {
InputFrame(b, b->links[k], proto, pkt);
return;
}
break;
case CODE_ECHOREQ:
Log(LG_ECHO, ("[%s] rec'd %s #%d, replying...",
b->name, FsmCodeName(hdr->code), hdr->id));
MBDATAU(pkt)[0] = CODE_ECHOREP;
NgFuncWritePppFrame(b, NG_PPP_BUNDLE_LINKNUM, PROTO_LCP, pkt);
return;
case CODE_ECHOREP:
Log(LG_ECHO, ("[%s] rec'd %s #%d",
b->name, FsmCodeName(hdr->code), hdr->id));
break;
default:
Log(LG_ERR, ("[%s] rec'd LCP %s #%d on MP link! (ignoring)",
b->name, FsmCodeName(hdr->code), hdr->id));
break;
}
break;
default:
Log(LG_ERR, ("[%s] rec'd proto %s on MP link! (ignoring)",
b->name, ProtoName(proto)));
break;
}
mbfree(pkt);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>