#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
#include "opt_inet.h"
#endif
#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet6/in6_var.h>
#include <netinet6/ip6_var.h>
#include <netinet6/ip6protosw.h>
#include <netinet6/nd6.h>
#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__)
#include <netinet/in_pcb.h>
#endif
#include <netinet6/in6_pcb.h>
#include <netinet6/mip6.h>
#include <net/net_osdep.h>
struct nd_prefix *mip6_home_prefix;
struct nd_prefix *mip6_primary_prefix;
struct in6_addr mip6_primary_defrtr;
int mip6_md_state = MIP6_MD_UNDEFINED;
int mip6_route_state = MIP6_ROUTE_NET;
int mip6_max_lost_advints = MIP6_MAX_LOST_ADVINTS;
int mip6_nd6_delay = 0;
int mip6_nd6_umaxtries = 0;
static void
mip6_tell_em(int state,
struct nd_prefix *hp,
struct nd_prefix *pp,
struct nd_defrouter *dr)
{
#if MIP6_DEBUG
mip6_debug("\nNew state: ");
switch (state) {
case MIP6_MD_HOME:
mip6_debug("HOME!\n");
break;
case MIP6_MD_FOREIGN:
mip6_debug("FOREIGN!\n");
break;
case MIP6_MD_UNDEFINED:
mip6_debug("UNDEFINED!\n");
break;
}
mip6_debug("Home Prefix = %s\n", hp ? ip6_sprintf(
&hp->ndpr_prefix.sin6_addr) : "NULL");
mip6_debug("Primary Prefix = %s\n", pp ? ip6_sprintf(
&pp->ndpr_prefix.sin6_addr) : "NULL");
mip6_debug("Default Router = %s\n", dr ? ip6_sprintf(
&dr->rtaddr) : "NULL");
#endif
mip6_new_defrtr(state, hp, pp, dr);
}
void
mip6_md_init()
{
struct nd_prefix *pr, *existing_pr = NULL;
struct nd_defrouter *dr;
struct in6_ifaddr *ia;
struct mip6_esm *esp;
int i, s, error;
for (esp = mip6_esmq; esp; esp = esp->next) {
pr = (struct nd_prefix *)MALLOC(sizeof(*pr), M_TEMP, M_WAITOK);
if (pr == NULL) {
log(LOG_ERR, "mip6_md_init: no mem for home prefix\n");
} else {
bzero(pr, sizeof(*pr));
pr->ndpr_ifp = esp->ifp;
pr->ndpr_plen = esp->prefix_len;
pr->ndpr_prefix.sin6_family = AF_INET6;
pr->ndpr_prefix.sin6_len = sizeof(pr->ndpr_prefix);
pr->ndpr_prefix.sin6_addr = esp->home_addr;
in6_prefixlen2mask(&pr->ndpr_mask, pr->ndpr_plen);
for (i = 0; i < 4; i++)
pr->ndpr_prefix.sin6_addr.s6_addr32[i] &=
pr->ndpr_mask.s6_addr32[i];
pr->ndpr_raf_onlink = 0;
pr->ndpr_raf_auto = 0;
if ( (existing_pr = prefix_lookup(pr)) ) {
_FREE(pr, M_TEMP);
pr = existing_pr;
}
pr->ndpr_vltime = ND6_INFINITE_LIFETIME;
pr->ndpr_pltime = ND6_INFINITE_LIFETIME;
if (in6_init_prefix_ltimes(pr)) {
log(LOG_ERR, "mip6_md_init: bad lifetimes\n");
goto failure;
}
s = splnet();
if (existing_pr != NULL) {
#if MIP6_DEBUG
mip6_debug("mip6_md_init: Home prefix already exists, "
"no need to create new prefix.\n");
#endif
goto skip_initialization;
}
pr->ndpr_statef_onlink = 0;
LIST_INIT(&pr->ndpr_advrtrs);
skip_initialization:
if (existing_pr != NULL) {
if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) {
ia = in6ifa_ifpwithaddr(pr->ndpr_ifp,
&pr->ndpr_addr);
if (ia) {
error = mip6_delete_ifaddr(
&ia->ia_addr.sin6_addr,
pr->ndpr_ifp);
if (error)
printf("%s: address assignment"
" error "
"(errno = %d).\n",
__FUNCTION__, error);
}
}
}
pr->ndpr_addr = esp->home_addr;
if (existing_pr == NULL) {
LIST_INSERT_HEAD(&nd_prefix, pr, ndpr_entry);
}
splx(s);
}
if (esp != mip6_esmq) {
#if MIP6_DEBUG
mip6_debug("%s: Only supporting one home address in this "
"version.\n", __FUNCTION__);
#endif
}
mip6_home_prefix = pr;
dr = TAILQ_FIRST(&nd_defrouter);
if (pr->ndpr_advrtrs.lh_first && dr &&
pfxrtr_lookup(pr, dr)) {
mip6_md_state = MIP6_MD_HOME;
if ((error = mip6_add_ifaddr(&pr->ndpr_addr, pr->ndpr_ifp, 64,
IN6_IFF_NODAD)) != 0)
printf("%s: address assignment error (errno = %d).\n",
__FUNCTION__, error);
mip6_route_state = MIP6_ROUTE_NET;
mip6_primary_prefix = mip6_home_prefix;
mip6_primary_defrtr = dr->rtaddr;
mip6_tell_em(MIP6_MD_HOME, mip6_home_prefix, NULL, dr);
}
else {
if (dr) {
mip6_md_state = MIP6_MD_FOREIGN;
if ((error = mip6_add_ifaddr(
&pr->ndpr_addr, pr->ndpr_ifp, 128,
IN6_IFF_NODAD)) != 0)
printf("%s: address assignment error "
"(errno = %d).\n",
__FUNCTION__, error);
mip6_route_state = MIP6_ROUTE_HOST;
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if ((pfxrtr_lookup(pr, dr) != NULL) &&
!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)&&
!IN6_IS_ADDR_MULTICAST(&pr->ndpr_addr) &&
!IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_addr)) {
break;
}
}
if (pr) {
mip6_primary_prefix = pr;
mip6_primary_defrtr = dr->rtaddr;
mip6_tell_em(MIP6_MD_FOREIGN, mip6_home_prefix,
pr, dr);
}
else {
#if MIP6_DEBUG
mip6_debug("%s: At FOREIGN, but no primary "
"prefix found!\n", __FUNCTION__);
#endif
goto undefined;
}
}
else {
undefined:
mip6_md_state = MIP6_MD_UNDEFINED;
if ((error = mip6_add_ifaddr(&pr->ndpr_addr,
pr->ndpr_ifp, 64,
IN6_IFF_NODAD)) != 0)
printf("%s: address assignment error "
"(errno = %d).\n", __FUNCTION__, error);
mip6_route_state = MIP6_ROUTE_NET;
mip6_primary_defrtr = in6addr_any;
mip6_primary_prefix = NULL;
mip6_tell_em(MIP6_MD_UNDEFINED, mip6_home_prefix,
NULL, NULL);
}
}
failure:
}
}
void
mip6_select_defrtr()
{
struct nd_prefix *pr = NULL;
struct nd_defrouter *dr, anydr;
struct nd_pfxrouter *pfxrtr;
struct rtentry *rt = NULL;
struct llinfo_nd6 *ln = NULL;
int s = splnet(), error, state;
pr = mip6_primary_prefix;
dr = mip6_primary_prefix ?
defrouter_lookup(&mip6_primary_defrtr,
mip6_primary_prefix->ndpr_ifp) : NULL;
state = mip6_md_state;
#if MIP6_DEBUG
mip6_debug("\n");
#endif
#if MIP6_DEBUG
mip6_debug("%s: previous primary dr = %s.\n", __FUNCTION__,
ip6_sprintf(&mip6_primary_defrtr));
mip6_debug("%s: dr = %s.\n", __FUNCTION__,
dr ? ip6_sprintf(&dr->rtaddr) : "NULL");
#endif
if ( (mip6_md_state == MIP6_MD_HOME) ||
(mip6_md_state == MIP6_MD_UNDEFINED) ) {
if ((pr = mip6_home_prefix) == NULL){
log(LOG_ERR, "mip6_select_defrtr: no home prefix\n");
splx(s);
return;
}
if ((pfxrtr = find_pfxlist_reachable_router(pr)) != NULL) {
#if MIP6_DEBUG
mip6_debug("%s: there are (reachable) pfxrtrs at "
"home.\n", __FUNCTION__);
#endif
if (!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr) &&
!(IN6_IS_ADDR_MULTICAST(&pr->ndpr_addr) ||
IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_addr))) {
state = MIP6_MD_HOME;
dr = pfxrtr->router;
TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry);
#if MIP6_DEBUG
mip6_debug("%s: picking %s as default router "
"on home subnet.\n",
__FUNCTION__,
ip6_sprintf(&(dr->rtaddr)));
#endif
goto found;
}
}
if (pr->ndpr_advrtrs.lh_first == NULL) {
#if MIP6_DEBUG
mip6_debug("%s: there are no pfxrtrs at home, trying "
"non-home instead.\n", __FUNCTION__);
#endif
}
#if MIP6_DEBUG
mip6_debug("%s: no home prefix router found.\n", __FUNCTION__);
#endif
}
if (TAILQ_FIRST(&nd_defrouter)) {
for (dr = TAILQ_FIRST(&nd_defrouter); dr;
dr = TAILQ_NEXT(dr, dr_entry)) {
if ((rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
ND6_IS_LLINFO_PROBREACH(ln)) {
for (pr = nd_prefix.lh_first; pr;
pr = pr->ndpr_next) {
if ((pfxrtr_lookup(pr, dr) != NULL) &&
!IN6_IS_ADDR_UNSPECIFIED(
&pr->ndpr_addr) &&
!IN6_IS_ADDR_MULTICAST(
&pr->ndpr_addr) &&
!IN6_IS_ADDR_LINKLOCAL(
&pr->ndpr_addr)) {
state = MIP6_MD_FOREIGN;
#if MIP6_DEBUG
mip6_debug("%s: new probably reachable defrtr %s on foreign subnet selected.\n", __FUNCTION__, ip6_sprintf(&dr->rtaddr));
#endif
TAILQ_REMOVE(&nd_defrouter,
dr, dr_entry);
TAILQ_INSERT_HEAD(
&nd_defrouter,
dr, dr_entry);
goto found;
}
}
}
}
for(dr = TAILQ_FIRST(&nd_defrouter); dr; dr = TAILQ_NEXT(dr, dr_entry)){
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if ((pfxrtr_lookup(pr, dr) != NULL) &&
!IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)&&
!IN6_IS_ADDR_MULTICAST(&pr->ndpr_addr) &&
!IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_addr)) {
state = MIP6_MD_FOREIGN;
#if MIP6_DEBUG
mip6_debug("%s: new (unreachable?) "
"defrtr %s on foreign subnet "
"selected.\n", __FUNCTION__,
ip6_sprintf(&dr->rtaddr));
#endif
TAILQ_REMOVE(&nd_defrouter, dr,
dr_entry);
TAILQ_INSERT_HEAD(&nd_defrouter, dr,
dr_entry);
goto found;
}
}
}
}
pr = NULL;
dr = NULL;
state = MIP6_MD_UNDEFINED;
#if MIP6_DEBUG
mip6_debug("%s: no new good defrtr found.\n", __FUNCTION__);
#endif
found:
#if MIP6_DEBUG
mip6_debug("%s: found: dr = %s.\n", __FUNCTION__, dr ? ip6_sprintf(&dr->rtaddr) : "NULL");
#endif
if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
#if MIP6_DEBUG
mip6_debug("%s: TAILQ: dr = %s.\n", __FUNCTION__, dr ? ip6_sprintf(&dr->rtaddr) : "NULL");
#endif
bzero(&anydr, sizeof(anydr));
defrouter_delreq(&anydr, 0);
defrouter_addreq(dr);
}
else {
if (!ip6_forwarding) {
bzero(&anydr, sizeof(anydr));
defrouter_delreq(&anydr, 0);
if (nd6_defifp) {
defrouter_addifreq(nd6_defifp);
}
else
log(LOG_INFO, "defrouter_select: "
"there's no default router and no default"
" interface\n");
}
}
if (dr) {
struct nd_pfxrouter *pfxrtr;
pfxrtr = pfxrtr_lookup(mip6_home_prefix, dr);
if (pfxrtr && dr == pfxrtr->router) {
#if MIP6_DEBUG
mip6_debug("%s: dr = %s is obviously a home pfxrtr.\n", __FUNCTION__, dr ? ip6_sprintf(&dr->rtaddr) : "NULL");
#endif
state = MIP6_MD_HOME;
pr = mip6_home_prefix;
}
}
if (IN6_ARE_ADDR_EQUAL(&mip6_primary_defrtr,
(dr ? &dr->rtaddr : &in6addr_any)) &&
!(dr && mip6_primary_prefix == NULL)) {
#if MIP6_DEBUG
mip6_debug("%s: Warning: Primary default router hasn't "
"changed! No action taken.\n", __FUNCTION__);
#endif
return;
}
if ((state == MIP6_MD_HOME || state == MIP6_MD_UNDEFINED)
&& mip6_route_state == MIP6_ROUTE_HOST) {
error = mip6_add_ifaddr(&mip6_home_prefix->ndpr_addr,
mip6_home_prefix->ndpr_ifp, 64,
IN6_IFF_NODAD);
if (error)
printf("%s: address assignment error (errno = %d).\n",
__FUNCTION__, error);
mip6_route_state = MIP6_ROUTE_NET;
}
else if (state == MIP6_MD_FOREIGN &&
mip6_route_state == MIP6_ROUTE_NET) {
error = mip6_add_ifaddr(&mip6_home_prefix->ndpr_addr,
mip6_home_prefix->ndpr_ifp, 128,
IN6_IFF_NODAD);
if (error)
printf("%s: address assignment error (errno = %d).\n",
__FUNCTION__, error);
mip6_route_state = MIP6_ROUTE_HOST;
}
#if MIP6_DEBUG
mip6_debug("mip6_primary_prefix = %s\n", mip6_primary_prefix ? ip6_sprintf(&mip6_primary_prefix->ndpr_prefix.sin6_addr) : "NULL");
mip6_debug("pr = %s\n", pr ? ip6_sprintf(&pr->ndpr_prefix.sin6_addr) : "NULL");
#endif
if (mip6_primary_prefix && (pr != mip6_primary_prefix)) {
register struct llinfo_nd6 *ln;
ln = llinfo_nd6.ln_next;
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt;
struct ifnet *ifp;
struct sockaddr_in6 *dst;
struct llinfo_nd6 *next = ln->ln_next;
if ((rt = ln->ln_rt) == NULL) {
ln = next;
continue;
}
if ((ifp = rt->rt_ifp) == NULL) {
ln = next;
continue;
}
dst = (struct sockaddr_in6 *)rt_key(rt);
if (!rt)
panic("rt=0 in %s(ln=%p)\n", __FUNCTION__, ln);
if (!dst)
panic("dst=0 in %s(ln=%p)\n", __FUNCTION__, ln);
if (ln->ln_expire == 0) {
ln = next;
continue;
}
#if MIP6_DEBUG
mip6_debug("Checking neighbor %s\n", dst ? ip6_sprintf(&dst->sin6_addr) : "NULL");
#endif
if (in6_are_prefix_equal(&dst->sin6_addr,
&mip6_primary_prefix->
ndpr_prefix.sin6_addr,
mip6_primary_prefix->
ndpr_plen)) {
struct mbuf *m = ln->ln_hold;
if (m) {
m_freem(m);
}
ln->ln_hold = NULL;
#if MIP6_DEBUG
mip6_debug("Deleting Neighbor %s.\n",
ip6_sprintf(&(satosin6(
rt_key(rt))->sin6_addr)));
#endif
#if IPSEC
#ifndef __OpenBSD__
key_sa_routechange(rt_key(rt));
#endif
#endif
#if MIP6_DEBUG
mip6_debug("Ref count = %d, now pfctlinput\n",
rt->rt_refcnt);
#endif
pfctlinput(PRC_REDIRECT_HOST, rt_key(rt));
#if 0
#if MIP6_DEBUG
mip6_debug("Ref count = %d, now rt_mip6msg\n",
rt->rt_refcnt);
#endif
rt_mip6msg(RTM_DELETE, ifp, rt);
#endif
#if MIP6_DEBUG
mip6_debug("Ref count = %d, now RTM_DELETE\n",
rt->rt_refcnt);
#endif
nd6_free(rt);
}
ln = next;
}
ln = llinfo_nd6.ln_next;
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt;
struct ifnet *ifp;
struct sockaddr_dl *sdl;
struct sockaddr_in6 *dst;
struct llinfo_nd6 *next = ln->ln_next;
if ((rt = ln->ln_rt) == NULL) {
ln = next;
continue;
}
if ((ifp = rt->rt_ifp) == NULL) {
ln = next;
continue;
}
dst = (struct sockaddr_in6 *)rt_key(rt);
if (!rt)
panic("rt=0 in %s(ln=%p)\n", __FUNCTION__, ln);
if (!dst)
panic("dst=0 in %s(ln=%p)\n", __FUNCTION__, ln);
if (ln->ln_expire == 0) {
ln = next;
continue;
}
#if MIP6_DEBUG
mip6_debug("Checking neighbor %s round 2\n", dst ? ip6_sprintf(&dst->sin6_addr) : "NULL");
#endif
if (in6_are_prefix_equal(&dst->sin6_addr,
&mip6_primary_prefix->
ndpr_prefix.sin6_addr,
mip6_primary_prefix->
ndpr_plen)) {
#if MIP6_DEBUG
mip6_debug("Deleting Neighbor %s round 2.\n",
ip6_sprintf(&(satosin6(
rt_key(rt))->sin6_addr)));
#endif
#if MIP6_DEBUG
mip6_debug("Ref count = %d, now RTM_DELETE\n",
rt->rt_refcnt);
#endif
if (rt && rt->rt_gateway &&
rt->rt_gateway->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)rt->
rt_gateway;
rtrequest(RTM_DELETE, rt_key(rt),
(struct sockaddr *)0,
rt_mask(rt), 0,
(struct rtentry **)0);
}
}
ln = next;
}
}
mip6_md_state = state;
mip6_primary_prefix = pr;
mip6_primary_defrtr = dr ? dr->rtaddr : in6addr_any;
switch (mip6_md_state) {
case MIP6_MD_HOME:
mip6_tell_em(mip6_md_state, mip6_home_prefix, NULL, dr);
break;
case MIP6_MD_FOREIGN:
mip6_tell_em(mip6_md_state, mip6_home_prefix, pr, dr);
break;
case MIP6_MD_UNDEFINED:
mip6_tell_em(mip6_md_state, mip6_home_prefix, NULL, NULL);
break;
}
splx(s);
return;
}
void
mip6_prelist_update(pr, dr)
struct nd_prefix *pr;
struct nd_defrouter *dr;
{
if (dr == NULL) {
return;
}
if (pr == mip6_home_prefix) {
if (mip6_md_state != MIP6_MD_HOME) {
#if MIP6_DEBUG
mip6_debug("%s: returning home.\n", __FUNCTION__);
#endif
mip6_md_state = MIP6_MD_HOME;
if (TAILQ_FIRST(&nd_defrouter) != NULL) {
defrouter_select();
}
else {
#if MIP6_DEBUG
mip6_debug("%s: Undef -> Home: no previous "
"router available "
"at this stage.\n", __FUNCTION__);
#endif
mip6_select_defrtr();
}
}
}
else if (mip6_md_state == MIP6_MD_UNDEFINED) {
if (TAILQ_FIRST(&nd_defrouter) != NULL) {
defrouter_select();
}
else {
#if MIP6_DEBUG
mip6_debug("%s: Strange, no default router available"
"at this stage.\n", __FUNCTION__);
#endif
mip6_select_defrtr();
}
}
}
void
mip6_eager_md(int enable)
{
mip6_config.eager_md = enable;
if (enable) {
mip6_max_lost_advints = 1;
if (!mip6_nd6_delay) {
mip6_nd6_delay = nd6_delay;
mip6_nd6_umaxtries = nd6_umaxtries;
}
nd6_delay = 1;
nd6_umaxtries = 1;
}
else {
mip6_max_lost_advints = MIP6_MAX_LOST_ADVINTS;
if (mip6_nd6_delay) {
nd6_delay = mip6_nd6_delay;
nd6_umaxtries = mip6_nd6_umaxtries;
mip6_nd6_delay = 0;
mip6_nd6_umaxtries = 0;
}
}
}
void
mip6_expired_defrouter(struct nd_defrouter *dr)
{
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
if (!dr)
return;
if (dr->advint_expire && dr->advint_expire < time_second) {
if (++(dr->advints_lost) < mip6_max_lost_advints) {
dr->advint_expire = time_second + dr->advint / 1000;
#if MIP6_DEBUG
mip6_debug("Adv Int #%d lost from router %s.\n",
dr->advints_lost, ip6_sprintf(&dr->rtaddr));
#endif
}
else {
dr->advint_expire = 0;
#if MIP6_DEBUG
mip6_debug("Adv Int #%d lost from router %s.\n",
dr->advints_lost, ip6_sprintf(&dr->rtaddr));
#endif
mip6_probe_defrouter(dr);
}
}
}
void
mip6_probe_defrouter(struct nd_defrouter *dr)
{
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
struct rtentry *rt;
struct llinfo_nd6 *ln;
if (!dr)
return;
if (!(rt = nd6_lookup(&dr->rtaddr, 0, NULL)))
return;
if ((rt->rt_flags & RTF_GATEWAY)
|| (rt->rt_flags & RTF_LLINFO) == 0
|| !rt->rt_llinfo
|| !rt->rt_gateway
|| rt->rt_gateway->sa_family != AF_LINK) {
return;
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
if ((ln->ln_state == ND6_LLINFO_INCOMPLETE)
|| (ln->ln_state == ND6_LLINFO_PROBE)
|| (ln->ln_state == ND6_LLINFO_WAITDELETE)
|| (ln->ln_state == ND6_LLINFO_NOSTATE))
return;
ln->ln_asked = 1;
ln->ln_state = ND6_LLINFO_PROBE;
ln->ln_expire = time_second +
nd_ifinfo[rt->rt_ifp->if_index].retrans / 1000;
nd6_ns_output(rt->rt_ifp, &dr->rtaddr, &dr->rtaddr,
ln, 0);
#if MIP6_DEBUG
mip6_debug("Probing defrouter %s\n", ip6_sprintf(&dr->rtaddr));
#endif
}
void
mip6_probe_pfxrtrs()
{
struct nd_pfxrouter *pfr;
if (!mip6_config.eager_md)
return;
if (!mip6_primary_prefix)
return;
#if MIP6_DEBUG
mip6_debug("New or detached prefix received, probe old routers:\n");
#endif
for (pfr = mip6_primary_prefix->ndpr_advrtrs.lh_first;
pfr; pfr = pfr->pfr_next) {
mip6_probe_defrouter(pfr->router);
}
}
void
mip6_store_advint(struct nd_opt_advint *ai,
struct nd_defrouter *dr)
{
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
if (ai->nd_opt_int_len != 1) {
log(LOG_INFO, "%s: bad Advertisement Interval Option "
"length\n", __FUNCTION__);
}
else if (dr) {
dr->advint = ntohl(ai->nd_opt_int_interval);
dr->advint_expire = time_second + dr->advint / 1000;
dr->advints_lost = 0;
}
}
int
mip6_delete_ifaddr(struct in6_addr *addr,
struct ifnet *ifp)
{
struct in6_aliasreq *ifra, dummy;
struct sockaddr_in6 *sa6;
struct in6_ifaddr *ia, *oia;
int s;
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) || defined (__APPLE__)
struct ifaddr *ifa;
#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;
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, &ifra->ifra_addr.sin6_addr);
if (ia == 0) {
splx(s);
return(EADDRNOTAVAIL);
}
if (ia == 0) {
ia = (struct in6_ifaddr *)
MALLOC(sizeof(*ia), 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) || defined (__APPLE__)
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++;
}
in6_purgeaddr(&ia->ia_ifa, ifp);
splx(s);
return(0);
}
#if 0
void
mip6_delete_ifaddr(struct in6_addr *addr,
struct ifnet *ifp)
{
struct in6_aliasreq in6_addreq;
int s, error = 0;
bzero(&in6_addreq, sizeof(in6_addreq));
in6_addreq.ifra_addr.sin6_len = sizeof(in6_addreq.ifra_addr);
in6_addreq.ifra_addr.sin6_family = AF_INET6;
in6_addreq.ifra_addr.sin6_addr = *addr;
s =splnet();
error = in6_control(NULL, SIOCDIFADDR_IN6, (caddr_t)&in6_addreq, ifp
#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) && !define (__APPLE__)
, NULL
#endif
);
splx(s);
if (error) {
#if MIP6_DEBUG
mip6_debug("%s: Attempt to delete addr %s failed.\n", __FUNCTION__,
ip6_sprintf(addr));
#endif
}
}
#endif
struct nd_prefix *
mip6_get_home_prefix(void)
{
return(mip6_home_prefix);
}
int
mip6_get_md_state(void)
{
return(mip6_md_state);
}
void
mip6_md_exit()
{
struct nd_prefix *pr;
pr = mip6_home_prefix;
if (pr && pr->ndpr_ifp && !IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) {
mip6_delete_ifaddr(&pr->ndpr_addr, pr->ndpr_ifp);
prelist_remove(pr);
mip6_home_prefix = NULL;
#if MIP6_DEBUG
mip6_debug("Home Prefix and Home Address removed.\n");
#endif
}
}