#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 <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#include <netinet6/nd6.h>
#include <netinet6/mip6.h>
#include <netinet6/mip6_common.h>
struct mip6_bul *mip6_bulq = NULL;
struct mip6_esm *mip6_esmq = NULL;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
struct callout_handle mip6_timer_outqueue_handle;
struct callout_handle mip6_timer_bul_handle;
struct callout_handle mip6_timer_esm_handle;
#endif
void
mip6_mn_init(void)
{
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
callout_handle_init(&mip6_timer_outqueue_handle);
callout_handle_init(&mip6_timer_bul_handle);
callout_handle_init(&mip6_timer_esm_handle);
#endif
printf("%s: MIP6 Mobile Node initialized\n", __FUNCTION__);
}
void
mip6_mn_exit()
{
struct mip6_output *outp, *outp_tmp;
struct mip6_bul *bulp;
struct mip6_esm *esp;
int s;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_outqueue, (void *)NULL,
mip6_timer_outqueue_handle);
untimeout(mip6_timer_bul, (void *)NULL, mip6_timer_bul_handle);
untimeout(mip6_timer_esm, (void *)NULL, mip6_timer_esm_handle);
#else
untimeout(mip6_timer_outqueue, (void *)NULL);
untimeout(mip6_timer_bul, (void *)NULL);
untimeout(mip6_timer_esm, (void *)NULL);
#endif
s = splnet();
for (outp = mip6_outq; outp;) {
outp_tmp = outp;
outp = outp->next;
if (outp_tmp->opt)
_FREE(outp_tmp->opt, M_TEMP);
if (outp_tmp->subopt)
_FREE(outp_tmp->subopt, M_TEMP);
_FREE(outp_tmp, M_TEMP);
}
mip6_outq = NULL;
for (bulp = mip6_bulq; bulp;)
bulp = mip6_bul_delete(bulp);
mip6_bulq = NULL;
for (esp = mip6_esmq; esp;)
esp = mip6_esm_delete(esp);
mip6_esmq = NULL;
splx(s);
}
void
mip6_new_defrtr(state, home_prefix, prim_prefix, def_router)
int state;
struct nd_prefix *home_prefix;
struct nd_prefix *prim_prefix;
struct nd_defrouter *def_router;
{
struct in6_addr *home_addr;
struct in6_addr *prim_addr;
struct mip6_esm *esp;
struct mip6_bul *bulp;
struct ifaddr *if_addr;
struct mip6_bu_data bu_data;
struct in6_addr ll_all_addr;
struct in6_addr old_coa;
struct sockaddr_in6 sin6;
u_int32_t lifetime;
u_long na_flags;
if (home_prefix != NULL)
home_addr = &home_prefix->ndpr_addr;
else {
log(LOG_ERR, "%s: No home address configured\n", __FUNCTION__);
return;
}
esp = mip6_esm_find(home_addr);
if (esp == NULL) {
log(LOG_ERR,
"%s: No event-state machine found\n", __FUNCTION__);
return;
}
if (prim_prefix != NULL)
prim_addr = &prim_prefix->ndpr_addr;
else
prim_addr = NULL;
if ((prim_prefix == NULL) && (state == MIP6_MD_UNDEFINED)) {
esp->state = MIP6_STATE_UNDEF;
esp->coa = in6addr_any;
if (esp->ha_fn != NULL) {
_FREE(esp->ha_fn, M_TEMP);
esp->ha_fn = NULL;
}
if (mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL, MIP6_NODE_MN,
(void *)esp))
return;
} else if ((prim_prefix == NULL) && (state == MIP6_MD_HOME)) {
esp->state = MIP6_STATE_DEREG;
old_coa = esp->coa;
esp->coa = esp->home_addr;
bulp = mip6_bul_find(NULL, home_addr);
if (bulp == NULL) {
esp->state = MIP6_STATE_HOME;
bzero(&sin6, sizeof(struct sockaddr_in6));
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = esp->home_addr;
if_addr = ifa_ifwithaddr((struct sockaddr *)&sin6);
if (if_addr == NULL)
return;
((struct in6_ifaddr *)if_addr)->ia6_flags |=
IN6_IFF_TENTATIVE;
nd6_dad_start(if_addr, NULL);
return;
}
bulp->lifetime = mip6_config.hr_lifetime;
bulp->refreshtime = bulp->lifetime;
bulp->coa = bulp->bind_addr;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
if (mip6_send_bu(bulp, &bu_data, NULL) != 0)
return;
if ( !IN6_IS_ADDR_UNSPECIFIED(&old_coa) &&
(esp->ha_fn != NULL)) {
lifetime = mip6_prefix_lifetime(&old_coa);
lifetime = min(lifetime, MIP6_BU_LIFETIME_DEFRTR);
if (mip6_tunnel(home_addr, &esp->ha_fn->addr,
MIP6_TUNNEL_ADD,
MIP6_NODE_MN, (void *)esp))
return;
mip6_send_bu2fn(&old_coa, esp->ha_fn, home_addr,
esp->ifp, lifetime);
_FREE(esp->ha_fn, M_TEMP);
esp->ha_fn = NULL;
}
ll_all_addr = in6addr_linklocal_allnodes;
na_flags = ND_NA_FLAG_OVERRIDE;
mip6_na_create(home_addr, &ll_all_addr, home_addr,
esp->prefix_len, na_flags, 1);
} else if ((prim_prefix != NULL) && (state == MIP6_MD_FOREIGN)) {
if (IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) {
mip6_build_ha_anycast(&esp->ha_hn, &esp->home_addr,
esp->prefix_len);
if (IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) {
log(LOG_ERR,
"%s: Could not create anycast address "
"for Mobile Node, wrong prefix length\n",
__FUNCTION__);
return;
}
}
if ((esp->state == MIP6_STATE_UNDEF) ||
(esp->state == MIP6_STATE_HOME) ||
(esp->state == MIP6_STATE_DEREG)) {
esp->state = MIP6_STATE_NOTREG;
esp->coa = *prim_addr;
if (esp->ha_fn != NULL) {
_FREE(esp->ha_fn, M_TEMP);
esp->ha_fn = NULL;
}
bulp = mip6_bul_find(NULL, &esp->home_addr);
if (bulp == NULL) {
bulp = mip6_bul_create(&esp->ha_hn,
&esp->home_addr,
prim_addr,
mip6_config.hr_lifetime,
1);
if (bulp == NULL)
return;
} else {
bulp->coa = *prim_addr;
bulp->lifetime = mip6_config.hr_lifetime;
bulp->refreshtime = bulp->lifetime;
}
bulp->coa = *prim_addr;
bulp->lifetime = mip6_config.hr_lifetime;
bulp->refreshtime = mip6_config.hr_lifetime;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
if (mip6_send_bu(bulp, &bu_data, NULL) != 0)
return;
} else if (esp->state == MIP6_STATE_REG ||
esp->state == MIP6_STATE_REREG ||
esp->state == MIP6_STATE_REGNEWCOA ||
esp->state == MIP6_STATE_NOTREG) {
esp->state = MIP6_STATE_REGNEWCOA;
old_coa = esp->coa;
esp->coa = *prim_addr;
bulp = mip6_bul_find(NULL, &esp->home_addr);
if (bulp == NULL) {
bulp = mip6_bul_create(&esp->ha_hn,
&esp->home_addr,
prim_addr,
mip6_config.hr_lifetime,
1);
if (bulp == NULL)
return;
}
bulp->coa = *prim_addr;
bulp->lifetime = mip6_config.hr_lifetime;
bulp->refreshtime = mip6_config.hr_lifetime;
bulp->no_of_sent_bu = 0;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
if (mip6_send_bu(bulp, &bu_data, NULL) != 0)
return;
if ( !IN6_IS_ADDR_UNSPECIFIED(&old_coa) &&
(esp->ha_fn)) {
lifetime = mip6_prefix_lifetime(&old_coa);
lifetime = min(lifetime,
MIP6_BU_LIFETIME_DEFRTR);
if (mip6_tunnel(prim_addr, &esp->ha_fn->addr,
MIP6_TUNNEL_MOVE,
MIP6_NODE_MN, (void *)esp))
return;
mip6_send_bu2fn(&old_coa, esp->ha_fn,
prim_addr,
esp->ifp, lifetime);
_FREE(esp->ha_fn, M_TEMP);
esp->ha_fn = NULL;
}
}
} else
esp->state = MIP6_STATE_UNDEF;
}
int
mip6_rec_ba(m_in, off)
struct mbuf *m_in;
int off;
{
struct mip6_esm *esp;
struct mip6_bul *bulp;
struct in6_addr *from_src;
struct in6_addr bind_addr;
u_int8_t hr_flag;
int error;
#if MIP6_DEBUG
u_int8_t var;
int ii, offset;
#endif
#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_ERR, "%s: No AH or ESP included in BA\n",
__FUNCTION__);
return IPPROTO_DONE;
}
#endif
#endif
if (mip6_inp->ba_opt->len < IP6OPT_BALEN) {
ip6stat.ip6s_badoptions++;
log(LOG_ERR, "%s: Length field in BA < 11\n", __FUNCTION__);
return IPPROTO_DONE;
}
if (mip6_inp->optflag & MIP6_DSTOPT_HA)
from_src = &mip6_inp->ha_opt->home_addr;
else
from_src = &mip6_inp->ip6_src;
bulp = mip6_bul_find(from_src, &mip6_inp->ip6_dst);
if (bulp == NULL) {
log(LOG_ERR, "%s: No Binding Update List entry found\n",
__FUNCTION__);
return IPPROTO_DONE;
}
if (mip6_inp->ba_opt->seqno != bulp->seqno) {
ip6stat.ip6s_badoptions++;
log(LOG_ERR,
"%s: Received sequence number not equal to sent\n",
__FUNCTION__);
return IPPROTO_DONE;
}
#if MIP6_DEBUG
mip6_debug("\nReceived Binding Acknowledgement\n");
mip6_debug("IP Header Src: %s\n", ip6_sprintf(from_src));
mip6_debug("IP Header Dst: %s\n",
ip6_sprintf(&mip6_inp->ip6_dst));
mip6_debug("Type/Length/Status: %x / %u / %u\n",
mip6_inp->ba_opt->type,
mip6_inp->ba_opt->len, mip6_inp->ba_opt->status);
mip6_debug("Seq no/Life time: %u / %u\n", mip6_inp->ba_opt->seqno,
mip6_inp->ba_opt->lifetime);
mip6_debug("Refresh time: %u\n", mip6_inp->ba_opt->refresh);
if (mip6_inp->ba_opt->len > IP6OPT_BALEN) {
offset = mip6_opt_offset(m_in, off, IP6OPT_BINDING_ACK);
if (offset == 0)
goto end_debug;
mip6_debug("Sub-options present (TLV coded)\n");
for (ii = IP6OPT_BALEN; ii < mip6_inp->ba_opt->len; ii++) {
if ((ii - IP6OPT_BALEN) % 16 == 0)
mip6_debug("\t0x:");
if ((ii - IP6OPT_BALEN) % 4 == 0)
mip6_debug(" ");
m_copydata(m_in, offset + 2 + ii, sizeof(var),
(caddr_t)&var);
mip6_debug("%02x", var);
if ((ii - IP6OPT_BALEN + 1) % 16 == 0)
mip6_debug("\n");
}
if ((ii - IP6OPT_BALEN) % 16)
mip6_debug("\n");
}
end_debug:
#endif
if (mip6_inp->ba_opt->status >= 128) {
bind_addr = bulp->bind_addr;
hr_flag = bulp->hr_flag;
mip6_bul_delete(bulp);
error = mip6_ba_error(from_src, &mip6_inp->ip6_dst,
&bind_addr, hr_flag);
return error;
}
bulp->no_of_sent_bu = 0;
bulp->update_rate = MIP6_MAX_UPDATE_RATE;
mip6_clear_retrans(bulp);
if (bulp->hr_flag) {
esp = mip6_esm_find(&bulp->bind_addr);
if (esp == NULL) {
log(LOG_ERR, "%s: No event-state machine found\n",
__FUNCTION__);
return IPPROTO_DONE;
}
if (esp->dad) {
esp->ha_hn = *from_src;
bulp->dst_addr = *from_src;
if (esp->dad->hal)
_FREE(esp->dad->hal, M_TEMP);
_FREE(esp->dad, M_TEMP);
esp->dad = NULL;
}
if (esp->state == MIP6_STATE_DEREG) {
mip6_bul_delete(bulp);
mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL,
MIP6_NODE_MN, (void *)esp);
mip6_update_cns(&esp->home_addr,
&esp->home_addr, 0, 0);
mip6_outq_flush();
esp->state = MIP6_STATE_HOME;
esp->coa = in6addr_any;
} else {
esp->state = MIP6_STATE_REG;
if (mip6_tunnel(&esp->coa, &esp->ha_hn,
MIP6_TUNNEL_MOVE, MIP6_NODE_MN,
(void *)esp))
return IPPROTO_DONE;
bulp->lifetime = mip6_inp->ba_opt->lifetime;
bulp->refreshtime = mip6_inp->ba_opt->refresh;
mip6_update_cns(&esp->home_addr, &esp->coa, 0,
bulp->lifetime);
}
}
return 0;
}
int
mip6_rec_br(m_in, off)
struct mbuf *m_in;
int off;
{
struct mip6_opt_bu *bu_opt;
struct in6_addr *from_src;
struct mip6_esm *esp;
struct mip6_bul *bulp_cn;
struct mip6_bul *bulp_ha;
struct mip6_subbuf *subbuf = NULL;
struct mip6_subopt_coa altcoa;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
#if MIP6_DEBUG
const struct mbuf *m = (const struct mbuf *)m_in;
u_int8_t var;
int ii, offset;
#endif
if (mip6_inp->br_opt->type != IP6OPT_BINDING_REQ) {
ip6stat.ip6s_badoptions++;
return IPPROTO_DONE;
}
#if MIP6_DEBUG
mip6_debug("\nReceived Binding Request\n");
mip6_debug("Type/Length: %x / %u\n", mip6_inp->br_opt->type,
mip6_inp->br_opt->len);
if (mip6_inp->br_opt->len > IP6OPT_BRLEN) {
offset = mip6_opt_offset(m_in, off, IP6OPT_BINDING_REQ);
if (offset == 0)
goto end_debug;
mip6_debug("Sub-options present (TLV coded)\n");
for (ii = IP6OPT_BRLEN; ii < mip6_inp->br_opt->len; ii++) {
if (m->m_len < offset + 2 + ii + 1)
break;
if ((ii - IP6OPT_BRLEN) % 16 == 0)
mip6_debug("\t0x:");
if ((ii - IP6OPT_BRLEN) % 4 == 0)
mip6_debug(" ");
m_copydata(m_in, offset + 2 + ii, sizeof(var),
(caddr_t)&var);
mip6_debug("%02x", var);
if ((ii - IP6OPT_BRLEN + 1) % 16 == 0)
mip6_debug("\n");
}
if ((ii - IP6OPT_BRLEN) % 16)
mip6_debug("\n");
}
end_debug:
#endif
if (mip6_inp->br_opt->len > IP6OPT_BRLEN) {
} else {
if (mip6_inp->optflag & MIP6_DSTOPT_HA)
from_src = &mip6_inp->ha_opt->home_addr;
else
from_src = &mip6_inp->ip6_src;
bulp_cn = mip6_bul_find(from_src, &mip6_inp->ip6_dst);
if (bulp_cn == NULL)
return IPPROTO_DONE;
esp = mip6_esm_find(&mip6_inp->ip6_dst);
if (esp == NULL) {
log(LOG_ERR, "%s: no event-state machine found\n",
__FUNCTION__);
return IPPROTO_DONE;
}
bulp_ha = mip6_bul_find(&esp->ha_hn, &mip6_inp->ip6_dst);
if (bulp_ha == NULL)
return IPPROTO_DONE;
if (bulp_ha->lifetime > bulp_cn->lifetime) {
bulp_cn->seqno += 1;
bu_opt = mip6_create_bu(0, 0, 0, bulp_cn->seqno,
bulp_ha->lifetime);
if (bu_opt == NULL)
return IPPROTO_DONE;
altcoa.type = IP6SUBOPT_ALTCOA;
altcoa.len = IP6OPT_COALEN;
altcoa.coa = bulp_cn->coa;
if (mip6_store_subopt(&subbuf, (caddr_t)&altcoa)
!= 0) {
if (subbuf)
_FREE(subbuf, M_TEMP);
return IPPROTO_DONE;
}
mip6_outq_create(bu_opt, subbuf, &esp->home_addr,
from_src, NOT_SENT);
bulp_cn->lifetime = bulp_ha->lifetime;
bulp_cn->refreshtime = bulp_ha->lifetime;
bulp_cn->lasttime = time_second;
bulp_cn->no_of_sent_bu = 0;
bulp_cn->update_rate = MIP6_MAX_UPDATE_RATE;
mip6_clear_retrans(bulp_cn);
}
}
return 0;
}
int
mip6_rec_hal(src, dst, hal)
struct in6_addr *src;
struct in6_addr *dst;
struct mip6_subopt_hal *hal;
{
struct mip6_esm *esp;
struct mip6_bul *bulp;
struct mip6_subbuf *subbuf;
struct mip6_bu_data bu_data;
int found, ii, new_len, index;
subbuf = NULL;
esp = mip6_esm_find(dst);
if (esp == NULL) {
log(LOG_ERR,
"%s: Couldn't find an event-state machine for "
"home address %s\n",
__FUNCTION__, ip6_sprintf(dst));
return IPPROTO_DONE;
}
found = 0;
if (hal == NULL)
new_len = IP6OPT_HALEN;
else {
index = hal->len / IP6OPT_HALEN;
for (ii = 0; ii < index; ii++) {
if (IN6_ARE_ADDR_EQUAL(&hal->halist[ii], src)) {
found = 1;
break;
}
}
if (found)
new_len = hal->len;
else
new_len = hal->len + IP6OPT_HALEN;
}
esp->dad = (struct mip6_dad *)MALLOC(sizeof(struct mip6_dad),
M_TEMP, M_WAITOK);
if (esp->dad == NULL)
return IPPROTO_DONE;
bzero(esp->dad, sizeof(struct mip6_dad));
index = new_len / IP6OPT_HALEN;
esp->dad->hal = (struct mip6_subopt_hal *)
MALLOC(sizeof(struct mip6_subopt_hal) +
((index - 1) * sizeof(struct in6_addr)),
M_TEMP, M_WAITOK);
if (esp->dad->hal == NULL)
return IPPROTO_DONE;
esp->dad->hal->type = IP6SUBOPT_HALIST;
esp->dad->hal->len = new_len;
if (found) {
for (ii = 0; ii < index; ii++) {
bcopy(&hal->halist[ii], &esp->dad->hal->halist[ii],
sizeof(struct in6_addr));
}
} else {
bcopy(src, &esp->dad->hal->halist[0], sizeof(struct in6_addr));
for (ii = 0; ii < index - 1; ii++) {
bcopy(&hal->halist[ii], &esp->dad->hal->halist[ii+1],
sizeof(struct in6_addr));
}
}
bulp = mip6_bul_find(src, dst);
if (bulp != NULL) {
log(LOG_ERR,
"%s: A BUL entry found but it shouldn't have been. "
"Internal error that must be looked into\n", __FUNCTION__);
return IPPROTO_DONE;
}
bulp = mip6_bul_create(&esp->dad->hal->halist[0], &esp->home_addr,
&esp->coa, MIP6_BU_LIFETIME_DHAAD, 1);
if (bulp == NULL)
return IPPROTO_DONE;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
if (mip6_send_bu(bulp, &bu_data, subbuf) != 0)
return IPPROTO_DONE;
if ((esp->dad->hal->len / IP6OPT_HALEN) == 1)
esp->dad->index = 0;
else
esp->dad->index = 1;
return 0;
};
int
mip6_rec_ramn(m, off)
struct mbuf *m;
int off;
{
struct ip6_hdr *ip6;
struct nd_router_advert *ra;
struct mip6_esm *esp;
struct nd_opt_hai *hai;
struct nd_opt_prefix_info *pi;
u_int8_t *opt_ptr;
int cur_off;
caddr_t icmp6msg;
int16_t tmp_pref;
time_t tmp_lifetime;
int icmp6len;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
ip6 = mtod(m, struct ip6_hdr *);
if (ip6->ip6_hlim != 255) {
log(LOG_INFO,
"%s: Invalid hlim %d in Router Advertisement\n",
__FUNCTION__,
ip6->ip6_hlim);
return 0;
}
if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
log(LOG_INFO,
"%s: Source address %s is not link-local\n", __FUNCTION__,
ip6_sprintf(&ip6->ip6_src));
return 0;
}
icmp6len = m->m_pkthdr.len - off;
icmp6msg = (caddr_t)MALLOC(icmp6len, M_TEMP, M_WAITOK);
if (icmp6msg == NULL)
return IPPROTO_DONE;
m_copydata(m, off, icmp6len, icmp6msg);
ra = (struct nd_router_advert *)icmp6msg;
cur_off = sizeof(struct nd_router_advert);
tmp_lifetime = ntohl(ra->nd_ra_router_lifetime);
tmp_pref = 0;
while (cur_off < icmp6len) {
opt_ptr = ((caddr_t)icmp6msg + cur_off);
if (*opt_ptr == ND_OPT_HA_INFORMATION) {
hai = (struct nd_opt_hai *)opt_ptr;
if (hai->nd_opt_hai_len != 1) {
ip6stat.ip6s_badoptions++;
return IPPROTO_DONE;
}
tmp_pref = ntohs(hai->nd_opt_hai_pref);
tmp_lifetime = ntohs(hai->nd_opt_hai_lifetime);
cur_off += 8;
continue;
} else {
if (*(opt_ptr + 1) == 0) {
ip6stat.ip6s_badoptions++;
return IPPROTO_DONE;
}
cur_off += *(opt_ptr + 1) * 8;
}
}
cur_off = sizeof(struct nd_router_advert);
while (cur_off < icmp6len) {
opt_ptr = ((caddr_t)icmp6msg + cur_off);
if (*opt_ptr == ND_OPT_PREFIX_INFORMATION) {
pi = (struct nd_opt_prefix_info *)opt_ptr;
if (pi->nd_opt_pi_len != 4) {
ip6stat.ip6s_badoptions++;
return IPPROTO_DONE;
}
if (!(pi->nd_opt_pi_flags_reserved &
ND_OPT_PI_FLAG_RTADDR)) {
cur_off += 4 * 8;
continue;
}
if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) ||
IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
cur_off += 4 * 8;
continue;
}
if (((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) >
0x10) && (pi->nd_opt_pi_prefix_len != 64)) {
cur_off += 4 * 8;
continue;
}
for (esp = mip6_esmq; esp; esp = esp->next) {
if (in6_are_prefix_equal(
&pi->nd_opt_pi_prefix,
&esp->coa,
pi->nd_opt_pi_prefix_len)) {
if (esp->ha_fn == NULL) {
esp->ha_fn = (struct mip6_hafn *)
MALLOC(sizeof(struct mip6_hafn), M_TEMP, M_WAITOK);
if (esp->ha_fn == NULL)
return ENOBUFS;
bzero(esp->ha_fn, sizeof(struct mip6_hafn));
esp->ha_fn->addr = pi->nd_opt_pi_prefix;
esp->ha_fn->prefix_len = pi->nd_opt_pi_prefix_len;
esp->ha_fn->pref = tmp_pref;
esp->ha_fn->time = time_second + tmp_lifetime;
} else {
if (tmp_pref > esp->ha_fn->pref) {
esp->ha_fn->addr = pi->nd_opt_pi_prefix;
esp->ha_fn->prefix_len = pi->nd_opt_pi_prefix_len;
esp->ha_fn->pref = tmp_pref;
esp->ha_fn->time = time_second + tmp_lifetime;
} else
esp->ha_fn->time = time_second + tmp_lifetime;
}
}
}
cur_off += 4 * 8;
continue;
} else {
if (*(opt_ptr + 1) == 0) {
ip6stat.ip6s_badoptions++;
return IPPROTO_DONE;
}
cur_off += *(opt_ptr + 1) * 8;
}
}
return 0;
}
int
mip6_route_optimize(m)
struct mbuf *m;
{
struct ip6_hdr *ip6;
struct mip6_esm *esp;
struct mip6_bul *bulp, *bulp_hr;
struct mip6_subbuf *subbuf;
struct mip6_bu_data bu_data;
struct mip6_subopt_coa altcoa;
time_t t;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
if (!(m->m_flags & M_MIP6TUNNEL))
return 0;
ip6 = mtod(m, struct ip6_hdr *);
esp = mip6_esm_find(&ip6->ip6_dst);
if (esp == NULL)
return 0;
bulp = mip6_bul_find(&ip6->ip6_src, &esp->home_addr);
if (bulp == NULL) {
bulp_hr = mip6_bul_find(NULL, &esp->home_addr);
if (bulp_hr == NULL)
return 0;
bulp = mip6_bul_create(&ip6->ip6_src, &esp->home_addr,
&esp->coa, bulp_hr->lifetime, 0);
if (bulp == NULL)
return IPPROTO_DONE;
} else {
if ((bulp->state) || (bulp->bu_flag == 0))
return 0;
t = (time_t)time_second;
#if MIP6_DEBUG
mip6_debug("%s: Rate limiting for sending BU\n", __FUNCTION__);
mip6_debug("(time - bulp->lasttime) < bulp->update_rate\n");
mip6_debug("time = %lu\n", (u_long)t);
mip6_debug("bulp->lasttimetime = %lu\n", bulp->lasttime);
mip6_debug("bulp->update_rate = %d\n", bulp->update_rate);
#endif
if ((t - bulp->lasttime) < bulp->update_rate)
return 0;
}
subbuf = NULL;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 0;
altcoa.type = IP6SUBOPT_ALTCOA;
altcoa.len = IP6OPT_COALEN;
altcoa.coa = bulp->coa;
if (mip6_store_subopt(&subbuf, (caddr_t)&altcoa)) {
if (subbuf) _FREE(subbuf, M_TEMP);
return IPPROTO_DONE;
}
if (mip6_send_bu(bulp, &bu_data, subbuf) != 0)
return IPPROTO_DONE;
return 0;
}
int
mip6_send_bu(bulp, data, subbuf)
struct mip6_bul *bulp;
struct mip6_bu_data *data;
struct mip6_subbuf *subbuf;
{
struct mbuf *m_ip6;
struct ip6_pktopts *pktopt;
struct mip6_opt_bu *bu_opt;
struct mip6_subbuf *bu_subopt;
struct mip6_esm *esp;
int error;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
#if MIP6_DEBUG
int ii;
u_int8_t var;
#endif
if (bulp == NULL)
return 0;
if (!bulp->bu_flag) {
log(LOG_INFO,
"%s: BU not sent to host %s due to an ICMP Parameter "
"Problem, Code 2, when a BU was sent previously\n",
__FUNCTION__, ip6_sprintf(&bulp->dst_addr));
return 0;
}
esp = mip6_esm_find(&bulp->bind_addr);
if (esp == NULL) {
log(LOG_ERR, "%s: We should never come here\n", __FUNCTION__);
return 0;
} else if (esp->state == MIP6_STATE_UNDEF) {
log(LOG_INFO,
"%s: Mobile Node with home address %s not connected to "
"any network. Binding Update could not be sent.\n",
__FUNCTION__, ip6_sprintf(&bulp->bind_addr));
return 0;
}
if ((data == NULL) && (subbuf == NULL)) {
if ((bulp->state == NULL) || (bulp->state->bu_opt == NULL)) {
log(LOG_ERR,
"%s: No existing BU option to send\n",
__FUNCTION__);
return 0;
}
bulp->seqno += 1;
bu_opt = bulp->state->bu_opt;
bu_opt->seqno = bulp->seqno;
bu_subopt = bulp->state->bu_subopt;
} else if (data != NULL) {
mip6_clear_retrans(bulp);
if (data->ack) {
bulp->state = mip6_create_retrans(bulp);
if (bulp->state == NULL)
return ENOBUFS;
}
bulp->seqno += 1;
bu_opt = mip6_create_bu(data->prefix_len, data->ack,
bulp->hr_flag,
bulp->seqno, bulp->lifetime);
if (bu_opt == NULL) {
mip6_clear_retrans(bulp);
bulp->seqno -= 1;
return ENOBUFS;
}
if (data->ack) {
bulp->state->bu_opt = bu_opt;
bulp->state->bu_subopt = subbuf;
bu_subopt = bulp->state->bu_subopt;
} else
bu_subopt = subbuf;
} else {
log(LOG_ERR,
"%s: Function parameter error. We should not come here\n",
__FUNCTION__);
return 0;
}
pktopt = (struct ip6_pktopts *)MALLOC(sizeof(struct ip6_pktopts),
M_TEMP, M_NOWAIT);
if (pktopt == NULL)
return ENOBUFS;
bzero(pktopt, sizeof(struct ip6_pktopts));
pktopt->ip6po_hlim = -1;
m_ip6 = mip6_create_ip6hdr(&bulp->bind_addr, &bulp->dst_addr,
IPPROTO_NONE);
if(m_ip6 == NULL) {
_FREE(pktopt, M_TEMP);
return ENOBUFS;
}
pktopt->ip6po_dest2 = mip6_create_dh((void *)bu_opt, bu_subopt,
IPPROTO_NONE);
if(pktopt->ip6po_dest2 == NULL) {
_FREE(pktopt, M_TEMP);
_FREE(m_ip6, M_TEMP);
return ENOBUFS;
}
mip6_config.enable_outq = 0;
error = ip6_output(m_ip6, pktopt, NULL, 0, NULL, NULL);
if (error) {
_FREE(pktopt->ip6po_dest2, M_TEMP);
_FREE(pktopt, M_TEMP);
mip6_config.enable_outq = 1;
log(LOG_ERR,
"%s: ip6_output function failed to send BU, error = %d\n",
__FUNCTION__, error);
return error;
}
mip6_config.enable_outq = 1;
bulp->lasttime = time_second;
bulp->no_of_sent_bu += 1;
if ( !(bu_opt->flags & MIP6_BU_AFLAG)) {
if (bulp->no_of_sent_bu >= MIP6_MAX_FAST_UPDATES)
bulp->update_rate = MIP6_SLOW_UPDATE_RATE;
}
#if MIP6_DEBUG
mip6_debug("\nSent Binding Update option (0x%x)\n", bu_opt);
mip6_debug("IP Header Src: %s\n", ip6_sprintf(&bulp->bind_addr));
mip6_debug("IP Header Dst: %s\n", ip6_sprintf(&bulp->dst_addr));
mip6_debug("Type/Length/Flags: %x / %u / ", bu_opt->type, bu_opt->len);
if (bu_opt->flags & MIP6_BU_AFLAG)
mip6_debug("A ");
if (bu_opt->flags & MIP6_BU_HFLAG)
mip6_debug("H ");
if (bu_opt->flags & MIP6_BU_RFLAG)
mip6_debug("R ");
mip6_debug("\n");
mip6_debug("Seq no/Life time: %u / %u\n", bu_opt->seqno,
bu_opt->lifetime);
mip6_debug("Prefix length: %u\n", bu_opt->prefix_len);
if (bu_subopt) {
mip6_debug("Sub-options present (TLV coded)\n");
for (ii = 0; ii < bu_subopt->len; ii++) {
if (ii % 16 == 0)
mip6_debug("\t0x:");
if (ii % 4 == 0)
mip6_debug(" ");
bcopy((caddr_t)&bu_subopt->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(pktopt->ip6po_dest2, M_TEMP);
_FREE(pktopt, M_TEMP);
return 0;
}
void
mip6_send_bu2fn(old_coa, old_ha, coa, esm_ifp, lifetime)
struct in6_addr *old_coa;
struct mip6_hafn *old_ha;
struct in6_addr *coa;
struct ifnet *esm_ifp;
u_int32_t lifetime;
{
struct mip6_esm *esp;
struct mip6_bul *bulp;
struct mip6_subbuf *subbuf;
struct mip6_bu_data bu_data;
struct mip6_subopt_coa altcoa;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
if (old_ha == NULL)
return;
else {
if (time_second > old_ha->time) {
log(LOG_INFO,
"%s: Timer had expired for Home Agent on "
"previous network. No BU sent\n",
__FUNCTION__);
return;
}
}
bulp = mip6_bul_find(NULL, old_coa);
if (bulp == NULL) {
bulp = mip6_bul_create(&old_ha->addr, old_coa, coa,
lifetime, 1);
if (bulp == NULL)
return;
} else {
bulp->dst_addr = old_ha->addr;
bulp->coa = *coa;
bulp->lifetime = lifetime;
bulp->refreshtime = lifetime;
mip6_clear_retrans(bulp);
}
esp = mip6_esm_create(esm_ifp, &old_ha->addr, coa, old_coa, 0,
MIP6_STATE_NOTREG, TEMPORARY,
MIP6_BU_LIFETIME_DEFRTR);
if (esp == NULL)
return;
subbuf = NULL;
bu_data.prefix_len = 0;
bu_data.ack = 0;
altcoa.type = IP6SUBOPT_ALTCOA;
altcoa.len = IP6OPT_COALEN;
altcoa.coa = *coa;
if (mip6_store_subopt(&subbuf, (caddr_t)&altcoa)) {
if (subbuf)
_FREE(subbuf, M_TEMP);
return;
}
if (mip6_send_bu(bulp, &bu_data, subbuf) != 0)
return;
}
void
mip6_update_cns(home_addr, coa, prefix_len, lifetime)
struct in6_addr *home_addr;
struct in6_addr *coa;
u_int8_t prefix_len;
u_int32_t lifetime;
{
struct mip6_bul *bulp;
for (bulp = mip6_bulq; bulp;) {
if (IN6_ARE_ADDR_EQUAL(home_addr, &bulp->bind_addr) &&
!IN6_ARE_ADDR_EQUAL(coa, &bulp->coa)) {
mip6_queue_bu(bulp, home_addr, coa,
prefix_len, lifetime);
if (IN6_ARE_ADDR_EQUAL(home_addr, coa) ||
(lifetime == 0))
bulp = mip6_bul_delete(bulp);
else
bulp = bulp->next;
} else
bulp = bulp->next;
}
}
void
mip6_queue_bu(bulp, home_addr, coa, prefix_len, lifetime)
struct mip6_bul *bulp;
struct in6_addr *home_addr;
struct in6_addr *coa;
u_int8_t prefix_len;
u_int32_t lifetime;
{
struct mip6_opt_bu *bu_opt;
struct mip6_subbuf *subbuf;
struct mip6_subopt_coa altcoa;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__)
long time_second = time.tv_sec;
#endif
if ((coa == NULL) || (bulp == NULL))
return;
if (bulp->bu_flag == 0) {
log(LOG_INFO,
"%s: BU not sent to host %s due to an ICMP Parameter "
"Problem, Code 2, when a BU was sent previously\n",
__FUNCTION__, ip6_sprintf(&bulp->dst_addr));
return;
}
subbuf = NULL;
altcoa.type = IP6SUBOPT_ALTCOA;
altcoa.len = IP6OPT_COALEN;
altcoa.coa = *coa;
if (mip6_store_subopt(&subbuf, (caddr_t)&altcoa)) {
if (subbuf)
_FREE(subbuf, M_TEMP);
return;
}
bulp->seqno += 1;
bu_opt = mip6_create_bu(prefix_len, 0, 0, bulp->seqno, lifetime);
if (bu_opt == NULL) {
log(LOG_ERR, "%s: Could not create a BU\n", __FUNCTION__);
return;
}
bulp->coa = *coa;
bulp->lifetime = lifetime;
bulp->refreshtime = lifetime;
bulp->lasttime = time_second;
bulp->no_of_sent_bu += 1;
mip6_clear_retrans(bulp);
mip6_outq_create(bu_opt, subbuf, home_addr, &bulp->dst_addr, NOT_SENT);
}
struct mip6_opt_bu *
mip6_create_bu(prefix_len, ack, hr, seqno, lifetime)
u_int8_t prefix_len;
int ack;
int hr;
u_int16_t seqno;
u_int32_t lifetime;
{
struct mip6_opt_bu *bu_opt;
bu_opt = (struct mip6_opt_bu *)MALLOC(sizeof(struct mip6_opt_bu),
M_TEMP, M_WAITOK);
if (bu_opt == NULL)
return NULL;
bzero(bu_opt, sizeof(struct mip6_opt_bu));
bu_opt->type = IP6OPT_BINDING_UPDATE;
bu_opt->len = IP6OPT_BULEN;
bu_opt->seqno = seqno;
bu_opt->lifetime = lifetime;
if (hr) {
bu_opt->flags |= MIP6_BU_HFLAG;
bu_opt->prefix_len = prefix_len;
if (ip6_forwarding)
bu_opt->flags |= MIP6_BU_RFLAG;
} else
bu_opt->prefix_len = 0;
if (ack)
bu_opt->flags |= MIP6_BU_AFLAG;
#if MIP6_DEBUG
mip6_debug("\nBinding Update option created (0x%x)\n", bu_opt);
#endif
return bu_opt;
}
void
mip6_stop_bu(ip6_dst)
struct in6_addr *ip6_dst;
{
struct mip6_bul *bulp;
for (bulp = mip6_bulq; bulp; bulp = bulp->next) {
if (IN6_ARE_ADDR_EQUAL(ip6_dst, &bulp->dst_addr))
bulp->bu_flag = 0;
}
}
int
mip6_ba_error(src, dst, bind_addr, hr_flag)
struct in6_addr *src;
struct in6_addr *dst;
struct in6_addr *bind_addr;
u_int8_t hr_flag;
{
struct mip6_bul *bulp;
struct mip6_esm *esp;
struct in6_addr *dst_addr;
struct mip6_bu_data bu_data;
u_int32_t lifetime;
int error, max_index;
if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_UNSPEC) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Reason unspecified) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_PROHIBIT) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Administratively prohibited) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
log(LOG_INFO, "Contact your system administrator\n");
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_RESOURCE) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Insufficient resources) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_HOMEREGNOSUP) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Home registration not supported) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_SUBNET) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Not home subnet) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_DHAAD) {
error = mip6_rec_hal(src, dst, mip6_inp->hal);
return error;
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_IFLEN) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Incorrect subnet prefix length) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
} else if (mip6_inp->ba_opt->status == MIP6_BA_STATUS_NOTHA) {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d "
"(Not Home Agent for this Mobile Node) from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
} else {
log(LOG_INFO,
"\nBinding Acknowledgement error = %d (Unknown) "
"from host %s\n",
mip6_inp->ba_opt->status, ip6_sprintf(src));
}
if (hr_flag) {
esp = mip6_esm_find(bind_addr);
if (esp == NULL) {
log(LOG_ERR,
"%s: No event-state machine found\n",
__FUNCTION__);
return IPPROTO_DONE;
}
if (esp->state == MIP6_STATE_DEREG) {
mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL,
MIP6_NODE_MN, (void *)esp);
mip6_update_cns(&esp->home_addr, &esp->home_addr,
0, 0);
mip6_outq_flush();
esp->state = MIP6_STATE_HOME;
esp->coa = in6addr_any;
return 0;
}
if (esp->dad) {
if (esp->dad->hal->len == IP6OPT_HALEN) {
if (esp->dad->hal)
_FREE(esp->dad->hal, M_TEMP);
_FREE(esp->dad, M_TEMP);
mip6_build_ha_anycast(&esp->ha_hn,
&esp->home_addr,
esp->prefix_len);
if (IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) {
log(LOG_ERR,
"%s: Could not create anycast "
"address for Mobile Node, "
"wrong prefix length\n",
__FUNCTION__);
return IPPROTO_DONE;
}
dst_addr = &esp->ha_hn;
lifetime = mip6_config.hr_lifetime;
} else {
dst_addr = &esp->dad->hal->halist[esp->dad->index];
max_index = (esp->dad->hal->len / IP6OPT_HALEN) - 1;
if (esp->dad->index == max_index)
esp->dad->index = 0;
else
esp->dad->index += 1;
lifetime = MIP6_BU_LIFETIME_DHAAD;
}
} else {
mip6_build_ha_anycast(&esp->ha_hn, &esp->home_addr,
esp->prefix_len);
if (IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) {
log(LOG_ERR,
"%s: Could not create anycast address for Mobile "
"Node, wrong prefix length\n", __FUNCTION__);
return IPPROTO_DONE;
}
dst_addr = &esp->ha_hn;
lifetime = mip6_config.hr_lifetime;
}
bulp = mip6_bul_create(dst_addr, &esp->home_addr, &esp->coa,
lifetime, 1);
if (bulp == NULL)
return IPPROTO_DONE;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
if (mip6_send_bu(bulp, &bu_data, NULL) != 0)
return IPPROTO_DONE;
}
return 0;
}
u_int32_t
mip6_prefix_lifetime(addr)
struct in6_addr *addr;
{
struct nd_prefix *pr;
u_int32_t min_time;
min_time = 0xffffffff;
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if (in6_are_prefix_equal(addr, &pr->ndpr_prefix.sin6_addr,
pr->ndpr_plen)) {
return pr->ndpr_vltime;
}
}
return min_time;
}
struct mip6_retrans *
mip6_create_retrans(bulp)
struct mip6_bul *bulp;
{
if (bulp == NULL)
return NULL;
mip6_clear_retrans(bulp);
bulp->state = (struct mip6_retrans *)MALLOC(
sizeof(struct mip6_retrans),
M_TEMP, M_WAITOK);
if (bulp->state == NULL)
return NULL;
bzero(bulp->state, sizeof(struct mip6_retrans));
bulp->state->bu_opt = NULL;
bulp->state->bu_subopt = NULL;
bulp->state->ba_timeout = 2;
bulp->state->time_left = 2;
return bulp->state;
}
void
mip6_clear_retrans(bulp)
struct mip6_bul *bulp;
{
if (bulp == NULL)
return;
if (bulp->state) {
if (bulp->state->bu_opt)
_FREE(bulp->state->bu_opt, M_TEMP);
if (bulp->state->bu_subopt)
_FREE(bulp->state->bu_subopt, M_TEMP);
_FREE(bulp->state, M_TEMP);
bulp->state = NULL;
}
return;
}
struct mip6_bul *
mip6_bul_find(dst_addr, bind_addr)
struct in6_addr *dst_addr;
struct in6_addr *bind_addr;
{
struct mip6_bul *bulp;
if (dst_addr == NULL) {
for (bulp = mip6_bulq; bulp; bulp = bulp->next) {
if (IN6_ARE_ADDR_EQUAL(bind_addr, &bulp->bind_addr) &&
(bulp->hr_flag))
break;
}
} else {
for (bulp = mip6_bulq; bulp; bulp = bulp->next) {
if (IN6_ARE_ADDR_EQUAL(dst_addr, &bulp->dst_addr) &&
IN6_ARE_ADDR_EQUAL(bind_addr, &bulp->bind_addr))
break;
}
if (bulp != NULL)
return bulp;
for (bulp = mip6_bulq; bulp; bulp = bulp->next) {
if ((bulp->dst_addr.s6_addr8[15] & 0x7f) ==
MIP6_ADDR_ANYCAST_HA &&
IN6_ARE_ADDR_EQUAL(bind_addr, &bulp->bind_addr)) {
break;
}
}
}
return bulp;
}
struct mip6_bul *
mip6_bul_create(dst_addr, bind_addr, coa, lifetime, hr)
struct in6_addr *dst_addr;
struct in6_addr *bind_addr;
struct in6_addr *coa;
u_int32_t lifetime;
u_int8_t hr;
{
struct mip6_bul *bulp;
int s;
bulp = (struct mip6_bul *)MALLOC(sizeof(struct mip6_bul),
M_TEMP, M_WAITOK);
if (bulp == NULL)
return NULL;
bzero(bulp, sizeof(struct mip6_bul));
bulp->next = NULL;
bulp->dst_addr = *dst_addr;
bulp->bind_addr = *bind_addr;
bulp->coa = *coa;
bulp->lifetime = lifetime;
bulp->refreshtime = lifetime;
bulp->seqno = 0;
bulp->lasttime = 0;
bulp->no_of_sent_bu = 0;
bulp->state = NULL;
bulp->bu_flag = 1;
bulp->hr_flag = hr;
bulp->update_rate = MIP6_MAX_UPDATE_RATE;
s = splnet();
if (mip6_bulq == NULL) {
mip6_bulq = bulp;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_bul_handle =
#endif
timeout(mip6_timer_bul, (void *)0, hz);
} else {
bulp->next = mip6_bulq;
mip6_bulq = bulp;
}
splx(s);
#if MIP6_DEBUG
mip6_debug("\nBinding Update List Entry created (0x%x)\n", bulp);
mip6_debug("Destination Address: %s\n", ip6_sprintf(&bulp->dst_addr));
mip6_debug("Binding Address: %s\n", ip6_sprintf(&bulp->bind_addr));
mip6_debug("Care-of Address: %s\n", ip6_sprintf(&bulp->coa));
mip6_debug("Life/Refresh time: %u / %u\n", bulp->lifetime,
bulp->refreshtime);
mip6_debug("Seq no/Home reg: %u / ", bulp->seqno);
if (bulp->hr_flag)
mip6_debug("TRUE\n");
else
mip6_debug("FALSE\n");
#endif
return bulp;
}
struct mip6_bul *
mip6_bul_delete(bul_remove)
struct mip6_bul *bul_remove;
{
struct mip6_bul *bulp;
struct mip6_bul *bulp_prev;
struct mip6_bul *bulp_next;
int s;
s = splnet();
bulp_next = NULL;
bulp_prev = NULL;
for (bulp = mip6_bulq; bulp; bulp = bulp->next) {
bulp_next = bulp->next;
if (bulp == bul_remove) {
if (bulp_prev == NULL)
mip6_bulq = bulp->next;
else
bulp_prev->next = bulp->next;
#if MIP6_DEBUG
mip6_debug("\nBU List Entry deleted (0x%x)\n", bulp);
#endif
mip6_clear_retrans(bulp);
_FREE(bulp, M_TEMP);
if (mip6_bulq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_bul, (void *)NULL,
mip6_timer_bul_handle);
callout_handle_init(&mip6_timer_bul_handle);
#else
untimeout(mip6_timer_bul, (void *)NULL);
#endif
}
break;
}
bulp_prev = bulp;
}
splx(s);
return bulp_next;
}
struct mip6_esm *
mip6_esm_find(home_addr)
struct in6_addr *home_addr;
{
struct mip6_esm *esp;
for (esp = mip6_esmq; esp; esp = esp->next) {
if (IN6_ARE_ADDR_EQUAL(home_addr, &esp->home_addr))
return esp;
}
return NULL;
}
struct mip6_esm *
mip6_esm_create(ifp, ha_hn, coa, home_addr, prefix_len, state,
type, lifetime)
struct ifnet *ifp;
struct in6_addr *ha_hn;
struct in6_addr *coa;
struct in6_addr *home_addr;
u_int8_t prefix_len;
int state;
enum esm_type type;
u_int16_t lifetime;
{
struct mip6_esm *esp, *esp_tmp;
int start_timer, s;
esp = (struct mip6_esm *)MALLOC(sizeof(struct mip6_esm),
M_TEMP, M_WAITOK);
if (esp == NULL) {
log(LOG_ERR,
"%s: Could not create an event-state machine\n",
__FUNCTION__);
return NULL;
}
bzero(esp, sizeof(struct mip6_esm));
esp->next = NULL;
esp->ifp = ifp;
esp->ep = NULL;
esp->state = state;
esp->type = type;
esp->home_addr = *home_addr;
esp->prefix_len = prefix_len;
esp->ha_hn = *ha_hn;
esp->coa = *coa;
esp->ha_fn = NULL;
esp->dad = NULL;
if (type == PERMANENT) {
esp->lifetime = 0xFFFF;
start_timer = 0;
} else {
esp->lifetime = lifetime;
start_timer = 1;
}
for (esp_tmp = mip6_esmq; esp_tmp; esp_tmp = esp_tmp->next) {
if (esp_tmp->type == TEMPORARY)
start_timer = 0;
}
s = splnet();
if (mip6_esmq == NULL)
mip6_esmq = esp;
else {
esp->next = mip6_esmq;
mip6_esmq = esp;
}
splx(s);
if (start_timer) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_esm_handle =
#endif
timeout(mip6_timer_esm, (void *)0, hz);
}
return esp;
}
struct mip6_esm *
mip6_esm_delete(esm_remove)
struct mip6_esm *esm_remove;
{
struct mip6_esm *esp;
struct mip6_esm *esp_prev;
struct mip6_esm *esp_next;
int s;
s = splnet();
esp_next = NULL;
esp_prev = NULL;
for (esp = mip6_esmq; esp; esp = esp->next) {
esp_next = esp->next;
if (esp == esm_remove) {
if (esp_prev == NULL)
mip6_esmq = esp->next;
else
esp_prev->next = esp->next;
mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL, MIP6_NODE_MN,
(void *)esp);
if (esp->dad) {
if (esp->dad->hal)
_FREE(esp->dad->hal, M_TEMP);
_FREE(esp->dad, M_TEMP);
}
if (esp->ha_fn) {
_FREE(esp->ha_fn, M_TEMP);
esp->ha_fn = NULL;
}
#if MIP6_DEBUG
mip6_debug("\nEvent-state machine deleted (0x%x)\n",
esp);
#endif
_FREE(esp, M_TEMP);
if (mip6_esmq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_esm, (void *)NULL,
mip6_timer_esm_handle);
callout_handle_init(&mip6_timer_esm_handle);
#else
untimeout(mip6_timer_esm, (void *)NULL);
#endif
}
break;
}
esp_prev = esp;
}
splx(s);
return esp_next;
}
int
mip6_outq_create(opt, subbuf, src_addr, dst_addr, flag)
void *opt;
struct mip6_subbuf *subbuf;
struct in6_addr *src_addr;
struct in6_addr *dst_addr;
enum send_state flag;
{
struct mip6_output *outp;
int s;
outp = (struct mip6_output *)MALLOC(sizeof(struct mip6_output),
M_TEMP, M_WAITOK);
if (outp == NULL)
return ENOBUFS;
bzero(outp, sizeof(struct mip6_output));
outp->next = NULL;
outp->opt = opt;
outp->subopt = subbuf;
outp->ip6_dst = *dst_addr;
outp->ip6_src = *src_addr;
outp->flag = flag;
outp->lifetime = MIP6_OUTQ_LIFETIME;
s = splnet();
if (mip6_outq == NULL) {
mip6_outq = outp;
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_outqueue_handle =
#endif
timeout(mip6_timer_outqueue, (void *)0,
hz * (MIP6_OUTQ_INTERVAL/10));
} else {
outp->next = mip6_outq;
mip6_outq = outp;
}
splx(s);
return 0;
}
struct mip6_output *
mip6_outq_delete(oqp_remove)
struct mip6_output *oqp_remove;
{
struct mip6_output *oqp;
struct mip6_output *oqp_prev;
struct mip6_output *oqp_next;
int s;
s = splnet();
oqp_next = NULL;
oqp_prev = NULL;
for (oqp = mip6_outq; oqp; oqp = oqp->next) {
oqp_next = oqp->next;
if (oqp == oqp_remove) {
if (oqp_prev == NULL)
mip6_outq = oqp->next;
else
oqp_prev->next = oqp->next;
if (oqp->opt)
_FREE(oqp->opt, M_TEMP);
if (oqp->subopt)
_FREE(oqp->subopt, M_TEMP);
#if MIP6_DEBUG
mip6_debug("\nOutput Queue entry deleted (0x%x)\n",
oqp);
#endif
_FREE(oqp, M_TEMP);
if (mip6_outq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_outqueue, (void *)NULL,
mip6_timer_outqueue_handle);
callout_handle_init(
&mip6_timer_outqueue_handle);
#else
untimeout(mip6_timer_outqueue, (void *)NULL);
#endif
}
break;
}
oqp_prev = oqp;
}
splx(s);
return oqp_next;
}
void
mip6_outq_flush()
{
struct mip6_output *outp;
struct ip6_pktopts *pktopt;
struct mip6_opt *opt;
struct mbuf *m_ip6;
int error;
int off;
int s;
s = splnet();
for (outp = mip6_outq; outp;) {
if (outp->flag == NOT_SENT) {
m_ip6 = mip6_create_ip6hdr(&outp->ip6_src,
&outp->ip6_dst,
IPPROTO_NONE);
if (m_ip6 == NULL) {
outp = outp->next;
continue;
}
pktopt = (struct ip6_pktopts *)
MALLOC(sizeof(struct ip6_pktopts),
M_TEMP, M_WAITOK);
if (pktopt == NULL) {
_FREE(m_ip6, M_TEMP);
outp = outp->next;
continue;
}
bzero(pktopt, sizeof(struct ip6_pktopts));
pktopt->ip6po_hlim = -1;
opt = (struct mip6_opt *)outp->opt;
off = 2;
if (opt->type == IP6OPT_BINDING_UPDATE) {
error = mip6_add_bu(&pktopt->ip6po_dest2,
&off,
(struct mip6_opt_bu *)
outp->opt,
outp->subopt);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
outp = outp->next;
continue;
}
} else if (opt->type == IP6OPT_BINDING_ACK) {
error = mip6_add_ba(&pktopt->ip6po_dest2,
&off,
(struct mip6_opt_ba *)
outp->opt,
outp->subopt);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
outp = outp->next;
continue;
}
} else if (opt->type == IP6OPT_BINDING_REQ) {
error = mip6_add_br(&pktopt->ip6po_dest2,
&off,
(struct mip6_opt_br *)
outp->opt,
outp->subopt);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
outp = outp->next;
continue;
}
}
mip6_config.enable_outq = 0;
error = ip6_output(m_ip6, pktopt, NULL, 0, NULL, NULL);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
mip6_config.enable_outq = 1;
outp = outp->next;
log(LOG_ERR,
"%s: ip6_output function failed, "
"error = %d\n", __FUNCTION__, error);
continue;
}
mip6_config.enable_outq = 1;
outp->flag = SENT;
#if MIP6_DEBUG
mip6_debug("\nEntry from Output Queue sent\n");
#endif
}
if (outp->flag == SENT)
outp = mip6_outq_delete(outp);
else
outp = outp->next;
if (mip6_outq == NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
untimeout(mip6_timer_outqueue, (void *)NULL,
mip6_timer_outqueue_handle);
callout_handle_init(&mip6_timer_outqueue_handle);
#else
untimeout(mip6_timer_outqueue, (void *)NULL);
#endif
}
}
splx(s);
}
void
mip6_timer_outqueue(arg)
void *arg;
{
struct mip6_output *outp;
struct ip6_pktopts *pktopt;
struct mip6_opt *opt;
struct mbuf *m_ip6;
int error;
int off;
#ifdef __APPLE__
boolean_t funnel_state;
funnel_state = thread_set_funneled(TRUE);
#endif
for (outp = mip6_outq; outp;) {
if (outp->flag == NOT_SENT)
outp->lifetime -= MIP6_OUTQ_INTERVAL;
if ((outp->flag == NOT_SENT) && (outp->lifetime <= 0)) {
m_ip6 = mip6_create_ip6hdr(&outp->ip6_src,
&outp->ip6_dst,
IPPROTO_NONE);
if (m_ip6 == NULL) {
outp = outp->next;
continue;
}
pktopt = (struct ip6_pktopts *)
MALLOC(sizeof(struct ip6_pktopts),
M_TEMP, M_WAITOK);
if (pktopt == NULL) {
_FREE(m_ip6, M_TEMP);
outp = outp->next;
continue;
}
bzero(pktopt, sizeof(struct ip6_pktopts));
pktopt->ip6po_hlim = -1;
opt = (struct mip6_opt *)outp->opt;
off = 2;
if (opt->type == IP6OPT_BINDING_UPDATE) {
error = mip6_add_bu(&pktopt->ip6po_dest2,
&off,
(struct mip6_opt_bu *)
outp->opt,
outp->subopt);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
outp = outp->next;
continue;
}
} else if (opt->type == IP6OPT_BINDING_ACK) {
error = mip6_add_ba(&pktopt->ip6po_dest2,
&off,
(struct mip6_opt_ba *)
outp->opt,
outp->subopt);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
outp = outp->next;
continue;
}
} else if (opt->type == IP6OPT_BINDING_REQ) {
error = mip6_add_br(&pktopt->ip6po_dest2,
&off,
(struct mip6_opt_br *)
outp->opt,
outp->subopt);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
outp = outp->next;
continue;
}
}
mip6_config.enable_outq = 0;
error = ip6_output(m_ip6, pktopt, NULL, 0, NULL, NULL);
if (error) {
_FREE(m_ip6, M_TEMP);
_FREE(pktopt, M_TEMP);
mip6_config.enable_outq = 1;
outp = outp->next;
log(LOG_ERR,
"%s: ip6_output function failed, "
"error = %d\n", __FUNCTION__, error);
continue;
}
mip6_config.enable_outq = 1;
outp->flag = SENT;
#if MIP6_DEBUG
mip6_debug("\nEntry from Output Queue sent\n");
#endif
}
if (outp->flag == SENT)
outp = mip6_outq_delete(outp);
else
outp = outp->next;
}
if (mip6_outq != NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_outqueue_handle =
#endif
timeout(mip6_timer_outqueue, (void *)0,
hz * (MIP6_OUTQ_INTERVAL/10));
}
#ifdef __APPLE__
(void) thread_set_funneled(funnel_state);
#endif
}
void
mip6_timer_bul(arg)
void *arg;
{
struct mip6_bul *bulp;
struct mip6_bul *new_bulp;
struct mip6_esm *esp;
struct mip6_opt_bu *bu_opt;
struct in6_addr *dst_addr;
struct mip6_subbuf *subbuf;
struct mip6_bu_data bu_data;
struct mip6_subopt_coa altcoa;
u_int32_t lifetime;
int max_index, s;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
#ifdef __APPLE__
boolean_t funnel_state;
funnel_state = thread_set_funneled(TRUE);
#endif
subbuf = NULL;
s = splnet();
for (bulp = mip6_bulq; bulp;) {
esp = mip6_esm_find(&bulp->bind_addr);
if (esp == NULL) {
bulp = bulp->next;
continue;
}
if (bulp->lifetime == 0xffffffff) {
bulp = bulp->next;
continue;
}
bulp->lifetime -= 1;
if (bulp->lifetime == 0) {
if ((bulp->hr_flag) && (esp->type == PERMANENT)) {
if ((esp->state == MIP6_STATE_REG) ||
(esp->state == MIP6_STATE_REREG) ||
(esp->state == MIP6_STATE_REGNEWCOA) ||
(esp->state == MIP6_STATE_NOTREG))
esp->state = MIP6_STATE_NOTREG;
else if ((esp->state == MIP6_STATE_HOME) ||
(esp->state == MIP6_STATE_DEREG))
esp->state = MIP6_STATE_DEREG;
else
esp->state = MIP6_STATE_UNDEF;
if (esp->dad) {
dst_addr = &esp->dad->hal->
halist[esp->dad->index];
max_index = (esp->dad->hal->len /
IP6OPT_HALEN) - 1;
if (esp->dad->index == max_index)
esp->dad->index = 0;
else
esp->dad->index += 1;
lifetime = MIP6_BU_LIFETIME_DHAAD;
} else {
dst_addr = &esp->ha_hn;
lifetime = mip6_config.hr_lifetime;
}
new_bulp = mip6_bul_create(dst_addr,
&esp->home_addr,
&bulp->coa,
lifetime, 1);
if (new_bulp == NULL)
break;
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
if (mip6_send_bu(new_bulp, &bu_data, NULL)
!= 0)
break;
}
bulp = mip6_bul_delete(bulp);
continue;
}
if (bulp->refreshtime > 0)
bulp->refreshtime -= 1;
if (bulp->bu_flag == 0) {
bulp = bulp->next;
continue;
}
if (bulp->state != NULL) {
bulp->state->time_left -= 1;
if (bulp->state->time_left == 0) {
if (bulp->hr_flag) {
bulp->state->bu_opt->lifetime =
bulp->lifetime;
bulp->state->bu_opt->seqno++;
if (mip6_send_bu(bulp, NULL, NULL)
!= 0)
break;
if (bulp->state->ba_timeout <
MIP6_MAX_BINDACK_TIMEOUT)
bulp->state->ba_timeout =
2 * bulp->state->
ba_timeout;
else
bulp->state->ba_timeout =
(u_int8_t)MIP6_MAX_BINDACK_TIMEOUT;
bulp->state->time_left = bulp->state->ba_timeout;
} else {
if (bulp->state->ba_timeout >= MIP6_MAX_BINDACK_TIMEOUT) {
bulp->no_of_sent_bu = 0;
mip6_clear_retrans(bulp);
} else {
bulp->state->bu_opt->lifetime = bulp->lifetime;
bulp->state->bu_opt->seqno++;
if (mip6_send_bu(bulp, NULL, NULL) != 0)
break;
bulp->state->ba_timeout = 2 * bulp->state->ba_timeout;
bulp->state->time_left = bulp->state->ba_timeout;
}
}
}
bulp = bulp->next;
continue;
}
if (bulp->refreshtime == 0) {
altcoa.type = IP6SUBOPT_ALTCOA;
altcoa.len = IP6OPT_COALEN;
altcoa.coa = bulp->coa;
if (mip6_store_subopt(&subbuf, (caddr_t)&altcoa)) {
if (subbuf)
_FREE(subbuf, M_TEMP);
break;
}
if (bulp->hr_flag) {
bu_data.prefix_len = esp->prefix_len;
bu_data.ack = 1;
bulp->lifetime = mip6_config.hr_lifetime;
if (mip6_send_bu(bulp, &bu_data, subbuf) != 0)
break;
} else {
bulp->seqno += 1;
bu_opt = mip6_create_bu(0, 0, 0, bulp->seqno,
mip6_config.hr_lifetime);
if (bu_opt == NULL)
break;
bulp->lasttime = time_second;
mip6_outq_create(bu_opt, subbuf, &bulp->bind_addr,
&bulp->dst_addr, NOT_SENT);
}
bulp = bulp->next;
continue;
}
bulp = bulp->next;
}
if (mip6_bulq != NULL) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_bul_handle =
#endif
timeout(mip6_timer_bul, (void *)0, hz);
}
splx(s);
#ifdef __APPLE__
(void) thread_set_funneled(funnel_state);
#endif
}
void
mip6_timer_esm(arg)
void *arg;
{
struct mip6_esm *esp;
int s, start_timer;
#ifdef __APPLE__
boolean_t funnel_state;
funnel_state = thread_set_funneled(TRUE);
#endif
s = splnet();
for (esp = mip6_esmq; esp;) {
if (esp->type == TEMPORARY) {
esp->lifetime -= 1;
if (esp->lifetime == 0)
esp = mip6_esm_delete(esp);
else
esp = esp->next;
continue;
}
esp = esp->next;
}
start_timer = 0;
for (esp = mip6_esmq; esp; esp = esp->next) {
if (esp->type == TEMPORARY) {
start_timer = 1;
break;
}
}
if (start_timer) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
mip6_timer_esm_handle =
#endif
timeout(mip6_timer_esm, (void *)0, hz);
}
splx(s);
#ifdef __APPLE__
(void) thread_set_funneled(funnel_state);
#endif
}
int mip6_write_config_data_mn(u_long cmd, void *arg)
{
struct mip6_esm *p;
struct ifnet *ifp;
struct mip6_input_data *input;
struct mip6_static_addr *np;
char ifn[10];
int retval = 0;
struct in6_addr any = in6addr_any;
switch (cmd) {
case SIOCACOADDR_MIP6:
input = (struct mip6_input_data *) arg;
np = (struct mip6_static_addr *)
MALLOC(sizeof(struct mip6_static_addr),
M_TEMP, M_WAITOK);
if (np == NULL)
return ENOBUFS;
np->ip6_addr = input->ip6_addr;
np->prefix_len = input->prefix_len;
np->ifp = ifunit(input->if_name);
if (np->ifp == NULL) {
strncpy(ifn, input->if_name, sizeof(ifn));
return EINVAL;
}
LIST_INSERT_HEAD(&mip6_config.fna_list, np, addr_entry);
break;
case SIOCAHOMEADDR_MIP6:
input = (struct mip6_input_data *) arg;
ifp = ifunit(input->if_name);
if (ifp == NULL)
return EINVAL;
p = mip6_esm_create(ifp, &input->ha_addr, &any,
&input->ip6_addr, input->prefix_len,
MIP6_STATE_UNDEF, PERMANENT, 0xFFFF);
if (p == NULL)
return EINVAL;
break;
case SIOCSBULIFETIME_MIP6:
mip6_config.bu_lifetime = ((struct mip6_input_data *)arg)->value;
break;
case SIOCSHRLIFETIME_MIP6:
mip6_config.hr_lifetime = ((struct mip6_input_data *)arg)->value;
break;
case SIOCDCOADDR_MIP6:
input = (struct mip6_input_data *) arg;
for (np = mip6_config.fna_list.lh_first; np != NULL;
np = np->addr_entry.le_next){
if (IN6_ARE_ADDR_EQUAL(&input->ip6_addr, &np->ip6_addr))
break;
}
if (np == NULL){
retval = EADDRNOTAVAIL;
return retval;
}
LIST_REMOVE(np, addr_entry);
break;
}
return retval;
}
int mip6_clear_config_data_mn(u_long cmd, caddr_t data)
{
int retval = 0;
int s;
struct mip6_static_addr *np;
struct mip6_bul *bulp;
s = splnet();
switch (cmd) {
case SIOCSFORADDRFLUSH_MIP6:
for (np = LIST_FIRST(&mip6_config.fna_list); np;
np = LIST_NEXT(np, addr_entry)) {
LIST_REMOVE(np, addr_entry);
}
break;
case SIOCSHADDRFLUSH_MIP6:
retval = EINVAL;
break;
case SIOCSBULISTFLUSH_MIP6:
for (bulp = mip6_bulq; bulp;)
bulp = mip6_bul_delete(bulp);
break;
}
splx(s);
return retval;
}
int mip6_enable_func_mn(u_long cmd, caddr_t data)
{
int enable;
int retval = 0;
enable = ((struct mip6_input_data *)data)->value;
switch (cmd) {
case SIOCSPROMMODE_MIP6:
mip6_config.enable_prom_mode = enable;
break;
case SIOCSBU2CN_MIP6:
mip6_config.enable_bu_to_cn = enable;
break;
case SIOCSREVTUNNEL_MIP6:
mip6_config.enable_rev_tunnel = enable;
break;
case SIOCSAUTOCONFIG_MIP6:
mip6_config.autoconfig = enable;
break;
case SIOCSEAGERMD_MIP6:
mip6_eager_md(enable);
break;
}
return retval;
}