#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/proc.h>
#include <sys/mbuf.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
#include <netinet6/mip6.h>
#include <netinet6/mip6_common.h>
extern int (*mip6_store_dstopt_pre_hook)(struct mbuf *, u_int8_t *,
u_int8_t, u_int8_t);
extern int (*mip6_rec_ctrl_sig_hook)(struct mbuf *, int);
extern int (*mip6_new_packet_hook)(struct mbuf *);
extern int (*mip6_icmp6_input_hook)(struct mbuf *, int);
extern int (*mip6_output_hook)(struct mbuf *, struct ip6_pktopts **);
extern int (*mip6_rec_ra_hook)(struct mbuf *, int);
extern struct in6_addr * (*mip6_global_addr_hook)(struct in6_addr *);
extern struct mip6_subopt_hal * (*mip6_hal_dynamic_hook)(struct in6_addr *);
extern int (*mip6_write_config_data_ha_hook)(u_long, void *);
extern int (*mip6_clear_config_data_ha_hook)(u_long, void *);
extern int (*mip6_enable_func_ha_hook)(u_long, caddr_t);
extern void (*mip6_icmp6_output_hook)(struct mbuf *);
extern int (*mip6_route_optimize_hook)(struct mbuf *);
extern void (*mip6_select_defrtr_hook)(void);
extern struct nd_prefix * (*mip6_get_home_prefix_hook)(void);
extern void (*mip6_prelist_update_hook)(struct nd_prefix *,
struct nd_defrouter *);
extern void (*mip6_expired_defrouter_hook)(struct nd_defrouter *);
extern void (*mip6_probe_pfxrtrs_hook)(void);
extern void (*mip6_store_advint_hook)(struct nd_opt_advint *,
struct nd_defrouter *);
extern int (*mip6_get_md_state_hook)(void);
extern int (*mip6_rec_ba_hook)(struct mbuf *, int);
extern int (*mip6_rec_br_hook)(struct mbuf *, int);
extern void (*mip6_stop_bu_hook)(struct in6_addr *);
extern int (*mip6_write_config_data_mn_hook)(u_long, void *);
extern int (*mip6_clear_config_data_mn_hook)(u_long, caddr_t);
extern int (*mip6_enable_func_mn_hook)(u_long, caddr_t);
extern void (*mip6_minus_a_case_hook)(struct nd_prefix *);
extern struct mip6_esm * (*mip6_esm_find_hook)(struct in6_addr *);
void
mip6_minus_a_case(struct nd_prefix *pr)
{
struct in6_addr addr;
if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr) ||
IN6_IS_ADDR_MULTICAST(&pr->ndpr_addr) ||
IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_addr)) {
return;
}
addr = in6addr_any;
mip6_esm_create(pr->ndpr_ifp, NULL, &addr, &pr->ndpr_addr,
pr->ndpr_plen, MIP6_STATE_UNDEF, PERMANENT, 0xFFFF);
#if MIP6_DEBUG
mip6_debug("Late Home Address %s found for autoconfig'd case. Starting"
" Mobile IPv6.\n", ip6_sprintf(&pr->ndpr_addr));
#endif
mip6_minus_a_case_hook = 0;
mip6_enable_hooks(MIP6_SPECIFIC_HOOKS);
mip6_md_init();
}
struct nd_prefix *
mip6_find_auto_home_addr(void)
{
struct nd_prefix *pr;
#if 0
struct in6_ifaddr *ia6;
#endif
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
#if MIP6_DEBUG
mip6_debug("%s: scanning prefix %s (pr = %p)\n", __FUNCTION__,
ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr);
#endif
if (IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr) ||
IN6_IS_ADDR_MULTICAST(&pr->ndpr_addr) ||
IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_addr)) {
continue;
}
#if 0
ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr);
if (ia6 && (ia6->ia6_flags | IN6_IFF_DETACHED))
continue;
else
break;
#else
#if MIP6_DEBUG
mip6_debug("%s: skipping detached test on prefix %s "
"(pr = %p)\n", __FUNCTION__,
ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr);
#endif
break;
#endif
#if 0
for (pfxrtr = pr->ndpr_advrtrs.lh_first; pfxrtr;
pfxrtr = pfxrtr->pfr_next) {
if ((pfxrtr->router->flags & ND_RA_FLAG_HA)
== ND_RA_FLAG_HA)
break;
}
#endif
}
if (pr) {
#if MIP6_DEBUG
mip6_debug("Found an autoconfigured home address "
"immediately: %s\n", ip6_sprintf(&pr->ndpr_addr));
#endif
}
else {
#if MIP6_DEBUG
mip6_debug("Couldn't find an autoconfigured home address "
"immediately.\n");
#endif
}
return pr;
}
void
mip6_enable_hooks(int scope)
{
int s;
s = splimp();
if (scope == MIP6_GENERIC_HOOKS) {
mip6_store_dstopt_pre_hook = mip6_store_dstopt_pre;
mip6_rec_ctrl_sig_hook = mip6_rec_ctrl_sig;
mip6_new_packet_hook = mip6_new_packet;
mip6_icmp6_input_hook = mip6_icmp6_input;
mip6_output_hook = mip6_output;
}
if (scope == MIP6_CONFIG_HOOKS) {
mip6_write_config_data_ha_hook = mip6_write_config_data_ha;
mip6_clear_config_data_ha_hook = mip6_clear_config_data_ha;
mip6_enable_func_ha_hook = mip6_enable_func_ha;
mip6_write_config_data_mn_hook = mip6_write_config_data_mn;
mip6_clear_config_data_mn_hook = mip6_clear_config_data_mn;
mip6_enable_func_mn_hook = mip6_enable_func_mn;
}
if (scope == MIP6_SPECIFIC_HOOKS) {
if (MIP6_IS_HA_ACTIVE) {
mip6_rec_ra_hook = mip6_rec_raha;
mip6_global_addr_hook = mip6_global_addr;
mip6_hal_dynamic_hook = mip6_hal_dynamic;
mip6_icmp6_output_hook = mip6_icmp6_output;
}
if (MIP6_IS_MN_ACTIVE) {
mip6_route_optimize_hook = mip6_route_optimize;
mip6_rec_ra_hook = mip6_rec_ramn;
mip6_select_defrtr_hook = mip6_select_defrtr;
mip6_get_home_prefix_hook = mip6_get_home_prefix;
mip6_prelist_update_hook = mip6_prelist_update;
mip6_expired_defrouter_hook = mip6_expired_defrouter;
mip6_probe_pfxrtrs_hook = mip6_probe_pfxrtrs;
mip6_store_advint_hook = mip6_store_advint;
mip6_get_md_state_hook = mip6_get_md_state;
mip6_rec_ba_hook = mip6_rec_ba;
mip6_rec_br_hook = mip6_rec_bu;
mip6_stop_bu_hook = mip6_stop_bu;
mip6_esm_find_hook = mip6_esm_find;
}
}
splx(s);
return;
}
void
mip6_disable_hooks(int scope)
{
int s;
s = splimp();
if (scope == MIP6_GENERIC_HOOKS) {
mip6_store_dstopt_pre_hook = 0;
mip6_rec_ctrl_sig_hook = 0;
mip6_new_packet_hook = 0;
mip6_icmp6_input_hook = 0;
mip6_output_hook = 0;
}
if (scope == MIP6_SPECIFIC_HOOKS) {
if (MIP6_IS_HA_ACTIVE) {
mip6_rec_ra_hook = 0;
mip6_global_addr_hook = 0;
mip6_hal_dynamic_hook = 0;
mip6_write_config_data_ha_hook = 0;
mip6_clear_config_data_ha_hook = 0;
mip6_enable_func_ha_hook = 0;
}
if (MIP6_IS_MN_ACTIVE) {
mip6_route_optimize_hook = 0;
mip6_rec_ra_hook = 0;
mip6_select_defrtr_hook = 0;
mip6_get_home_prefix_hook = 0;
mip6_prelist_update_hook = 0;
mip6_expired_defrouter_hook = 0;
mip6_probe_pfxrtrs_hook = 0;
mip6_store_advint_hook = 0;
mip6_get_md_state_hook = 0;
mip6_rec_ba_hook = 0;
mip6_rec_br_hook = 0;
mip6_stop_bu_hook = 0;
mip6_write_config_data_mn_hook = 0;
mip6_clear_config_data_mn_hook = 0;
mip6_enable_func_mn_hook = 0;
mip6_esm_find_hook = 0;
mip6_minus_a_case_hook = 0;
}
}
splx(s);
return;
}
int
mip6_attach(int module)
{
if (mip6_module) {
#if MIP6_DEBUG
char *old = "?", *new = "?";
if (mip6_module == MIP6_HA_MODULE)
strcpy(old, "Home Agent");
if (mip6_module == MIP6_MN_MODULE)
strcpy(old, "Mobile Node");
if (module == MIP6_HA_MODULE)
strcpy(new, "Home Agent");
if (module == MIP6_MN_MODULE)
strcpy(new, "Mobile Node");
mip6_debug("Can't switch operation mode from %s to %s \n"
"- please deactivate first (\"mip6config -x\")\n",
old, new);
#endif
return EINVAL;
}
switch (module) {
case MIP6_HA_MODULE:
printf("%s: attach ha\n", __FUNCTION__);
mip6_module = module;
mip6_ha_init();
break;
case MIP6_MN_MODULE:
printf("%s: attach mn\n", __FUNCTION__);
mip6_module = module;
mip6_mn_init();
break;
default:
#if MIP6_DEBUG
mip6_debug("%s: illegal attach (module = %d)\n", __FUNCTION__,
module);
#endif
return EINVAL;
}
if (MIP6_IS_MN_ACTIVE) {
if(mip6_get_home_prefix_hook)
return 0;
if (mip6_config.autoconfig) {
struct nd_prefix *pr;
struct in6_addr addr;
addr = in6addr_any;
if ((pr = mip6_find_auto_home_addr()) != NULL) {
mip6_esm_create(pr->ndpr_ifp, &addr, &addr,
&pr->ndpr_addr,pr->ndpr_plen,
MIP6_STATE_UNDEF, PERMANENT,
0xFFFF);
mip6_enable_hooks(MIP6_SPECIFIC_HOOKS);
mip6_md_init();
}
else {
#if MIP6_DEBUG
mip6_debug("Waiting for Router Advertisement "
"to give me an address.\n");
#endif
mip6_minus_a_case_hook = mip6_minus_a_case;
}
}
else {
mip6_enable_hooks(MIP6_SPECIFIC_HOOKS);
mip6_md_init();
}
}
if (MIP6_IS_HA_ACTIVE) {
mip6_enable_hooks(MIP6_SPECIFIC_HOOKS);
}
return 0;
}
int
mip6_release(void)
{
mip6_disable_hooks(MIP6_SPECIFIC_HOOKS);
if (MIP6_IS_MN_ACTIVE) {
mip6_mn_exit();
mip6_md_exit();
}
if (MIP6_IS_HA_ACTIVE)
mip6_ha_exit();
mip6_module = 0;
return 0;
}