#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "pppd.h"
#include "eui64.h"
#include "fsm.h"
#include "ipcp.h"
#include "ipv6cp.h"
#include "magic.h"
#include "pathnames.h"
ipv6cp_options ipv6cp_wantoptions[NUM_PPP];
ipv6cp_options ipv6cp_gotoptions[NUM_PPP];
ipv6cp_options ipv6cp_allowoptions[NUM_PPP];
ipv6cp_options ipv6cp_hisoptions[NUM_PPP];
int no_ifaceid_neg = 0;
static bool ipv6cp_is_up;
static void ipv6cp_resetci __P((fsm *));
static int ipv6cp_cilen __P((fsm *));
static void ipv6cp_addci __P((fsm *, u_char *, int *));
static int ipv6cp_ackci __P((fsm *, u_char *, int));
static int ipv6cp_nakci __P((fsm *, u_char *, int));
static int ipv6cp_rejci __P((fsm *, u_char *, int));
static int ipv6cp_reqci __P((fsm *, u_char *, int *, int));
static void ipv6cp_up __P((fsm *));
static void ipv6cp_down __P((fsm *));
static void ipv6cp_finished __P((fsm *));
fsm ipv6cp_fsm[NUM_PPP];
static 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,
"IPV6CP",
NULL
};
static int setifaceid __P((char **arg, option_t *));
static option_t ipv6cp_option_list[] = {
{ "ipv6", o_special, (void *)setifaceid,
"Set interface identifiers for IPV6" },
{ "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
"Disable IPv6 and IPv6CP" },
{ "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
"Disable IPv6 and IPv6CP" },
{ "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
"Enable IPv6 and IPv6CP", 1 },
{ "ipv6cp-accept-local", o_bool, &ipv6cp_wantoptions[0].accept_local,
"Accept peer's interface identifier for us", 1 },
{ "ipv6cp-use-ipaddr", o_bool, &ipv6cp_wantoptions[0].use_ip,
"Use (default) IPv4 address as interface identifier", 1 },
#if defined(SOL2)
{ "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
"Use unique persistent value for link local address", 1 },
#endif
{ "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
"Set timeout for IPv6CP" },
{ "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
"Maximum number of IPV6CP Terminate-Request" },
{ "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
"Maximum number of IPV6CP Configure-Request" },
{ "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
"Maximum number of IPV6CP Configure-Nak" },
{ NULL }
};
static void ipv6cp_init __P((int));
static void ipv6cp_open __P((int));
static void ipv6cp_close __P((int, char *));
static void ipv6cp_lowerup __P((int));
static void ipv6cp_lowerdown __P((int));
static void ipv6cp_input __P((int, u_char *, int));
static void ipv6cp_protrej __P((int));
static int ipv6cp_printpkt __P((u_char *, int,
void (*) __P((void *, const char *, ...)), void *));
static void ipv6_check_options __P((void));
static int ipv6_demand_conf __P((int));
static int ipv6_active_pkt __P((u_char *, int));
struct protent ipv6cp_protent = {
PPP_IPV6CP,
ipv6cp_init,
ipv6cp_input,
ipv6cp_protrej,
ipv6cp_lowerup,
ipv6cp_lowerdown,
ipv6cp_open,
ipv6cp_close,
ipv6cp_printpkt,
NULL,
0,
"IPV6CP",
"IPV6",
ipv6cp_option_list,
ipv6_check_options,
ipv6_demand_conf,
ipv6_active_pkt
};
static void ipv6cp_clear_addrs __P((int, eui64_t, eui64_t));
static void ipv6cp_script __P((char *));
static void ipv6cp_script_done __P((void *, int));
#define CILEN_VOID 2
#define CILEN_COMPRESS 4
#define CILEN_IFACEID 10
#define CODENAME(x) ((x) == CODE_CONFACK ? "ACK" : \
(x) == CODE_CONFNAK ? "NAK" : "REJ")
static enum script_state {
s_down,
s_up
} ipv6cp_script_state;
static pid_t ipv6cp_script_pid;
static int
setifaceid(argv, opt)
char **argv;
option_t *opt;
{
char *comma, *arg;
ipv6cp_options *wo = &ipv6cp_wantoptions[0];
struct in6_addr addr;
#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;
comma = strchr(arg, ',');
if (comma != arg) {
if (comma != NULL)
*comma = '\0';
if (inet_pton(AF_INET6, arg, &addr) != 1 || !VALIDID(addr)) {
option_error("Illegal interface identifier (local): %s", arg);
return 0;
}
eui64_copy(addr.s6_addr32[2], wo->ourid);
wo->opt_local = 1;
}
if (comma != NULL && *++comma != '\0') {
if (inet_pton(AF_INET6, comma, &addr) != 1 || !VALIDID(addr)) {
option_error("Illegal interface identifier (remote): %s", comma);
return 0;
}
eui64_copy(addr.s6_addr32[2], wo->hisid);
wo->opt_remote = 1;
}
ipv6cp_protent.enabled_flag = 1;
return 1;
}
static char *
llv6_ntoa(ifaceid)
eui64_t ifaceid;
{
struct in6_addr addr;
static char addrstr[26];
BZERO(&addr, sizeof (addr));
addr.s6_addr[0] = 0xfe;
addr.s6_addr[1] = 0x80;
eui64_copy(ifaceid, addr.s6_addr[8]);
(void) inet_ntop(AF_INET6, &addr, addrstr, 26);
return addrstr;
}
static void
ipv6cp_init(unit)
int unit;
{
fsm *f = &ipv6cp_fsm[unit];
ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
f->unit = unit;
f->protocol = PPP_IPV6CP;
f->callbacks = &ipv6cp_callbacks;
fsm_init(&ipv6cp_fsm[unit]);
BZERO(wo, sizeof(*wo));
BZERO(ao, sizeof(*ao));
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(unit)
int unit;
{
fsm_open(&ipv6cp_fsm[unit]);
}
static void
ipv6cp_close(unit, reason)
int unit;
char *reason;
{
fsm_close(&ipv6cp_fsm[unit], reason);
}
static void
ipv6cp_lowerup(unit)
int unit;
{
fsm_lowerup(&ipv6cp_fsm[unit]);
}
static void
ipv6cp_lowerdown(unit)
int unit;
{
fsm_lowerdown(&ipv6cp_fsm[unit]);
}
static void
ipv6cp_input(unit, p, len)
int unit;
u_char *p;
int len;
{
fsm_input(&ipv6cp_fsm[unit], p, len);
}
static void
ipv6cp_protrej(unit)
int unit;
{
fsm_protreject(&ipv6cp_fsm[unit]);
}
static void
ipv6cp_resetci(f)
fsm *f;
{
ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
if (!wo->opt_local) {
eui64_magic_nz(wo->ourid);
}
*go = *wo;
eui64_zero(go->hisid);
}
static int
ipv6cp_cilen(f)
fsm *f;
{
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
return (LENCIIFACEID(go->neg_ifaceid) +
LENCIVJ(go->neg_vj));
}
static void
ipv6cp_addci(f, ucp, lenp)
fsm *f;
u_char *ucp;
int *lenp;
{
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
int len = *lenp;
#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; \
}
#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);
ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
*lenp -= len;
}
static int
ipv6cp_ackci(f, p, len)
fsm *f;
u_char *p;
int len;
{
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
u_short cilen, citype, cishort;
eui64_t ifaceid;
#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; \
}
#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);
ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
if (len != 0)
goto bad;
return (1);
bad:
IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
return (0);
}
static int
ipv6cp_nakci(f, p, len)
fsm *f;
u_char *p;
int len;
{
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
u_char citype, cilen, *next;
u_short cishort;
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 \
}
#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 \
}
NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
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) {
try.vj_protocol = cishort;
} else {
try.neg_vj = 0;
}
}
);
#else
NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
{
try.neg_vj = 0;
}
);
#endif
while (len > CILEN_VOID) {
GETCHAR(citype, p);
GETCHAR(cilen, p);
if( (len -= cilen) < 0 )
goto bad;
next = p + cilen - 2;
switch (citype) {
case CI_COMPRESSTYPE:
if (go->neg_vj || no.neg_vj ||
(cilen != CILEN_COMPRESS))
goto bad;
no.neg_vj = 1;
break;
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;
}
p = next;
}
if (len != 0)
goto bad;
if (f->state != OPENED)
*go = try;
return 1;
bad:
IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
return 0;
}
static int
ipv6cp_rejci(f, p, len)
fsm *f;
u_char *p;
int len;
{
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
u_char cilen;
u_short cishort;
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; \
}
#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; \
}
REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
if (len != 0)
goto bad;
if (f->state != OPENED)
*go = try;
return 1;
bad:
IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
return 0;
}
static int
ipv6cp_reqci(f, p, lenp, dont_nak)
fsm *f;
u_char *p;
int *lenp;
int dont_nak;
{
ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
u_char *p0, *nakp, *rejp, *prev;
int ret, newret;
int len, cilen, type;
eui64_t ifaceid;
u_short cishort;
ret = CODE_CONFACK;
rejp = p0 = p;
nakp = nak_buffer;
BZERO(ho, sizeof(*ho));
for (len = *lenp; len > 0; len -= cilen, p = prev + cilen) {
newret = CODE_CONFACK;
if ((len < 2) || p[1] > len) {
return (0);
}
prev = p;
GETCHAR(type, p);
GETCHAR(cilen, p);
switch (type) {
case CI_IFACEID:
IPV6CPDEBUG(("ipv6cp: received interface identifier "));
if (!ao->neg_ifaceid) {
newret = CODE_CONFREJ;
break;
}
if (cilen != CILEN_IFACEID) {
newret = CODE_CONFNAK;
eui64_copy(wo->hisid, ifaceid);
} else {
eui64_get(ifaceid, p);
IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
newret = CODE_CONFREJ;
break;
}
if (!eui64_iszero(wo->hisid) &&
!eui64_equals(ifaceid, wo->hisid) &&
eui64_iszero(go->hisid)) {
newret = CODE_CONFNAK;
eui64_copy(wo->hisid, ifaceid);
} else if (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->ourid)) {
newret = CODE_CONFNAK;
if (eui64_iszero(go->hisid))
eui64_copy(wo->hisid, ifaceid);
while (eui64_iszero(ifaceid) ||
eui64_equals(ifaceid, go->ourid))
eui64_magic(ifaceid);
}
}
if (newret == CODE_CONFNAK) {
PUTCHAR(type, nakp);
PUTCHAR(CILEN_IFACEID, nakp);
eui64_put(ifaceid, nakp);
}
ho->neg_ifaceid = 1;
eui64_copy(ifaceid, ho->hisid);
break;
case CI_COMPRESSTYPE:
IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
if (!ao->neg_vj) {
newret = CODE_CONFREJ;
break;
}
if (cilen != CILEN_COMPRESS) {
newret = CODE_CONFNAK;
cishort = ao->vj_protocol;
} else {
GETSHORT(cishort, p);
IPV6CPDEBUG(("(%d)", cishort));
#ifdef IPV6CP_COMP
if (cishort != IPV6CP_COMP) {
newret = CODE_CONFNAK;
cishort = IPV6CP_COMP;
}
#else
newret = CODE_CONFREJ;
break;
#endif
}
ho->neg_vj = 1;
ho->vj_protocol = cishort;
break;
default:
newret = CODE_CONFREJ;
break;
}
IPV6CPDEBUG((" (%s)\n", CODENAME(newret)));
if (cilen < 2)
cilen = 2;
if (newret == CODE_CONFACK && ret != CODE_CONFACK)
continue;
if (newret == CODE_CONFNAK) {
if (dont_nak) {
newret = CODE_CONFREJ;
} else {
if (ret == CODE_CONFREJ)
continue;
ret = CODE_CONFNAK;
}
}
if (newret == CODE_CONFREJ) {
ret = CODE_CONFREJ;
if (prev != rejp)
(void) BCOPY(prev, rejp, cilen);
rejp += cilen;
}
}
if (ret != CODE_CONFREJ && !ho->neg_ifaceid &&
wo->req_ifaceid && !dont_nak) {
if (ret == CODE_CONFACK)
wo->req_ifaceid = 0;
ret = CODE_CONFNAK;
PUTCHAR(CI_IFACEID, nakp);
PUTCHAR(CILEN_IFACEID, nakp);
eui64_put(wo->hisid, nakp);
}
switch (ret) {
case CODE_CONFACK:
*lenp = p - p0;
sys_block_proto(PPP_IPV6);
break;
case CODE_CONFNAK:
*lenp = nakp - nak_buffer;
(void) BCOPY(nak_buffer, p0, *lenp);
break;
case CODE_CONFREJ:
*lenp = rejp - p0;
break;
}
IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(ret)));
return (ret);
}
static void
ipv6_check_options()
{
ipv6cp_options *wo = &ipv6cp_wantoptions[0];
#if defined(SOL2)
if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
if (ether_to_eui64(&wo->ourid)) {
wo->opt_local = 1;
}
}
#endif
if (wo->use_ip) {
if ((ipcp_wantoptions[0].accept_local ||
ipcp_wantoptions[0].ouraddr == 0) && eui64_iszero(wo->ourid)) {
warn("either IPv4 or IPv6 local address should be non-zero for ipv6cp-use-ipaddr");
}
if ((ipcp_wantoptions[0].accept_remote ||
ipcp_wantoptions[0].hisaddr == 0) && eui64_iszero(wo->hisid)) {
warn("either IPv4 or IPv6 remote address should be non-zero for ipv6cp-use-ipaddr");
}
}
if (!wo->opt_local) {
if (wo->use_ip && eui64_iszero(wo->ourid)) {
eui64_setlo32(wo->ourid, 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, ntohl(ipcp_wantoptions[0].hisaddr));
if (!eui64_iszero(wo->hisid))
wo->opt_remote = 1;
}
}
if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
fatal("local/remote LL address required for demand-dialling\n");
}
}
static int
ipv6_demand_conf(u)
int u;
{
ipv6cp_options *wo = &ipv6cp_wantoptions[u];
#if SIF6UPFIRST
if (!sif6up(u))
return 0;
#endif
if (!sif6addr(u, wo->ourid, wo->hisid))
return 0;
#if !SIF6UPFIRST
if (!sif6up(u))
return 0;
#endif
if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
return 0;
notice("local LL address %s", llv6_ntoa(wo->ourid));
notice("remote LL address %s", llv6_ntoa(wo->hisid));
return 1;
}
static void
ipv6cp_up(f)
fsm *f;
{
ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
IPV6CPDEBUG(("ipv6cp: up"));
if (!ho->neg_ifaceid)
ho->hisid = wo->hisid;
if(!no_ifaceid_neg) {
if (eui64_iszero(ho->hisid)) {
error("Could not determine remote LL address");
ipv6cp_close(f->unit, "Could not determine remote LL address");
return;
}
if (eui64_iszero(go->ourid)) {
error("Could not determine local LL address");
ipv6cp_close(f->unit, "Could not determine local LL address");
return;
}
if (eui64_equals(go->ourid, ho->hisid)) {
error("local and remote LL addresses are equal");
ipv6cp_close(f->unit, "local and remote LL addresses are equal");
return;
}
}
#ifdef IPV6CP_COMP
if (sif6comp(f->unit, ho->neg_vj) != 1) {
ipv6cp_close(f->unit, "Could not enable TCP compression");
return;
}
#endif
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->unit, go->ourid, ho->hisid);
if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
if (debug)
warn("sif6addr failed");
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
}
demand_rexmit(PPP_IPV6);
if (sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS) != 1) {
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
} else {
#if !SIF6UPFIRST
if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
if (debug)
warn("sif6addr failed");
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
#endif
#if defined(SOL2)
if (!sif6up(f->unit)) {
if (debug)
warn("sifup failed (IPV6)");
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
#else
if (!sifup(f->unit)) {
if (debug)
warn("sifup failed (IPV6)");
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
#endif
#if SIF6UPFIRST
if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
if (debug)
warn("sif6addr failed");
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
#endif
if (sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS) != 1) {
ipv6cp_close(f->unit, "Interface configuration failed");
return;
}
notice("local LL address %s", llv6_ntoa(go->ourid));
notice("remote LL address %s", llv6_ntoa(ho->hisid));
}
np_up(f->unit, PPP_IPV6);
ipv6cp_is_up = 1;
script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
ipv6cp_script_state = s_up;
ipv6cp_script(_PATH_IPV6UP);
}
sys_unblock_proto(PPP_IPV6);
}
static void
ipv6cp_down(f)
fsm *f;
{
IPV6CPDEBUG(("ipv6cp: down"));
update_link_stats(f->unit);
if (ipv6cp_is_up) {
ipv6cp_is_up = 0;
np_down(f->unit, PPP_IPV6);
}
#ifdef IPV6CP_COMP
if (sif6comp(f->unit, 0) != 1) {
if (debug)
warn("Failed to disable TCP compression.");
}
#endif
if (demand) {
if (sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE) != 1) {
if (debug)
warn("Failed to enable queueing on outgoing packets.");
}
} else {
if (sifnpmode(f->unit, PPP_IPV6, NPMODE_ERROR) != 1) {
if (debug)
warn("Could not set interface to drop packets.");
}
#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
#if defined(SOL2)
if (sif6down(f->unit) != 1)
warn("Couldn not bring interface down.");
#else
if (sifdown(f->unit) != 1)
warn("Could not bring interface down.");
#endif
#endif
ipv6cp_clear_addrs(f->unit,
ipv6cp_gotoptions[f->unit].ourid,
ipv6cp_hisoptions[f->unit].hisid);
#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
if (sifdown(f->unit) != 1)
warn("Could not bring interface down.");
#endif
}
if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
ipv6cp_script_state = s_down;
ipv6cp_script(_PATH_IPV6DOWN);
}
}
static void
ipv6cp_clear_addrs(unit, ourid, hisid)
int unit;
eui64_t ourid;
eui64_t hisid;
{
if (cif6addr(unit, ourid, hisid) != 1)
warn("Could not clear addresses");
}
static void
ipv6cp_finished(f)
fsm *f;
{
np_finished(f->unit, PPP_IPV6);
}
static void
ipv6cp_script_done(arg, status)
void *arg;
int status;
{
ipv6cp_script_pid = 0;
switch (ipv6cp_script_state) {
case s_up:
if (ipv6cp_fsm[0].state != OPENED) {
ipv6cp_script_state = s_down;
ipv6cp_script(_PATH_IPV6DOWN);
}
break;
case s_down:
if (ipv6cp_fsm[0].state == OPENED) {
ipv6cp_script_state = s_up;
ipv6cp_script(_PATH_IPV6UP);
}
break;
}
}
static void
ipv6cp_script(script)
char *script;
{
char strspeed[32], strlocal[26], strremote[26];
char *argv[8];
(void) slprintf(strspeed, sizeof (strspeed), "%d", baud_rate);
(void) strlcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid),
sizeof (strlocal));
(void) strlcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid),
sizeof (strremote));
argv[0] = script;
argv[1] = ifname;
argv[2] = devnam;
argv[3] = strspeed;
argv[4] = strlocal;
argv[5] = strremote;
argv[6] = ipparam;
argv[7] = NULL;
ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, NULL);
}
static int
ipv6cp_printpkt(p, plen, printer, arg)
u_char *p;
int plen;
void (*printer) __P((void *, const char *, ...));
void *arg;
{
int code, id, len, olen;
u_char *pstart, *optend;
u_short cishort;
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;
printer(arg, " %s id=0x%x", code_name(code, 1), id);
len -= HEADERLEN;
switch (code) {
case CODE_CONFREQ:
case CODE_CONFACK:
case CODE_CONFNAK:
case CODE_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) {
case CI_COMPRESSTYPE:
if (olen >= CILEN_COMPRESS) {
p += 2;
GETSHORT(cishort, p);
printer(arg, "compress 0x%x", cishort);
}
break;
case CI_IFACEID:
if (olen == CILEN_IFACEID) {
p += 2;
eui64_get(ifaceid, p);
printer(arg, "addr %s", llv6_ntoa(ifaceid));
}
break;
}
printer(arg, "%8.*B>", optend-p, p);
p = optend;
}
break;
case CODE_TERMACK:
case CODE_TERMREQ:
if (len > 0 && *p >= ' ' && *p < 0x7f) {
printer(arg, " ");
print_string((char *)p, len, printer, arg);
p += len;
len = 0;
}
break;
}
printer(arg, " %32.*B", len, p);
return p - pstart;
}
#define TCP_HDRLEN 20
#define TH_FIN 0x01
static int
ipv6_active_pkt(pkt, len)
u_char *pkt;
int len;
{
u_char *tcp;
struct in6_addr addr;
char fromstr[26];
char tostr[26];
len -= PPP_HDRLEN;
pkt += PPP_HDRLEN;
if (len < IP6_HDRLEN) {
dbglog("IPv6 packet of length %d is not activity", len);
return 0;
}
(void) BCOPY(get_ip6src(pkt), &addr, sizeof (addr));
(void) inet_ntop(AF_INET6, &addr, fromstr, 26);
(void) BCOPY(get_ip6dst(pkt), &addr, sizeof (addr));
(void) inet_ntop(AF_INET6, &addr, tostr, 26);
if (get_ip6nh(pkt) == IPPROTO_FRAGMENT) {
dbglog("IPv6 fragment from %s->%s is not activity", fromstr, tostr);
return 0;
}
if (get_ip6nh(pkt) != IPPROTO_TCP) {
info("IPv6 proto %d from %s->%s is activity", get_ip6nh(pkt), fromstr,
tostr);
return 1;
}
if (len < IP6_HDRLEN + TCP_HDRLEN) {
dbglog("Bad TCP length %d<%d+%d %s->%s is not activity", len,
IP6_HDRLEN, TCP_HDRLEN, fromstr, tostr);
return 0;
}
tcp = pkt + IP6_HDRLEN;
if ((get_tcpflags(tcp) & TH_FIN) != 0 &&
len == IP6_HDRLEN + get_tcpoff(tcp) * 4) {
dbglog("Empty TCP FIN %s->%s is not activity", fromstr, tostr);
return 0;
}
info("TCP %d data %s%s->%s is activity", len - IP6_HDRLEN - TCP_HDRLEN,
tcp_flag_decode(get_tcpflags(tcp)), fromstr, tostr);
return 1;
}