File:
[ELWIX - Embedded LightWeight unIX -] /
embedaddon /
mpd /
src /
ipv6cp.c
Revision
1.1.1.1 (vendor branch):
download - view:
text,
annotated -
select for diffs -
revision graph
Mon Jul 22 08:44:29 2013 UTC (11 years, 5 months ago) by
misho
Branches:
mpd,
MAIN
CVS tags:
v5_8p7,
v5_8p1_cross,
v5_8p1,
v5_8,
v5_7p0,
v5_7,
v5_6,
HEAD
5.7
/*
* ipv6cp.c
*
* Written by Alexander Motin <mav@FreeBSD.org>
*/
#include "ppp.h"
#include "ipv6cp.h"
#include "fsm.h"
#include "ip.h"
#include "iface.h"
#include "msg.h"
#include "ngfunc.h"
#include "util.h"
#include <netgraph.h>
#include <sys/mbuf.h>
/*
* DEFINITIONS
*/
#define IPV6CP_KNOWN_CODES ( (1 << CODE_CONFIGREQ) \
| (1 << CODE_CONFIGACK) \
| (1 << CODE_CONFIGNAK) \
| (1 << CODE_CONFIGREJ) \
| (1 << CODE_TERMREQ) \
| (1 << CODE_TERMACK) \
| (1 << CODE_CODEREJ) )
#define TY_INTIDENT 1
#define TY_COMPPROTO 2
#define IPV6CP_REJECTED(p,x) ((p)->peer_reject & (1<<(x)))
#define IPV6CP_PEER_REJ(p,x) do{(p)->peer_reject |= (1<<(x));}while(0)
#define IPV6CP_VJCOMP_MIN_MAXCHAN (NG_VJC_MIN_CHANNELS - 1)
#define IPV6CP_VJCOMP_MAX_MAXCHAN (NG_VJC_MAX_CHANNELS - 1)
#define IPV6CP_VJCOMP_DEFAULT_MAXCHAN IPV6CP_VJCOMP_MAX_MAXCHAN
/* Set menu options */
enum {
SET_ENABLE,
SET_DISABLE,
SET_ACCEPT,
SET_DENY,
SET_YES,
SET_NO
};
/*
* INTERNAL FUNCTIONS
*/
static void Ipv6cpConfigure(Fsm fp);
static void Ipv6cpUnConfigure(Fsm fp);
static u_char *Ipv6cpBuildConfigReq(Fsm fp, u_char *cp);
static void Ipv6cpDecodeConfig(Fsm fp, FsmOption list, int num, int mode);
static void Ipv6cpLayerStart(Fsm fp);
static void Ipv6cpLayerFinish(Fsm fp);
static void Ipv6cpLayerUp(Fsm fp);
static void Ipv6cpLayerDown(Fsm fp);
static void Ipv6cpFailure(Fsm fp, enum fsmfail reason);
static int Ipv6cpSetCommand(Context ctx, int ac, char *av[], void *arg);
void CreateInterfaceID(u_char *intid, int random);
/*
* GLOBAL VARIABLES
*/
const struct cmdtab Ipv6cpSetCmds[] = {
{ "enable [opt ...]", "Enable option",
Ipv6cpSetCommand, NULL, 2, (void *) SET_ENABLE},
{ "disable [opt ...]", "Disable option",
Ipv6cpSetCommand, NULL, 2, (void *) SET_DISABLE},
{ "accept [opt ...]", "Accept option",
Ipv6cpSetCommand, NULL, 2, (void *) SET_ACCEPT},
{ "deny [opt ...]", "Deny option",
Ipv6cpSetCommand, NULL, 2, (void *) SET_DENY},
{ "yes [opt ...]", "Enable and accept option",
Ipv6cpSetCommand, NULL, 2, (void *) SET_YES},
{ "no [opt ...]", "Disable and deny option",
Ipv6cpSetCommand, NULL, 2, (void *) SET_NO},
{ NULL },
};
/*
* INTERNAL VARIABLES
*/
static const struct fsmoptinfo gIpv6cpConfOpts[] = {
{ "INTIDENT", TY_INTIDENT, 8, 8, TRUE },
{ "COMPPROTO", TY_COMPPROTO, 4, 4, FALSE },
{ NULL }
};
static const struct confinfo gConfList[] = {
/* { 1, IPV6CP_CONF_VJCOMP, "vjcomp" },*/
{ 0, 0, NULL },
};
static const struct fsmtype gIpv6cpFsmType = {
"IPV6CP",
PROTO_IPV6CP,
IPV6CP_KNOWN_CODES,
FALSE,
LG_IPV6CP, LG_IPV6CP2,
NULL,
Ipv6cpLayerUp,
Ipv6cpLayerDown,
Ipv6cpLayerStart,
Ipv6cpLayerFinish,
Ipv6cpBuildConfigReq,
Ipv6cpDecodeConfig,
Ipv6cpConfigure,
Ipv6cpUnConfigure,
NULL,
NULL,
NULL,
NULL,
Ipv6cpFailure,
NULL,
NULL,
NULL,
};
/*
* Ipv6cpStat()
*/
int
Ipv6cpStat(Context ctx, int ac, char *av[], void *arg)
{
Ipv6cpState const ipv6cp = &ctx->bund->ipv6cp;
Fsm fp = &ipv6cp->fsm;
Printf("[%s] %s [%s]\r\n", Pref(fp), Fsm(fp), FsmStateName(fp->state));
Printf("Interface identificators:\r\n");
Printf("\tSelf: %02x%02x:%02x%02x:%02x%02x:%02x%02x\r\n",
ipv6cp->myintid[0], ipv6cp->myintid[1], ipv6cp->myintid[2], ipv6cp->myintid[3],
ipv6cp->myintid[4], ipv6cp->myintid[5], ipv6cp->myintid[6], ipv6cp->myintid[7]);
Printf("\tPeer: %02x%02x:%02x%02x:%02x%02x:%02x%02x\r\n",
ipv6cp->hisintid[0], ipv6cp->hisintid[1], ipv6cp->hisintid[2], ipv6cp->hisintid[3],
ipv6cp->hisintid[4], ipv6cp->hisintid[5], ipv6cp->hisintid[6], ipv6cp->hisintid[7]);
Printf("IPV6CP Options:\r\n");
OptStat(ctx, &ipv6cp->conf.options, gConfList);
return(0);
}
/*
* CreateInterfaceID()
*/
void
CreateInterfaceID(u_char *intid, int r)
{
struct sockaddr_dl hwaddr;
u_char *ether;
if (!r) {
if (!GetEther(NULL, &hwaddr)) {
ether = (u_char *) LLADDR(&hwaddr);
intid[0]=ether[0] ^ 0x02; /* reverse the u/l bit*/
intid[1]=ether[1];
intid[2]=ether[2];
intid[3]=0xff;
intid[4]=0xfe;
intid[5]=ether[3];
intid[6]=ether[4];
intid[7]=ether[5];
return;
}
}
srandomdev();
((u_int32_t*)intid)[0]=(((u_int32_t)random()) % 0xFFFFFFFF) + 1;
((u_int32_t*)intid)[1]=(((u_int32_t)random()) % 0xFFFFFFFF) + 1;
intid[0] &= 0xfd;
}
/*
* Ipv6cpInit()
*/
void
Ipv6cpInit(Bund b)
{
Ipv6cpState ipv6cp = &b->ipv6cp;
/* Init state machine */
memset(ipv6cp, 0, sizeof(*ipv6cp));
FsmInit(&ipv6cp->fsm, &gIpv6cpFsmType, b);
CreateInterfaceID(ipv6cp->myintid,0);
}
/*
* Ipv6cpInst()
*/
void
Ipv6cpInst(Bund b, Bund bt)
{
Ipv6cpState ipv6cp = &b->ipv6cp;
/* Init state machine */
memcpy(ipv6cp, &bt->ipv6cp, sizeof(*ipv6cp));
FsmInst(&ipv6cp->fsm, &bt->ipv6cp.fsm, b);
}
/*
* Ipv6cpConfigure()
*/
static void
Ipv6cpConfigure(Fsm fp)
{
Bund b = (Bund)fp->arg;
Ipv6cpState const ipv6cp = &b->ipv6cp;
/* FSM stuff */
ipv6cp->peer_reject = 0;
}
/*
* Ipv6cpUnConfigure()
*/
static void
Ipv6cpUnConfigure(Fsm fp)
{
}
/*
* Ipv6cpBuildConfigReq()
*/
static u_char *
Ipv6cpBuildConfigReq(Fsm fp, u_char *cp)
{
Bund b = (Bund)fp->arg;
Ipv6cpState const ipv6cp = &b->ipv6cp;
cp = FsmConfValue(cp, TY_INTIDENT, 8, ipv6cp->myintid);
/* Done */
return(cp);
}
/*
* Ipv6cpLayerStart()
*
* Tell the lower layer (the bundle) that we need it
*/
static void
Ipv6cpLayerStart(Fsm fp)
{
BundNcpsStart((Bund)(fp->arg), NCP_IPV6CP);
}
/*
* Ipv6cpLayerFinish()
*
* Tell the lower layer (the bundle) that we no longer need it
*/
static void
Ipv6cpLayerFinish(Fsm fp)
{
BundNcpsFinish((Bund)(fp->arg), NCP_IPV6CP);
}
/*
* Ipv6cpLayerUp()
*
* Called when IPV6CP has reached the OPEN state
*/
static void
Ipv6cpLayerUp(Fsm fp)
{
Bund b = (Bund)fp->arg;
Ipv6cpState const ipv6cp = &b->ipv6cp;
/* Report */
Log(fp->log, ("[%s] %02x%02x:%02x%02x:%02x%02x:%02x%02x -> %02x%02x:%02x%02x:%02x%02x:%02x%02x", b->name,
ipv6cp->myintid[0], ipv6cp->myintid[1], ipv6cp->myintid[2], ipv6cp->myintid[3],
ipv6cp->myintid[4], ipv6cp->myintid[5], ipv6cp->myintid[6], ipv6cp->myintid[7],
ipv6cp->hisintid[0], ipv6cp->hisintid[1], ipv6cp->hisintid[2], ipv6cp->hisintid[3],
ipv6cp->hisintid[4], ipv6cp->hisintid[5], ipv6cp->hisintid[6], ipv6cp->hisintid[7]));
/* Enable IP packets in the PPP node */
b->pppConfig.bund.enableIPv6 = 1;
NgFuncSetConfig(b);
BundNcpsJoin(b, NCP_IPV6CP);
}
/*
* Ipv6cpLayerDown()
*
* Called when IPV6CP leaves the OPEN state
*/
static void
Ipv6cpLayerDown(Fsm fp)
{
Bund b = (Bund)fp->arg;
BundNcpsLeave(b, NCP_IPV6CP);
/* Turn off IP packets */
b->pppConfig.bund.enableIPv6 = 0;
NgFuncSetConfig(b);
}
/*
* Ipv6cpUp()
*/
void
Ipv6cpUp(Bund b)
{
FsmUp(&b->ipv6cp.fsm);
}
/*
* Ipv6cpDown()
*/
void
Ipv6cpDown(Bund b)
{
FsmDown(&b->ipv6cp.fsm);
}
/*
* Ipv6cpOpen()
*/
void
Ipv6cpOpen(Bund b)
{
FsmOpen(&b->ipv6cp.fsm);
}
/*
* Ipv6cpClose()
*/
void
Ipv6cpClose(Bund b)
{
FsmClose(&b->ipv6cp.fsm);
}
/*
* Ipv6cpOpenCmd()
*/
int
Ipv6cpOpenCmd(Context ctx)
{
if (ctx->bund->tmpl)
Error("impossible to open template");
FsmOpen(&ctx->bund->ipv6cp.fsm);
return (0);
}
/*
* Ipv6cpCloseCmd()
*/
int
Ipv6cpCloseCmd(Context ctx)
{
if (ctx->bund->tmpl)
Error("impossible to close template");
FsmClose(&ctx->bund->ipv6cp.fsm);
return (0);
}
/*
* Ipv6cpFailure()
*/
static void
Ipv6cpFailure(Fsm fp, enum fsmfail reason)
{
Bund b = (Bund)fp->arg;
RecordLinkUpDownReason(b, NULL, 0, STR_PROTO_ERR, STR_IPV6CP_FAILED, FsmFailureStr(reason));
}
/*
* Ipv6cpDecodeConfig()
*/
static void
Ipv6cpDecodeConfig(Fsm fp, FsmOption list, int num, int mode)
{
Bund b = (Bund)fp->arg;
Ipv6cpState const ipv6cp = &b->ipv6cp;
int k;
/* Decode each config option */
for (k = 0; k < num; k++) {
FsmOption const opt = &list[k];
FsmOptInfo const oi = FsmFindOptInfo(gIpv6cpConfOpts, opt->type);
if (!oi) {
Log(LG_IPV6CP, ("[%s] UNKNOWN[%d] len=%d", b->name, opt->type, opt->len));
if (mode == MODE_REQ)
FsmRej(fp, opt);
continue;
}
if (!oi->supported) {
Log(LG_IPV6CP, ("[%s] %s", b->name, oi->name));
if (mode == MODE_REQ) {
Log(LG_IPV6CP, ("[%s] Not supported", b->name));
FsmRej(fp, opt);
}
continue;
}
if (opt->len < oi->minLen + 2 || opt->len > oi->maxLen + 2) {
Log(LG_IPV6CP, ("[%s] %s", b->name, oi->name));
if (mode == MODE_REQ) {
Log(LG_IPV6CP, ("[%s] bogus len=%d min=%d max=%d", b->name, opt->len, oi->minLen + 2, oi->maxLen + 2));
FsmRej(fp, opt);
}
continue;
}
switch (opt->type) {
case TY_INTIDENT:
{
Log(LG_IPV6CP2, ("[%s] %s %02x%02x:%02x%02x:%02x%02x:%02x%02x", b->name, oi->name,
opt->data[0], opt->data[1], opt->data[2], opt->data[3],
opt->data[4], opt->data[5], opt->data[6], opt->data[7]));
switch (mode) {
case MODE_REQ:
if ((((u_int32_t*)opt->data)[0]==0) && (((u_int32_t*)opt->data)[1]==0)) {
Log(LG_IPV6CP2, ("[%s] Empty INTIDENT, propose our.", b->name));
CreateInterfaceID(ipv6cp->hisintid, 1);
memcpy(opt->data, ipv6cp->hisintid, 8);
FsmNak(fp, opt);
} else if (bcmp(opt->data, ipv6cp->myintid, 8) == 0) {
Log(LG_IPV6CP2, ("[%s] Duplicate INTIDENT, generate and propose other.", b->name));
CreateInterfaceID(ipv6cp->hisintid, 1);
memcpy(opt->data, ipv6cp->hisintid, 8);
FsmNak(fp, opt);
} else {
Log(LG_IPV6CP2, ("[%s] It's OK.", b->name));
memcpy(ipv6cp->hisintid, opt->data, 8);
FsmAck(fp, opt);
}
break;
case MODE_NAK:
Log(LG_IPV6CP2, ("[%s] I agree to get this to myself.", b->name));
memcpy(ipv6cp->myintid, opt->data, 8);
break;
case MODE_REJ:
IPV6CP_PEER_REJ(ipv6cp, opt->type);
break;
}
}
break;
default:
assert(0);
}
}
}
/*
* Ipv6cpInput()
*
* Deal with an incoming IPV6CP packet
*/
void
Ipv6cpInput(Bund b, Mbuf bp)
{
FsmInput(&b->ipv6cp.fsm, bp);
}
/*
* Ipv6cpSetCommand()
*/
static int
Ipv6cpSetCommand(Context ctx, int ac, char *av[], void *arg)
{
Ipv6cpState const ipv6cp = &ctx->bund->ipv6cp;
if (ac == 0)
return(-1);
switch ((intptr_t)arg) {
case SET_ACCEPT:
AcceptCommand(ac, av, &ipv6cp->conf.options, gConfList);
break;
case SET_DENY:
DenyCommand(ac, av, &ipv6cp->conf.options, gConfList);
break;
case SET_ENABLE:
EnableCommand(ac, av, &ipv6cp->conf.options, gConfList);
break;
case SET_DISABLE:
DisableCommand(ac, av, &ipv6cp->conf.options, gConfList);
break;
case SET_YES:
YesCommand(ac, av, &ipv6cp->conf.options, gConfList);
break;
case SET_NO:
NoCommand(ac, av, &ipv6cp->conf.options, gConfList);
break;
default:
assert(0);
}
return(0);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>