#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && PPP_IPV6_SUPPORT
#if 0#endif
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/fsm.h"
#include "netif/ppp/ipcp.h"
#include "netif/ppp/ipv6cp.h"
#include "netif/ppp/magic.h"
#if 0#endif
static void ipv6cp_resetci(fsm *f);
static int ipv6cp_cilen(fsm *f);
static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp);
static int ipv6cp_ackci(fsm *f, u_char *p, int len);
static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject);
static int ipv6cp_rejci(fsm *f, u_char *p, int len);
static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree);
static void ipv6cp_up(fsm *f);
static void ipv6cp_down(fsm *f);
static void ipv6cp_finished(fsm *f);
static const fsm_callbacks ipv6cp_callbacks = {
ipv6cp_resetci,
ipv6cp_cilen,
ipv6cp_addci,
ipv6cp_ackci,
ipv6cp_nakci,
ipv6cp_rejci,
ipv6cp_reqci,
ipv6cp_up,
ipv6cp_down,
NULL,
ipv6cp_finished,
NULL,
NULL,
NULL,
"IPV6CP"
};
#if PPP_OPTIONS
static int setifaceid(char **arg));
static void printifaceid(option_t *,
void (*)(void *, char *, ...), void *));
static option_t ipv6cp_option_list[] = {
{ "ipv6", o_special, (void *)setifaceid,
"Set interface identifiers for IPV6",
OPT_A2PRINTER, (void *)printifaceid },
{ "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
"Enable IPv6 and IPv6CP", OPT_PRIO | 1 },
{ "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
"Disable IPv6 and IPv6CP", OPT_PRIOSUB },
{ "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
"Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS },
{ "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
"Accept peer's interface identifier for us", 1 },
{ "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
"Use (default) IPv4 address as interface identifier", 1 },
{ "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
"Use uniquely-available persistent value for link local address", 1 },
{ "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
"Set timeout for IPv6CP", OPT_PRIO },
{ "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
"Set max #xmits for term-reqs", OPT_PRIO },
{ "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
"Set max #xmits for conf-reqs", OPT_PRIO },
{ "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
"Set max #conf-naks for IPv6CP", OPT_PRIO },
{ NULL }
};
#endif
static void ipv6cp_init(ppp_pcb *pcb);
static void ipv6cp_open(ppp_pcb *pcb);
static void ipv6cp_close(ppp_pcb *pcb, const char *reason);
static void ipv6cp_lowerup(ppp_pcb *pcb);
static void ipv6cp_lowerdown(ppp_pcb *pcb);
static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len);
static void ipv6cp_protrej(ppp_pcb *pcb);
#if PPP_OPTIONS
static void ipv6_check_options(void);
#endif
#if DEMAND_SUPPORT
static int ipv6_demand_conf(int u);
#endif
#if PRINTPKT_SUPPORT
static int ipv6cp_printpkt(const u_char *p, int plen,
void (*printer)(void *, const char *, ...), void *arg);
#endif
#if DEMAND_SUPPORT
static int ipv6_active_pkt(u_char *pkt, int len);
#endif
const struct protent ipv6cp_protent = {
PPP_IPV6CP,
ipv6cp_init,
ipv6cp_input,
ipv6cp_protrej,
ipv6cp_lowerup,
ipv6cp_lowerdown,
ipv6cp_open,
ipv6cp_close,
#if PRINTPKT_SUPPORT
ipv6cp_printpkt,
#endif
#if PPP_DATAINPUT
NULL,
#endif
#if PRINTPKT_SUPPORT
"IPV6CP",
"IPV6",
#endif
#if PPP_OPTIONS
ipv6cp_option_list,
ipv6_check_options,
#endif
#if DEMAND_SUPPORT
ipv6_demand_conf,
ipv6_active_pkt
#endif
};
static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid);
#if 0#endif
#define CILEN_VOID 2
#define CILEN_COMPRESS 4
#define CILEN_IFACEID 10
#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
(x) == CONFNAK ? "NAK" : "REJ")
#if 0#endif
static char *llv6_ntoa(eui64_t ifaceid);
#if PPP_OPTIONS
static int
setifaceid(argv)
char **argv;
{
char *comma, *arg, c;
ipv6cp_options *wo = &ipv6cp_wantoptions[0];
struct in6_addr addr;
static int prio_local, prio_remote;
#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
(((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
arg = *argv;
if ((comma = strchr(arg, ',')) == NULL)
comma = arg + strlen(arg);
if (comma != arg) {
c = *comma;
*comma = '\0';
if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
option_error("Illegal interface identifier (local): %s", arg);
return 0;
}
if (option_priority >= prio_local) {
eui64_copy(addr.s6_addr32[2], wo->ourid);
wo->opt_local = 1;
prio_local = option_priority;
}
*comma = c;
}
if (*comma != 0 && *++comma != '\0') {
if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
option_error("Illegal interface identifier (remote): %s", comma);
return 0;
}
if (option_priority >= prio_remote) {
eui64_copy(addr.s6_addr32[2], wo->hisid);
wo->opt_remote = 1;
prio_remote = option_priority;
}
}
if (override_value("+ipv6", option_priority, option_source))
ipv6cp_protent.enabled_flag = 1;
return 1;
}
static void
printifaceid(opt, printer, arg)
option_t *opt;
void (*printer)(void *, char *, ...));
void *arg;
{
ipv6cp_options *wo = &ipv6cp_wantoptions[0];
if (wo->opt_local)
printer(arg, "%s", llv6_ntoa(wo->ourid));
printer(arg, ",");
if (wo->opt_remote)
printer(arg, "%s", llv6_ntoa(wo->hisid));
}
#endif
static char *
llv6_ntoa(eui64_t ifaceid)
{
static char b[26];
sprintf(b, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ifaceid.e8[0], ifaceid.e8[1], ifaceid.e8[2], ifaceid.e8[3],
ifaceid.e8[4], ifaceid.e8[5], ifaceid.e8[6], ifaceid.e8[7]);
return b;
}
static void ipv6cp_init(ppp_pcb *pcb) {
fsm *f = &pcb->ipv6cp_fsm;
ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
f->pcb = pcb;
f->protocol = PPP_IPV6CP;
f->callbacks = &ipv6cp_callbacks;
fsm_init(f);
#if 0#endif
wo->accept_local = 1;
wo->neg_ifaceid = 1;
ao->neg_ifaceid = 1;
#ifdef IPV6CP_COMP
wo->neg_vj = 1;
ao->neg_vj = 1;
wo->vj_protocol = IPV6CP_COMP;
#endif
}
static void ipv6cp_open(ppp_pcb *pcb) {
fsm_open(&pcb->ipv6cp_fsm);
}
static void ipv6cp_close(ppp_pcb *pcb, const char *reason) {
fsm_close(&pcb->ipv6cp_fsm, reason);
}
static void ipv6cp_lowerup(ppp_pcb *pcb) {
fsm_lowerup(&pcb->ipv6cp_fsm);
}
static void ipv6cp_lowerdown(ppp_pcb *pcb) {
fsm_lowerdown(&pcb->ipv6cp_fsm);
}
static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len) {
fsm_input(&pcb->ipv6cp_fsm, p, len);
}
static void ipv6cp_protrej(ppp_pcb *pcb) {
fsm_lowerdown(&pcb->ipv6cp_fsm);
}
static void ipv6cp_resetci(fsm *f) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
wo->req_ifaceid = wo->neg_ifaceid && ao->neg_ifaceid;
if (!wo->opt_local) {
eui64_magic_nz(wo->ourid);
}
*go = *wo;
eui64_zero(go->hisid);
}
static int ipv6cp_cilen(fsm *f) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
#ifdef IPV6CP_COMP
#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
#endif
#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
return (LENCIIFACEID(go->neg_ifaceid) +
#ifdef IPV6CP_COMP
LENCIVJ(go->neg_vj) +
#endif
0);
}
static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
int len = *lenp;
#ifdef IPV6CP_COMP
#define ADDCIVJ(opt, neg, val) \
if (neg) { \
int vjlen = CILEN_COMPRESS; \
if (len >= vjlen) { \
PUTCHAR(opt, ucp); \
PUTCHAR(vjlen, ucp); \
PUTSHORT(val, ucp); \
len -= vjlen; \
} else \
neg = 0; \
}
#endif
#define ADDCIIFACEID(opt, neg, val1) \
if (neg) { \
int idlen = CILEN_IFACEID; \
if (len >= idlen) { \
PUTCHAR(opt, ucp); \
PUTCHAR(idlen, ucp); \
eui64_put(val1, ucp); \
len -= idlen; \
} else \
neg = 0; \
}
ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
#ifdef IPV6CP_COMP
ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
#endif
*lenp -= len;
}
static int ipv6cp_ackci(fsm *f, u_char *p, int len) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
u_short cilen, citype;
#ifdef IPV6CP_COMP
u_short cishort;
#endif
eui64_t ifaceid;
#ifdef IPV6CP_COMP
#define ACKCIVJ(opt, neg, val) \
if (neg) { \
int vjlen = CILEN_COMPRESS; \
if ((len -= vjlen) < 0) \
goto bad; \
GETCHAR(citype, p); \
GETCHAR(cilen, p); \
if (cilen != vjlen || \
citype != opt) \
goto bad; \
GETSHORT(cishort, p); \
if (cishort != val) \
goto bad; \
}
#endif
#define ACKCIIFACEID(opt, neg, val1) \
if (neg) { \
int idlen = CILEN_IFACEID; \
if ((len -= idlen) < 0) \
goto bad; \
GETCHAR(citype, p); \
GETCHAR(cilen, p); \
if (cilen != idlen || \
citype != opt) \
goto bad; \
eui64_get(ifaceid, p); \
if (! eui64_equals(val1, ifaceid)) \
goto bad; \
}
ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
#ifdef IPV6CP_COMP
ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
#endif
if (len != 0)
goto bad;
return (1);
bad:
IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
return (0);
}
static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
u_char citype, cilen, *next;
#ifdef IPV6CP_COMP
u_short cishort;
#endif
eui64_t ifaceid;
ipv6cp_options no;
ipv6cp_options try_;
BZERO(&no, sizeof(no));
try_ = *go;
#define NAKCIIFACEID(opt, neg, code) \
if (go->neg && \
len >= (cilen = CILEN_IFACEID) && \
p[1] == cilen && \
p[0] == opt) { \
len -= cilen; \
INCPTR(2, p); \
eui64_get(ifaceid, p); \
no.neg = 1; \
code \
}
#ifdef IPV6CP_COMP
#define NAKCIVJ(opt, neg, code) \
if (go->neg && \
((cilen = p[1]) == CILEN_COMPRESS) && \
len >= cilen && \
p[0] == opt) { \
len -= cilen; \
INCPTR(2, p); \
GETSHORT(cishort, p); \
no.neg = 1; \
code \
}
#endif
NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
if (treat_as_reject) {
try_.neg_ifaceid = 0;
} else if (go->accept_local) {
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->hisid))
eui64_magic(ifaceid);
try_.ourid = ifaceid;
IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
}
);
#ifdef IPV6CP_COMP
NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
{
if (cishort == IPV6CP_COMP && !treat_as_reject) {
try_.vj_protocol = cishort;
} else {
try_.neg_vj = 0;
}
}
);
#endif
while (len >= CILEN_VOID) {
GETCHAR(citype, p);
GETCHAR(cilen, p);
if ( cilen < CILEN_VOID || (len -= cilen) < 0 )
goto bad;
next = p + cilen - 2;
switch (citype) {
#ifdef IPV6CP_COMP
case CI_COMPRESSTYPE:
if (go->neg_vj || no.neg_vj ||
(cilen != CILEN_COMPRESS))
goto bad;
no.neg_vj = 1;
break;
#endif
case CI_IFACEID:
if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
goto bad;
try_.neg_ifaceid = 1;
eui64_get(ifaceid, p);
if (go->accept_local) {
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->hisid))
eui64_magic(ifaceid);
try_.ourid = ifaceid;
}
no.neg_ifaceid = 1;
break;
default:
break;
}
p = next;
}
if (len != 0)
goto bad;
if (f->state != PPP_FSM_OPENED)
*go = try_;
return 1;
bad:
IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
return 0;
}
static int ipv6cp_rejci(fsm *f, u_char *p, int len) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
u_char cilen;
#ifdef IPV6CP_COMP
u_short cishort;
#endif
eui64_t ifaceid;
ipv6cp_options try_;
try_ = *go;
#define REJCIIFACEID(opt, neg, val1) \
if (go->neg && \
len >= (cilen = CILEN_IFACEID) && \
p[1] == cilen && \
p[0] == opt) { \
len -= cilen; \
INCPTR(2, p); \
eui64_get(ifaceid, p); \
\
if (! eui64_equals(ifaceid, val1)) \
goto bad; \
try_.neg = 0; \
}
#ifdef IPV6CP_COMP
#define REJCIVJ(opt, neg, val) \
if (go->neg && \
p[1] == CILEN_COMPRESS && \
len >= p[1] && \
p[0] == opt) { \
len -= p[1]; \
INCPTR(2, p); \
GETSHORT(cishort, p); \
\
if (cishort != val) \
goto bad; \
try_.neg = 0; \
}
#endif
REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
#ifdef IPV6CP_COMP
REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
#endif
if (len != 0)
goto bad;
if (f->state != PPP_FSM_OPENED)
*go = try_;
return 1;
bad:
IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
return 0;
}
static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
u_char *cip, *next;
u_short cilen, citype;
#ifdef IPV6CP_COMP
u_short cishort;
#endif
eui64_t ifaceid;
int rc = CONFACK;
int orc;
u_char *p;
u_char *ucp = inp;
int l = *len;
BZERO(ho, sizeof(*ho));
next = inp;
while (l) {
orc = CONFACK;
cip = p = next;
if (l < 2 ||
p[1] < 2 ||
p[1] > l) {
IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
orc = CONFREJ;
cilen = l;
l = 0;
goto endswitch;
}
GETCHAR(citype, p);
GETCHAR(cilen, p);
l -= cilen;
next += cilen;
switch (citype) {
case CI_IFACEID:
IPV6CPDEBUG(("ipv6cp: received interface identifier "));
if (!ao->neg_ifaceid ||
cilen != CILEN_IFACEID) {
orc = CONFREJ;
break;
}
eui64_get(ifaceid, p);
IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
orc = CONFREJ;
break;
}
if (!eui64_iszero(wo->hisid) &&
!eui64_equals(ifaceid, wo->hisid) &&
eui64_iszero(go->hisid)) {
orc = CONFNAK;
ifaceid = wo->hisid;
go->hisid = ifaceid;
DECPTR(sizeof(ifaceid), p);
eui64_put(ifaceid, p);
} else
if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
orc = CONFNAK;
if (eui64_iszero(go->hisid))
ifaceid = wo->hisid;
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->ourid))
eui64_magic(ifaceid);
go->hisid = ifaceid;
DECPTR(sizeof(ifaceid), p);
eui64_put(ifaceid, p);
}
ho->neg_ifaceid = 1;
ho->hisid = ifaceid;
break;
#ifdef IPV6CP_COMP
case CI_COMPRESSTYPE:
IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
if (!ao->neg_vj ||
(cilen != CILEN_COMPRESS)) {
orc = CONFREJ;
break;
}
GETSHORT(cishort, p);
IPV6CPDEBUG(("(%d)", cishort));
if (!(cishort == IPV6CP_COMP)) {
orc = CONFREJ;
break;
}
ho->neg_vj = 1;
ho->vj_protocol = cishort;
break;
#endif
default:
orc = CONFREJ;
break;
}
endswitch:
IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
if (orc == CONFACK &&
rc != CONFACK)
continue;
if (orc == CONFNAK) {
if (reject_if_disagree)
orc = CONFREJ;
else {
if (rc == CONFREJ)
continue;
if (rc == CONFACK) {
rc = CONFNAK;
ucp = inp;
}
}
}
if (orc == CONFREJ &&
rc != CONFREJ) {
rc = CONFREJ;
ucp = inp;
}
if (ucp != cip)
MEMCPY(ucp, cip, cilen);
INCPTR(cilen, ucp);
}
if (rc != CONFREJ && !ho->neg_ifaceid &&
wo->req_ifaceid && !reject_if_disagree) {
if (rc == CONFACK) {
rc = CONFNAK;
ucp = inp;
wo->req_ifaceid = 0;
}
PUTCHAR(CI_IFACEID, ucp);
PUTCHAR(CILEN_IFACEID, ucp);
eui64_put(wo->hisid, ucp);
}
*len = ucp - inp;
IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
return (rc);
}
#if PPP_OPTIONS
static void ipv6_check_options() {
ipv6cp_options *wo = &ipv6cp_wantoptions[0];
if (!ipv6cp_protent.enabled_flag)
return;
if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
if (ether_to_eui64(&wo->ourid)) {
wo->opt_local = 1;
}
}
if (!wo->opt_local) {
if (wo->use_ip && eui64_iszero(wo->ourid)) {
eui64_setlo32(wo->ourid, lwip_ntohl(ipcp_wantoptions[0].ouraddr));
if (!eui64_iszero(wo->ourid))
wo->opt_local = 1;
}
while (eui64_iszero(wo->ourid))
eui64_magic(wo->ourid);
}
if (!wo->opt_remote) {
if (wo->use_ip && eui64_iszero(wo->hisid)) {
eui64_setlo32(wo->hisid, lwip_ntohl(ipcp_wantoptions[0].hisaddr));
if (!eui64_iszero(wo->hisid))
wo->opt_remote = 1;
}
}
if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
option_error("local/remote LL address required for demand-dialling\n");
exit(1);
}
}
#endif
#if DEMAND_SUPPORT
static int ipv6_demand_conf(int u) {
ipv6cp_options *wo = &ipv6cp_wantoptions[u];
if (!sif6up(u))
return 0;
if (!sif6addr(u, wo->ourid, wo->hisid))
return 0;
if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
return 0;
ppp_notice(("ipv6_demand_conf"));
ppp_notice(("local LL address %s", llv6_ntoa(wo->ourid)));
ppp_notice(("remote LL address %s", llv6_ntoa(wo->hisid)));
return 1;
}
#endif
static void ipv6cp_up(fsm *f) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
IPV6CPDEBUG(("ipv6cp: up"));
if (!ho->neg_ifaceid)
ho->hisid = wo->hisid;
#if 0#endif
if (eui64_iszero(ho->hisid)) {
ppp_error(("Could not determine remote LL address"));
ipv6cp_close(f->pcb, "Could not determine remote LL address");
return;
}
if (eui64_iszero(go->ourid)) {
ppp_error(("Could not determine local LL address"));
ipv6cp_close(f->pcb, "Could not determine local LL address");
return;
}
if (eui64_equals(go->ourid, ho->hisid)) {
ppp_error(("local and remote LL addresses are equal"));
ipv6cp_close(f->pcb, "local and remote LL addresses are equal");
return;
}
#if 0#endif
#if 0#endif
#ifdef IPV6CP_COMP
sif6comp(f->unit, ho->neg_vj);
#endif
#if DEMAND_SUPPORT
if (demand) {
if (! eui64_equals(go->ourid, wo->ourid) ||
! eui64_equals(ho->hisid, wo->hisid)) {
if (! eui64_equals(go->ourid, wo->ourid))
warn("Local LL address changed to %s",
llv6_ntoa(go->ourid));
if (! eui64_equals(ho->hisid, wo->hisid))
warn("Remote LL address changed to %s",
llv6_ntoa(ho->hisid));
ipv6cp_clear_addrs(f->pcb, go->ourid, ho->hisid);
if (!sif6addr(f->pcb, go->ourid, ho->hisid)) {
if (debug)
warn("sif6addr failed");
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
}
demand_rexmit(PPP_IPV6);
sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
} else
#endif
{
if (!sif6addr(f->pcb, go->ourid, ho->hisid)) {
PPPDEBUG(LOG_DEBUG, ("sif6addr failed"));
ipv6cp_close(f->pcb, "Interface configuration failed");
return;
}
if (!sif6up(f->pcb)) {
PPPDEBUG(LOG_DEBUG, ("sif6up failed (IPV6)"));
ipv6cp_close(f->pcb, "Interface configuration failed");
return;
}
#if DEMAND_SUPPORT
sifnpmode(f->pcb, PPP_IPV6, NPMODE_PASS);
#endif
ppp_notice(("local LL address %s", llv6_ntoa(go->ourid)));
ppp_notice(("remote LL address %s", llv6_ntoa(ho->hisid)));
}
np_up(f->pcb, PPP_IPV6);
pcb->ipv6cp_is_up = 1;
#if 0#endif
}
static void ipv6cp_down(fsm *f) {
ppp_pcb *pcb = f->pcb;
ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
IPV6CPDEBUG(("ipv6cp: down"));
#if PPP_STATS_SUPPORT
update_link_stats(f->unit);
#endif
if (pcb->ipv6cp_is_up) {
pcb->ipv6cp_is_up = 0;
np_down(f->pcb, PPP_IPV6);
}
#ifdef IPV6CP_COMP
sif6comp(f->unit, 0);
#endif
#if DEMAND_SUPPORT
if (demand) {
sifnpmode(f->pcb, PPP_IPV6, NPMODE_QUEUE);
} else
#endif
{
#if DEMAND_SUPPORT
sifnpmode(f->pcb, PPP_IPV6, NPMODE_DROP);
#endif
ipv6cp_clear_addrs(f->pcb,
go->ourid,
ho->hisid);
sif6down(f->pcb);
}
#if 0#endif
}
static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid) {
cif6addr(pcb, ourid, hisid);
}
static void ipv6cp_finished(fsm *f) {
np_finished(f->pcb, PPP_IPV6);
}
#if 0#endif
#if PRINTPKT_SUPPORT
static const char* const ipv6cp_codenames[] = {
"ConfReq", "ConfAck", "ConfNak", "ConfRej",
"TermReq", "TermAck", "CodeRej"
};
static int ipv6cp_printpkt(const u_char *p, int plen,
void (*printer)(void *, const char *, ...), void *arg) {
int code, id, len, olen;
const u_char *pstart, *optend;
#ifdef IPV6CP_COMP
u_short cishort;
#endif
eui64_t ifaceid;
if (plen < HEADERLEN)
return 0;
pstart = p;
GETCHAR(code, p);
GETCHAR(id, p);
GETSHORT(len, p);
if (len < HEADERLEN || len > plen)
return 0;
if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ipv6cp_codenames))
printer(arg, " %s", ipv6cp_codenames[code-1]);
else
printer(arg, " code=0x%x", code);
printer(arg, " id=0x%x", id);
len -= HEADERLEN;
switch (code) {
case CONFREQ:
case CONFACK:
case CONFNAK:
case CONFREJ:
while (len >= 2) {
GETCHAR(code, p);
GETCHAR(olen, p);
p -= 2;
if (olen < 2 || olen > len) {
break;
}
printer(arg, " <");
len -= olen;
optend = p + olen;
switch (code) {
#ifdef IPV6CP_COMP
case CI_COMPRESSTYPE:
if (olen >= CILEN_COMPRESS) {
p += 2;
GETSHORT(cishort, p);
printer(arg, "compress ");
printer(arg, "0x%x", cishort);
}
break;
#endif
case CI_IFACEID:
if (olen == CILEN_IFACEID) {
p += 2;
eui64_get(ifaceid, p);
printer(arg, "addr %s", llv6_ntoa(ifaceid));
}
break;
default:
break;
}
while (p < optend) {
GETCHAR(code, p);
printer(arg, " %.2x", code);
}
printer(arg, ">");
}
break;
case TERMACK:
case TERMREQ:
if (len > 0 && *p >= ' ' && *p < 0x7f) {
printer(arg, " ");
ppp_print_string(p, len, printer, arg);
p += len;
len = 0;
}
break;
default:
break;
}
for (; len > 0; --len) {
GETCHAR(code, p);
printer(arg, " %.2x", code);
}
return p - pstart;
}
#endif
#if DEMAND_SUPPORT
#define IP6_HDRLEN 40
#define IP6_NHDR_FRAG 44
#define TCP_HDRLEN 20
#define TH_FIN 0x01
#define get_ip6nh(x) (((unsigned char *)(x))[6])
#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
#define get_tcpflags(x) (((unsigned char *)(x))[13])
static int ipv6_active_pkt(u_char *pkt, int len) {
u_char *tcp;
len -= PPP_HDRLEN;
pkt += PPP_HDRLEN;
if (len < IP6_HDRLEN)
return 0;
if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
return 0;
if (get_ip6nh(pkt) != IPPROTO_TCP)
return 1;
if (len < IP6_HDRLEN + TCP_HDRLEN)
return 0;
tcp = pkt + IP6_HDRLEN;
if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
return 0;
return 1;
}
#endif
#endif