#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
#include "opt_inet.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/ioccom.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <net/if_gif.h>
#include <net/if_dl.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/ip_encap.h>
#include <netinet/icmp6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/ip6protosw.h>
#include <netinet6/mip6.h>
#include <netinet6/mip6_common.h>
#if MIP6_DEBUG
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <machine/stdarg.h>
#include <sys/syslog.h>
#endif
#include <net/net_osdep.h>
int (*mip6_rec_ra_hook)(struct mbuf *, int) = 0;
struct in6_addr * (*mip6_global_addr_hook)(struct in6_addr *) = 0;
struct mip6_subopt_hal * (*mip6_hal_dynamic_hook)(struct in6_addr *) = 0;
int (*mip6_write_config_data_ha_hook)(u_long, void *) = 0;
int (*mip6_clear_config_data_ha_hook)(u_long, void *) = 0;
int (*mip6_enable_func_ha_hook)(u_long, caddr_t) = 0;
int (*mip6_rec_ba_hook)(struct mbuf *, int) = 0;
int (*mip6_rec_br_hook)(struct mbuf *, int) = 0;
void (*mip6_stop_bu_hook)(struct in6_addr *) = 0;
int (*mip6_write_config_data_mn_hook)(u_long, void *) = 0;
int (*mip6_clear_config_data_mn_hook)(u_long, caddr_t) = 0;
int (*mip6_enable_func_mn_hook)(u_long, caddr_t) = 0;
#if MIP6_DEBUG
int mip6_debug_is_enabled = 0;
#endif
struct mip6_bc *mip6_bcq = NULL;
struct mip6_na *mip6_naq = NULL;
struct mip6_prefix *mip6_pq = NULL;
struct mip6_config mip6_config;
struct mip6_link_list *mip6_llq = NULL;
#if 0
#if MIP6_HA
u_int8_t mip6_module = MIP6_HA_MODULE;
#elif defined(MIP6_MN)
u_int8_t mip6_module = MIP6_MN_MODULE;
#else
u_int8_t mip6_module = 0;
#endif
#else
u_int8_t mip6_module = 0;
#endif
extern struct ip6protosw mip6_tunnel_protosw;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
struct callout_handle mip6_timer_na_handle;
struct callout_handle mip6_timer_bc_handle;
struct callout_handle mip6_timer_prefix_handle;
#endif
struct in6_addr in6addr_linklocal;
struct in6_addr in6addr_sitelocal;
struct in6_addr in6addr_aha_64;
struct in6_addr in6addr_aha_nn;
void
mip6_init(void)
{
static int mip6_init_done = 0;
if (mip6_init_done)
return;
in6addr_linklocal.s6_addr32[0] = MIP6_ADDR_INT32_ULL;
in6addr_linklocal.s6_addr32[1] = 0x00000000;
in6addr_linklocal.s6_addr32[2] = 0x00000000;
in6addr_linklocal.s6_addr32[3] = 0x00000000;
in6addr_sitelocal.s6_addr32[0] = MIP6_ADDR_INT32_USL;
in6addr_sitelocal.s6_addr32[1] = 0x00000000;
in6addr_sitelocal.s6_addr32[2] = 0x00000000;
in6addr_sitelocal.s6_addr32[3] = 0x00000000;
in6addr_aha_64.s6_addr32[0] = 0x00000000;
in6addr_aha_64.s6_addr32[1] = 0xffffffff;
in6addr_aha_64.s6_addr32[2] = MIP6_ADDR_INT32_AHA2;
in6addr_aha_64.s6_addr32[3] = MIP6_ADDR_INT32_AHA1;
in6addr_aha_nn.s6_addr32[0] = 0x00000000;
in6addr_aha_nn.s6_addr32[1] = 0xffffffff;
in6addr_aha_nn.s6_addr32[2] = 0xffffffff;
in6addr_aha_nn.s6_addr32[3] = MIP6_ADDR_INT32_AHA1;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
callout_handle_init(&mip6_timer_na_handle);
callout_handle_init(&mip6_timer_bc_handle);
callout_handle_init(&mip6_timer_prefix_handle);
#endif
bzero(&mip6_config, sizeof(struct mip6_config));
LIST_INIT(&mip6_config.fna_list);
mip6_config.bu_lifetime = 600;
mip6_config.br_update = 60;
mip6_config.hr_lifetime = 3600;
mip6_config.enable_outq = 1;
mip6_enable_hooks(MIP6_GENERIC_HOOKS);
mip6_enable_hooks(MIP6_CONFIG_HOOKS);
mip6_init_done = 1;
printf("%s: MIP6 initialized\n", __FUNCTION__);
}
void
mip6_exit()
{
struct mip6_na *nap, *nap_tmp;
struct mip6_bc *bcp, *bcp_nxt;
struct mip6_prefix *prefix;
int s;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_na, (void *)NULL, mip6_timer_na_handle);
untimeout(mip6_timer_bc, (void *)NULL , mip6_timer_bc_handle);
untimeout(mip6_timer_prefix, (void *)NULL, mip6_timer_prefix_handle);
#else
untimeout(mip6_timer_na, (void *)NULL);
untimeout(mip6_timer_bc, (void *)NULL);
untimeout(mip6_timer_prefix, (void *)NULL);
#endif
s = splnet();
for (nap = mip6_naq; nap;) {
nap_tmp = nap;
nap = nap->next;
_FREE(nap_tmp, M_TEMP);
}
mip6_naq = NULL;
for (bcp = mip6_bcq; bcp;) {
mip6_bc_delete(bcp, &bcp_nxt);
bcp = bcp_nxt;
}
mip6_bcq = NULL;
for (prefix = mip6_pq; prefix;)
prefix = mip6_prefix_delete(prefix);
mip6_pq = NULL;
splx(s);
}
int
mip6_rec_ctrl_sig(m_in, off)
struct mbuf *m_in;
int off;
{
register struct ip6_hdr *ip6;
int res;
#if MIP6_DEBUG
static int count = 0;
count += 1;
mip6_debug("\nMIPv6 Start processing a control signal (%d)\n", count);
#endif
res = 0;
if (mip6_inp == NULL) {
log(LOG_ERR, "%s: Variabel mip6_inp is NULL\n",
__FUNCTION__);
return IPPROTO_DONE;
}
ip6 = mtod(m_in, struct ip6_hdr *);
mip6_inp->ip6_src = ip6->ip6_src;
mip6_inp->ip6_dst = ip6->ip6_dst;
if (mip6_inp->optflag & MIP6_DSTOPT_BU) {
res = mip6_rec_bu(m_in, off);
if (res != 0) {
#if MIP6_DEBUG
mip6_debug("\nMIPv6 Error processing control "
"signal BU (%d)\n", count);
#endif
return res;
}
}
if (MIP6_IS_MN_ACTIVE) {
if (mip6_inp->optflag & MIP6_DSTOPT_BA) {
if (mip6_rec_ba_hook)
res = (*mip6_rec_ba_hook)(m_in, off);
if (res != 0) {
#if MIP6_DEBUG
mip6_debug("\nMIPv6 Error processing control "
"signal BA (%d)\n", count);
#endif
return res;
}
}
}
if (MIP6_IS_MN_ACTIVE) {
if (mip6_inp->optflag & MIP6_DSTOPT_BR) {
if (mip6_rec_br_hook)
res = (*mip6_rec_br_hook)(m_in, off);
if (res != 0) {
#if MIP6_DEBUG
mip6_debug("\nMIPv6 Error processing control "
"signal BR (%d)\n", count);
#endif
return res;
}
}
}
if (mip6_inp->optflag & MIP6_DSTOPT_HA)
mip6_ha2srcaddr(m_in);
#if MIP6_DEBUG
mip6_debug("\nMIPv6 Finished processing a control signal (%d)\n",
count);
#endif
return 0;
}
int
mip6_icmp6_input(m, off)
struct mbuf *m;
int off;
{
struct ip6_hdr *ip6;
struct ip6_hdr *ip6_icmp;
struct icmp6_hdr *icmp6;
struct mip6_bc *bcp;
struct mip6_bc *bcp_nxt;
struct nd_router_advert *ra;
u_int8_t *err_ptr;
int icmp6len, err_off, res = 0;
ip6 = mtod(m, struct ip6_hdr *);
icmp6len = m->m_pkthdr.len - off;
icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
switch (icmp6->icmp6_type) {
case ICMP6_DST_UNREACH:
if ((off + sizeof(struct icmp6_hdr) +
sizeof(struct ip6_hdr)) >= m->m_pkthdr.len)
return 0;
ip6_icmp = (struct ip6_hdr *) ((caddr_t)icmp6 +
sizeof(struct icmp6_hdr));
bcp = mip6_bc_find(&ip6_icmp->ip6_dst);
if (bcp && !bcp->hr_flag)
mip6_bc_delete(bcp, &bcp_nxt);
break;
case ICMP6_PARAM_PROB:
if (icmp6->icmp6_code != ICMP6_PARAMPROB_OPTION)
break;
err_off = ntohl(icmp6->icmp6_data32[0]);
if ((off + sizeof(struct icmp6_hdr) + err_off) >=
m->m_pkthdr.len)
return 0;
ip6_icmp = (struct ip6_hdr *)((caddr_t)icmp6 +
sizeof(struct icmp6_hdr));
err_ptr = (u_int8_t *) ((caddr_t)icmp6 +
sizeof(struct icmp6_hdr) +
err_off);
if (MIP6_IS_MN_ACTIVE && (*err_ptr == IP6OPT_BINDING_UPDATE)) {
if (mip6_stop_bu_hook)
(*mip6_stop_bu_hook)(&ip6_icmp->ip6_dst);
}
if (*err_ptr == IP6OPT_HOME_ADDRESS) {
log(LOG_ERR,
"Node %s does not recognize Home Address option\n",
ip6_sprintf(&ip6_icmp->ip6_dst));
}
break;
case ND_ROUTER_ADVERT:
if (icmp6->icmp6_code != 0)
break;
if (icmp6len < sizeof(struct nd_router_advert))
break;
ra = (struct nd_router_advert *)icmp6;
if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_HA) == 0)
break;
if (mip6_rec_ra_hook) {
res = mip6_rec_ra_hook(m, off);
if (res) return res;
break;
}
}
return 0;
}
int
mip6_rec_bu(m_in, off)
struct mbuf *m_in;
int off;
{
struct in6_addr *src_addr;
struct mip6_subopt_hal *hal;
struct mip6_bc *bcp;
struct mip6_bc *bcp_nxt;
struct in6_addr *coa;
struct mip6_subbuf *subbuf;
struct in6_addr ll_allnode;
u_int32_t min_time;
u_long na_flags = 0;
int send_na;
int res, error;
u_int8_t rtr;
#if MIP6_DEBUG
u_int8_t var;
int offset, ii;
#endif
subbuf = NULL;
if (mip6_inp->coa)
coa = &mip6_inp->coa->coa;
else
coa = &mip6_inp->ip6_src;
#if IPSEC
#ifndef __OpenBSD__
if ( !((m_in->m_flags & M_AUTHIPHDR && m_in->m_flags & M_AUTHIPDGM) ||
(m_in->m_flags & M_AUTHIPDGM && m_in->m_flags & M_DECRYPTED))) {
ip6stat.ip6s_badoptions++;
log(LOG_INFO,
"%s: No AH or ESP header in BU from host %s\n",
__FUNCTION__,
ip6_sprintf(coa));
return IPPROTO_DONE;
}
#endif
#endif
if ((mip6_inp->optflag & MIP6_DSTOPT_HA) == 0) {
ip6stat.ip6s_badoptions++;
log(LOG_INFO,
"%s: No Home Address option included in BU from host %s\n",
__FUNCTION__, ip6_sprintf(coa));
return IPPROTO_DONE;
}
if (mip6_inp->bu_opt->len < IP6OPT_BULEN) {
ip6stat.ip6s_badoptions++;
log(LOG_INFO,
"%s: Length field to short (%d) in BU from host %s\n",
__FUNCTION__, mip6_inp->bu_opt->len, ip6_sprintf(coa));
return IPPROTO_DONE;
}
send_na = 0;
bcp = mip6_bc_find(&mip6_inp->ha_opt->home_addr);
if (bcp != NULL) {
if (MIP6_LEQ(mip6_inp->bu_opt->seqno, bcp->seqno)) {
ip6stat.ip6s_badoptions++;
log(LOG_INFO,
"%s: Received sequence number (%d) <= "
"current (%d) in BU from host %s\n",
__FUNCTION__, mip6_inp->bu_opt->seqno,
bcp->seqno, ip6_sprintf(coa));
return IPPROTO_DONE;
}
if (!bcp->hr_flag)
send_na = 1;
} else
send_na = 1;
#if MIP6_DEBUG
mip6_debug("\nReceived Binding Update\n");
mip6_debug("IP Header Src: %s\n",
ip6_sprintf(&mip6_inp->ip6_src));
mip6_debug("IP Header Dst: %s\n",
ip6_sprintf(&mip6_inp->ip6_dst));
mip6_debug("Type/Length/Flags: %x / %u / ",
mip6_inp->bu_opt->type, mip6_inp->bu_opt->len);
if (mip6_inp->bu_opt->flags & MIP6_BU_AFLAG)
mip6_debug("A ");
if (mip6_inp->bu_opt->flags & MIP6_BU_HFLAG)
mip6_debug("H ");
if (mip6_inp->bu_opt->flags & MIP6_BU_RFLAG)
mip6_debug("R ");
mip6_debug("\n");
mip6_debug("Seq no/Life time: %u / %u\n",
mip6_inp->bu_opt->seqno,
mip6_inp->bu_opt->lifetime);
mip6_debug("Prefix length: %u\n",
mip6_inp->bu_opt->prefix_len);
if (mip6_inp->bu_opt->len > IP6OPT_BULEN) {
offset = mip6_opt_offset(m_in, off, IP6OPT_BINDING_UPDATE);
if (offset == 0) goto end_debug;
mip6_debug("Sub-options present (TLV coded)\n");
for (ii = IP6OPT_BULEN; ii < mip6_inp->bu_opt->len; ii++) {
if ((ii - IP6OPT_BULEN) % 16 == 0)
mip6_debug("\t0x:");
if ((ii - IP6OPT_BULEN) % 4 == 0)
mip6_debug(" ");
m_copydata(m_in, offset + 2 + ii, sizeof(var),
(caddr_t)&var);
mip6_debug("%02x", var);
if ((ii - IP6OPT_BULEN + 1) % 16 == 0)
mip6_debug("\n");
}
if ((ii - IP6OPT_BULEN) % 16)
mip6_debug("\n");
}
end_debug:
#endif
src_addr = NULL;
hal = NULL;
if (MIP6_IS_HA_ACTIVE) {
if ((mip6_inp->ip6_dst.s6_addr8[15] & 0x7f) ==
MIP6_ADDR_ANYCAST_HA) {
if (mip6_global_addr_hook)
src_addr = (*mip6_global_addr_hook)
(&mip6_inp->ip6_dst);
if (src_addr == NULL) {
log(LOG_ERR,
"%s: No global source address found\n",
__FUNCTION__);
return IPPROTO_DONE;
}
if (mip6_hal_dynamic_hook)
hal = (*mip6_hal_dynamic_hook)(src_addr);
if (mip6_store_subopt(&subbuf, (caddr_t)hal)) {
if (subbuf) _FREE(subbuf, M_TEMP);
return IPPROTO_DONE;
}
error = mip6_send_ba(src_addr,
&mip6_inp->ha_opt->home_addr,
coa, subbuf, MIP6_BA_STATUS_DHAAD,
mip6_inp->bu_opt->seqno, 0);
return error;
}
}
if ((mip6_inp->bu_opt->lifetime != 0) &&
(! IN6_ARE_ADDR_EQUAL(&mip6_inp->ha_opt->home_addr, coa))) {
error = 0;
if (mip6_inp->bu_opt->flags & MIP6_BU_HFLAG) {
if ((!ip6_forwarding || !MIP6_IS_HA_ACTIVE) &&
(mip6_inp->bu_opt->flags & MIP6_BU_AFLAG)) {
error = mip6_send_ba(
&mip6_inp->ip6_dst,
&mip6_inp->ha_opt->home_addr,
coa, NULL, MIP6_BA_STATUS_HOMEREGNOSUP,
mip6_inp->bu_opt->seqno, 0);
return error;
}
res = mip6_addr_on_link(&mip6_inp->ha_opt->home_addr,
mip6_inp->bu_opt->prefix_len);
if ((res != 0) &&
(mip6_inp->bu_opt->flags & MIP6_BU_AFLAG)) {
error = mip6_send_ba(
&mip6_inp->ip6_dst,
&mip6_inp->ha_opt->home_addr,
coa, NULL, res,
mip6_inp->bu_opt->seqno, 0);
return error;
}
min_time = mip6_min_lifetime(
&mip6_inp->ha_opt->home_addr,
mip6_inp->bu_opt->prefix_len);
min_time = min(min_time,
mip6_inp->bu_opt->lifetime);
rtr = mip6_inp->bu_opt->flags & MIP6_BU_RFLAG;
bcp = mip6_bc_find(&mip6_inp->ha_opt->home_addr);
if (bcp)
mip6_bc_update(bcp, coa, min_time, 1, rtr,
mip6_inp->bu_opt->prefix_len,
mip6_inp->bu_opt->seqno,
bcp->info, bcp->lasttime);
else {
bcp = mip6_bc_create(
&mip6_inp->ha_opt->home_addr,
coa, min_time, 1, rtr,
mip6_inp->bu_opt->prefix_len,
mip6_inp->bu_opt->seqno);
if (bcp == NULL)
return IPPROTO_DONE;
}
if (mip6_inp->bu_opt->flags & MIP6_BU_AFLAG) {
error = mip6_send_ba(&mip6_inp->ip6_dst,
&bcp->home_addr,
&bcp->coa,
NULL,
MIP6_BA_STATUS_ACCEPT,
bcp->seqno,
bcp->lifetime);
if (error)
return error;
}
error = mip6_tunnel(&mip6_inp->ip6_dst,
&bcp->coa,
MIP6_TUNNEL_MOVE, MIP6_NODE_HA,
(void *)bcp);
if (error)
return IPPROTO_DONE;
error = mip6_proxy(&bcp->home_addr,
&mip6_inp->ip6_dst, RTM_ADD);
if (error) {
#if MIP6_DEBUG
mip6_debug("%s: set proxy error = %d\n",
__FUNCTION__, error);
#endif
return IPPROTO_DONE;
}
if (send_na) {
ll_allnode = in6addr_linklocal_allnodes;
na_flags |= ND_NA_FLAG_OVERRIDE;
if (mip6_inp->bu_opt->flags & MIP6_BU_RFLAG)
na_flags |= ND_NA_FLAG_ROUTER;
mip6_na_create(&mip6_inp->ha_opt->home_addr,
&ll_allnode,
&mip6_inp->ha_opt->home_addr,
mip6_inp->bu_opt->prefix_len,
na_flags, 1);
}
} else {
rtr = mip6_inp->bu_opt->flags & MIP6_BU_RFLAG;
bcp = mip6_bc_find(&mip6_inp->ha_opt->home_addr);
if (bcp)
mip6_bc_update(bcp, coa,
mip6_inp->bu_opt->lifetime,
0, rtr,
mip6_inp->bu_opt->prefix_len,
mip6_inp->bu_opt->seqno,
bcp->info, bcp->lasttime);
else {
bcp = mip6_bc_create(
&mip6_inp->ha_opt->home_addr,
coa, mip6_inp->bu_opt->lifetime,
0, rtr, mip6_inp->bu_opt->prefix_len,
mip6_inp->bu_opt->seqno);
if (bcp == NULL)
return IPPROTO_DONE;
}
if (mip6_inp->bu_opt->flags & MIP6_BU_AFLAG) {
error = mip6_send_ba(&mip6_inp->ip6_dst,
&bcp->home_addr,
&bcp->coa, NULL,
MIP6_BA_STATUS_ACCEPT,
bcp->seqno,
bcp->lifetime);
return error;
}
}
return 0;
}
if ((mip6_inp->bu_opt->lifetime == 0) ||
(IN6_ARE_ADDR_EQUAL(&mip6_inp->ha_opt->home_addr, coa))) {
if (mip6_inp->bu_opt->flags & MIP6_BU_HFLAG) {
error = 0;
if (((bcp == NULL) || (bcp->hr_flag == 0)) &&
(mip6_inp->bu_opt->flags & MIP6_BU_AFLAG)) {
error = mip6_send_ba(
&mip6_inp->ip6_dst,
&mip6_inp->ha_opt->home_addr,
coa, NULL, MIP6_BA_STATUS_NOTHA,
mip6_inp->bu_opt->seqno, 0);
return error;
}
error = mip6_bc_delete(bcp, &bcp_nxt);
if (error)
return IPPROTO_DONE;
if (mip6_inp->bu_opt->flags & MIP6_BU_AFLAG) {
error = mip6_send_ba(
&mip6_inp->ip6_dst,
&mip6_inp->ha_opt->home_addr,
coa, NULL, MIP6_BA_STATUS_ACCEPT,
mip6_inp->bu_opt->seqno, 0);
if (error)
return error;
}
} else {
if (bcp != NULL) {
error = mip6_bc_delete(bcp, &bcp_nxt);
if (error)
return IPPROTO_DONE;
}
if (mip6_inp->bu_opt->flags & MIP6_BU_AFLAG) {
error = mip6_send_ba(
&mip6_inp->ip6_dst,
&mip6_inp->ha_opt->home_addr,
coa, NULL, MIP6_BA_STATUS_ACCEPT,
mip6_inp->bu_opt->seqno, 0);
if (error)
return error;
}
}
return 0;
}
return 0;
}
void
mip6_ha2srcaddr(m)
struct mbuf *m;
{
register struct ip6_hdr *ip6;
#if MIP6_DEBUG
mip6_debug("\nReceived Home Address Option\n");
mip6_debug("Type/Length: %x / %u\n", mip6_inp->ha_opt->type,
mip6_inp->ha_opt->len);
mip6_debug("Home Address: %s\n",
ip6_sprintf(&mip6_inp->ha_opt->home_addr));
#endif
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_src = mip6_inp->ha_opt->home_addr;
}
int
mip6_send_ba(ip6_src, ip6_dst, coa, subbuf, status, seqno, lifetime)
struct in6_addr *ip6_src;
struct in6_addr *ip6_dst;
struct in6_addr *coa;
struct mip6_subbuf *subbuf;
u_int8_t status;
u_int16_t seqno;
u_int32_t lifetime;
{
struct mbuf *m_ip6;
struct mip6_opt_ba *ba_opt;
struct ip6_pktopts *opt;
int error;
#if MIP6_DEBUG
u_int8_t var;
int ii;
#endif
opt = (struct ip6_pktopts *)MALLOC ip6_pktopts),
M_TEMP, M_WAITOK);
if (opt == NULL)
return IPPROTO_DONE;
bzero(opt, sizeof(struct ip6_pktopts));
opt->ip6po_hlim = -1;
m_ip6 = mip6_create_ip6hdr(ip6_src, ip6_dst, IPPROTO_NONE);
if(m_ip6 == NULL)
return IPPROTO_DONE;
opt->ip6po_rhinfo.ip6po_rhi_rthdr = mip6_create_rh(coa,
IPPROTO_DSTOPTS);
if(opt->ip6po_rhinfo.ip6po_rhi_rthdr == NULL)
return IPPROTO_DONE;
ba_opt = mip6_create_ba(status, seqno, lifetime);
if (ba_opt == NULL)
return IPPROTO_DONE;
opt->ip6po_dest2 = mip6_create_dh((void *)ba_opt, subbuf,
IPPROTO_NONE);
if(opt->ip6po_dest2 == NULL)
return IPPROTO_DONE;
mip6_config.enable_outq = 0;
error = ip6_output(m_ip6, opt, NULL, 0, NULL, NULL);
if (error) {
_FREE(opt->ip6po_rhinfo.ip6po_rhi_rthdr, M_TEMP);
_FREE(opt->ip6po_dest2, M_TEMP);
_FREE(ba_opt, M_TEMP);
mip6_config.enable_outq = 1;
log(LOG_ERR,
"%s: ip6_output function failed to send BA, error = %d\n",
__FUNCTION__, error);
return error;
}
mip6_config.enable_outq = 1;
#if MIP6_DEBUG
mip6_debug("\nSent Binding Acknowledgement\n");
mip6_debug("IP Header Src: %s\n", ip6_sprintf(ip6_src));
mip6_debug("IP Header Dst: %s\n", ip6_sprintf(ip6_dst));
mip6_debug("Type/Length/Status: %x / %u / %u\n",
ba_opt->type, ba_opt->len, ba_opt->status);
mip6_debug("Seq no/Life time: %u / %u\n",
ba_opt->seqno, ba_opt->lifetime);
mip6_debug("Refresh time: %u\n", ba_opt->refresh);
if (subbuf) {
mip6_debug("Sub-options present (TLV coded)\n");
for (ii = 0; ii < subbuf->len; ii++) {
if (ii % 16 == 0)
mip6_debug("\t0x:");
if (ii % 4 == 0)
mip6_debug(" ");
bcopy((caddr_t)&subbuf->buffer[ii], (caddr_t)&var, 1);
mip6_debug("%02x", var);
if ((ii + 1) % 16 == 0)
mip6_debug("\n");
}
if (ii % 16)
mip6_debug("\n");
}
#endif
_FREE(opt->ip6po_rhinfo.ip6po_rhi_rthdr, M_TEMP);
_FREE(opt->ip6po_dest2, M_TEMP);
_FREE(ba_opt, M_TEMP);
return 0;
}
void
mip6_send_na(nap)
struct mip6_na *nap;
{
struct mip6_prefix *pq;
struct nd_prefix *pr;
struct in6_addr new_addr;
struct in6_addr sl_addr;
nap->no -= 1;
#if MIP6_DEBUG
mip6_debug("\nSent Neighbor Advertisement (0x%x)\n", nap);
#endif
if (nap->prefix_len == 0) {
nd6_na_output(nap->ifp, &nap->dst_addr, &nap->target_addr,
nap->flags, nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&nap->target_addr));
#endif
}
if ((MIP6_IS_HA_ACTIVE) && (nap->prefix_len != 0)) {
for (pq = mip6_pq; pq; pq = pq->next) {
if ((nap->prefix_len == pq->prefix_len) &&
in6_are_prefix_equal(&pq->prefix,
&nap->target_addr,
pq->prefix_len)) {
mip6_build_in6addr(&new_addr,
&nap->target_addr,
&pq->prefix,
pq->prefix_len);
nd6_na_output(nap->ifp, &nap->dst_addr,
&new_addr, nap->flags,
nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&new_addr));
#endif
} else
continue;
if (nap->prefix_len == 64) {
bcopy((caddr_t)&in6addr_sitelocal,
(caddr_t)&sl_addr, 6);
bcopy((caddr_t)&nap->target_addr + 6,
(caddr_t)&sl_addr + 6, 2);
mip6_build_in6addr(&new_addr,
&nap->target_addr,
&sl_addr,
nap->prefix_len);
nd6_na_output(nap->ifp,
&nap->dst_addr,
&new_addr,
nap->flags,
nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&new_addr));
#endif
mip6_build_in6addr(&new_addr,
&nap->target_addr,
&in6addr_linklocal,
nap->prefix_len);
nd6_na_output(nap->ifp,
&nap->dst_addr,
&new_addr,
nap->flags,
nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&new_addr));
#endif
}
}
} else {
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if ((nap->prefix_len == pr->ndpr_plen) &&
in6_are_prefix_equal(&nap->target_addr,
&pr->ndpr_addr,
pr->ndpr_plen)) {
mip6_build_in6addr(
&new_addr,
&nap->target_addr,
&pr->ndpr_prefix.sin6_addr,
pr->ndpr_plen);
nd6_na_output(nap->ifp,
&nap->dst_addr,
&new_addr,
nap->flags,
nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&new_addr));
#endif
} else
continue;
if (nap->prefix_len == 64) {
bcopy((caddr_t)&in6addr_sitelocal,
(caddr_t)&sl_addr, 6);
bcopy((caddr_t)&nap->target_addr + 6,
(caddr_t)&sl_addr + 6, 2);
mip6_build_in6addr(&new_addr,
&nap->target_addr,
&sl_addr,
nap->prefix_len);
nd6_na_output(nap->ifp,
&nap->dst_addr,
&new_addr,
nap->flags,
nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&new_addr));
#endif
mip6_build_in6addr(&new_addr,
&nap->target_addr,
&in6addr_linklocal,
nap->prefix_len);
nd6_na_output(nap->ifp,
&nap->dst_addr,
&new_addr,
nap->flags,
nap->use_link_opt, NULL);
#if MIP6_DEBUG
mip6_debug("Target Address: %s\n",
ip6_sprintf(&new_addr));
#endif
}
}
}
return;
}
struct mbuf *
mip6_create_ip6hdr(ip6_src, ip6_dst, next)
struct in6_addr *ip6_src;
struct in6_addr *ip6_dst;
u_int8_t next;
{
struct ip6_hdr *ip6;
struct mbuf *m;
ip6 = (struct ip6_hdr *)MALLOC ip6_hdr),
M_TEMP, M_WAITOK);
if (ip6 == NULL)
return NULL;
bzero(ip6, sizeof(struct ip6_hdr));
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_plen = 0;
ip6->ip6_nxt = next;
ip6->ip6_hlim = IPV6_DEFHLIM;
ip6->ip6_src = *ip6_src;
ip6->ip6_dst = *ip6_dst;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
return NULL;
}
m->m_len = sizeof(*ip6);
m->m_pkthdr.len = m->m_len;
m->m_pkthdr.rcvif = NULL;
bcopy((caddr_t)ip6, mtod(m, caddr_t), sizeof(*ip6));
_FREE(ip6, M_TEMP);
return m;
}
struct ip6_rthdr *
mip6_create_rh(coa, next)
struct in6_addr *coa;
u_int8_t next;
{
struct ip6_rthdr0 *rthdr0;
int len;
len = sizeof(struct ip6_rthdr0) + sizeof(struct in6_addr);
rthdr0 = (struct ip6_rthdr0 *)MALLOC M_TEMP, M_WAITOK);
if (rthdr0 == NULL)
return NULL;
bzero(rthdr0, len);
rthdr0->ip6r0_nxt = next;
rthdr0->ip6r0_len = 2;
rthdr0->ip6r0_type = 0;
rthdr0->ip6r0_segleft = 1;
rthdr0->ip6r0_reserved = 0;
bcopy((caddr_t)coa, (caddr_t)rthdr0 + sizeof(struct ip6_rthdr0),
sizeof(struct in6_addr));
return (struct ip6_rthdr *)rthdr0;
}
struct mip6_opt_ba *
mip6_create_ba(status, seqno, lifetime)
u_int8_t status;
u_int16_t seqno;
u_int32_t lifetime;
{
struct mip6_opt_ba *ba_opt;
ba_opt = (struct mip6_opt_ba *)MALLOC mip6_opt_ba),
M_TEMP, M_WAITOK);
if (ba_opt == NULL)
return NULL;
bzero(ba_opt, sizeof(struct mip6_opt_ba));
ba_opt->type = IP6OPT_BINDING_ACK;
ba_opt->len = IP6OPT_BALEN;
ba_opt->status = status;
ba_opt->seqno = seqno;
ba_opt->lifetime = lifetime;
if (MIP6_IS_HA_ACTIVE)
ba_opt->refresh = (ba_opt->lifetime * 8) / 10;
else
ba_opt->refresh = ba_opt->lifetime;
return ba_opt;
}
struct ip6_dest *
mip6_create_dh(arg_opt, arg_sub, next)
void *arg_opt;
struct mip6_subbuf *arg_sub;
u_int8_t next;
{
struct mip6_opt *opt;
struct ip6_dest *dest;
int off;
int error;
opt = (struct mip6_opt *)arg_opt;
dest = NULL;
if (opt->type == IP6OPT_BINDING_ACK) {
off = 3;
error = mip6_add_ba(&dest, &off,
(struct mip6_opt_ba *)opt, arg_sub);
if (error) {
if (dest != NULL)
_FREE(dest, M_TEMP);
return NULL;
}
dest->ip6d_nxt = next;
} else if (opt->type == IP6OPT_BINDING_UPDATE) {
off = 2;
error = mip6_add_bu(&dest, &off,
(struct mip6_opt_bu *)opt, arg_sub);
if (error) {
if (dest != NULL)
_FREE(dest, M_TEMP);
return NULL;
}
dest->ip6d_nxt = next;
}
return dest;
}
int
mip6_opt_offset(m_in, off, type)
struct mbuf *m_in;
int off;
int type;
{
int ii;
u_int8_t opttype;
u_int8_t optlen;
u_int32_t len;
u_int8_t len8;
u_int32_t offset;
m_copydata(m_in, off + 1, sizeof(len8), (caddr_t)&len8);
len = (len8 + 1) << 3;
offset = 0;
for (ii = 2; ii < len;) {
m_copydata(m_in, off + ii, sizeof(opttype), (caddr_t)&opttype);
if (opttype == type) {
offset = off + ii;
break;
} else if (opttype == IP6OPT_PAD1) {
ii += 1;
continue;
} else {
ii += 1;
}
m_copydata(m_in, off + ii, sizeof(optlen), (caddr_t)&optlen);
ii += 1 + optlen;
}
return offset;
}
int
mip6_addr_on_link(addr, prefix_len)
struct in6_addr *addr;
int prefix_len;
{
struct mip6_prefix *pr;
for (pr = mip6_pq; pr; pr = pr->next) {
if (in6_are_prefix_equal(addr, &pr->prefix, pr->prefix_len)) {
if (prefix_len == 0)
return 0;
if (pr->prefix_len == prefix_len)
return 0;
else
return MIP6_BA_STATUS_IFLEN;
}
}
return MIP6_BA_STATUS_SUBNET;
}
u_int32_t
mip6_min_lifetime(addr, prefix_len)
struct in6_addr *addr;
int prefix_len;
{
struct mip6_prefix *pr;
u_int32_t min_time;
min_time = 0xffffffff;
for (pr = mip6_pq; pr; pr = pr->next) {
if (prefix_len == 0) {
if (in6_are_prefix_equal(addr, &pr->prefix,
pr->prefix_len)) {
return pr->valid_time;
}
} else
min_time = min(min_time, pr->valid_time);
}
return min_time;
}
void
mip6_build_in6addr(new_addr, id, prefix, prefix_len)
struct in6_addr *new_addr;
struct in6_addr *id;
const struct in6_addr *prefix;
int prefix_len;
{
u_int8_t byte_pr, byte_id;
int ii, jj;
for (ii = 0; ii < prefix_len / 8; ii++)
new_addr->s6_addr8[ii] = prefix->s6_addr8[ii];
if (prefix_len % 8) {
byte_pr = prefix->s6_addr8[ii];
byte_pr = byte_pr >> (8 - (prefix_len % 8));
byte_pr = byte_pr << (8 - (prefix_len % 8));
byte_id = id->s6_addr8[ii];
byte_id = byte_id << (prefix_len % 8);
byte_id = byte_id >> (prefix_len % 8);
new_addr->s6_addr8[ii] = byte_pr | byte_id;
ii += 1;
}
for (jj = ii; jj < 16; jj++)
new_addr->s6_addr8[jj] = id->s6_addr8[jj];
}
void
mip6_build_ha_anycast(new_addr, prefix, prefix_len)
struct in6_addr *new_addr;
const struct in6_addr *prefix;
int prefix_len;
{
struct in6_addr addr;
if (prefix->s6_addr8[0] == 0xff) {
*new_addr = in6addr_any;
return;
}
if (((prefix->s6_addr8[0] & 0xe0) != 0) && (prefix_len != 64)) {
*new_addr = in6addr_any;
return;
}
if (((prefix->s6_addr8[0] & 0xe0) != 0) && (prefix_len == 64))
addr = in6addr_aha_64;
else
addr = in6addr_aha_nn;
mip6_build_in6addr(new_addr, &addr, prefix, prefix_len);
}
int
mip6_add_ifaddr(struct in6_addr *addr,
struct ifnet *ifp,
int plen,
int flags)
{
struct in6_aliasreq *ifra, dummy;
struct sockaddr_in6 *sa6;
struct sockaddr_in6 oldaddr;
struct in6_ifaddr *ia, *oia;
struct in6_addrlifetime *lt;
int error = 0, hostIsNew, prefixIsNew;
int s;
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
struct ifaddr *ifa;
#endif
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
time_t time_second = (time_t)time.tv_sec;
#endif
bzero(&dummy, sizeof(dummy));
ifra = &dummy;
ifra->ifra_addr.sin6_len = sizeof(ifra->ifra_addr);
ifra->ifra_addr.sin6_family = AF_INET6;
ifra->ifra_addr.sin6_addr = *addr;
if (plen != 0) {
ifra->ifra_prefixmask.sin6_len =
sizeof(ifra->ifra_prefixmask);
ifra->ifra_prefixmask.sin6_family = AF_INET6;
in6_prefixlen2mask(&ifra->ifra_prefixmask.sin6_addr, plen);
}
ifra->ifra_flags = flags;
ifra->ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
ifra->ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
sa6 = &ifra->ifra_addr;
if (ifp == 0)
return EOPNOTSUPP;
s = splnet();
if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
if (sa6->sin6_addr.s6_addr16[1] == 0) {
sa6->sin6_addr.s6_addr16[1] =
htons(ifp->if_index);
}
else if (sa6->sin6_addr.s6_addr16[1] !=
htons(ifp->if_index)) {
splx(s);
return(EINVAL);
}
if (sa6->sin6_scope_id) {
if (sa6->sin6_scope_id !=
(u_int32_t)ifp->if_index) {
splx(s);
return(EINVAL);
}
sa6->sin6_scope_id = 0;
}
}
ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
if (ia == 0) {
ia = (struct in6_ifaddr *)
MALLOC M_IFADDR, M_WAITOK);
if (ia == NULL) {
splx(s);
return (ENOBUFS);
}
bzero((caddr_t)ia, sizeof(*ia));
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
ia->ia_ifa.ifa_dstaddr
= (struct sockaddr *)&ia->ia_dstaddr;
ia->ia_ifa.ifa_netmask
= (struct sockaddr *)&ia->ia_prefixmask;
ia->ia_ifp = ifp;
if ((oia = in6_ifaddr) != NULL) {
for ( ; oia->ia_next; oia = oia->ia_next)
continue;
oia->ia_next = ia;
} else
in6_ifaddr = ia;
ia->ia_ifa.ifa_refcnt++;
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
if ((ifa = ifp->if_addrlist) != NULL) {
for ( ; ifa->ifa_next; ifa = ifa->ifa_next)
continue;
ifa->ifa_next = &ia->ia_ifa;
} else
ifp->if_addrlist = &ia->ia_ifa;
#else
TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa,
ifa_list);
#endif
ia->ia_ifa.ifa_refcnt++;
}
lt = &ifra->ifra_lifetime;
if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME
&& lt->ia6t_vltime + time_second < time_second) {
splx(s);
return EINVAL;
}
if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME
&& lt->ia6t_pltime + time_second < time_second) {
splx(s);
return EINVAL;
}
prefixIsNew = 0;
hostIsNew = 1;
if (ifra->ifra_addr.sin6_len == 0) {
ifra->ifra_addr = ia->ia_addr;
hostIsNew = 0;
} else if (IN6_ARE_ADDR_EQUAL(&ifra->ifra_addr.sin6_addr,
&ia->ia_addr.sin6_addr))
hostIsNew = 0;
if (ifra->ifra_prefixmask.sin6_len) {
in6_ifscrub(ifp, ia);
ia->ia_prefixmask = ifra->ifra_prefixmask;
prefixIsNew = 1;
}
if ((ifp->if_flags & IFF_POINTOPOINT) &&
(ifra->ifra_dstaddr.sin6_family == AF_INET6)) {
in6_ifscrub(ifp, ia);
oldaddr = ia->ia_dstaddr;
ia->ia_dstaddr = ifra->ifra_dstaddr;
if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_dstaddr.sin6_addr)) {
if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] == 0) {
ia->ia_dstaddr.sin6_addr.s6_addr16[1]
= htons(ifp->if_index);
} else if (ia->ia_dstaddr.sin6_addr.s6_addr16[1] !=
htons(ifp->if_index)) {
ia->ia_dstaddr = oldaddr;
splx(s);
return(EINVAL);
}
}
prefixIsNew = 1;
}
if (ifra->ifra_addr.sin6_family == AF_INET6 &&
(hostIsNew || prefixIsNew))
{
error = in6_ifinit(ifp, ia, &ifra->ifra_addr, 0);
}
if (ifra->ifra_addr.sin6_family == AF_INET6
&& hostIsNew && (ifp->if_flags & IFF_MULTICAST)) {
int error_local = 0;
struct in6_addr llsol;
bzero(&llsol, sizeof(struct in6_addr));
llsol.s6_addr16[0] = htons(0xff02);
llsol.s6_addr16[1] = htons(ifp->if_index);
llsol.s6_addr32[1] = 0;
llsol.s6_addr32[2] = htonl(1);
llsol.s6_addr32[3] =
ifra->ifra_addr.sin6_addr.s6_addr32[3];
llsol.s6_addr8[12] = 0xff;
(void)in6_addmulti(&llsol, ifp, &error_local);
if (error == 0)
error = error_local;
}
ia->ia6_flags = ifra->ifra_flags;
ia->ia6_flags &= ~IN6_IFF_DUPLICATED;
ia->ia6_flags &= ~IN6_IFF_NODAD;
ia->ia6_lifetime = ifra->ifra_lifetime;
if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
ia->ia6_lifetime.ia6t_expire =
time_second + ia->ia6_lifetime.ia6t_vltime;
} else
ia->ia6_lifetime.ia6t_expire = 0;
if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
ia->ia6_lifetime.ia6t_preferred =
time_second + ia->ia6_lifetime.ia6t_pltime;
} else
ia->ia6_lifetime.ia6t_preferred = 0;
switch (ifp->if_type) {
case IFT_ARCNET:
case IFT_ETHER:
case IFT_FDDI:
#if 0
case IFT_ATM:
case IFT_SLIP:
case IFT_PPP:
#endif
if ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) {
ia->ia6_flags |= IN6_IFF_TENTATIVE;
nd6_dad_start((struct ifaddr *)ia, NULL);
}
break;
case IFT_DUMMY:
case IFT_FAITH:
case IFT_GIF:
case IFT_LOOP:
default:
break;
}
if (hostIsNew) {
int iilen;
int error_local = 0;
iilen = (sizeof(ia->ia_prefixmask.sin6_addr) << 3) -
in6_mask2len(&ia->ia_prefixmask.sin6_addr);
error_local = in6_prefix_add_ifid(iilen, ia);
if (error == 0)
error = error_local;
}
splx(s);
return error;
}
int
mip6_tunnel_output(mp, bc)
struct mbuf **mp;
struct mip6_bc *bc;
{
struct sockaddr_in6 dst;
const struct encaptab *ep = bc->ep;
struct mbuf *m = *mp;
struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)&ep->src;
struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)&ep->dst;
struct ip6_hdr *ip6;
u_int8_t itos;
int len;
bzero(&dst, sizeof(dst));
dst.sin6_len = sizeof(struct sockaddr_in6);
dst.sin6_family = AF_INET6;
dst.sin6_addr = bc->coa;
if (ep->af != AF_INET6 || ep->dst.ss_len != dst.sin6_len ||
bcmp(&ep->dst, &dst, dst.sin6_len) != 0 )
return EFAULT;
if (IN6_IS_ADDR_UNSPECIFIED(&sin6_src->sin6_addr)) {
return EFAULT;
}
len = m->m_pkthdr.len;
if (m->m_len < sizeof(*ip6)) {
m = m_pullup(m, sizeof(*ip6));
if (!m)
return ENOBUFS;
}
ip6 = mtod(m, struct ip6_hdr *);
itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
if (m && m->m_len < sizeof(struct ip6_hdr))
m = m_pullup(m, sizeof(struct ip6_hdr));
if (m == NULL) {
#if MIP6_DEBUG
printf("ENOBUFS in mip6_tunnel_output %d\n", __LINE__);
#endif
return ENOBUFS;
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_plen = htons((u_short)len);
ip6->ip6_nxt = IPPROTO_IPV6;
ip6->ip6_hlim = ip6_gif_hlim;
ip6->ip6_src = sin6_src->sin6_addr;
if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
ip6->ip6_dst = sin6_dst->sin6_addr;
else
return ENETUNREACH;
*mp = m;
return 0;
}
int
mip6_tunnel_input(mp, offp, proto)
struct mbuf **mp;
int *offp, proto;
{
struct mbuf *m = *mp;
struct ip6_hdr *ip6;
int s, af = 0;
u_int32_t otos;
ip6 = mtod(m, struct ip6_hdr *);
otos = ip6->ip6_flow;
m_adj(m, *offp);
switch (proto) {
case IPPROTO_IPV6:
{
struct ip6_hdr *ip6;
af = AF_INET6;
if (m->m_len < sizeof(*ip6)) {
m = m_pullup(m, sizeof(*ip6));
if (!m)
return IPPROTO_DONE;
}
m->m_flags |= M_MIP6TUNNEL;
ip6 = mtod(m, struct ip6_hdr *);
s = splimp();
if (IF_QFULL(&ip6intrq)) {
IF_DROP(&ip6intrq);
m_freem(m);
splx(s);
return IPPROTO_DONE;
}
IF_ENQUEUE(&ip6intrq, m);
#if 0
schednetisr(NETISR_IPV6);
#endif
splx(s);
break;
}
default:
#if MIP6_DEBUG
mip6_debug("%s: protocol %d not supported.\n", __FUNCTION__,
proto);
#endif
m_freem(m);
return IPPROTO_DONE;
}
return IPPROTO_DONE;
}
int
mip6_tunnel(ip6_src, ip6_dst, action, start, entry)
struct in6_addr *ip6_src;
struct in6_addr *ip6_dst;
int action;
int start;
void *entry;
{
const struct encaptab *ep;
const struct encaptab **ep_store;
struct sockaddr_in6 src, srcm;
struct sockaddr_in6 dst, dstm;
struct in6_addr mask;
int mask_len = 128;
ep_store = NULL;
if ((start == MIP6_NODE_MN) && (entry != NULL))
ep_store = &((struct mip6_esm *)entry)->ep;
else if ((start == MIP6_NODE_HA) && (entry != NULL))
ep_store = &((struct mip6_bc *)entry)->ep;
else {
#if MIP6_DEBUG
printf("%s: Tunnel not modified\n", __FUNCTION__);
#endif
return 0;
}
if (action == MIP6_TUNNEL_DEL) {
if (ep_store && *ep_store) {
encap_detach(*ep_store);
*ep_store = NULL;
}
return 0;
}
if ((action == MIP6_TUNNEL_ADD) || (action == MIP6_TUNNEL_MOVE)) {
if (action == MIP6_TUNNEL_MOVE && ep_store && *ep_store) {
encap_detach(*ep_store);
*ep_store = NULL;
}
bzero(&src, sizeof(src));
src.sin6_family = AF_INET6;
src.sin6_len = sizeof(struct sockaddr_in6);
src.sin6_addr = *ip6_src;
in6_prefixlen2mask(&mask, mask_len);
bzero(&srcm, sizeof(srcm));
srcm.sin6_family = AF_INET6;
srcm.sin6_len = sizeof(struct sockaddr_in6);
srcm.sin6_addr = mask;
bzero(&dst, sizeof(dst));
dst.sin6_family = AF_INET6;
dst.sin6_len = sizeof(struct sockaddr_in6);
dst.sin6_addr = *ip6_dst;
in6_prefixlen2mask(&mask, mask_len);
bzero(&dstm, sizeof(dstm));
dstm.sin6_family = AF_INET6;
dstm.sin6_len = sizeof(struct sockaddr_in6);
dstm.sin6_addr = mask;
ep = encap_attach(AF_INET6, -1,
(struct sockaddr *)&src,
(struct sockaddr *)&srcm,
(struct sockaddr *)&dst,
(struct sockaddr *)&dstm,
(struct protosw *)&mip6_tunnel_protosw,
NULL);
if (ep == NULL)
return EINVAL;
*ep_store = ep;
return 0;
}
return EINVAL;
}
int
mip6_proxy(struct in6_addr* addr,
struct in6_addr* local,
int cmd)
{
struct sockaddr_in6 mask ;
struct sockaddr_in6 sa6;
struct sockaddr_dl *sdl;
struct ifaddr *ifa;
struct ifnet *ifp;
int flags, error;
struct rtentry *nrt;
if (cmd == RTM_DELETE) {
struct rtentry *rt;
bzero(&sa6, sizeof(sa6));
sa6.sin6_family = AF_INET6;
sa6.sin6_len = sizeof(sa6);
sa6.sin6_addr = *addr;
#ifdef __FreeBSD__ || defined (__APPLE__)
rt = rtalloc1((struct sockaddr *)&sa6, 1, 0UL);
#else
rt = rtalloc1((struct sockaddr *)&sa6, 1);
#endif
if (rt == NULL)
return EHOSTUNREACH;
error = rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0,
rt_mask(rt), 0, (struct rtentry **)0);
rt->rt_refcnt--;
rt = NULL;
return error;
}
bzero(&sa6, sizeof(sa6));
sa6.sin6_family = AF_INET6;
sa6.sin6_len = sizeof(sa6);
sa6.sin6_addr = *local;
ifa = ifa_ifwithaddr((struct sockaddr *)&sa6);
if (ifa == NULL)
return EINVAL;
sa6.sin6_addr = *addr;
ifp = ifa->ifa_ifp;
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa;
ifa = ifa->ifa_list.tqe_next)
#endif
if (ifa->ifa_addr->sa_family == AF_LINK)
break;
if (!ifa)
return EINVAL;
MALLOC(sdl, struct sockaddr_dl *, ifa->ifa_addr->sa_len, M_IFMADDR,
M_WAITOK);
bcopy((struct sockaddr_dl *)ifa->ifa_addr, sdl, ifa->ifa_addr->sa_len);
bzero(&mask, sizeof(mask));
mask.sin6_family = AF_INET6;
mask.sin6_len = sizeof(mask);
in6_len2mask(&mask.sin6_addr, 128);
flags = (RTF_STATIC | RTF_ANNOUNCE | RTA_NETMASK);
error = rtrequest(RTM_ADD, (struct sockaddr *)&sa6,
(struct sockaddr *)sdl,
(struct sockaddr *)&mask, flags, &nrt);
if (error == 0) {
if (nrt) {
nrt->rt_rmx.rmx_expire = 0;
nrt->rt_genmask = NULL;
nrt->rt_refcnt--;
}
else
error = EINVAL;
}
_FREE(sdl, M_IFMADDR);
return error;
}
struct mip6_bc *
mip6_bc_find(home_addr)
struct in6_addr *home_addr;
{
struct mip6_bc *bcp;
for (bcp = mip6_bcq; bcp; bcp = bcp->next) {
if (IN6_ARE_ADDR_EQUAL(home_addr, &bcp->home_addr))
return bcp;
}
return NULL;
}
struct mip6_bc *
mip6_bc_create(home_addr, coa, lifetime, hr, rtr, prefix_len, seqno)
struct in6_addr *home_addr;
struct in6_addr *coa;
u_int32_t lifetime;
u_int8_t hr;
u_int8_t rtr;
u_int8_t prefix_len;
u_int16_t seqno;
{
struct mip6_bc *bcp;
int s;
bcp = (struct mip6_bc *)MALLOC mip6_bc),
M_TEMP, M_WAITOK);
if (bcp == NULL)
return NULL;
bzero((caddr_t)bcp, sizeof(struct mip6_bc));
bcp->next = NULL;
bcp->home_addr = *home_addr;
bcp->coa = *coa;
bcp->lifetime = lifetime;
bcp->hr_flag = hr;
bcp->prefix_len = prefix_len;
bcp->seqno = seqno;
bcp->lasttime = 0;
bcp->ep = NULL;
if (bcp->hr_flag)
bcp->rtr_flag = rtr;
else {
bcp->rtr_flag = 0;
if (mip6_config.br_update > 60)
bcp->info.br_interval = 60;
else if (mip6_config.br_update < 2)
bcp->info.br_interval = 2;
else
bcp->info.br_interval = mip6_config.br_update;
}
s = splnet();
if (mip6_bcq == NULL) {
mip6_bcq = bcp;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_bc_handle =
#endif
timeout(mip6_timer_bc, (void *)0, hz);
} else {
bcp->next = mip6_bcq;
mip6_bcq = bcp;
}
splx(s);
#if MIP6_DEBUG
mip6_debug("\nBinding Cache Entry created (0x%x)\n", bcp);
mip6_debug("Home Addr/Prefix len: %s / %u\n",
ip6_sprintf(&bcp->home_addr), bcp->prefix_len);
mip6_debug("Care-of Address: %s\n", ip6_sprintf(&bcp->coa));
mip6_debug("Remaining lifetime: %u\n", bcp->lifetime);
mip6_debug("Sequence number: %u\n", bcp->seqno);
mip6_debug("Home reg/Router: ");
if (bcp->hr_flag)
mip6_debug("TRUE / ");
else
mip6_debug("FALSE / ");
if (bcp->rtr_flag)
mip6_debug("TRUE\n");
else
mip6_debug("FALSE\n");
#endif
return bcp;
}
void
mip6_bc_update(bcp, coa, lifetime, hr, rtr, prefix_len, seqno, info, lasttime)
struct mip6_bc *bcp;
struct in6_addr *coa;
u_int32_t lifetime;
u_int8_t hr;
u_int8_t rtr;
u_int8_t prefix_len;
u_int16_t seqno;
struct bc_info info;
time_t lasttime;
{
bcp->coa = *coa;
bcp->lifetime = lifetime;
bcp->hr_flag = hr;
bcp->prefix_len = prefix_len;
bcp->seqno = seqno;
if (bcp->hr_flag) {
bcp->rtr_flag = rtr;
bzero((caddr_t)&bcp->info, sizeof(struct bc_info));
} else {
bcp->rtr_flag = 0;
if (info.br_interval > 60)
bcp->info.br_interval = 60;
else if (info.br_interval < 2)
bcp->info.br_interval = 2;
else
bcp->info.br_interval = info.br_interval;
}
bcp->lasttime = lasttime;
#if MIP6_DEBUG
mip6_debug("\nBinding Cache Entry updated (0x%x)\n", bcp);
mip6_debug("Home Addr/Prefix len: %s / %u\n",
ip6_sprintf(&bcp->home_addr), bcp->prefix_len);
mip6_debug("Care-of Address: %s\n", ip6_sprintf(&bcp->coa));
mip6_debug("Remaining lifetime: %u\n", bcp->lifetime);
mip6_debug("Sequence number: %u\n", bcp->seqno);
mip6_debug("Home reg/Router: ");
if (bcp->hr_flag)
mip6_debug("TRUE / ");
else
mip6_debug("FALSE / ");
if (bcp->rtr_flag)
mip6_debug("TRUE\n");
else
mip6_debug("FALSE\n");
#endif
return;
}
int
mip6_bc_delete(bcp_del, bcp_nxt)
struct mip6_bc *bcp_del;
struct mip6_bc **bcp_nxt;
{
struct mip6_bc *bcp;
struct mip6_bc *bcp_prev;
struct mip6_bc *bcp_next;
int s, error = 0;
s = splnet();
bcp_prev = NULL;
bcp_next = NULL;
for (bcp = mip6_bcq; bcp; bcp = bcp->next) {
bcp_next = bcp->next;
if (bcp != bcp_del) {
bcp_prev = bcp;
continue;
}
if (bcp_prev == NULL)
mip6_bcq = bcp->next;
else
bcp_prev->next = bcp->next;
if (bcp->hr_flag) {
error = mip6_proxy(&bcp->home_addr, NULL, RTM_DELETE);
if (error) {
#if MIP6_DEBUG
mip6_debug("%s: delete proxy error = %d\n",
__FUNCTION__, error);
#endif
*bcp_nxt = bcp_next;
return error;
}
mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL, MIP6_NODE_HA,
(void *)bcp);
}
#if MIP6_DEBUG
mip6_debug("\nBinding Cache Entry deleted (0x%x)\n", bcp);
#endif
_FREE(bcp, M_TEMP);
if (mip6_bcq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_bc, (void *)NULL,
mip6_timer_bc_handle);
callout_handle_init(&mip6_timer_bc_handle);
#else
untimeout(mip6_timer_bc, (void *)NULL);
#endif
}
break;
}
splx(s);
*bcp_nxt = bcp_next;
return error;
}
struct mip6_na *
mip6_na_create(home_addr, dst_addr, target_addr, prefix_len,
flags, use_link_opt)
struct in6_addr *home_addr;
struct in6_addr *dst_addr;
struct in6_addr *target_addr;
u_int8_t prefix_len;
u_long flags;
int use_link_opt;
{
struct mip6_na *nap;
struct mip6_link_list *llp;
struct mip6_ha_list *halp;
struct mip6_addr_list *addrp;
struct nd_prefix *pr;
int s, start_timer = 0;
llp = NULL;
halp = NULL;
addrp = NULL;
pr = NULL;
if (mip6_naq == NULL)
start_timer = 1;
nap = (struct mip6_na *)MALLOC mip6_na),
M_TEMP, M_WAITOK);
if (nap == NULL)
return NULL;
bzero(nap, sizeof(struct mip6_na));
nap->next = NULL;
nap->home_addr = *home_addr;
nap->dst_addr = *dst_addr;
nap->target_addr = *target_addr;
nap->prefix_len = prefix_len;
nap->flags = flags;
nap->use_link_opt = use_link_opt;
nap->no = MIP6_MAX_ADVERT_REXMIT;
if (MIP6_IS_HA_ACTIVE) {
for (llp = mip6_llq; llp; llp = llp->next) {
for (halp = llp->ha_list; halp; halp = halp->next) {
for (addrp = halp->addr_list; addrp;
addrp = addrp->next) {
if (in6_are_prefix_equal(
home_addr,
&addrp->ip6_addr,
addrp->prefix_len))
break;
}
if (addrp != NULL)
break;
}
if (addrp != NULL)
break;
}
if (addrp == NULL) {
log(LOG_ERR,
"%s: No interface found for sending Neighbor "
"Advertisements at\n", __FUNCTION__);
return NULL;
}
nap->ifp = llp->ifp;
}
if (MIP6_IS_MN_ACTIVE) {
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if (!pr->ndpr_stateflags.onlink)
continue;
if (in6_are_prefix_equal(home_addr,
&pr->ndpr_prefix.sin6_addr,
pr->ndpr_plen))
break;
}
if (pr == NULL) {
log(LOG_ERR,
"%s: No interface found for sending Neighbor "
"Advertisements at\n", __FUNCTION__);
return NULL;
}
nap->ifp = pr->ndpr_ifp;
}
s = splnet();
nap->next = mip6_naq;
mip6_naq = nap;
splx(s);
#if MIP6_DEBUG
mip6_debug("\nCreated Neighbor Advertisement List entry (0x%x)\n",
nap);
mip6_debug("Interface being used: %s\n", if_name(nap->ifp));
mip6_debug("Home Addr/Prefix len: %s / %d\n",
ip6_sprintf(&nap->home_addr), nap->prefix_len);
mip6_debug("Destination Address: %s\n", ip6_sprintf(&nap->dst_addr));
mip6_debug("Target Address: %s\n",
ip6_sprintf(&nap->target_addr));
if (nap->use_link_opt)
mip6_debug("Incl Target ll_addr : TRUE\n");
else
mip6_debug("Incl Target ll_addr : FALSE\n");
#endif
mip6_send_na(nap);
if (start_timer) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_na_handle =
#endif
timeout(mip6_timer_na, (void *)0, hz);
}
return nap;
}
struct mip6_na *
mip6_na_delete(nap_del)
struct mip6_na *nap_del;
{
struct mip6_na *nap;
struct mip6_na *nap_prev;
struct mip6_na *nap_next;
int s;
s = splnet();
nap_prev = NULL;
nap_next = NULL;
for (nap = mip6_naq; nap; nap = nap->next) {
nap_next = nap->next;
if (nap == nap_del) {
if (nap_prev == NULL)
mip6_naq = nap->next;
else
nap_prev->next = nap->next;
#if MIP6_DEBUG
mip6_debug("\nNeighbor Advertisement Entry "
"deleted (0x%x)\n", nap);
#endif
_FREE(nap, M_TEMP);
if (mip6_naq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_na, (void *)NULL,
mip6_timer_na_handle);
callout_handle_init(&mip6_timer_na_handle);
#else
untimeout(mip6_timer_na, (void *)NULL);
#endif
}
break;
}
nap_prev = nap;
}
splx(s);
return nap_next;
}
struct mip6_prefix *
mip6_prefix_find(prefix, prefix_len)
struct in6_addr *prefix;
u_int8_t prefix_len;
{
struct mip6_prefix *pq;
for (pq = mip6_pq; pq; pq = pq->next) {
if (in6_are_prefix_equal(&pq->prefix, prefix, prefix_len))
return pq;
}
return NULL;
}
struct mip6_prefix *
mip6_prefix_create(ifp, prefix, prefix_len, valid_time)
struct ifnet *ifp;
struct in6_addr *prefix;
u_int8_t prefix_len;
u_int32_t valid_time;
{
struct mip6_prefix *pq;
int s, start_timer = 0;
if (mip6_pq == NULL)
start_timer = 1;
pq = (struct mip6_prefix *)MALLOC mip6_prefix),
M_TEMP, M_WAITOK);
if (pq == NULL)
return NULL;
bzero(pq, sizeof(struct mip6_prefix));
s = splnet();
pq->next = mip6_pq;
pq->ifp = ifp;
pq->prefix = *prefix;
pq->prefix_len = prefix_len;
pq->valid_time = valid_time;
mip6_pq = pq;
splx(s);
#if MIP6_DEBUG
mip6_debug("\nInternal Prefix list entry created (0x%x)\n", pq);
mip6_debug("Interface: %s\n", if_name(ifp));
mip6_debug("Prefix: %s\n", ip6_sprintf(&pq->prefix));
mip6_debug("Prefix len: %d\n", pq->prefix_len);
mip6_debug("Life time: %d\n", htonl(pq->valid_time));
#endif
if (start_timer) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_prefix_handle =
#endif
timeout(mip6_timer_prefix, (void *)0, hz);
}
return pq;
}
struct mip6_prefix *
mip6_prefix_delete(pre_del)
struct mip6_prefix *pre_del;
{
struct mip6_prefix *pre;
struct mip6_prefix *pre_prev;
struct mip6_prefix *pre_next;
int s;
s = splnet();
pre_next = NULL;
pre_prev = NULL;
for (pre = mip6_pq; pre; pre = pre->next) {
pre_next = pre->next;
if (pre == pre_del) {
if (pre_prev == NULL)
mip6_pq = pre->next;
else
pre_prev->next = pre->next;
#if MIP6_DEBUG
mip6_debug("\nMIPv6 prefix entry deleted (0x%x)\n", pre);
#endif
_FREE(pre, M_TEMP);
if (mip6_pq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_prefix, (void *)NULL,
mip6_timer_prefix_handle);
callout_handle_init(&mip6_timer_prefix_handle);
#else
untimeout(mip6_timer_prefix, (void *)NULL);
#endif
}
break;
}
pre_prev = pre;
}
splx(s);
return pre_next;
}
void
mip6_timer_na(arg)
void *arg;
{
struct mip6_na *nap;
int s;
#ifdef __APPLE__
boolean_t funnel_state;
funnel_state = thread_funnel_set(network_flock, TRUE);
#endif
s = splnet();
for (nap = mip6_naq; nap;) {
mip6_send_na(nap);
if (nap->no <= 0)
nap = mip6_na_delete(nap);
else
nap = nap->next;
}
splx(s);
if (mip6_naq != NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_na_handle =
#endif
timeout(mip6_timer_na, (void *)0, hz);
}
#ifdef __APPLE__
(void) thread_funnel_set(network_flock, FALSE);
#endif
}
void
mip6_timer_bc(arg)
void *arg;
{
struct mip6_bc *bcp;
struct mip6_bc *bcp_nxt;
int s;
#ifdef __APPLE__
boolean_t funnel_state;
funnel_state = thread_funnel_set(network_flock, TRUE);
#endif
s = splnet();
for (bcp = mip6_bcq; bcp;) {
bcp->lifetime -= 1;
if (bcp->lifetime == 0) {
mip6_bc_delete(bcp, &bcp_nxt);
bcp = bcp_nxt;
} else
bcp = bcp->next;
}
splx(s);
if (mip6_bcq != NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_bc_handle =
#endif
timeout(mip6_timer_bc, (void *)0, hz);
}
#ifdef __APPLE__
(void) thread_funnel_set(network_flock, FALSE);
#endif
return;
}
void
mip6_timer_prefix_funneled(arg)
void *arg;
{
#ifdef __APPLE__
boolean_t funnel_state;
funnel_state = thread_funnel_set(network_flock, TRUE);
#endif
mip6_timer_prefix(arg);
#ifdef __APPLE__
(void) thread_funnel_set(network_flock, FALSE);
#endif
}
void
mip6_timer_prefix(arg)
void *arg;
{
struct mip6_prefix *pq_entry;
int s;
s = splnet();
for (pq_entry = mip6_pq; pq_entry;) {
pq_entry->valid_time -= 1;
if (pq_entry->valid_time == 0)
pq_entry = mip6_prefix_delete(pq_entry);
else
pq_entry = pq_entry->next;
}
splx(s);
if (mip6_pq != NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_prefix_handle =
#endif
timeout(mip6_timer_prefix, (void *)0, hz);
}
return;
}
int
#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3)
mip6_ioctl(so, cmd, data, ifp, p)
struct socket *so;
u_long cmd;
caddr_t data;
struct ifnet *ifp;
struct proc *p;
#else
mip6_ioctl(so, cmd, data, ifp)
struct socket *so;
u_long cmd;
caddr_t data;
struct ifnet *ifp;
#endif
{
int res;
res = 0;
switch (cmd) {
case SIOCSBCFLUSH_MIP6:
case SIOCSDEFCONFIG_MIP6:
res = mip6_clear_config_data(cmd, data);
return res;
case SIOCSBRUPDATE_MIP6:
res = mip6_write_config_data(cmd, data);
return res;
case SIOCSHAPREF_MIP6:
if (mip6_write_config_data_ha_hook)
res = (*mip6_write_config_data_ha_hook)
(cmd, data);
break;
case SIOCACOADDR_MIP6:
case SIOCAHOMEADDR_MIP6:
case SIOCSBULIFETIME_MIP6:
case SIOCSHRLIFETIME_MIP6:
case SIOCDCOADDR_MIP6:
if (mip6_write_config_data_mn_hook)
res = (*mip6_write_config_data_mn_hook)
(cmd, data);
break;
case SIOCSDEBUG_MIP6:
case SIOCSENABLEBR_MIP6:
case SIOCSATTACH_MIP6:
res = mip6_enable_func(cmd, data);
return res;
case SIOCSFWDSLUNICAST_MIP6:
case SIOCSFWDSLMULTICAST_MIP6:
if (mip6_enable_func_ha_hook)
res = (*mip6_enable_func_ha_hook)(cmd, data);
break;
case SIOCSPROMMODE_MIP6:
case SIOCSBU2CN_MIP6:
case SIOCSREVTUNNEL_MIP6:
case SIOCSAUTOCONFIG_MIP6:
case SIOCSEAGERMD_MIP6:
if (mip6_enable_func_mn_hook)
res = (*mip6_enable_func_mn_hook)(cmd, data);
break;
case SIOCSRELEASE_MIP6:
mip6_release();
return res;
default:
res = EOPNOTSUPP;
break;
}
if (MIP6_IS_HA_ACTIVE) {
res = 0;
switch (cmd) {
case SIOCSHALISTFLUSH_MIP6:
if (mip6_clear_config_data_ha_hook)
res = (*mip6_clear_config_data_ha_hook)
(cmd, data);
break;
default:
res = EOPNOTSUPP;
break;
}
}
if (MIP6_IS_MN_ACTIVE) {
res = 0;
switch (cmd) {
case SIOCSFORADDRFLUSH_MIP6:
case SIOCSHADDRFLUSH_MIP6:
case SIOCSBULISTFLUSH_MIP6:
if (mip6_clear_config_data_mn_hook)
res = (*mip6_clear_config_data_mn_hook)
(cmd, data);
break;
default:
res = EOPNOTSUPP;
break;
}
}
if (res) {
#if MIP6_DEBUG
printf("%s: unknown command: %lu\n", __FUNCTION__, (u_long)cmd);
#endif
}
return res;
}
#if MIP6_DEBUG
void mip6_debug(char *fmt, ...)
{
#ifndef __bsdi__
va_list ap;
if (!mip6_debug_is_enabled)
return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
#endif
}
void
mip6_enable_debug(int status)
{
mip6_debug_is_enabled = status;
}
#endif
int mip6_write_config_data(u_long cmd, caddr_t data)
{
int retval = 0;
switch (cmd) {
case SIOCSBRUPDATE_MIP6:
mip6_config.br_update = *(u_int8_t *)data;
break;
}
return retval;
}
int mip6_clear_config_data(u_long cmd, caddr_t data)
{
int s, retval = 0;
struct mip6_bc *bcp, *bcp_nxt;
s = splnet();
switch (cmd) {
case SIOCSBCFLUSH_MIP6:
for (bcp = mip6_bcq; bcp;) {
if(!bcp->hr_flag) {
mip6_bc_delete(bcp, &bcp_nxt);
bcp = bcp_nxt;
} else
bcp = bcp->next;
}
break;
case SIOCSDEFCONFIG_MIP6:
mip6_config.bu_lifetime = 600;
mip6_config.br_update = 60;
mip6_config.hr_lifetime = 3600;
mip6_config.enable_outq = 1;
break;
}
splx(s);
return retval;
}
int mip6_enable_func(u_long cmd, caddr_t data)
{
int enable;
int retval = 0;
enable = ((struct mip6_input_data *)data)->value;
switch (cmd) {
case SIOCSDEBUG_MIP6:
#if MIP6_DEBUG
mip6_enable_debug(enable);
#else
printf("No Mobile IPv6 debug information available!\n");
#endif
break;
case SIOCSENABLEBR_MIP6:
mip6_config.enable_br = enable;
break;
case SIOCSATTACH_MIP6:
printf("%s: attach %d\n", __FUNCTION__, enable);
retval = mip6_attach(enable);
break;
}
return retval;
}