diff -uN --exclude=CVS kame-20010611/TODO.mobile-ip6 kame/TODO.mobile-ip6 --- kame-20010611/TODO.mobile-ip6 Sun Jun 10 19:19:15 2001 +++ kame/TODO.mobile-ip6 Mon Jun 11 13:47:42 2001 @@ -1,11 +1,41 @@ TODOs for mobile-ip6 KAME project -$KAME: TODO.mobile-ip6,v 1.12 2001/06/10 10:19:15 keiichi Exp $ +$KAME: TODO.mobile-ip6,v 1.11 2001/03/29 05:34:27 itojun Exp $ -- provide a single mobile-ip6 stack. +- Finish DHAAD over icmp6. +- Finish Home network renumbering and tunneled RS/RA. +- IPsec issues: other than manual keys of type "any". +- KLD support for platforms that support it. (not interesting /Mattias) +- Test. +- there's no vprintf() in kernel for bsdi[34]. (sys/netinet6/mip6.c) -The Ericsson's code integrated before has been removed. Tha last -kame-snap that includes MIP code is 20010604. You can also retreive a -patch for the 20010611 snap from -ftp://ftp.kame.net/pub/kame/contrib/mip6/ericsson. +done: +- ioctl should use error code declared in sys/errno.h, not others. +- "struct input_data" in mip6_common.h is way too generic name and + I've changed it into mip6_input_data. +- function prototypes with __P(). +- rtadvd needs to preserve non-mobileip6 behavior so I added a command + line option (-m). +- tcpdump can use netinet6/mip6.h decls so I've used them. + may need to back out the change or integrate part of mip6.h into + print-{icmp6,ip6opts}.c when we integrate the change into + tcpdump.org tree. +- portability fixes for non-freebsd3. +- Nuke in6_control() calls from within the kernel. +- Remove MIP6_MN and MIP6_HA #ifdef. Too many #ifdef will kill us, and it is + painful to reconfigure kernel every time we switch the operation. ioctl + or sysctl should switch the behavior (it is okay to have one-way switch: + if can require reboot to go back to normal stationary node). +- Make routing table/inpcb refresh code portable. Use of in6_rtchange with + rnh->rnh_walktree is suggested. +- make use of encap6_attach(), instead of gif interface. +- Indentation. mip6*.c is not consistent even within a files. At least + rule should be consistent across the file. +- relationship with IPsec. +- Call ip6_output from mip6_tunnel_output. +- mbuf leakage. +- Minimize copying of structs. +- No faked home nd_prefix. +- Home addresses are not bound to physical interfaces. +- Support I-D v13. diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/kame/rtadvd/config.c kame/kame/kame/rtadvd/config.c --- kame-20010611/kame/kame/rtadvd/config.c Fri Jun 8 13:46:19 2001 +++ kame/kame/kame/rtadvd/config.c Mon Jun 11 12:53:57 2001 @@ -1,4 +1,4 @@ -/* $KAME: config.c,v 1.48 2001/06/08 04:46:19 jinmei Exp $ */ +/* $KAME: config.c,v 1.50 2001/06/11 03:12:41 itojun Exp $ */ /* * Copyright (C) 1998 WIDE Project. @@ -76,7 +76,7 @@ extern struct rainfo *ralist; -#ifdef defined(__FreeBSD__) && __FreeBSD__ <= 3 /* XXX: see PORTABILITY */ +#if defined(__FreeBSD__) && __FreeBSD__ <= 3 /* XXX: see PORTABILITY */ #define LONGLONG "%qd" #else #define LONGLONG "%lld" @@ -161,7 +161,7 @@ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { syslog(LOG_ERR, - "<%s> maxinterval (%d) on %s is invalid " + "<%s> maxinterval (%ld) on %s is invalid " "(must be between %e and %u)", __FUNCTION__, val, intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL); exit(1); @@ -170,7 +170,7 @@ MAYHAVE(val, "mininterval", tmp->maxinterval/3); if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) { syslog(LOG_ERR, - "<%s> mininterval (%d) on %s is invalid " + "<%s> mininterval (%ld) on %s is invalid " "(must be between %e and %d)", __FUNCTION__, val, intface, MIN_MININTERVAL, (tmp->maxinterval * 3) / 4); @@ -202,7 +202,7 @@ MAYHAVE(val, "rltime", tmp->maxinterval * 3); if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { syslog(LOG_ERR, - "<%s> router lifetime (%d) on %s is invalid " + "<%s> router lifetime (%ld) on %s is invalid " "(must be 0 or between %d and %d)", __FUNCTION__, val, intface, tmp->maxinterval, tmp->maxinterval, MAXROUTERLIFETIME); @@ -239,7 +239,7 @@ if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> retrans time (" LONGLONG ") on %s out of range", - __FUNCTION__, val64, intface); + __FUNCTION__, (long long)val64, intface); exit(1); } tmp->retranstimer = (u_int32_t)val64; @@ -383,7 +383,7 @@ if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> vltime (" LONGLONG ") for %s/%d on %s is out of range", - __FUNCTION__, val64, + __FUNCTION__, (long long)val64, addr, pfx->prefixlen, intface); exit(1); } @@ -403,7 +403,7 @@ syslog(LOG_ERR, "<%s> pltime (" LONGLONG ") for %s/%d on %s is out of range", - __FUNCTION__, val64, + __FUNCTION__, (long long)val64, addr, pfx->prefixlen, intface); exit(1); } @@ -436,10 +436,10 @@ } else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) { syslog(LOG_ERR, - "<%s> advertised link mtu (%ld) on %s is invalid (must " + "<%s> advertised link mtu (%lu) on %s is invalid (must " "be between least MTU (%d) and physical link MTU (%d)", - __FUNCTION__, tmp->linkmtu, intface, IPV6_MMTU, - tmp->phymtu); + __FUNCTION__, (unsigned long)tmp->linkmtu, intface, + IPV6_MMTU, tmp->phymtu); exit(1); } @@ -534,7 +534,7 @@ if (val64 < 0 || val64 > 0xffffffff) { syslog(LOG_ERR, "<%s> route lifetime (" LONGLONG ") for %s/%d on %s out of range", __FUNCTION__, - (long)val64, addr, rti->prefixlen, intface); + (long long)val64, addr, rti->prefixlen, intface); exit(1); } rti->ltime = (u_int32_t)val64; diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/kame/rtadvd/dump.c kame/kame/kame/rtadvd/dump.c --- kame-20010611/kame/kame/rtadvd/dump.c Fri Jun 8 13:49:43 2001 +++ kame/kame/kame/rtadvd/dump.c Mon Jun 11 12:53:58 2001 @@ -1,4 +1,4 @@ -/* $KAME: dump.c,v 1.19 2001/06/08 04:49:43 jinmei Exp $ */ +/* $KAME: dump.c,v 1.20 2001/06/11 03:09:54 itojun Exp $ */ /* * Copyright (C) 2000 WIDE Project. @@ -65,7 +65,7 @@ static char *ether_str __P((struct sockaddr_dl *)); static void if_dump __P((void)); -#ifdef defined(__FreeBSD__) && __FreeBSD__ <= 3 /* XXX: see PORTABILITY */ +#if defined(__FreeBSD__) && __FreeBSD__ <= 3 /* XXX: see PORTABILITY */ #define LONGLONG "%qu" #else #define LONGLONG "%llu" diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/dest6.c kame/kame/sys/netinet6/dest6.c --- kame-20010611/kame/sys/netinet6/dest6.c Mon Jun 4 17:52:43 2001 +++ kame/kame/sys/netinet6/dest6.c Thu Mar 29 14:34:30 2001 @@ -1,4 +1,4 @@ -/* $KAME: dest6.c,v 1.28 2001/06/04 08:52:43 keiichi Exp $ */ +/* $KAME: dest6.c,v 1.27 2001/03/29 05:34:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -60,6 +60,10 @@ #endif #include +#ifdef MIP6 +#include +#endif + /* * Destination options header processing. */ @@ -175,6 +179,20 @@ */ break; + +#ifdef MIP6 + case IP6OPT_BINDING_UPDATE: + case IP6OPT_BINDING_ACK: + case IP6OPT_BINDING_REQ: + if (dstoptlen < IP6OPT_MINLEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (mip6_dstopt(m, dstopts, opt, dstoptlen) == -1) + goto bad; + optlen = *(opt + 1) + 2; + break; +#endif /* MIP6 */ default: /* unknown option */ optlen = ip6_unknown_opt(opt, m, diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/icmp6.c kame/kame/sys/netinet6/icmp6.c --- kame-20010611/kame/sys/netinet6/icmp6.c Mon Jun 4 17:53:12 2001 +++ kame/kame/sys/netinet6/icmp6.c Mon Jun 4 10:25:01 2001 @@ -1,4 +1,4 @@ -/* $KAME: icmp6.c,v 1.213 2001/06/04 08:53:12 keiichi Exp $ */ +/* $KAME: icmp6.c,v 1.212 2001/06/01 05:35:52 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -127,6 +127,10 @@ #include #endif +#ifdef MIP6 +#include +#endif + #include #ifdef HAVE_NRL_INPCB @@ -556,6 +560,10 @@ switch (icmp6->icmp6_type) { case ICMP6_DST_UNREACH: icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); +#ifdef MIP6 + if (mip6_icmp6_input(m, off, icmp6len)) + goto freeit; +#endif switch (code) { case ICMP6_DST_UNREACH_NOROUTE: code = PRC_UNREACH_NET; @@ -621,6 +629,10 @@ break; case ICMP6_PARAMPROB_HEADER: case ICMP6_PARAMPROB_OPTION: +#ifdef MIP6 + if (mip6_icmp6_input(m, off, icmp6len)) + goto freeit; +#endif code = PRC_PARAMPROB; break; default: @@ -850,6 +862,10 @@ m = NULL; goto freeit; } +#ifdef MIP6 + if (mip6_icmp6_input(n, off, icmp6len)) + goto freeit; +#endif nd6_ra_input(n, off, icmp6len); /* m stays. */ break; @@ -909,6 +925,18 @@ if (icmp6len < sizeof(struct icmp6_router_renum)) goto badlen; break; + +#ifdef MIP6 + case ICMP6_HADISCOV_REQUEST: + if (mip6_icmp6_input(m, off, icmp6len)) + goto freeit; + break; + + case ICMP6_HADISCOV_REPLY: + if (mip6_icmp6_input(m, off, icmp6len)) + goto freeit; + break; +#endif default: nd6log((LOG_DEBUG, diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/in6.c kame/kame/sys/netinet6/in6.c --- kame-20010611/kame/sys/netinet6/in6.c Mon Jun 4 17:53:39 2001 +++ kame/kame/sys/netinet6/in6.c Thu May 24 16:43:59 2001 @@ -1,4 +1,4 @@ -/* $KAME: in6.c,v 1.188 2001/06/04 08:53:39 keiichi Exp $ */ +/* $KAME: in6.c,v 1.187 2001/05/24 07:43:59 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -127,6 +127,13 @@ #include #endif +#ifdef MIP6 +#include +#include + +struct nd_prefix *(*mip6_get_home_prefix_hook) __P((void)); +#endif /* MIP6 */ + #include #if defined(__FreeBSD__) && __FreeBSD__ >= 3 @@ -464,6 +471,48 @@ case SIOCGETMIFCNT_IN6: return (mrt6_ioctl(cmd, data)); } + +#ifdef MIP6 + /* These require root privileges */ + switch (cmd) { + case SIOCSDEBUG_MIP6: + case SIOCSBCFLUSH_MIP6: + case SIOCSDEFCONFIG_MIP6: + case SIOCSBRUPDATE_MIP6: + case SIOCSENABLEBR_MIP6: + case SIOCSATTACH_MIP6: + case SIOCSRELEASE_MIP6: + + case SIOCSHALISTFLUSH_MIP6: + case SIOCSHAPREF_MIP6: + case SIOCSFWDSLUNICAST_MIP6: + case SIOCSFWDSLMULTICAST_MIP6: + + case SIOCSFORADDRFLUSH_MIP6: + case SIOCSHADDRFLUSH_MIP6: + case SIOCSBULISTFLUSH_MIP6: + case SIOCACOADDR_MIP6: + case SIOCAHOMEADDR_MIP6: + case SIOCAHOMEPREF_MIP6: + case SIOCSBULIFETIME_MIP6: + case SIOCSHRLIFETIME_MIP6: + case SIOCDCOADDR_MIP6: + case SIOCSPROMMODE_MIP6: + case SIOCSBU2CN_MIP6: + case SIOCSREVTUNNEL_MIP6: + case SIOCSAUTOCONFIG_MIP6: + case SIOCSEAGERMD_MIP6: + if (!privileged) + return(EPERM); + /* Anyone can use these or the user is root */ + /* case SIOCXVERYSAFECOMMAND_MIP6: */ +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) + return mip6_ioctl(so, cmd, data, ifp, p); +#else + return mip6_ioctl(so, cmd, data, ifp); +#endif + } +#endif /* MIP6 */ if (ifp == NULL) return(EOPNOTSUPP); diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/in6.h kame/kame/sys/netinet6/in6.h --- kame-20010611/kame/sys/netinet6/in6.h Mon Jun 11 09:01:27 2001 +++ kame/kame/sys/netinet6/in6.h Mon Jun 11 11:53:08 2001 @@ -77,7 +77,7 @@ * has the table of implementation/integration differences. */ #define __KAME__ -#define __KAME_VERSION "SNAP 20010611" +#define __KAME_VERSION "from cvs repository" /* * Local port number conventions: @@ -811,6 +811,7 @@ #define M_DECRYPTED M_PROTO3 #define M_LOOP M_PROTO4 #define M_AUTHIPDGM M_PROTO5 +#define M_MIP6TUNNEL M_PROTO6 #endif #ifdef _KERNEL diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/in6_proto.c kame/kame/sys/netinet6/in6_proto.c --- kame-20010611/kame/sys/netinet6/in6_proto.c Mon Jun 4 21:55:26 2001 +++ kame/kame/sys/netinet6/in6_proto.c Mon Jun 11 11:09:50 2001 @@ -182,6 +182,10 @@ #include #endif +#ifdef MIP6 +#include +#endif + #include #ifndef offsetof @@ -460,6 +464,22 @@ #endif }; #endif /*NGIF*/ + +#ifdef MIP6 +struct ip6protosw mip6_tunnel_protosw = +{ SOCK_RAW, &inet6domain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR, + mip6_tunnel_input, rip6_output, 0, rip6_ctloutput, +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + 0, +#else + rip6_usrreq, +#endif + 0, 0, 0, 0, +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + &rip6_usrreqs +#endif +}; +#endif /* MIP6 */ #ifdef __FreeBSD__ extern int in6_inithead __P((void **, int)); diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/in6_src.c kame/kame/sys/netinet6/in6_src.c --- kame-20010611/kame/sys/netinet6/in6_src.c Wed Jun 6 18:52:17 2001 +++ kame/kame/sys/netinet6/in6_src.c Mon Jun 11 11:10:28 2001 @@ -64,6 +64,7 @@ * @(#)in_pcb.c 8.2 (Berkeley) 1/4/94 */ +/* for MIP6 */ #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include "opt_inet.h" #include "opt_inet6.h" @@ -85,6 +86,9 @@ #include #include #include +#ifdef MIP6 +#include +#endif #include #include @@ -114,6 +118,13 @@ extern struct ifnet loif[NLOOP]; #endif +#ifdef MIP6 +#include +#include + +extern struct nd_prefix *(*mip6_get_home_prefix_hook) __P((void)); +#endif + /* * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. @@ -264,6 +275,106 @@ return(&satosin6(&ia6->ia_addr)->sin6_addr); } } + +#ifdef MIP6 + /* + * This is needed to assure that a (primary) Home Address is used + * for outgoing packets when not at home. We can't choose any other + * address if we want to keep connections up during movement. + */ + if (MIP6_IS_MN_ACTIVE && mip6_hifp) { + struct ifaddr *ifa; + struct in6_ifaddr *ifa6, *depr_ifa6 = NULL; +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = mip6_hifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#elif defined(__FreeBSD__) && __FreeBSD__ >= 4 + TAILQ_FOREACH(ifa, &mip6_hifp->if_addrlist, ifa_list) +#else + for (ifa = mip6_hifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) +#endif + { + int ifa_plen; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ifa6 = (struct in6_ifaddr *)ifa; + + if ((ifa6->ia6_flags & IN6_IFF_HOME) == 0) + continue; + + if (IFA6_IS_INVALID(ifa6)) + continue; + + if (in6_addrscope(dst) != + in6_addrscope(&ifa6->ia_addr.sin6_addr)) + continue; + + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, + NULL); + if (ifa_plen != mip6_phpl || + !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, + &mip6_php, ifa_plen)) + continue; + + if (IFA6_IS_DEPRECATED(ifa6) && depr_ifa6 == NULL) { + depr_ifa6 = ifa6; + continue; + } + + /* + * At least one matched preferred address. + */ + break; + } + if ((ifa6 = (struct in6_ifaddr *)ifa) == NULL && + depr_ifa6 != NULL) + /* + * Use deprecated as last resort. + */ + ifa6 = depr_ifa6; + + if (ifa6) { +#ifdef MIP6_DEBUG + /* Noisy but useful */ + mip6_debug("%s: Local address %s is chosen for pcb to " + "dest %s.\n", __FUNCTION__, + ip6_sprintf(&ifa6->ia_addr.sin6_addr), + ip6_sprintf(dst)); +#endif + return(&ifa6->ia_addr.sin6_addr); + } +#ifdef MIP6_DEBUG + else + /* Noisy but useful */ + mip6_debug("%s: No home address chosen for pcb to " + "dest %s.\n", __FUNCTION__, + ip6_sprintf(dst)); +#endif + /* Fall through */ + } +#endif /* MIP6 */ +#ifdef OLDMIP6 + if (mip6_get_home_prefix_hook) { /* Only Mobile Node */ + struct nd_prefix *pr; + if ((pr = (*mip6_get_home_prefix_hook)()) && + !IN6_IS_ADDR_UNSPECIFIED(&pr->ndpr_addr)) { + if (in6_addrscope(dst) == + in6_addrscope(&pr->ndpr_addr)) { +#ifdef MIP6_DEBUG + /* Noisy but useful */ + mip6_debug("%s: Local address %s is chosen " + "for pcb to dest %s.\n", + __FUNCTION__, + ip6_sprintf(&pr->ndpr_addr), + ip6_sprintf(dst)); +#endif + return(&pr->ndpr_addr); + } + } + } +#endif /* OLDMIP6 */ /* * If route is known or can be allocated now, diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/ip6_forward.c kame/kame/sys/netinet6/ip6_forward.c --- kame-20010611/kame/sys/netinet6/ip6_forward.c Mon Jun 4 17:57:48 2001 +++ kame/kame/sys/netinet6/ip6_forward.c Thu May 17 12:48:30 2001 @@ -1,4 +1,4 @@ -/* $KAME: ip6_forward.c,v 1.70 2001/06/04 08:57:48 keiichi Exp $ */ +/* $KAME: ip6_forward.c,v 1.69 2001/05/17 03:48:30 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -87,6 +87,10 @@ #include #endif +#ifdef MIP6 +#include +#endif + #include #ifdef NEW_STRUCT_ROUTE @@ -343,6 +347,43 @@ } skip_ipsec: #endif /* IPSEC */ + +#ifdef OLDMIP6 + { + struct mip6_bc *bc; + + bc = mip6_bc_find(NULL, &ip6->ip6_dst); + if ((bc != NULL) && (bc->flags & IP6_BUF_HOME)) { + if (mip6_tunnel_output(&m, bc) != 0) { + ip6stat.ip6s_cantforward++; + if (mcopy) m_freem(mcopy); + m_freem(m); + return; + } + } + ip6 = mtod(m, struct ip6_hdr *); /* m has changed */ + } +#endif /* OLDMIP6 */ +#ifdef MIP6 + { + struct mip6_bc *bc; + + bc = mip6_bc_find(NULL, &ip6->ip6_dst); + if ((bc != NULL) && (bc->flags & IP6_BUF_HOME)) { + if (mip6_tunnel_output(&m, bc) != 0) { +#ifdef MIP6_DEBUG + mip6_debug("%s: can't forward packet to MN\n", + __FUNCTION__); +#endif + ip6stat.ip6s_cantforward++; + if (mcopy) + m_freem(mcopy); +/* m_freem(m); Correct? */ + } + return; + } + } +#endif /* MIP6 */ dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; if (!srcrt) { diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/ip6_input.c kame/kame/sys/netinet6/ip6_input.c --- kame-20010611/kame/sys/netinet6/ip6_input.c Mon Jun 4 21:03:43 2001 +++ kame/kame/sys/netinet6/ip6_input.c Mon Jun 11 11:10:42 2001 @@ -137,6 +137,10 @@ #include #endif +#ifdef MIP6 +#include +#endif + #if defined(IPV6FIREWALL) || (defined(__FreeBSD__) && __FreeBSD__ >= 4) #include #endif @@ -330,6 +334,11 @@ ip6_init2((void *)0); #endif +#ifdef MIP6 + /* Initialize the Mobile IPv6 code */ + mip6_init(); +#endif + #ifdef MEASURE_PERFORMANCE in6h_hashinit(); #endif @@ -1233,6 +1242,18 @@ } #endif +#ifdef MIP6 + /* + Check if the packet was tunneled after all extion + headers have been processed. + */ + if ((nxt != IPPROTO_HOPOPTS) && (nxt != IPPROTO_DSTOPTS) && + (nxt != IPPROTO_ROUTING) && (nxt != IPPROTO_FRAGMENT) && + (nxt != IPPROTO_ESP) && (nxt != IPPROTO_AH)) { + if (mip6_route_optimize(m)) + goto bad; + } +#endif nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } return; diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/ip6_output.c kame/kame/sys/netinet6/ip6_output.c --- kame-20010611/kame/sys/netinet6/ip6_output.c Mon Jun 4 21:03:43 2001 +++ kame/kame/sys/netinet6/ip6_output.c Mon Jun 11 11:11:00 2001 @@ -146,6 +146,10 @@ #include +#ifdef MIP6 +#include +#endif /* MIP6 */ + #if defined(__FreeBSD__) && __FreeBSD__ >= 3 static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); #endif @@ -196,6 +200,18 @@ * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one, * which is rt_rmx.rmx_mtu. + * + * If MIP6 is active it will have to add a Home Address option to DH1 if + * the mobile node is roaming or a Routing Header type 0 if there exist + * a Binding Cache entry for the destination node or a BU option to DH2 + * if the mobile node initiates communication and no BUL entry exist. + * The only way to do this is to allocate new memory, copy the user data + * to the new buffer and then add the Home Address option, BU option and + * routing header type 0 respectively. MIP6 will set two flags in "struct + * pktopts" to restore the original contents once ip6_output is completed. + * To make this work, make sure that function exit is made through label + * alldone. + * */ int ip6_output(m0, opt, ro, flags, im6o, ifpp) @@ -281,6 +297,17 @@ bzero(&exthdrs, sizeof(exthdrs)); +#ifdef MIP6 + /* + * Mobile IPv6 + * + * Call Mobile IPv6 to check if there are any options that have to + * be added to the packet. + */ + if (mip6_output(m, &opt)) + goto freehdrs; +#endif /* MIP6 */ + if (opt) { /* Hop-by-Hop options header */ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); @@ -602,6 +629,17 @@ #endif } +#ifdef MIP6 + /* + * Mobile IPv6 + * + * After the IPsec processing the IPv6 header source address + * and the address currently stored in the Home Address option + * field must be exchanged + */ + mip6_addr_exchange(m, exthdrs.ip6e_dest1); +#endif /* MIP6 */ + /* * If there is a routing header, replace destination address field * with the first hop of the routing header. @@ -723,7 +761,11 @@ error = ipsp_process_packet(m, tdb, AF_INET6, 0, NULL); splx(s); +#ifdef MIP6 + goto alldone; +#else return error; /* Nothing more to be done */ +#endif } #else if (needipsec && needipsectun) { @@ -1408,6 +1450,30 @@ key_freesp(sp); #endif /* IPSEC */ +#ifdef MIP6 +#if defined(__OpenBSD__) && defined(IPSEC) +alldone: +#endif + if (opt && opt->ip6po_flags & IP6PO_NEWDH1) { + if (opt->ip6po_dest1) + free(opt->ip6po_dest1, M_TEMP); + opt->ip6po_dest1 = opt->ip6po_orgdh1; + } + if (opt && opt->ip6po_flags & IP6PO_NEWDH2) { + if (opt->ip6po_dest2) + free(opt->ip6po_dest2, M_TEMP); + opt->ip6po_dest2 = opt->ip6po_orgdh2; + } + if (opt && opt->ip6po_flags & IP6PO_NEWRH0) { + if (opt->ip6po_rthdr) + free(opt->ip6po_rthdr, M_TEMP); + opt->ip6po_rthdr = opt->ip6po_orgrh0; + } + if (opt && opt->ip6po_flags & IP6PO_MIP6OPT) { + free(opt, M_TEMP); + opt = NULL; + } +#endif return(error); freehdrs: diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6.c kame/kame/sys/netinet6/mip6.c --- kame-20010611/kame/sys/netinet6/mip6.c Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6.c Mon Jun 4 10:25:01 2001 @@ -0,0 +1,3289 @@ +/* $KAME: mip6.c,v 1.39 2001/06/01 22:54:40 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Authors: Conny Larsson + * Mattias Pettersson + * + */ + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" +#endif +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ipsec.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) +#include +#elif defined(__OpenBSD__) +#include +#endif + +#ifdef MIP6_DEBUG +#include +#include +#include +#include +#include +#endif + +#include + +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_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; + + +#ifdef MIP6_DEBUG +int mip6_debug_is_enabled = 0; +#endif + + +/* Declaration of Global variables. */ +struct mip6_bc *mip6_bcq = NULL; /* First entry in BC list */ +struct mip6_na *mip6_naq = NULL; /* First entry in NA list */ +struct mip6_prefix *mip6_prq = NULL; /* First entry in prefix list */ +struct mip6_halst *mip6_haq = NULL; /* First entry in Home Agents */ +struct mip6_config mip6_config; /* Config parameters for MIPv6 */ + + +u_int8_t mip6_module = 0; /* Info about loaded modules */ + +extern struct ip6protosw mip6_tunnel_protosw; + +#ifdef __NetBSD__ +struct callout mip6_timer_bc_ch = CALLOUT_INITIALIZER; +struct callout mip6_timer_na_ch = CALLOUT_INITIALIZER; +struct callout mip6_timer_pr_ch = CALLOUT_INITIALIZER; +struct callout mip6_timer_ha_ch = CALLOUT_INITIALIZER; +#elif (defined(__FreeBSD__) && __FreeBSD__ >= 3) +struct callout mip6_timer_bc_ch; +struct callout mip6_timer_na_ch; +struct callout mip6_timer_pr_ch; +struct callout mip6_timer_ha_ch; +#elif defined(__OpenBSD__) +struct timeout mip6_timer_bc_ch; +struct timeout mip6_timer_na_ch; +struct timeout mip6_timer_pr_ch; +struct timeout mip6_timer_ha_ch; +#endif + + +/* Definitions of some costant IP6 addresses. */ +struct in6_addr in6addr_linklocal; +struct in6_addr in6addr_aha_64; +struct in6_addr in6addr_aha_nn; + + +/* + ############################################################################## + # + # INITIALIZATION AND EXIT FUNCTIONS + # These functions are executed when either the mobile node specific MIPv6 + # code or the home agent specific MIPv6 code is activated and deactivated + # respectively. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_init + * Description: Initialization of MIPv6 variables that must be initialized + * before the code is executed. + ****************************************************************************** + */ +void +mip6_init(void) +{ + static int mip6_init_done = 0; + + if (mip6_init_done) + return; + + /* Initialize global addresses. */ + 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_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 + /* Initialize handle for timer functions. */ + callout_init(&mip6_timer_bc_ch); + callout_init(&mip6_timer_na_ch); + callout_init(&mip6_timer_pr_ch); + callout_init(&mip6_timer_ha_ch); +#endif + + /* Initialize global variable */ + bzero(&mip6_config, sizeof(struct mip6_config)); + + /* Set default values for MIP6 configuration parameters. */ + LIST_INIT(&mip6_config.fna_list); + + mip6_config.bu_lifetime = 600; + mip6_config.br_update = 60; + mip6_config.hr_lifetime = 3600; + + mip6_hifp = ifunit("lo0"); + + printf("Mobile Node initialized\n"); + mip6_enable_hooks(MIP6_GENERIC_HOOKS); + mip6_enable_hooks(MIP6_CONFIG_HOOKS); + + mip6_init_done = 1; + printf("Initializing Mobile IPv6\n"); +} + + + +/* + ****************************************************************************** + * Function: mip6_exit + * Description: This function is called when the module is unloaded (relesed) + * from the kernel. + ****************************************************************************** + */ +void +mip6_exit(void) +{ + struct mip6_na *nap, *nap_tmp; + struct mip6_bc *bcp, *bcp_nxt; + struct mip6_prefix *pfx; + struct mip6_halst *hap; + int s; + + /* Cancel outstanding timeout function calls. */ +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_bc_ch); + callout_stop(&mip6_timer_na_ch); + callout_stop(&mip6_timer_pr_ch); + callout_stop(&mip6_timer_ha_ch); +#else + untimeout(mip6_timer_bc, (void *)NULL); + untimeout(mip6_timer_na, (void *)NULL); + untimeout(mip6_timer_prefix, (void *)NULL); + untimeout(mip6_timer_hal, (void *)NULL); +#endif + + /* Remove each entry in every queue. */ + s = splnet(); + for (bcp = mip6_bcq; bcp;) { + mip6_bc_delete(bcp, &bcp_nxt); + bcp = bcp_nxt; + } + mip6_bcq = NULL; + + for (nap = mip6_naq; nap;) { + nap_tmp = nap; + nap = nap->next; + free(nap_tmp, M_TEMP); + } + mip6_naq = NULL; + + for (pfx = mip6_prq; pfx;) { + pfx = mip6_prefix_delete(pfx); + } + mip6_prq = NULL; + + for (hap = mip6_haq; hap;) { + hap = mip6_hal_delete(hap); + } + mip6_haq = NULL; + splx(s); +} + + + +/* + ############################################################################## + # + # FUNCTIONS FOR PROCESSING OF INBOUND MIPV6 OPTIONS + # Below are functions used for processing of received MIPv6 options (BU, BA + # and BR) and its sub-options. These options are received by the dest6_input() + # function, which calls the mip6_dstopt() function. The mip6_dstopt() function + # is a dispatcher function. + # As a result of processing an option other functions will be called which + # eventually results in either a response or an action. The functions for + # sending responses are also defined under this section. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_validate_bu + * Description: Validate received Binding Update option (see 8.2). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_validate_bu(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BU option in DH */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6aux *ip6a = NULL; + struct ip6_hdr *ip6; + struct mip6_bc *bcp; + struct mbuf *n; + + bu_opt = (struct ip6_opt_binding_update *)(opt); + ip6 = mtod(m, struct ip6_hdr *); + + /* Make sure that the BU is protected by an AH (see 4.4). */ +#ifdef IPSEC +#ifndef __OpenBSD__ + if ( !(m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM)) { + log(LOG_ERR, + "%s: BU not protected by AH from host %s\n", + __FUNCTION__, ip6_sprintf(&ip6->ip6_src)); + return -1; + } +#endif +#endif + + /* Make sure that the BU contains a valid Home Address option. */ + n = ip6_findaux(m); + if (!n) return -1; + + ip6a = mtod(n, struct ip6aux *); + if ((ip6a == NULL) || (ip6a->ip6a_flags & IP6A_HASEEN) == 0) { + log(LOG_ERR, + "%s: No Home Address option found for BU from host %s\n", + __FUNCTION__, ip6_sprintf(&ip6->ip6_src)); + return -1; + } + + /* Make sure that the length field in the BU is >= IP6OPT_BULEN. */ + if (bu_opt->ip6ou_len < IP6OPT_BULEN) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, + "%s: Length field to short (%d) in BU from host %s\n", + __FUNCTION__, bu_opt->ip6ou_len, + ip6_sprintf(&ip6->ip6_src)); + return -1; + } + + /* The sequence no in the BU must be greater than the sequence + number in the previous BU recieved (modulo 2^^16). */ + bcp = mip6_bc_find(&ip6->ip6_dst, &ip6a->ip6a_home); + if (bcp != NULL) { + if (MIP6_LEQ(ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno), + bcp->seqno)) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, + "%s: Received sequence no (%d) <= current " + "seq no (%d) in BU from host %s\n", + __FUNCTION__, + ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno), + bcp->seqno, ip6_sprintf(&ip6->ip6_src)); + return -1; + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_validate_subopt + * Description: Validates sub-options included in MIPv6 options (see 5.5). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_validate_subopt(dh, subopt, optlen) +struct ip6_dest *dh; /* Ptr to beginning of DH */ +u_int8_t *subopt; /* Ptr to first sub-option in current option */ +u_int8_t optlen; /* Remaining option length */ +{ + /* Validate all sub-options for current option */ + while (optlen > 0) { + switch (*subopt) { + case IP6OPT_PAD1: + optlen -= 1; + subopt += 1; + break; + case IP6OPT_PADN: + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + case IP6SUBOPT_UNIQUEID: + /* Verify alignment requirement: 2n */ + if ((subopt - (u_int8_t *)dh) % 2 != 0) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, + "%s: Alignment failure in Unique " + "Identifier sub-option\n", + __FUNCTION__); + return -1; + } + + if (*(subopt + 1) != IP6OPT_UIDLEN) + return -1; + + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + case IP6SUBOPT_ALTCOA: + /* Verify alignment requirement: 8n+6 */ + if ((subopt - (u_int8_t *)dh) % 8 != 6) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, + "%s: Alignment failure in " + "Alternate COA sub-option\n", + __FUNCTION__); + return -1; + } + + if (*(subopt + 1) != IP6OPT_COALEN) + return -1; + + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + default: + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_process_bu + * Description: Process a received Binding Update option (see 8.2). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_process_bu(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BU option in DH */ +{ + struct ip6_opt_binding_update *bu_opt; + struct in6_addr *coa; + struct ip6_hdr *ip6; + struct mip6_subopt_altcoa *altcoa; + struct mip6_bc *bcp, *bcp_nxt; + struct ip6aux *ip6a = NULL; + struct mbuf *n; + u_int8_t *subopt, optlen; + u_long flags = 0; + int res; + + bu_opt = (struct ip6_opt_binding_update *)(opt); + ip6 = mtod(m, struct ip6_hdr *); + + n = ip6_findaux(m); + if (!n) return -1; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return -1; + + /* Find the care-of address used by the MN when sending the BU. */ + subopt = opt + IP6OPT_MINLEN + IP6OPT_BULEN; + optlen = *(opt + 1) - IP6OPT_BULEN; + altcoa = mip6_find_subopt_altcoa(subopt, optlen); + if (altcoa == NULL) + coa = &ip6a->ip6a_careof; + else + coa = (struct in6_addr *)&altcoa->coa; + +#ifdef MIP6_DEBUG + mip6_print_opt(m, opt); +#endif + + /* Shall Dynamic Home Agent Address Discovery be performed? */ + + /* Check if BU includes Unique Identifier sub-option is present. */ + /* XXX Code have to be added. */ + + /* Is this a request to cache a binding for the MN? (see 8.2) */ + if ((ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime) != 0) && + (! IN6_ARE_ADDR_EQUAL(&ip6a->ip6a_home, coa))) { + /* Request to cache a binding. Processing depends on H-bit. */ + if (bu_opt->ip6ou_flags & IP6_BUF_HOME) { + /* According to section 9.3. */ + res = mip6_accept_bu(m, opt); + if (res == -1) return -1; + else if (res == -2) return 0; + +#ifdef MIP6_TBD + if (bu_opt->ip6ou_flags & IP6_BUF_DAD) { + /* Reply will be returned once the DAD has + been completed. */ + return 0; + } +#endif + + bcp = mip6_cache_binding(m, opt, coa); + if (bcp) + res = MIP6_BA_STATUS_ACCEPT; + else + res = MIP6_BA_STATUS_UNSPEC; + + if (mip6_build_send_ba(m, opt, bcp, NULL, res) == -1) + return -1; + if (bcp == NULL) return 0; + + /* Create a new or move existing tunnel to the MN. */ + res = mip6_tunnel(&ip6->ip6_dst, &bcp->peer_coa, + MIP6_TUNNEL_MOVE, MIP6_NODE_HA, + (void *)bcp); + if (res) return -1; + + /* The HA should act as proxy for the MN and inter- + cept packets while it is at a FN. (see 9.5) */ + mip6_proxy_control(bcp, RTM_ADD); + + if (bcp->flags & IP6_BUF_ROUTER) + flags |= ND_NA_FLAG_ROUTER; + flags |= ND_NA_FLAG_OVERRIDE; + mip6_intercept_control(&bcp->peer_home, + bcp->prefixlen, flags); + } else { + /* According to section 8.3. */ + bcp = mip6_cache_binding(m, opt, coa); + if (bcp) + res = MIP6_BA_STATUS_ACCEPT; + else + res = MIP6_BA_STATUS_UNSPEC; + + if (mip6_build_send_ba(m, opt, bcp, NULL, res) == -1) + return -1; + } + return 0; + } + + /* Check if this is a request to delete a binding for the MN. */ + if ((ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime) == 0) || + (IN6_ARE_ADDR_EQUAL(&ip6a->ip6a_home, coa))) { + /* Request to delete a binding. Processing depends on H-bit. */ + if (bu_opt->ip6ou_flags & IP6_BUF_HOME) { + /* According to section 9.4. */ + bcp = mip6_bc_find(&ip6->ip6_dst, &ip6a->ip6a_home); + + /* Validation before deletion of BC entry. */ + if (bcp == NULL || !(bcp->flags & IP6_BUF_HOME)) { + if (mip6_build_send_ba(m, opt, bcp, NULL, + MIP6_BA_STATUS_NOTHA) + == -1) + return -1; + else + return 0; + } + + /* Stop acting as a proxy for the MN, i.e. remove + address(es) from the routing table (see 9.5) */ + mip6_proxy_control(bcp, RTM_DELETE); + + /* Send BA back to the MN. */ + if (mip6_build_send_ba(m, opt, bcp, NULL, + MIP6_BA_STATUS_ACCEPT) == -1) + return -1; + + /* Remove the existing tunnel to the MN. This is + handled by the mip6_bc_delete() function */ + res = mip6_bc_delete(bcp, &bcp_nxt); + if (res) return -1; + } else { + /* According to section 8.4. */ + bcp = mip6_bc_find(&ip6->ip6_dst, &ip6a->ip6a_home); + + if (!(bu_opt->ip6ou_flags & IP6_BUF_ACK) && + (bcp == NULL)) { + /* Accepted and no BC entry to delete and + no requirement to send a BA. */ + return 0; + } + + if (bcp) + res = MIP6_BA_STATUS_ACCEPT; + else + res = MIP6_BA_STATUS_UNSPEC; + + /* Send BA back to the MN. */ + if (mip6_build_send_ba(m, opt, bcp, NULL, res) == -1) + return -1; + if (bcp == NULL) return 0; + + /* Delete BC entry */ + res = mip6_bc_delete(bcp, &bcp_nxt); + if (res) return -1; + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_find_subopt_uid + * Description: Find the Unique Identifier sub-option in the BU option. + * Ret value: Ptr to uid sub-option or NULL + ****************************************************************************** + */ +struct mip6_subopt_uid * +mip6_find_subopt_uid(subopt, optlen) +u_int8_t *subopt; /* Ptr to first sub-option in current option */ +u_int8_t optlen; /* Remaining option length */ +{ + struct mip6_subopt_uid *uid = NULL; + + /* Search all sub-options for current option */ + while (optlen > 0) { + switch (*subopt) { + case IP6OPT_PAD1: + optlen -= 1; + subopt += 1; + break; + case IP6SUBOPT_UNIQUEID: + uid = (struct mip6_subopt_uid *)subopt; + return uid; + default: + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + } + } + return uid; +} + + + +/* + ****************************************************************************** + * Function: mip6_find_subopt_altcoa + * Description: Find the Alternate care-of address sub-option in the BU option. + * Ret value: Ptr to Alternate care-of address sub-option or NULL + ****************************************************************************** + */ +struct mip6_subopt_altcoa * +mip6_find_subopt_altcoa(subopt, optlen) +u_int8_t *subopt; /* Ptr to first sub-option in current option */ +u_int8_t optlen; /* Remaining option length */ +{ + struct mip6_subopt_altcoa *altcoa = NULL; + + /* Search all sub-options for current option */ + while (optlen > 0) { + switch (*subopt) { + case IP6OPT_PAD1: + optlen -= 1; + subopt += 1; + break; + case IP6SUBOPT_ALTCOA: + altcoa = (struct mip6_subopt_altcoa *)subopt; + return altcoa; + default: + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + } + } + return altcoa; +} + + + +/* + ****************************************************************************** + * Function: mip6_cache_binding + * Description: As a result of receiving a BU the node will cache the mobile + * node's binding. The receiving node should create a new BC + * entry or update its existing BC entry (see 8.3 and 9.3). + * Ret value: Pointer to BC entry or NULL + ****************************************************************************** + */ +struct mip6_bc * +mip6_cache_binding(m, opt, coa) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BU option in DH */ +struct in6_addr *coa; /* Care-of address for peer node */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6_hdr *ip6; + struct mip6_bc *bcp; + struct ip6aux *ip6a = NULL; + struct mbuf *n; + u_int32_t lifetime; + + n = ip6_findaux(m); + if (!n) return NULL; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return NULL; + ip6 = mtod(m, struct ip6_hdr *); + + /* Find out which lifetime to use in the BA */ + bu_opt = (struct ip6_opt_binding_update *)opt; + if (bu_opt->ip6ou_flags & IP6_BUF_HOME) { + lifetime = mip6_min_lifetime(&ip6a->ip6a_home, + bu_opt->ip6ou_prefixlen); + lifetime = min(lifetime, + ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime)); + } else { + lifetime = ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime); + } + + /* Create a new or update an existing BC entry. */ + bcp = mip6_bc_find(&ip6->ip6_dst, &ip6a->ip6a_home); + if (bcp) + mip6_bc_update(opt, bcp, coa, lifetime); + else + bcp = mip6_bc_create(m, opt, coa, lifetime); + return bcp; +} + + + +/* + ****************************************************************************** + * Function: mip6_build_send_ba + * Description: As a result of receiving a BU the node must send a BA if the + * A-bit is set in the BU. If the node rejects the BU and does + * not create or update a BC entry a BA must be sent, even if the + * A-bit was not set in the BU (see section 8.5, 5.2, 8.9, 9.4). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_build_send_ba(m, opt, bcp, subbuf, status) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BU option in DH */ +struct mip6_bc *bcp; /* BC entry if accept, NULL if reject */ +struct mip6_buffer *subbuf; /* Buffer of BA sub-options or NULL */ +u_int8_t status; /* Result of the Binding Update request */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6_opt_binding_ack *ba_opt; + struct mip6_subopt_altcoa *altcoa; + struct mip6_buffer dh2; + struct ip6_rthdr *ip6_rthdr = NULL; + struct ip6_ext *ext_hdr; + struct in6_addr *coa; + struct ip6_hdr *ip6; + struct ip6aux *ip6a = NULL; + struct mbuf *n, *mo = NULL; + u_int8_t *subopt, *ba_pos, optlen; + u_int16_t seqno; + int res; + + bu_opt = (struct ip6_opt_binding_update *)opt; + ba_opt = NULL; + ip6 = mtod(m, struct ip6_hdr *); + + n = ip6_findaux(m); + if (!n) return -1; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return -1; + + /* Find the care-of address used by the MN when sending the BU. */ + subopt = opt + IP6OPT_MINLEN + IP6OPT_BULEN; + optlen = *(opt + 1) - IP6OPT_BULEN; + altcoa = mip6_find_subopt_altcoa(subopt, optlen); + if (altcoa == NULL) + coa = &ip6a->ip6a_careof; + else + coa = (struct in6_addr *)&altcoa->coa; + + /* Send a BA to the MN if the A-bit is set and it was accepted. */ + if ((bu_opt->ip6ou_flags & IP6_BUF_ACK) && bcp) { + mo = mip6_create_ip6hdr(&ip6->ip6_dst, &bcp->peer_home, + IPPROTO_NONE, 0); + if (mo == NULL) return -1; + + if ((ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime) == 0) || + (IN6_ARE_ADDR_EQUAL(&ip6a->ip6a_home, coa))) { + /* If de-registration of primary care-of address */ + ip6_rthdr = mip6_create_rh(coa, IPPROTO_DSTOPTS); + } else { + /* If registration of primary care-of address */ + ip6_rthdr = mip6_create_rh(&bcp->peer_coa, + IPPROTO_DSTOPTS); + } + if (ip6_rthdr == NULL) { + free(mo, M_TEMP); + return -1; + } + + if (status >= MIP6_BA_STATUS_UNSPEC) + status = MIP6_BA_STATUS_ACCEPT; + + if ((ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime) == 0) || + (IN6_ARE_ADDR_EQUAL(&ip6a->ip6a_home, coa))) { + /* If de-registration of primary care-of address */ + seqno = ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno); + ba_opt = mip6_create_ba(status, seqno, 0); + } else { + /* If registration of primary care-of address */ + ba_opt = mip6_create_ba(status, bcp->seqno, + bcp->lifetime); + } + if (ba_opt == NULL) { + free(mo, M_TEMP); + free(ip6_rthdr, M_TEMP); + return -1; + } + + bzero((caddr_t)&dh2, sizeof(dh2)); + ba_pos = mip6_add_opt2dh((u_int8_t *)ba_opt, &dh2); + mip6_add_subopt2dh(subbuf, &dh2, ba_pos); + mip6_align(&dh2); + ext_hdr = (struct ip6_ext *)&dh2.buf; + ext_hdr->ip6e_nxt = IPPROTO_NONE; + + res = mip6_send_ba(mo, ip6_rthdr, (struct ip6_dest *)dh2.buf); + if (res == -1) { + if (mo) free(mo, M_TEMP); + if (ip6_rthdr) free(ip6_rthdr, M_TEMP); + free(ba_opt, M_TEMP); + return -1; + } + } + + if (bcp == NULL) { + mo = mip6_create_ip6hdr(&ip6->ip6_dst, &ip6a->ip6a_home, + IPPROTO_NONE, 0); + if (mo == NULL) return -1; + + ip6_rthdr = mip6_create_rh(coa, IPPROTO_DSTOPTS); + if (ip6_rthdr == NULL) { + free(mo, M_TEMP); + return -1; + } + + if (status < MIP6_BA_STATUS_UNSPEC) + status = MIP6_BA_STATUS_UNSPEC; + + seqno = ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno); + ba_opt = mip6_create_ba(status, seqno, 0); + if (ba_opt == NULL) { + free(mo, M_TEMP); + free(ip6_rthdr, M_TEMP); + return -1; + } + + bzero((caddr_t)&dh2, sizeof(dh2)); + ba_pos = mip6_add_opt2dh((u_int8_t *)ba_opt, &dh2); + mip6_add_subopt2dh(subbuf, &dh2, ba_pos); + mip6_align(&dh2); + ext_hdr = (struct ip6_ext *)&dh2.buf; + ext_hdr->ip6e_nxt = IPPROTO_NONE; + + res = mip6_send_ba(mo, ip6_rthdr, (struct ip6_dest *)dh2.buf); + if (res == -1) { + if (ip6_rthdr) free(ip6_rthdr, M_TEMP); + free(ba_opt, M_TEMP); + return -1; + } + } + + /* Remove allocated memory (mo is removed by ip6_output). */ + if (ip6_rthdr) free(ip6_rthdr, M_TEMP); + if (ba_opt) free(ba_opt, M_TEMP); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_create_ip6hdr + * Description: Create and fill in data for an IPv6 header to be used by + * packets originating from MIPv6. In addition to this memory + * is reserved for payload, if necessary. + * Ret value: NULL if a IPv6 header could not be created. + * Otherwise, pointer to a mbuf including the IPv6 header. + ****************************************************************************** + */ +struct mbuf * +mip6_create_ip6hdr(ip6_src, ip6_dst, next, plen) +struct in6_addr *ip6_src; /* Source address for packet */ +struct in6_addr *ip6_dst; /* Destination address for packet */ +u_int8_t next; /* Next header following the IPv6 header */ +u_int32_t plen; /* Payload length (zero if no payload */ +{ + struct ip6_hdr *ip6; /* IPv6 header */ + struct mbuf *mo; /* Ptr to mbuf allocated for output data */ + u_int32_t maxlen; + + /* Allocate memory for the IPv6 header and fill it with data */ + ip6 = (struct ip6_hdr *)malloc(sizeof(struct ip6_hdr), + M_TEMP, M_NOWAIT); + 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; + + /* Allocate memory for mbuf and copy IPv6 header to mbuf. */ + maxlen = sizeof(struct ip6_hdr) + plen; + MGETHDR(mo, M_DONTWAIT, MT_DATA); + if (mo && (maxlen >= MHLEN)) { + MCLGET(mo, M_DONTWAIT); + if ((mo->m_flags & M_EXT) == 0) { + m_free(mo); + mo = NULL; + } + } + if (mo == NULL) { + free(ip6, M_TEMP); + return NULL; + } + + mo->m_len = maxlen; + mo->m_pkthdr.len = mo->m_len; + mo->m_pkthdr.rcvif = NULL; + bcopy((caddr_t)ip6, mtod(mo, caddr_t), sizeof(*ip6)); + free(ip6, M_TEMP); + return mo; +} + + + +/* + ****************************************************************************** + * Function: mip6_create_rh + * Description: Create a routing header of type 0 and add the COA for the MN. + * Ret value: A pointer to the ip6_rthdr structure if everything is OK. + * Otherwise NULL. + ****************************************************************************** + */ +struct ip6_rthdr * +mip6_create_rh(coa, next) +struct in6_addr *coa; /* Care-of address for the MN */ +u_int8_t next; /* Next header following the routing header */ +{ + struct ip6_rthdr0 *rthdr0; /* Routing header type 0 */ + int len; + + len = sizeof(struct ip6_rthdr0) + sizeof(struct in6_addr); + rthdr0 = (struct ip6_rthdr0 *)malloc(len, M_TEMP, M_NOWAIT); + 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; +} + + + +/* + ****************************************************************************** + * Function: mip6_create_ba + * Description: Create a Binding Acknowledgement option for transmission. + * Ret value: NULL if a BA option could not be created. + * Otherwise, pointer to the BA option. + ****************************************************************************** + */ +struct ip6_opt_binding_ack * +mip6_create_ba(status, seqno, lifetime) +u_int8_t status; /* Result of the Binding Update request */ +u_int16_t seqno; /* Sequence number in the BU being acknowledged */ +u_int32_t lifetime; /* Proposed lifetime in the BU */ +{ + struct ip6_opt_binding_ack *ba_opt; /* BA option */ + u_int32_t rtime; + int len; + + /* Allocate a Binding Aknowledgement option and set values */ + len = sizeof(struct ip6_opt_binding_ack); + ba_opt = (struct ip6_opt_binding_ack *)malloc(len, M_TEMP, M_NOWAIT); + if (ba_opt == NULL) return NULL; + bzero(ba_opt, sizeof(*ba_opt)); + + ba_opt->ip6oa_type = IP6OPT_BINDING_ACK; + ba_opt->ip6oa_len = IP6OPT_BALEN; + ba_opt->ip6oa_status = status; + bcopy((caddr_t)&seqno, ba_opt->ip6oa_seqno, sizeof(seqno)); + bcopy((caddr_t)&lifetime, ba_opt->ip6oa_lifetime, sizeof(lifetime)); + + /* Calculate value for refresh time */ + if (MIP6_IS_HA_ACTIVE) + rtime = (lifetime * 8) / 10; + else + rtime = lifetime; + + bcopy((caddr_t)&rtime, ba_opt->ip6oa_refresh, sizeof(rtime)); + return ba_opt; +} + + + +/* + ****************************************************************************** + * Function: mip6_send_ba + * Description: Sends a BA back to the MN sending the BU. The packet includes + * a routing header a destination header where the BA is stored. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_send_ba(mo, ip6_rthdr, dh2) +struct mbuf *mo; /* Ptr to beginning of outgoing mbuf */ +struct ip6_rthdr *ip6_rthdr; /* Routing header (type 0) */ +struct ip6_dest *dh2; /* Destination Header 2 */ +{ + struct ip6_pktopts *pktopts; /* Options for IPv6 packet */ + struct ip6_hdr *ip6; + u_int8_t *ptr; + int res, ii; + + pktopts = (struct ip6_pktopts *)malloc(sizeof(struct ip6_pktopts), + M_TEMP, M_NOWAIT); + if (pktopts == NULL) return -1; + init_ip6pktopts(pktopts); + + pktopts->ip6po_rhinfo.ip6po_rhi_rthdr = ip6_rthdr; + pktopts->ip6po_dest2 = dh2; + + res = ip6_output(mo, pktopts, NULL, 0, NULL, NULL); + if (res) { + free(pktopts, M_TEMP); + log(LOG_ERR, + "%s: ip6_output function failed to send BA, error = %d\n", + __FUNCTION__, res); + return -1; + } + +#ifdef MIP6_DEBUG + ip6 = mtod(mo, struct ip6_hdr *); + + mip6_debug("\nSent Binding Acknowledgement\n"); + mip6_debug("IP Header Src: %s\n", ip6_sprintf(&ip6->ip6_src)); + mip6_debug("IP Header Dst: %s\n", ip6_sprintf(&ip6->ip6_dst)); + mip6_debug("Destination Header 2 Contents\n"); + + ptr = (u_int8_t *)dh2; + for (ii = 0; ii < ((dh2->ip6d_len + 1) << 3); ii++, ptr++) { + if (ii % 16 == 0) mip6_debug("\t0x:"); + if (ii % 4 == 0) mip6_debug(" "); + mip6_debug("%02x ", *ptr); + if ((ii + 1) % 16 == 0) mip6_debug("\n"); + } + if (ii % 16) mip6_debug("\n"); +#endif + + free(pktopts, M_TEMP); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_in6addr + * Description: Build an in6 address from a prefix and the interface id. The + * length of the different parts is decided by the prefix length. + * Ret value: Pointer to address or NULL + ****************************************************************************** + */ +struct in6_addr * +mip6_in6addr(prefix, id, prefixlen) +const struct in6_addr *prefix; /* Prefix part of the address */ +struct in6_addr *id; /* Interface id part of the address */ +int prefixlen; /* Prefix length (bits) */ +{ + struct in6_addr *new_addr; /* New address built in this function */ + u_int8_t byte_pr; + u_int8_t byte_id; + int ii, jj; + + new_addr = (struct in6_addr *)malloc(sizeof(struct in6_addr), + M_TEMP, M_NOWAIT); + if (new_addr == NULL) return NULL; + + for (ii = 0; ii < prefixlen / 8; ii++) + new_addr->s6_addr8[ii] = prefix->s6_addr8[ii]; + + if (prefixlen % 8) { + /* Add the last bits of the prefix to the common byte. */ + byte_pr = prefix->s6_addr8[ii]; + byte_pr = byte_pr >> (8 - (prefixlen % 8)); + byte_pr = byte_pr << (8 - (prefixlen % 8)); + + /* Then, add the first bits of the interface id to the + common byte. */ + byte_id = id->s6_addr8[ii]; + byte_id = byte_id << (prefixlen % 8); + byte_id = byte_id >> (prefixlen % 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]; + return new_addr; +} + + + +/* + ****************************************************************************** + * Function: mip6_intercept_control + * Description: When a home agent becomes proxy for a mobile node or when a + * mobile node returns to its home link, the home agent or the + * mobile node must multicast onto the home link a Neighbor + * Advertisement. + * If the home agent sends the NA it must be multicasted for + * each prefix of the mobile node if the prefix length is non- + * zero, otherwise only for the mobile nodes home address. + * If the mobile node sends the NA it must be sent for each of + * its home addresses, as defined by the current on-link prefixes. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_intercept_control(taddr, prefixlen, flags) +struct in6_addr *taddr; /* Target address for MN */ +u_int8_t prefixlen; /* Prefix length for MN address */ +u_long flags; /* Flags for the NA message */ +{ + struct mip6_prefix *prr; /* Prefix list kept by router (HA) */ + struct nd_prefix *prh; /* Prefix list kept by host (MN) */ + struct ifnet *ifp; + struct ifaddr *ifa; + struct in6_ifaddr *ifa6; + struct in6_addr *laddr, *prefix, *naddr; + int ifa_plen; + u_int8_t plen, sent_for_ll_addr; + + if (MIP6_IS_HA_ACTIVE) { + /* Intercepting packets for mobile node (see 9.5) */ + sent_for_ll_addr = 0; + + for (prr = mip6_prq; prr; prr = prr->next) { + prefix = &prr->prefix; + plen = prr->prefixlen; + ifp = prr->ifp; + + /* Should this only be done for the home address */ + if (prefixlen == 0) { + /* Find interface for sending NA */ + if (in6_are_prefix_equal(taddr, prefix, plen)){ + mip6_intercept_packet(taddr, flags, + ifp); + break; + } + continue; + } + + /* The prefix length must be equal */ + if (plen != prefixlen) continue; + + /* Build home address to send NA for */ + naddr = mip6_in6addr(prefix, taddr, plen); + if (naddr == NULL) continue; + + /* Start intercept packet for home address */ + mip6_intercept_packet(naddr, flags, ifp); + + /* Send for link-local address if prefix len == 64 */ + if ((plen == 64) && !sent_for_ll_addr) { + laddr = mip6_in6addr(&in6addr_linklocal, + taddr, plen); + if (laddr == NULL) { + free(naddr, M_TEMP); + continue; + } + + mip6_intercept_packet(laddr, flags, ifp); + sent_for_ll_addr = 1; + free(laddr, M_TEMP); + } + free(naddr, M_TEMP); + } + } + + if (MIP6_IS_MN_ACTIVE) { + /* Returning home (see 10.20) */ + + /* All home addresses are located at the loopback + interface. */ + ifp = mip6_hifp; + if (ifp == NULL) return; + + /* Loop through all addresses at "lo0" */ + sent_for_ll_addr = 0; + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#elif defined(__FreeBSD__) && __FreeBSD__ >= 4 + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; + ifa = ifa->ifa_list.tqe_next) +#endif + { + /* Find addresses of interest. */ + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ifa6 = (struct in6_ifaddr *)ifa; + if ((ifa6->ia6_flags & IN6_IFF_HOME) == 0) + continue; + + /* The prefix length must be equal */ + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, + NULL); + if (ifa_plen != prefixlen) + continue; + + /* Finf outgoing interface */ + for (prh = nd_prefix.lh_first; prh; + prh = prh->ndpr_next) { + if (prh->ndpr_stateflags & NDPRF_HOME && + prh->ndpr_stateflags & NDPRF_ONLINK) + ifp = prh->ndpr_ifp; + } + + /* Start intercept packet for home address */ + mip6_intercept_packet(&ifa6->ia_addr.sin6_addr, + flags, ifp); + + /* Send for link-local address if prefix len == 64 */ + if ((ifa_plen == 64) && !sent_for_ll_addr) { + laddr = mip6_in6addr(&in6addr_linklocal, + &ifa6->ia_addr.sin6_addr, + ifa_plen); + if (laddr == NULL) + continue; + + mip6_intercept_packet(laddr, flags, ifp); + sent_for_ll_addr = 1; + free(laddr, M_TEMP); + } + } + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_intercept_packet + * Description: Create a NA entry and add it to the internal MIPv6 list of + * Neighbor Advertisements that should be sent. + * The NA will be repeateadly sent (MIP6_MAX_ADVERT_REXMIT times) + * by either the Mobile Node when returning to its home link or + * by the Home Agent when acting as a proxy for a Mobile Node + * while away from its home network. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_intercept_packet(taddr, flags, ifp) +struct in6_addr *taddr; /* Target address to send NA for */ +u_long flags; /* Flags for the NA message */ +struct ifnet *ifp; /* Use this interface when sending the NA */ +{ + struct mip6_na *nap; + int s, start_timer = 0; + + nap = (struct mip6_na *)malloc(sizeof(struct mip6_na), + M_TEMP, M_NOWAIT); + if (nap == NULL) return ; + bzero(nap, sizeof(struct mip6_na)); + + nap->next = NULL; + nap->ifp = ifp; + nap->target_addr = *taddr; + nap->flags = flags; + nap->link_opt = 1; + nap->no = MIP6_MAX_ADVERT_REXMIT; + + /* Add the new na entry first to the list. */ + if (mip6_naq == NULL) start_timer = 1; + s = splnet(); + nap->next = mip6_naq; + mip6_naq = nap; + splx(s); + +#ifdef MIP6_DEBUG + mip6_debug("\nCreated NA List entry (0x%x)\n", nap); + mip6_debug("Interface: %s\n", if_name(nap->ifp)); + mip6_debug("Target Address: %s\n", ip6_sprintf(&nap->target_addr)); + mip6_debug("Flags: "); + if (nap->flags & ND_NA_FLAG_OVERRIDE) mip6_debug("O "); + if (nap->flags & ND_NA_FLAG_ROUTER) mip6_debug("R "); + if (nap->flags & ND_NA_FLAG_SOLICITED) mip6_debug("S "); + mip6_debug("\n"); + mip6_debug("Target link layer address option: TRUE\n"); +#endif + + if (start_timer) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_na_ch, hz, mip6_timer_na, NULL); +#else + timeout(mip6_timer_na, (void *)0, hz); +#endif + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_tunnel + * Description: Create, move or delete a tunnel from the Home Agent to the MN + * or from the Mobile Node to the Home Agent. + * Ret value: Standard error codes. + ****************************************************************************** + */ +int +mip6_tunnel(ip6_src, ip6_dst, action, start, entry) +struct in6_addr *ip6_src; /* Tunnel start point */ +struct in6_addr *ip6_dst; /* Tunnel end point */ +int action; /* Action: MIP6_TUNNEL_{ADD,MOVE,DEL} */ +int start; /* Either the Home Agent or the Mobile Node */ +void *entry; /* BC or ESM depending on start variable */ +{ + const struct encaptab *ep; /* Encapsulation entry */ + const struct encaptab **ep_store; /* Where to store encap reference */ + 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 { +#ifdef MIP6_DEBUG + mip6_debug("%s: Tunnel not modified\n", __FUNCTION__); +#endif + return 0; + } + + if (action == MIP6_TUNNEL_DEL) { + /* Moving to Home network. Remove tunnel. */ + 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) { + /* Remove the old encapsulation entry first. */ + 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; +} + + + +/* + ############################################################################## + # + # FUNCTIONS FOR PROCESSING OF ICMP6 MESSAGES + # Below are functions used for processing of icmp6 messages. Both sent and + # received messages are handled by these functions. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_icmp6_input + * Description: Some icmp6 messages are of interest for MIPv6 and must be + * taken care of accordingly. Once such a message is discoverd + * in function icmp6_input() a call to this function is done. + * Further processing depends on the message type. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_icmp6_input(m, off, icmp6len) +struct mbuf *m; /* Ptr to beginning of mbuf */ +int off; /* Offset from start of mbuf to ICMP6 message */ +int icmp6len; /* Total ICMP6 payload length */ +{ + struct ip6_hdr *ip6; /* IPv6 header */ + struct icmp6_hdr *icmp6; /* ICMP6 header */ + struct mip6_bc *bcp; /* Binding Cache list entry */ + struct mip6_bc *bcp_nxt; /* Binding Cache list entry */ + struct in6_addr *lhome; /* Local home address (sent pkg) */ + struct in6_addr *phome; /* Peer home address (sent pkg) */ + struct nd_router_advert *ra; /* Router Advertisement */ + struct mip6_bul *bulp; /* Binding Update List entry */ + u_int8_t *pp; + int offset; + + ip6 = mtod(m, struct ip6_hdr *); + icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); + pp = (u_int8_t *)ip6 + off; + + switch (icmp6->icmp6_type) { + case ICMP6_DST_UNREACH: + /* Receiving ICMP error messages (see 8.8) */ + mip6_icmp6_find_addr(pp, icmp6len, &lhome, &phome); + bcp = mip6_bc_find(lhome, phome); + if (bcp) mip6_bc_delete(bcp, &bcp_nxt); + break; + case ICMP6_PARAM_PROB: + /* Receiving ICMP error messages (see 10.14) */ + if (!MIP6_IS_MN_ACTIVE) return 0; + + if (icmp6->icmp6_code != ICMP6_PARAMPROB_OPTION) + break; + + offset = sizeof(struct icmp6_hdr); + offset += ntohl(*(u_int32_t *)icmp6->icmp6_data32); + if ((offset + 1) > icmp6len) break; + + mip6_icmp6_find_addr(pp, icmp6len, &lhome, &phome); + if (*(pp + offset) == IP6OPT_BINDING_UPDATE) { + bulp = mip6_bul_find(phome, lhome); + if (bulp) bulp->send_flag = 0; + } else if (*(pp + offset) == IP6OPT_HOME_ADDRESS) { + log(LOG_ERR, + "Node %s does not recognize Home " + "Address option\n", + ip6_sprintf(phome)); + } + break; + case ND_ROUTER_ADVERT: + /* Receiving Router Advertisement (see 9.1, 10.15) */ + if (!(MIP6_IS_HA_ACTIVE || MIP6_IS_MN_ACTIVE)) + return 0; + + if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { + log(LOG_ERR, + "%s: Src %s is not link-local\n", + __FUNCTION__, ip6_sprintf(&ip6->ip6_src)); + return -1; + } + + ra = (struct nd_router_advert *)icmp6; + if (!(ra->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT)) + return 0; + + if (mip6_icmp6_ra(m, off, icmp6len)) + return -1; + break; + case ICMP6_HADISCOV_REQUEST: + /* XXX Add code */ + break; + case ICMP6_HADISCOV_REPLY: + /* XXX Add code */ + break; + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_icmp6_find_addr + * Description: If a correspondent node receives an ICMPv6 Destination + * Unreachable after sending packets to a mobile node, based on + * an entry in its Binding Cache, it should remove that entry. + * The correspondent node my itself be a mobile node. + * Ret value: local_home Local home address in ICMP IPv6 packet + * peer_home Peer home address in ICMP IPv6 packet + ****************************************************************************** + */ +void +mip6_icmp6_find_addr(pp, plen, local_home, peer_home) +u_int8_t *pp; /* Pointer to beginning of icmp6 payload */ +int plen; /* Total icmp6 payload length */ +struct in6_addr **local_home; /* Local home address */ +struct in6_addr **peer_home; /* Peer home address */ +{ + struct ip6_opt_home_address *ha; /* Home Address option */ + struct ip6_hdr *ip6; /* IPv6 header */ + struct ip6_ext *ehdr; /* Extension header */ + struct in6_addr *lh; /* Local home address */ + struct in6_addr *ph; /* Peer home address */ + struct ip6_rthdr0 *rh; + u_int8_t *eopt, nxt, olen; + int off, elen, eoff; + int rlen, addr_off; + + off = sizeof(struct icmp6_hdr); + ip6 = (struct ip6_hdr *)(pp + off); + nxt = ip6->ip6_nxt; + off += sizeof(struct ip6_hdr); + + lh = &ip6->ip6_src; + ph = &ip6->ip6_dst; + + /* Search original IPv6 header extensions for Routing Header type 0 + and for home address option (if I'm a mobile node). */ + while ((off + 2) < plen) { + if (nxt == IPPROTO_HOPOPTS) { + ehdr = (struct ip6_ext *)(pp + off); + nxt = ehdr->ip6e_nxt; + off += (ehdr->ip6e_len + 1) << 3; + continue; + } + + if (nxt == IPPROTO_DSTOPTS) { + ehdr = (struct ip6_ext *)(pp + off); + elen = (ehdr->ip6e_len + 1) << 3; + eoff = 2; + eopt = pp + off + eoff; + while ((eoff + 2) < elen) { + if (*eopt == IP6OPT_PAD1) { + eoff += 1; + eopt += 1; + continue; + } + if (*eopt == IP6OPT_HOME_ADDRESS) { + olen = *(eopt + 1) + 2; + if ((off + eoff + olen) > plen) + break; + + ha = (struct ip6_opt_home_address *) + eopt; + lh = (struct in6_addr *)ha->ip6oh_addr; + eoff += olen; + eopt += olen; + continue; + } + eoff += *(eopt + 1) + 2; + eopt += *(eopt + 1) + 2; + } + nxt = ehdr->ip6e_nxt; + off += (ehdr->ip6e_len + 1) << 3; + continue; + } + + if (nxt == IPPROTO_ROUTING) { + rh = (struct ip6_rthdr0 *)(pp + off); + rlen = (rh->ip6r0_len + 1) << 3; + if ((off + rlen) > plen) break; + if (rh->ip6r0_type != 0) break; + if ((rh->ip6r0_type != 0) || (rh->ip6r0_len % 2)) { + nxt = rh->ip6r0_nxt; + off += (rh->ip6r0_len + 1) << 3; + continue; + } + + addr_off = 8 + (((rh->ip6r0_len / 2) - 1) << 3); + ph = (struct in6_addr *)(pp + off + addr_off); + + nxt = rh->ip6r0_nxt; + off += (rh->ip6r0_len + 1) << 3; + continue; + } + + /* Only look at the unfragmentable part. Other headers + may be present but they are of no interest. */ + break; + } + + *local_home = lh; + *peer_home = ph; +} + + + +/* + ****************************************************************************** + * Function: mip6_icmp6_ra + * Description: Processes an incoming Router Advertisement with a H-bit set + * in the flags variable (checked by the calling function), see + * 9.1 and 10.15. + * Note: The Home Agent uses the information for sending RAs to mobile + * nodes currently located at a foreign network for which it has + * a "home registration" entry. + * It is also used by the mobile node when sending a BU to a + * home agent at a previous foreign network, which is the only + * thing that the mobile node uses this information for. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_icmp6_ra(m, off, icmp6len) +struct mbuf *m; /* Ptr to beginning of mbuf */ +int off; /* Offset from start of mbuf to ICMP6 message */ +int icmp6len; /* Total ICMP6 payload length */ +{ + struct ifnet *ifp; /* Receiving interface */ + struct ip6_hdr *ip6; /* IPv6 header */ + struct nd_router_advert *ra; /* Router Advertisement */ + + ip6 = mtod(m, struct ip6_hdr *); + ra = (struct nd_router_advert *)((u_int8_t *)ip6 + off); + ifp = m->m_pkthdr.rcvif; + + /* Look through the RA options and do appropriate updates */ + if (mip6_icmp6_ra_options(ifp, &ip6->ip6_src, ra, icmp6len)) + return -1; + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_icmp6_ra_options + * Description: Search through all the options in the Router Advertisement + * and store them in the Home Agent list and Prefix list (see + * 9.1 and 10.15). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_icmp6_ra_options(ifp, ll_addr, ra, icmp6len) +struct ifnet *ifp; /* Receiving/sending interface */ +struct in6_addr *ll_addr; /* Link local address of Home Agent */ +struct nd_router_advert *ra; /* Ptr to beginning of RA message */ +int icmp6len; /* Total ICMP6 payload length */ +{ + struct nd_opt_homeagent_info *hai; /* Home Agent info option */ + struct nd_opt_prefix_info *pi; /* Ptr to prefix information */ + u_int8_t *opt; /* Ptr to current option in RA */ + struct mip6_prefix *prp; /* Prefix list entry */ + struct in6_addr *anyadr; /* Anycast address for HA */ + struct in6_addr *pfx; + struct mip6_halst *halp; + int off; /* Offset from start of RA */ + u_int32_t pfxvt, pfxpt; + u_int16_t lifetime, pref; + u_int8_t pfxlen, pfxflags; + int err; + + /* First, see if there is a Home Agent information option + included in the RA. */ + lifetime = ntohs(ra->nd_ra_router_lifetime); + pref = 0; + + hai = NULL; + off = sizeof(struct nd_router_advert); + while (off < icmp6len) { + opt = (u_int8_t *)ra + off; + if (*opt == ND_OPT_HOMEAGENT_INFO) { + /* Check the home agent information option */ + hai = (struct nd_opt_homeagent_info *)opt; + if (hai->nd_opt_hai_len != 1) { + ip6stat.ip6s_badoptions++; + return -1; + } + + pref = ntohs(hai->nd_opt_hai_preference); + lifetime = ntohs(hai->nd_opt_hai_lifetime); + off += 8; + continue; + } else { + if (*(opt + 1) == 0) { + ip6stat.ip6s_badoptions++; + return -1; + } + off += *(opt + 1) << 3; + } + } + + /* Should the HA list entry be removed? */ + halp = mip6_hal_find(ifp, ll_addr); + if (halp && hai && (lifetime == 0)) { + mip6_hal_delete(halp); + return 0; + } + + /* Update Home Agent list entry */ + if ((halp == NULL) && (lifetime == 0)) + return 0; + + if (halp == NULL) { + halp = mip6_hal_create(ifp, ll_addr, lifetime, pref); + if (halp == NULL) return -1; + } else { + halp->lifetime = lifetime; + halp->pref = pref; + mip6_hal_sort(halp); + } + + /* Update Prefix Information list for Home Agent */ + off = sizeof(struct nd_router_advert); + while (off < icmp6len) { + opt = (u_int8_t *)ra + off; + if (*opt == ND_OPT_PREFIX_INFORMATION) { + /* Check the prefix information option */ + pi = (struct nd_opt_prefix_info *)opt; + if (pi->nd_opt_pi_len != 4) { + ip6stat.ip6s_badoptions++; + return -1; + } + + if (!(pi->nd_opt_pi_flags_reserved & + ND_OPT_PI_FLAG_ROUTER)) { + off += 4 * 8; + continue; + } + + if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) || + IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { + off += 4 * 8; + continue; + } + + /* Aggregatable unicast address, RFC 2374 */ + if (((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) > 0x10) + && (pi->nd_opt_pi_prefix_len != 64)) { + off += 4 * 8; + continue; + } + + /* Store the address if not already present */ + pfx = &pi->nd_opt_pi_prefix; + pfxlen = pi->nd_opt_pi_prefix_len; + pfxvt = ntohl(pi->nd_opt_pi_valid_time); + pfxpt = ntohl(pi->nd_opt_pi_preferred_time); + pfxflags = pi->nd_opt_pi_flags_reserved; + + prp = mip6_prefix_find(ifp, pfx, pfxlen); + if (prp == NULL) { + prp = mip6_prefix_create(ifp, pfx, pfxlen, + pfxflags, pfxvt, + pfxpt); + if (prp == NULL) return -1; + + if (MIP6_IS_HA_ACTIVE) { + /* Add HA anycast address to i/f */ + anyadr = mip6_in6addr_any(pfx, pfxlen); + err = mip6_add_ifaddr(anyadr, ifp, + pfxlen, + IN6_IFF_ANYCAST); + if (err) { + log(LOG_ERR, + "%s: address assignment " + " error (errno = %d).\n", + __FUNCTION__, err); + } + } + } else + mip6_prefix_update(prp, pfxflags, + pfxvt, pfxpt); + + if (mip6_prefix_add_addr(prp, pfx, halp)) return -1; + off += 4 * 8; + continue; + } else { + if (*(opt + 1) == 0) { + ip6stat.ip6s_badoptions++; + return -1; + } + off += *(opt + 1) << 3; + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_ifaddr + * Description: Similar to "ifconfig prefixlen ". + * Ret value: Standard error codes. + ****************************************************************************** + */ +int +mip6_add_ifaddr(struct in6_addr *addr, + struct ifnet *ifp, + int plen, + int flags) /* Note: IN6_IFF_NODAD available flag */ +{ + struct in6_aliasreq *ifra, dummy; + struct sockaddr_in6 *sa6; + struct in6_ifaddr *ia; + int s, error = 0; + + 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); + /* XXXYYY Should the prefix also change its prefixmask? */ + } + + ifra->ifra_flags = flags; + ifra->ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra->ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + + sa6 = &ifra->ifra_addr; + + /* "ifconfig ifp inet6 Home_Address prefixlen 64/128 (alias?)" */ + if (ifp == 0) return EOPNOTSUPP; + + s = splnet(); /* necessary? */ + + /* + * Find address for this interface, if it exists. + */ + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + if (sa6->sin6_addr.s6_addr16[1] == 0) { + /* interface ID is not embedded by the user */ + 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; /* ifid is contradict */ + } + 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; /* XXX: good way? */ + } + } + ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); + + error = in6_update_ifa(ifp, ifra, ia); + + splx(s); + return error; +} + + + + +#if 0 + +/* Move this function to mip6_ha.c. Copy the same "section" header as its + current location. */ + +/* + ****************************************************************************** + * Function: mip6_icmp6_hadiscov_request + * Description: Processing of an incoming ICMP6 message requesting "Dynamic + * Home Agent Address Discovery", see 9.2. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_icmp6_hadiscov_request(m, off, icmp6len) +struct mbuf *m; /* Ptr to beginning of mbuf */ +int off; /* Offset from start of mbuf to ICMP6 message */ +int icmp6len; /* Total ICMP6 payload length */ +{ + struct ifnet *ifp; /* Receiving interface */ + struct ip6_hdr *ip6; /* IPv6 header */ + struct ip6aux *ip6a = NULL; + struct mbuf *n; + struct mip6_halst *halp; /* Home Agent list entry */ + u_int16_t lifetime; + int s; + + ip6 = mtod(m, struct ip6_hdr *); + ifp = m->m_pkthdr.rcvif; + + /* Find the home agent that sent the RA */ + ra = (struct nd_router_advert *)((u_int8_t *)ip6 + off); + lifetime = ntohs(ra->nd_ra_router_lifetime); + + + n = ip6_findaux(m); + if (!n) return NULL; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return NULL; + + + + /* Find the home agent that sent the RA */ + + + bzero((caddr_t)&discov_rep, sizeof(struct ha_discov_rep)) + discov_rep.type = ICMP6_HADISCOV_REPLY; + discov_rep.code = 0; + discov_rep.id = discov_req.id; + + + /* XXX If my own home address is the first one it should not + be in the list. */ + + /* Calculate checksum !!!! (when everything has been added */ + /* increase space for mip6_buffer 2048 */ + discov_buf.off = sizeof(struct ha_discov_rep); + for (halp = mip6_haq; halp; halp = halp->next) { + size = discov_buf.off + sizeof(struct ip6_hdr); + size += (struct in6_addr); + if (size > MIN_MTU) break; + + /* Search the prefix list for a global HA address */ + addr_found = 0; + for (pr = mip6_prq; pr; pr = pr->next) { + for (ap = pr->addrlst; ap; ap = ap->next) { + if (ap->hap == halp) { + size = sizeof(struct in6_addr); + bcopy((caddr_t)&ap->ip6_addr, + discov_buf.buf + discov_buf.off, + size); + discov_buf.off += size; + addr_found = 1 + break; + } + } + if (addr_found) break; + } + } + + + if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { + nd6log((LOG_ERR, + "ICMP6 checksum error(%d|%x) %s\n", + icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src))); + icmp6stat.icp6s_checksum++; + goto freeit; + } + + res = ip6_output(mo, pktopts, NULL, 0, NULL, NULL); + if (res) { + free(pktopts, M_TEMP); + log(LOG_ERR, + "%s: ip6_output function failed to send BA, error = %d\n", + __FUNCTION__, res); + return -1; + } + + + return 0; +} + +#endif + + + + + + + +/* + ############################################################################## + # + # LIST FUNCTIONS + # The correspondent node maintains a Binding Cache list for each node from + # which it has received a BU. + # It also maintains a list of Neighbor Advertisements that shall be sent + # either by the home agent when start acting as a proxy for the mobile node + # or by the mobile node when returning to the home network. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_bc_find + * Description: Find an entry in the Binding Cache list. If variable local_home + * is NULL an entry for which only the peer_home address match is + * searched for. + * Ret value: Pointer to Binding Cache entry or NULL if no entry found. + ****************************************************************************** + */ +struct mip6_bc * +mip6_bc_find(local_home, peer_home) +struct in6_addr *local_home; /* Local nodes home address */ +struct in6_addr *peer_home; /* Home Address for peer MN */ +{ + struct mip6_bc *bcp; /* Entry in the Binding Cache list */ + + for (bcp = mip6_bcq; bcp; bcp = bcp->next) { + if (local_home == NULL) { + if (IN6_ARE_ADDR_EQUAL(peer_home, &bcp->peer_home)) + return bcp; + else + continue; + } + + if (IN6_ARE_ADDR_EQUAL(local_home, &bcp->local_home) && + IN6_ARE_ADDR_EQUAL(peer_home, &bcp->peer_home)) + return bcp; + } + return NULL; +} + + + +/* + ****************************************************************************** + * Function: mip6_bc_create + * Description: Create a new Binding Cache entry as a result of receiving a + * Binding Update option. Add it first to the Binding Cache list + * and set parameters for the entry. + * Ret value: Pointer to the created BC entry or NULL. + * Note 1: If the BC timeout function has not been started it is started. + * The BC timeout function will be called once every second until + * there are no more entries in the BC list. + * Note 2: The gif i/f is created/updated in function mip6_tunnel and + * should not be taken care of here. + ****************************************************************************** + */ +struct mip6_bc * +mip6_bc_create(m, opt, coa, lifetime) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BU option in DH */ +struct in6_addr *coa; /* COA for the mobile node (peer) */ +u_int32_t lifetime; /* Remaining lifetime for this BC entry */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6aux *ip6a = NULL; + struct ip6_hdr *ip6; + struct mip6_bc *bcp; + struct mbuf *n; + int s; + + bcp = (struct mip6_bc *)malloc(sizeof(struct mip6_bc), + M_TEMP, M_NOWAIT); + if (bcp == NULL) return NULL; + bzero((caddr_t)bcp, sizeof(struct mip6_bc)); + + bu_opt = (struct ip6_opt_binding_update *)(opt); + ip6 = mtod(m, struct ip6_hdr *); + + n = ip6_findaux(m); + if (!n) return NULL; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return NULL; + + bcp->next = NULL; + bcp->local_home = ip6->ip6_dst; + bcp->peer_home = ip6a->ip6a_home; + bcp->peer_coa = *coa; + bcp->lifetime = lifetime; + bcp->flags |= bu_opt->ip6ou_flags & IP6_BUF_HOME; + bcp->seqno = ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno); + bcp->ep = NULL; + + if (bcp->flags & IP6_BUF_HOME) { + bcp->prefixlen = bu_opt->ip6ou_prefixlen; + bcp->flags |= bu_opt->ip6ou_flags & IP6_BUF_ROUTER; + } else { + bcp->prefixlen = 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; + } + + /* Insert the entry as the first entry in the Binding Cache list. */ + s = splnet(); + if (mip6_bcq == NULL) { + mip6_bcq = bcp; +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_bc_ch, hz, mip6_timer_bc, NULL); +#else + timeout(mip6_timer_bc, (void *)0, hz); +#endif + } else { + bcp->next = mip6_bcq; + mip6_bcq = bcp; + } + splx(s); + +#ifdef MIP6_DEBUG + mip6_debug("\nBinding Cache Entry created (0x%x)\n", bcp); + mip6_debug("Local home address: %s\n", ip6_sprintf(&bcp->local_home)); + mip6_debug("Peer home address: %s\n", ip6_sprintf(&bcp->peer_home)); + mip6_debug("Peer c/o address: %s\n", ip6_sprintf(&bcp->peer_coa)); + mip6_debug("Remaining lifetime: %u\n", bcp->lifetime); + mip6_debug("Sequence number: %u\n", bcp->seqno); + mip6_debug("Prefix length: %u\n", bcp->prefixlen); + mip6_debug("Flags: "); + if (bcp->flags & IP6_BUF_HOME) mip6_debug("H "); + if (bcp->flags & IP6_BUF_ROUTER) mip6_debug("R "); + mip6_debug("\n"); +#endif + return bcp; +} + + + +/* + ****************************************************************************** + * Function: mip6_bc_update + * Description: Update an existing Binding Cache entry as a result of receiving + * a Binding Update option. + * Ret value: Void + * Note: The gif i/f is created/updated in function mip6_tunnel and + * should not be taken care of here. + ****************************************************************************** + */ +void +mip6_bc_update(opt, bcp, coa, lifetime) +u_int8_t *opt; /* Ptr to BU option in DH */ +struct mip6_bc *bcp; /* BC entry being updated */ +struct in6_addr *coa; /* COA for the mobile node (peer) */ +u_int32_t lifetime; /* Remaining lifetime for this BC entry */ +{ + struct ip6_opt_binding_update *bu_opt; + + bu_opt = (struct ip6_opt_binding_update *)(opt); + + bcp->peer_coa = *coa; + bcp->lifetime = lifetime; + bcp->flags |= bu_opt->ip6ou_flags & IP6_BUF_HOME; + bcp->seqno = ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno); + + if (bcp->flags & IP6_BUF_HOME) { + bcp->prefixlen = bu_opt->ip6ou_prefixlen; + bcp->flags |= bu_opt->ip6ou_flags & IP6_BUF_ROUTER;; + bzero((caddr_t)&bcp->info, sizeof(struct mip6_bc_info)); + } else { + bcp->prefixlen = 0; + bcp->flags &= ~IP6_BUF_ROUTER; + + if (bcp->info.br_interval > 60) + bcp->info.br_interval = 60; + if (bcp->info.br_interval < 2) + bcp->info.br_interval = 2; + bcp->info.sent_brs = 0; + bcp->info.lasttime = 0; + } + +#ifdef MIP6_DEBUG + mip6_debug("\nBinding Cache Entry updated (0x%x)\n", bcp); + mip6_debug("Local home address: %s\n", ip6_sprintf(&bcp->local_home)); + mip6_debug("Peer home address: %s\n", ip6_sprintf(&bcp->peer_home)); + mip6_debug("Peer c/o address: %s\n", ip6_sprintf(&bcp->peer_coa)); + mip6_debug("Remaining lifetime: %u\n", bcp->lifetime); + mip6_debug("Sequence number: %u\n", bcp->seqno); + mip6_debug("Prefix length: %u\n", bcp->prefixlen); + mip6_debug("Flags: "); + if (bcp->flags & IP6_BUF_HOME) mip6_debug("H "); + if (bcp->flags & IP6_BUF_ROUTER) mip6_debug("R "); + mip6_debug("\n"); +#endif + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_bc_delete + * Description: Delete an entry in the Binding Cache list. + * Ret value: Error code + * Pointer to next entry in list or NULL if last entry removed. + ****************************************************************************** + */ +int +mip6_bc_delete(bcp_del, bcp_nxt) +struct mip6_bc *bcp_del; /* Pointer to BC entry to delete */ +struct mip6_bc **bcp_nxt; /* Returns next entry in the list */ +{ + struct mip6_bc *bcp; /* Current entry in the BC list */ + struct mip6_bc *bcp_prev; /* Previous entry in the BC list */ + struct mip6_bc *bcp_next; /* Next entry in the BC list */ + int s, error = 0; + + if (bcp_del == NULL) { + *bcp_nxt = NULL; + return error; + } + + 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; + } + + /* Make sure that the list pointers are correct. */ + if (bcp_prev == NULL) + mip6_bcq = bcp->next; + else + bcp_prev->next = bcp->next; + + if (bcp->flags & IP6_BUF_HOME) { + /* The HA should stop acting as a proxy for the MN. */ + mip6_proxy_control(bcp, RTM_DELETE); + + /* Delete the existing tunnel to the MN. */ + error = mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL, + MIP6_NODE_HA, (void *)bcp); + if (error) { + *bcp_nxt = bcp_next; + return error; + } + } + +#ifdef MIP6_DEBUG + mip6_debug("\nBinding Cache Entry deleted (0x%x)\n", bcp); +#endif + free(bcp, M_TEMP); + + /* Remove the timer if the BC queue is empty */ + if (mip6_bcq == NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_bc_ch); +#else + untimeout(mip6_timer_bc, (void *)NULL); +#endif + } + break; + } + splx(s); + + *bcp_nxt = bcp_next; + return error; +} + + + +/* + ****************************************************************************** + * Function: mip6_na_delete + * Description: Delete an entry in the NA list. + * Ret value: Pointer to next entry in list or NULL if last entry removed. + ****************************************************************************** + */ +struct mip6_na * +mip6_na_delete(nap_del) +struct mip6_na *nap_del; /* Pointer to NA entry to delete */ +{ + struct mip6_na *nap; /* Current entry in the NA list */ + struct mip6_na *nap_prev; /* Previous entry in the NA list */ + struct mip6_na *nap_next; /* Next entry in the NA list */ + 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; + +#ifdef MIP6_DEBUG + mip6_debug("\nNeighbor Advertisement Entry " + "deleted (0x%x)\n", nap); +#endif + free(nap, M_TEMP); + + /* Remove the timer if the NA queue is empty */ + if (mip6_naq == NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_na_ch); +#else + untimeout(mip6_timer_na, (void *)NULL); +#endif + } + break; + } + nap_prev = nap; + } + splx(s); + return nap_next; +} + + + +/* + ****************************************************************************** + * Function: mip6_prefix_find + * Description: Finds an existing prefix entry in the prefix list. + * Ret value: Pointer to found prefix list entry or NULL. + ****************************************************************************** + */ +struct mip6_prefix * +mip6_prefix_find(ifp, prefix, prefixlen) +struct ifnet *ifp; /* Interface */ +struct in6_addr *prefix; /* Prefix to search for */ +u_int8_t prefixlen; /* Prefix length */ +{ + struct mip6_prefix *prq; + + for (prq = mip6_prq; prq; prq = prq->next) { + if (in6_are_prefix_equal(&prq->prefix, prefix, prefixlen) && + (prq->ifp == ifp)) + return prq; + } + return NULL; +} + + + +/* + ****************************************************************************** + * Function: mip6_prefix_create + * Description: Create a prefix and add it as the first entry in the list. + * Start the timer if not started already. + * Ret value: Pointer to created prefix list entry or NULL. + ****************************************************************************** + */ +struct mip6_prefix * +mip6_prefix_create(ifp, prefix, prefixlen, flags, validtime, preftime) +struct ifnet *ifp; /* Interface */ +struct in6_addr *prefix; /* Prefix */ +u_int8_t prefixlen; /* Prefix length */ +u_int8_t flags; /* Flags in Prefix information option */ +u_int32_t validtime; /* Valid lifetime (s) */ +u_int32_t preftime; /* Preferred lifetime (s) */ +{ + struct mip6_prefix *prq; + int s, start_timer = 0; + + if (mip6_prq == NULL) start_timer = 1; + + prq = (struct mip6_prefix *)malloc(sizeof(struct mip6_prefix), + M_TEMP, M_NOWAIT); + if (prq == NULL) return NULL; + bzero(prq, sizeof(struct mip6_prefix)); + + s = splnet(); + prq->next = mip6_prq; + prq->ifp = ifp; + prq->prefix = *prefix; + prq->prefixlen = prefixlen; + prq->flags = flags; + prq->timecnt = validtime; + prq->validtime = validtime; + prq->preftime = preftime; + prq->addrlst = NULL; + mip6_prq = prq; + splx(s); + +#ifdef MIP6_DEBUG + mip6_debug("\nMIP6 Prefix list entry created (0x%x)\n", prq); + mip6_debug("Interface: %s\n", if_name(ifp)); + mip6_debug("Prefix: %s\n", ip6_sprintf(&prq->prefix)); + mip6_debug("Prefix len: %d\n", prq->prefixlen); + mip6_debug("Flags: "); + if (prq->flags & ND_OPT_PI_FLAG_ONLINK) mip6_debug("L "); + if (prq->flags & ND_OPT_PI_FLAG_AUTO) mip6_debug("A "); + if (prq->flags & ND_OPT_PI_FLAG_ROUTER) mip6_debug("R "); + mip6_debug("\n"); + mip6_debug("Valid Lifetime: "); + mip6_print_sec(prq->validtime); + mip6_debug("Preferred Lifetime: "); + mip6_print_sec(prq->preftime); +#endif + + if (start_timer) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_pr_ch, hz, mip6_timer_prefix, NULL); +#else + timeout(mip6_timer_prefix, (void *)0, hz); +#endif + } + return prq; +} + + + +/* + ****************************************************************************** + * Function: mip6_prefix_update + * Description: Update an existing prefix. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_prefix_update(prp, flags, validtime, preftime) +struct mip6_prefix *prp; /* Prefix list entry */ +u_int8_t flags; /* Flags in Prefix information option */ +u_int32_t validtime; /* Valid lifetime (s) */ +u_int32_t preftime; /* Preferred lifetime (s) */ +{ + if (prp == NULL) return; + + if ((prp->flags == flags) && (prp->preftime == preftime) && + (prp->validtime == validtime)) { + prp->timecnt = validtime; + return; + } + + /* XXX Add code + Set some kind om "flag" to indicate that a RA + must be sent to the mobile node. + */ + + prp->flags = flags; + prp->timecnt = validtime; + prp->validtime = validtime; + prp->preftime = preftime; + +#if 0 +#ifdef MIP6_DEBUG + mip6_debug("\nMIP6 Prefix list entry updated (0x%x)\n", prp); + mip6_debug("Flags: "); + if (prp->flags & ND_OPT_PI_FLAG_ONLINK) mip6_debug("L "); + if (prp->flags & ND_OPT_PI_FLAG_AUTO) mip6_debug("A "); + if (prp->flags & ND_OPT_PI_FLAG_ROUTER) mip6_debug("R "); + mip6_debug("\n"); + mip6_debug("Valid Lifetime: "); + mip6_print_sec(prp->validtime); + mip6_debug("Preferred Lifetime: "); + mip6_print_sec(prp->preftime); +#endif +#endif + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_prefix_add_addr + * Description: Add a global address to the list of global addresses that a + * prefix is keeping + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_prefix_add_addr(prefix, global_addr, hap) +struct mip6_prefix *prefix; /* Add address to this prefix */ +struct in6_addr *global_addr; /* Global home agent address */ +struct mip6_halst *hap; /* HA list that the address came from */ +{ + struct mip6_prefix *pfx; + struct mip6_addrlst *addrp; + int s, size; + + for (pfx = mip6_prq; pfx; pfx = pfx->next) { + if (prefix != pfx) continue; + + size = sizeof(struct mip6_addrlst); + addrp = (struct mip6_addrlst *)malloc(size, M_TEMP,M_NOWAIT); + if (addrp == NULL) return -1; + addrp->hap = hap; + addrp->ip6_addr = *global_addr; + + /* Add the global address as the first entry */ + s = splnet(); + addrp->next = pfx->addrlst; + pfx->addrlst = addrp; + splx(s); + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_prefix_delete + * Description: Delete the requested prefix list entry. + * Ret value: Ptr to next entry in list or NULL if last entry removed. + ****************************************************************************** + */ +struct mip6_prefix * +mip6_prefix_delete(pfx_del) +struct mip6_prefix *pfx_del; /* Prefix list entry to be deleted */ +{ + struct mip6_prefix *pfx; /* Current entry in the list */ + struct mip6_prefix *pfx_prev; /* Previous entry in the list */ + struct mip6_prefix *pfx_next; /* Next entry in the list */ + struct mip6_addrlst *ap, *ap_next; + int s; + + /* Find the requested entry in the link list. */ + s = splnet(); + pfx_next = NULL; + pfx_prev = NULL; + for (pfx = mip6_prq; pfx; pfx = pfx->next) { + pfx_next = pfx->next; + if (pfx == pfx_del) { + if (pfx_prev == NULL) + mip6_prq = pfx->next; + else + pfx_prev->next = pfx->next; + + for (ap = pfx->addrlst; ap;) { + ap_next = ap->next; + free(ap, M_TEMP); + ap = ap_next; + } +#ifdef MIP6_DEBUG + mip6_debug("\nPrefix entry deleted (0x%x)\n", pfx); +#endif + free(pfx, M_TEMP); + + /* Remove the timer if the prefix queue is empty */ + if (mip6_prq == NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_pr_ch); +#else + untimeout(mip6_timer_prefix, (void *)NULL); +#endif + } + break; + } + pfx_prev = pfx; + } + splx(s); + return pfx_next; +} + + + +/* + ****************************************************************************** + * Function: mip6_hal_find + * Description: Find a Home Agent list entry at a specific link. There will + * be one entry for each node sending a Router Advertisement + * with the H-bit set including a Prefix Information option + * with the R-bit set, for which the Router lifetime or the + * Home Agent lifetime (included in a separate option) is not 0. + * Ret value: Pointer to found Home Agent list entry or NULL. + ****************************************************************************** + */ +struct mip6_halst * +mip6_hal_find(ifp, ll_addr) +struct ifnet *ifp; /* Receiving/sending interface */ +struct in6_addr *ll_addr; /* Link local address to search for */ +{ + struct mip6_halst *halp; + + for (halp = mip6_haq; halp; halp = halp->next) { + if (ifp != halp->ifp) continue; + if (!IN6_ARE_ADDR_EQUAL(&halp->ll_addr, ll_addr)) continue; + return halp; + } + return NULL; +} + + + +/* + ****************************************************************************** + * Function: mip6_hal_create + * Description: Create a Home Agent list entry for a specific link. + * Ret value: Pointer to created Home Agent list entry or NULL. + ****************************************************************************** + */ +struct mip6_halst * +mip6_hal_create(ifp, ll_addr, lifetime, pref) +struct ifnet *ifp; /* Receiving/sending interface */ +struct in6_addr *ll_addr; /* Link local address for Home Agent */ +u_int16_t lifetime; /* Home Agent lifetime */ +u_int16_t pref; /* Home Agent Preference */ +{ + struct mip6_halst *halp; + int s, size; + int start_timer = 0; + + if (mip6_haq == NULL) start_timer = 1; + + size = sizeof(struct mip6_halst); + halp = (struct mip6_halst *)malloc(size, M_TEMP, M_NOWAIT); + if (halp == NULL) return NULL; + bzero(halp, sizeof(struct mip6_halst)); + + /* Fill in data. */ + halp->ifp = ifp; + halp->ll_addr = *ll_addr; + halp->lifetime = lifetime; + halp->pref = pref; + + if (mip6_haq == NULL) { + s = splnet(); + halp->next = NULL; + mip6_haq = halp; + splx(s); + } else { + /* Add the HA list entry to the list in decending order */ + mip6_hal_sort(halp); + } + +#ifdef MIP6_DEBUG + mip6_debug("\nMIP6 HA list entry created (0x%x)\n", halp); + mip6_debug("Interface: %s\n", if_name(ifp)); + mip6_debug("Link-local address: %s\n", ip6_sprintf(&halp->ll_addr)); + mip6_debug("Lifetime: "); + mip6_print_sec((u_int32_t)halp->lifetime); + mip6_debug("Preference: %d\n", halp->pref); +#endif + + if (start_timer) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_ha_ch, hz, mip6_timer_hal, NULL); +#else + timeout(mip6_timer_hal, (void *)0, hz); +#endif + } + return halp; +} + + + +/* + ****************************************************************************** + * Function: mip6_hal_sort + * Description: Add a new entry to the HA list in decending order or move an + * existing entry. This might be necessary if the preference for + * an existing HA list entry changes. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_hal_sort(halp_entry) +struct mip6_halst *halp_entry; /* Home Agent list entry to sort */ +{ + struct mip6_halst *halp; /* Current HA list entry */ + struct mip6_halst *halp_prev; /* Previous HA list entry */ + int s; + + /* If the HA list entry is empty, just add the new entry. */ + s = splnet(); + if (mip6_haq == NULL) { + mip6_haq = halp_entry; + halp_entry->next = NULL; + splx(s); + return; + } + + /* Check if the entry already exist in the HA list. */ + halp_prev = NULL; + for (halp = mip6_haq; halp; halp = halp->next) { + if (halp == halp_entry) break; + halp_prev = halp; + } + + if (halp) { + /* Entry found, detach it. */ + if (halp_prev == NULL) + mip6_haq = halp->next; + else + halp_prev->next = halp->next; + } + + /* Add HA list entry to the list. */ + if (mip6_haq == NULL) { + mip6_haq = halp_entry; + halp_entry->next = NULL; + splx(s); + return; + } + + halp_prev = NULL; + for (halp = mip6_haq; halp; halp = halp->next) { + if (halp->pref > halp_entry->pref) { + halp_prev = halp; + if (halp->next == NULL) { + /* Add as last entry */ + halp->next = halp_entry; + halp_entry->next = NULL; + break; + } + continue; + } + + /* Add entry to HA list. */ + if (halp_prev == NULL) { + mip6_haq = halp_entry; + halp_entry->next = halp; + } else { + halp_prev->next = halp_entry; + halp_entry->next = halp; + } + break; + } + splx(s); + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_hal_delete + * Description: Delete a Home Agent list entry. If there are any address list + * entries associated with the Home Agent entry they are deleted + * as well. + * Ret value: Pointer to the next Home Agent list entry. + * NULL if the remaining list is empty or end of list reached. + ****************************************************************************** + */ +struct mip6_halst * +mip6_hal_delete(halp_del) +struct mip6_halst *halp_del; /* Home Agent entry to delete */ +{ + struct mip6_halst *halp; /* Current HA list entry */ + struct mip6_halst *halp_prev; /* Previous HA list entry */ + struct mip6_halst *halp_next; /* Next HA list entry */ + struct mip6_prefix *pfx; /* Prefix list entry */ + struct mip6_addrlst *ap; /* Address list entry */ + int s; + + s = splnet(); + halp_next = NULL; + halp_prev = NULL; + for (halp = mip6_haq; halp; halp = halp->next) { + halp_next = halp->next; + if (halp == halp_del) { + if (halp_prev == NULL) + mip6_haq = halp->next; + else + halp_prev->next = halp->next; + + /* Remove all references to this entry */ + for (pfx = mip6_prq; pfx; pfx = pfx->next) { + for (ap = pfx->addrlst; ap; ap = ap->next) { + if (ap->hap == halp) ap->hap = NULL; + } + } +#ifdef MIP6_DEBUG + mip6_debug("\nHA list entry deleted (0x%x)\n", halp); +#endif + free(halp, M_TEMP); + + /* Remove the timer if the prefix queue is empty */ + if (mip6_haq == NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_ha_ch); +#else + untimeout(mip6_timer_hal, (void *)NULL); +#endif + } + break; + } + halp_prev = halp; + } + splx(s); + return halp_next; +} + + + +/* + ############################################################################## + # + # TIMER FUNCTIONS + # These functions are called at regular basis. They operate on the lists, e.g. + # reducing timer counters and removing entries from the list if needed. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_timer_na + * Description: Called once every second. For each entry in the list a Neighbor + * Advertisement is sent until the counter value reaches 0. Then + * the entry is removed. + * Ret value: - + ****************************************************************************** + */ +void +mip6_timer_na(arg) +void *arg; /* Not used */ +{ + struct mip6_na *nap; /* Neighbor Advertisement entry */ + int s; + + /* Go through the entire list of Neighbor Advertisement entries. */ + s = splnet(); + for (nap = mip6_naq; nap;) { + nd6_na_output(nap->ifp, &in6addr_linklocal_allnodes, + &nap->target_addr, nap->flags, + nap->link_opt, NULL); + nap->no -= 1; + if (nap->no <= 0) + nap = mip6_na_delete(nap); + else + nap = nap->next; + } + splx(s); + + /* Call timer function again if more entries in the list. */ + if (mip6_naq != NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_na_ch, hz, mip6_timer_na, NULL); +#else + timeout(mip6_timer_na, (void *)0, hz); +#endif + } +} + + + +/* + ****************************************************************************** + * Function: mip6_timer_bc + * Description: Called once every second. For each entry in the BC list, a + * counter is reduced by 1 until it reaches the value of zero, + * then the entry is removed. + * Ret value: - + ****************************************************************************** + */ +void +mip6_timer_bc(arg) +void *arg; /* Not used */ +{ + struct mip6_bc *bcp; /* Current entry in the BC list */ + struct mip6_bc *bcp_nxt; /* Next BC list entry */ + int s; + + /* Go through the entire list of Binding Cache entries. */ + 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); + + /* XXX */ + /* Code have to be added to take care of bc_info.br_interval + variable. */ + /* We have to send a BR when the mip6_bc.lifetime == + mip6_bc.bc_info.br_interval. */ + if (mip6_bcq != NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_bc_ch, hz, mip6_timer_bc, NULL); +#else + timeout(mip6_timer_bc, (void *)0, hz); +#endif + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_timer_prefix + * Description: Called once every second. Search the list of prefixes and if + * a prefix has timed out it is removed from the list. + * Ret value: - + ****************************************************************************** + */ +void +mip6_timer_prefix(arg) +void *arg; /* Not used */ +{ + struct mip6_prefix *pfxp; /* Current entry in the prefix list */ + int s; + + /* Go through the entire list of prefix entries. */ + s = splnet(); + for (pfxp = mip6_prq; pfxp;) { + pfxp->timecnt -= 1; + if (pfxp->timecnt == 0) + pfxp = mip6_prefix_delete(pfxp); + else + pfxp = pfxp->next; + } + splx(s); + + if (mip6_prq != NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_pr_ch, hz, mip6_timer_prefix, NULL); +#else + timeout(mip6_timer_prefix, (void *)0, hz); +#endif + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_timer_hal + * Description: Called once every second. Search the list of home agents and + * if a home agent has timed out it is removed from the list. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_timer_hal(arg) +void *arg; /* Not used */ +{ + struct mip6_halst *halp; /* Current entry in home agent list */ + int s; + + /* Go through the entire list of home agents. */ + s = splnet(); + for (halp = mip6_haq; halp;) { + halp->lifetime -= 1; + if (halp->lifetime <= 0) + halp = mip6_hal_delete(halp); + else + halp = halp->next; + } + splx(s); + + if (mip6_haq != NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_ha_ch, hz, mip6_timer_hal, NULL); +#else + timeout(mip6_timer_hal, (void *)0, hz); +#endif + } + return; +} + + + +/* + ############################################################################## + # + # IOCTL AND DEBUG FUNCTIONS + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_ioctl + * Description: The ioctl handler for MIPv6. These are used by the + * configuration program to set and get various parameters. + * Ret value: 0 or error code + ****************************************************************************** + */ +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; + + /* Note: privileges already checked in in6_control(). */ + + res = 0; + + if (MIP6_IS_HA_ACTIVE) { + switch (cmd) { + case SIOCSHALISTFLUSH_MIP6: + if (mip6_clear_config_data_ha_hook) + res = (*mip6_clear_config_data_ha_hook) + (cmd, data); + return res; + } + } + + if (MIP6_IS_MN_ACTIVE) { + 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); + return res; + } + } + 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: + /* Note: this one can be run before attach. */ + if (mip6_write_config_data_ha_hook) + res = (*mip6_write_config_data_ha_hook) + (cmd, data); + return res; + + case SIOCACOADDR_MIP6: + case SIOCAHOMEADDR_MIP6: + case SIOCAHOMEPREF_MIP6: + case SIOCSBULIFETIME_MIP6: + case SIOCSHRLIFETIME_MIP6: + case SIOCDCOADDR_MIP6: + case SIOCSEAGERMD_MIP6: + /* Note: these can be run before attach. */ + if (mip6_write_config_data_mn_hook) + res = (*mip6_write_config_data_mn_hook) + (cmd, data); + return res; + + case SIOCSDEBUG_MIP6: + case SIOCSENABLEBR_MIP6: + case SIOCSATTACH_MIP6: + res = mip6_enable_func(cmd, data); + return res; + + case SIOCSFWDSLUNICAST_MIP6: + case SIOCSFWDSLMULTICAST_MIP6: + /* Note: these can be run before attach. */ + if (mip6_enable_func_ha_hook) + res = (*mip6_enable_func_ha_hook)(cmd, data); + return res; + + case SIOCSPROMMODE_MIP6: + case SIOCSBU2CN_MIP6: + case SIOCSREVTUNNEL_MIP6: + case SIOCSAUTOCONFIG_MIP6: + /* Note: these can be run before attach. */ + if (mip6_enable_func_mn_hook) + res = (*mip6_enable_func_mn_hook)(cmd, data); + return res; + + case SIOCSRELEASE_MIP6: + mip6_release(); + return res; + + default: + res = EOPNOTSUPP; +#ifdef MIP6_DEBUG + printf("%s: unknown command: %lx\n", __FUNCTION__,(u_long)cmd); +#endif + return res; + } +} + + + +/* + ****************************************************************************** + * Function: mip6_debug + * Description: This function displays MIPv6 debug messages to the console + * if activated with the configuration program. Note that this + * is included only when "options MIP6_DEBUG" is defined. + * Ret value: - + ****************************************************************************** + */ +#ifdef MIP6_DEBUG +void +#if __STDC__ +mip6_debug(char *fmt, ...) +#else +mip6_debug(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ +#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 /* MIP6_DEBUG */ + + + +/* + ****************************************************************************** + * Function: mip6_print_sec + * Description: Converts an integer of seconds into hours, minutes and seconds. + * Ret value: void + ****************************************************************************** + */ +void +mip6_print_sec(seconds) +u_int32_t seconds; +{ + u_int32_t sec; + int f; + + sec = seconds; + f = 0; + if (sec >= 86400) { + printf("%dd ", sec / 86400); + sec %= 86400; + f = 1; + } + if (f || sec >= 3600) { + printf("%dh ", sec / 3600); + sec %= 3600; + f = 1; + } + if (f || sec >= 60) { + printf("%dm ", sec / 60); + sec %= 60; + f = 1; + } + printf("%ds\n", sec); +} + + + +/* + ****************************************************************************** + * Function: mip6_write_config_data + * Description: This function is called to write certain config values for + * MIPv6. The data is written into the global config structure. + * Ret value: - + ****************************************************************************** + */ +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; +} + + + +/* + ****************************************************************************** + * Function: mip6_clear_config_data + * Description: This function is called to clear internal lists handled by + * MIPv6. + * Ret value: - + ****************************************************************************** + */ +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->flags & IP6_BUF_HOME)) { + 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; + + /* XXX Extra action needed? */ + mip6_config.fwd_sl_unicast = 0; + mip6_config.fwd_sl_multicast = 0; + mip6_config.enable_prom_mode = 0; + mip6_config.enable_bu_to_cn = 0; + mip6_config.enable_rev_tunnel = 0; + mip6_config.enable_br = 0; + mip6_eager_md(0); + break; + } + splx(s); + return retval; +} + + + +/* + ****************************************************************************** + * Function: mip6_enable_func + * Description: This function is called to enable or disable certain functions + * in mip6. The data is written into the global config struct. + * Ret value: - + ****************************************************************************** + */ +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: +#ifdef 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); /* RM */ + retval = mip6_attach(enable); + break; + } + return retval; +} diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6.h kame/kame/sys/netinet6/mip6.h --- kame-20010611/kame/sys/netinet6/mip6.h Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6.h Thu Mar 29 14:34:31 2001 @@ -0,0 +1,721 @@ +/* $KAME: mip6.h,v 1.13 2001/03/29 05:34:31 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Authors: Conny Larsson + * Mattias Pettersson + * + */ + +#ifndef _NETINET6_MIP6_H_ +#define _NETINET6_MIP6_H_ + +#include +#include + +struct ifnet; + +/* + * Definition For Mobile Internet Protocol Version 6. + * Draft draft-ietf-mobileip-ipv6-13.txt + */ + +/* Definition of MIPv6 states for the Event-State machine */ +#define MIP6_STATE_UNDEF 0x01 +#define MIP6_STATE_HOME 0x02 +#define MIP6_STATE_DEREG 0x03 +#define MIP6_STATE_NOTREG 0x04 +#define MIP6_STATE_REG 0x05 +#define MIP6_STATE_REREG 0x06 +#define MIP6_STATE_REGNEWCOA 0x07 + + +/* Definition of states used by the move detection algorithm used by MIPv6. */ +#define MIP6_MD_BOOT 0x01 +#define MIP6_MD_UNDEFINED 0x02 +#define MIP6_MD_HOME 0x03 +#define MIP6_MD_FOREIGN 0x04 + + +/* Definition of Home Address route states used by the move detection + algorithm used by MIPv6. */ +#define MIP6_ROUTE_NET 0x01 +#define MIP6_ROUTE_HOST 0x02 + + +/* Type of node calling mip6_tunnel */ +#define MIP6_NODE_MN 0x01 +#define MIP6_NODE_HA 0x02 + + +/* Movement Detection default values */ +#define MIP6_MAX_LOST_ADVINTS 3 + + +/* Scope for hook activation */ +#define MIP6_GENERIC_HOOKS 0x01 +#define MIP6_SPECIFIC_HOOKS 0x02 +#define MIP6_CONFIG_HOOKS 0x03 + + +/* Definition of states for tunnels set up by the Home Agent and the MN. */ +#define MIP6_TUNNEL_ADD 0 +#define MIP6_TUNNEL_MOVE 1 +#define MIP6_TUNNEL_DEL 2 + + +/* Definition of length for different destination options */ +#define IP6OPT_BULEN 8 /* Length of BU option */ +#define IP6OPT_BALEN 11 /* Length of BA option */ +#define IP6OPT_BRLEN 0 /* Length of BR option */ +#define IP6OPT_HALEN 16 /* Length of HA option */ +#define IP6OPT_UIDLEN 2 /* Length of Unique Identifier sub-option */ +#define IP6OPT_COALEN 16 /* Length of Alternate COA sub-option */ + + +/* Definition of sub-options used by the Destination Options */ +#define IP6SUBOPT_UNIQUEID 0x02 /* Unique Identifier (BU, BR) */ +#define IP6SUBOPT_ALTCOA 0x04 /* Alternate COA (BU) */ + + +/* Definition of timers for signals */ +#define MIP6_BU_LIFETIME 600 /* Lifetime for BU (s) */ +#define MIP6_BU_LIFETIME_HAFN 60 /* Lifetime for BU sent to HA on + previous network (s) */ +#define MIP6_BU_LIFETIME_HADISCOV 16 /* Lifetime for BU when Dynamic Home + Agent Address Discovery (s) */ +#define MIP6_MAX_FAST_UPDATES 5 /* Max number of fast updates (BUs) + being sent */ +#define MIP6_MAX_UPDATE_RATE 1 /* Rate limiting for sending successive + fast BUs (sec) */ +#define MIP6_SLOW_UPDATE_RATE 10 /* Rate limiting for sending successive + slow BUs (sec) */ +#define MIP6_MAX_BINDACK_TIMEOUT 256 /* Max time to wait for a BA */ +#define MIP6_MAX_ADVERT_REXMIT 3 /* Max retransmission of NA when + returning to home link */ + + +/* Definition of Binding Acknowledgement status field */ +#define MIP6_BA_STATUS_ACCEPT 0 /* Binding Update accepted */ +#define MIP6_BA_STATUS_UNSPEC 128 /* Reason unspecified */ +#define MIP6_BA_STATUS_PROHIBIT 130 /* Administratively prohibited */ +#define MIP6_BA_STATUS_RESOURCE 131 /* Insufficient resources */ +#define MIP6_BA_STATUS_HOMEREGNOSUP 132 /* Home registration not supported */ +#define MIP6_BA_STATUS_SUBNET 133 /* Not home subnet */ +#define MIP6_BA_STATUS_IFLEN 136 /* Incorrect interface id length */ +#define MIP6_BA_STATUS_NOTHA 137 /* Not home agent for this MN */ +#define MIP6_BA_STATUS_DAD 138 /* DAD failed */ + + +/* Macro for modulo 2^^16 comparison */ +#define MIP6_LEQ(a,b) ((int16_t)((a)-(b)) <= 0) + + +/* Macros started with MIP6_ADDR is Mobile IPv6 local */ +#define MIP6_ADDR_ANYCAST_HA 0x7e + +#if BYTE_ORDER == BIG_ENDIAN +#define MIP6_ADDR_INT32_ULL 0xfe800000 /* Unicast Link Local */ +#define MIP6_ADDR_INT32_USL 0xfec00000 /* Unicast Site Local */ +#define MIP6_ADDR_INT32_AHA1 0xfffffffe /* Anycast Home Agent bit 97-128 */ +#define MIP6_ADDR_INT32_AHA2 0xfdffffff /* Anycast Home Agent bit 65-96 */ +#elif BYTE_ORDER == LITTLE_ENDIAN +#define MIP6_ADDR_INT32_ULL 0x000080fe +#define MIP6_ADDR_INT32_USL 0x0000c0fe +#define MIP6_ADDR_INT32_AHA1 0xfeffffff +#define MIP6_ADDR_INT32_AHA2 0xfffffffd +#endif + + +/* Definition of some useful macros to handle IP6 addresses */ +extern struct in6_addr in6addr_linklocal; +extern struct in6_addr in6addr_sitelocal; +extern struct in6_addr in6addr_aha_64; /* 64 bits identifier */ +extern struct in6_addr in6addr_aha_nn; /* 121-nn bits identifier */ + + +/* Definition of id for sending of Dynamic Home Agent Address Discovery. */ +u_int16_t mip6_hadiscov_id; + +/* Definition of event-state machine type. */ +enum esm_type {PERMANENT, TEMPORARY}; + + +/* Configuration parameters needed for MIPv6. Controlled by the user */ +struct mip6_static_addr { + LIST_ENTRY(mip6_static_addr) addr_entry; /* Next IPv6 address list */ + struct ifnet *ifp; /* Interface */ + u_int8_t prefix_len; /* Prefix length for address */ + struct in6_addr ip6_addr; /* Address used at foreign network */ +}; + + +/* + * fna_list List of pre-assigned care-of addresses to be used at + * foreign networks that the MN might visit + * bu_lifetime Used by the MN when sending a BU to the CN if it wants + * to use a smaller value than received in the home + * registration acknowledgement + * br_update Indicates when the CN sends a BR to the MN. The value + * should be given as percentage of the bu_lifetime + * ha_pref Preference for the Home Agent + * hr_lifetime Default life time for home registration (only sent to the + * Home Agent) + * fwd_sl_unicast Enable forwarding of site local unicast dest addresses + * fwd_sl_multicast Enable forwarding of site local multicast dest addresses + * enable_prom_mode Enable link layer promiscus mode (used by move detection) + * enable_bu_to_cn Enable BU being sent to the CN (Route optimization on/off) + * enable_rev_tunnel Enable tunneling of packets from MN to CN via Home Agent + * enable_br Enable sending BR to the MN + * autoconfig Only enable MIP6 if the mip6 deamon is running + * eager_md Enable eager Movement Detection + */ +struct mip6_config { + LIST_HEAD(fna_list, mip6_static_addr) fna_list; + u_int32_t bu_lifetime; + u_int8_t br_update; + int16_t ha_pref; + u_int32_t hr_lifetime; + u_int8_t fwd_sl_unicast; + u_int8_t fwd_sl_multicast; + u_int8_t enable_prom_mode; + u_int8_t enable_bu_to_cn; + u_int8_t enable_rev_tunnel; + u_int8_t enable_br; + u_int8_t autoconfig; + u_int8_t eager_md; +}; + + +/* Unique Identifier sub-option format */ +struct mip6_subopt_uid { + u_int8_t type; /* Sub-option type */ + u_int8_t len; /* Length (octets) excl. type and len fields */ + u_int8_t uid[2]; /* Unique identifier */ +} __attribute__ ((__packed__)); + + +/* Alternate Care-of Address sub-option format */ +struct mip6_subopt_altcoa { + u_int8_t type; /* Sub-option type */ + u_int8_t len; /* Length (octets) excl. type and len fields */ + u_int8_t coa[16]; /* Alternate COA */ +} __attribute__ ((__packed__)); + + +/* Buffer for storing a consequtive sequence of sub-options */ +struct mip6_buffer { + int off; /* Offset in buffer */ + u_int8_t buf[2048]; /* Must be at least IPV6_MMTU */ +}; + + +/* The event-state machine must be maintained for each Home Address. */ +struct mip6_hadiscov { + struct mip6_buffer *hal; /* List of Home Agent addresses */ + u_int16_t pos; /* Position for entry in list to use */ + u_int16_t sent_hadiscov_id; +}; + +struct mip6_esm { + struct mip6_esm *next; /* Ptr to next entry in the list */ + struct ifnet *ifp; /* Interface for home address */ + const struct encaptab *ep; /* Encapsulation attach (MN -> HA) */ + int state; /* State for the home address */ + enum esm_type type; /* Type of event-state machine */ + struct in6_addr home_addr; /* Home address */ + struct in6_addr home_pref; /* Home prefix */ + struct in6_addr ifid; /* I/f ID, this group of addresses */ + u_int8_t prefixlen; /* Prefix_len for Home Address */ + u_int16_t lifetime; /* If type=PERMANENT 0xFFFF */ + struct in6_addr ha_hn; /* HA address (home link) */ + struct in6_addr coa; /* Current primary care-of address */ + struct mip6_hadiscov *hadiscov; /* Dynamic HA Address Discovery */ +}; + + +/* Binding Cache parameters. Bindings for other IPv6 nodes. */ +/* Maintained by each node. */ +struct mip6_bc_info { + u_int32_t br_interval; /* % of mip6_lifetime, max 60s, min 2s */ + u_int8_t sent_brs; /* Number of sent BRs to a Mobile Node */ + time_t lasttime; /* Time when BR was last sent */ +}; + +struct mip6_bc { + struct mip6_bc *next; /* Next entry in the list */ + struct in6_addr local_home; /* Local nodes home address */ + struct in6_addr peer_home; /* Home Address for peer MN */ + struct in6_addr peer_coa; /* COA for peer MN */ + u_int32_t lifetime; /* Remaining lifetime */ + u_int8_t flags; /* Received flags in BU */ + u_int8_t prefixlen; /* Prefix length in last BU */ + u_int16_t seqno; /* Maximum sequence number */ + const struct encaptab *ep; /* Encapsulation attach (HA->MN) */ + struct mip6_bc_info info; /* Arbitrary info (if not HA) */ +}; + + + +/* Binding Update List parameters. Information for each BU sent by this MN */ +/* Each MN maintains this list. */ +struct mip6_retrans { + struct ip6_opt_binding_update *opt; /* BU option */ + struct mip6_buffer *subopt; /* BU sub-options */ + u_int32_t ba_timeout; /* Exponential back-off */ + u_int8_t timeleft; /* Next retransmission */ +}; + +struct mip6_update { + u_int32_t sent_bus; /* Number of sent BU to a MN */ + u_int8_t update_rate; /* Seconds between consequtive BUs */ +}; + +struct mip6_bul { + struct mip6_bul *next; /* Next entry in the list */ + struct in6_addr peer_home; /* Dst address for sent BU */ + struct in6_addr local_home; /* Home Address or previous COA */ + struct in6_addr local_coa; /* COA sent in the BU */ + u_int32_t sent_lifetime; /* Initial lifetime in sent BU */ + u_int32_t lifetime; /* Remaining binding lifetime */ + u_int32_t refresh; /* Refresh time for the BU */ + u_int16_t seqno; /* Last seq number sent */ + time_t lasttime; /* Time when BU was last sent */ + u_int8_t send_flag; /* Send future BU (T/F) */ + u_int8_t flags; /* A, H-bit in sent BU */ + struct mip6_retrans retrans; /* If A-bit set in flags */ + struct mip6_update update; /* If A-bit not set in flags */ +}; + +#define bul_opt retrans.opt +#define bul_subopt retrans.subopt +#define bul_timeout retrans.ba_timeout +#define bul_timeleft retrans.timeleft +#define bul_sent update.sent_bus +#define bul_rate update.update_rate + + +/* Home Agent List parameters. Information about each other HA on the link + that this node is serving as a HA. One HA list for each link it is + serving. */ +/* Each HA maintains this list. */ +struct mip6_halst { + struct mip6_halst *next; /* Ptr to next entry in the list */ + struct ifnet *ifp; /* Receiving/sending interface */ + struct in6_addr ll_addr; /* HA link-local address */ + u_int16_t lifetime; /* Remaining HA lifetime */ + int16_t pref; /* Preference for this HA */ +}; + +struct mip6_addrlst { + struct mip6_addrlst *next; /* Ptr to next entry in the list */ + struct mip6_halst *hap; /* HA advertising this address */ + struct in6_addr ip6_addr; /* Global IPv6 address */ +}; + +struct mip6_prefix { + struct mip6_prefix *next; /* Next entry in the list */ + struct ifnet *ifp; /* Receiving/sending interface */ + struct in6_addr prefix; /* Prefix (on-link) */ + u_int8_t prefixlen; /* Prefix length for */ + u_int8_t flags; /* Flags in prefix info */ + u_int32_t timecnt; /* Timeout value */ + u_int32_t validtime; /* Valid lifetime */ + u_int32_t preftime; /* Preferred lifetime */ + struct mip6_addrlst *addrlst; /* List of global addresses */ +} __attribute__ ((packed)); + + +/* Neighbor Advertisement information stored for retransmission when the + Mobile Node is returning to its Home Network or the Home Agent is + requested to act as a proxy for the Mobile Node when it is moving to a + Foreign Network. */ +struct mip6_na +{ + struct mip6_na *next; /* Ptr to next entry in the list */ + struct ifnet *ifp; /* Interface for sending the NA */ + struct in6_addr target_addr; /* Target address for MN */ + u_long flags; /* Flags for the NA message */ + int link_opt; /* Incl. target link layer address + option (0 = no / 1 = yes) */ + int no; /* Remaining times to send the NA */ +}; + +#ifdef _KERNEL + +#define MIP6_IS_MN_ACTIVE ((mip6_module & MIP6_MN_MODULE) == MIP6_MN_MODULE) +#define MIP6_IS_HA_ACTIVE ((mip6_module & MIP6_HA_MODULE) == MIP6_HA_MODULE) + +#define MIP6_EAGER_PREFIX (mip6_config.eager_md >= 2) +#define MIP6_EAGER_FREQ 5 /* Run nd6_timer 5 times more often */ + +/* External definition of global variables. */ +extern struct mip6_esm *mip6_esmq; /* Ptr to list of Home Addresses */ +extern struct mip6_bc *mip6_bcq; /* First entry in the BC list */ +extern struct mip6_prefix *mip6_prq; /* First entry in prefix list */ +extern struct mip6_bul *mip6_bulq; +extern struct mip6_halst *mip6_haq; +extern struct mip6_na *mip6_naq; +extern struct mip6_config mip6_config; /* Config parameters for MIP6 */ + +extern struct in6_addr mip6_php; /* Primary Home Prefix */ +extern u_int8_t mip6_phpl; /* Primary Home Prefix Length */ +extern struct nd_prefix *mip6_phpp; /* Primary Home Prefix Pointer */ +extern struct nd_prefix *mip6_pp; /* Primary (Care-of) Prefix */ +extern struct in6_addr mip6_pdr; /* Primary Default Router */ +extern struct ifnet *mip6_hifp; /* ifp of Home Addresses */ +extern int mip6_new_homeaddr; + +extern u_int8_t mip6_module; /* Info about loaded modules (MN/HA) */ +extern int mip6_md_state; /* Movement Detection state */ +extern int mip6_route_state; /* Home Address route state */ +extern int mip6_max_lost_advints; /* No. lost Adv before start of NUD */ +extern int mip6_nd6_delay; +extern int mip6_nd6_umaxtries; + + +/* External declaration of function prototypes (mip6_io.c) */ +extern int mip6_route_optimize + __P((struct mbuf *)); +extern int mip6_dstopt + __P((struct mbuf *, struct ip6_dest *, u_int8_t *, int)); +extern void mip6_print_subopt + __P((u_int8_t *, u_int8_t)); +extern void mip6_print_opt + __P((struct mbuf *, u_int8_t *)); +extern void mip6_find_offset + __P((struct mip6_buffer *)); +extern void mip6_add_subopt2buf + __P((u_int8_t *, struct mip6_buffer *)); +extern u_int8_t *mip6_add_opt2dh + __P((u_int8_t *, struct mip6_buffer *)); +extern void mip6_add_subopt2dh + __P((struct mip6_buffer *, struct mip6_buffer *, u_int8_t *)); +extern void mip6_align + __P((struct mip6_buffer *)); +extern int mip6_output + __P((struct mbuf *, struct ip6_pktopts **)); +extern int mip6_add_rh + __P((struct ip6_pktopts **, struct mip6_bc *)); +extern int mip6_add_ha + __P((struct mbuf *, struct ip6_pktopts **, struct mip6_esm *)); +extern void mip6_addr_exchange + __P((struct mbuf *, struct mbuf *)); +extern int mip6_add_bu + __P((struct ip6_pktopts **, struct mip6_esm *, struct in6_addr *)); +extern int mip6_tunnel_input + __P((struct mbuf **, int *, int)); +extern int mip6_tunnel_output + __P((struct mbuf **, struct mip6_bc *)); + + +/* External declaration of function prototypes (mip6.c) */ +extern void mip6_init + __P((void)); +extern void mip6_exit + __P((void)); +extern int mip6_validate_bu + __P((struct mbuf *, u_int8_t *)); +extern int mip6_validate_subopt + __P((struct ip6_dest *, u_int8_t *, u_int8_t)); +extern int mip6_process_bu + __P((struct mbuf *, u_int8_t *)); +extern struct mip6_subopt_uid *mip6_find_subopt_uid + __P((u_int8_t *, u_int8_t)); +extern struct mip6_subopt_altcoa *mip6_find_subopt_altcoa + __P((u_int8_t *, u_int8_t)); +extern struct mip6_bc *mip6_cache_binding + __P((struct mbuf *, u_int8_t *, struct in6_addr *)); +extern int mip6_build_send_ba + __P((struct mbuf *, u_int8_t *, struct mip6_bc *, + struct mip6_buffer *, u_int8_t)); +extern struct mbuf *mip6_create_ip6hdr + __P((struct in6_addr *, struct in6_addr *, u_int8_t, u_int32_t)); +extern struct ip6_rthdr *mip6_create_rh + __P((struct in6_addr *, u_int8_t)); +extern struct ip6_opt_binding_ack *mip6_create_ba + __P((u_int8_t, u_int16_t, u_int32_t)); +extern int mip6_send_ba + __P((struct mbuf *, struct ip6_rthdr *, struct ip6_dest *)); +extern struct in6_addr *mip6_in6addr + __P((const struct in6_addr *, struct in6_addr *, int)); +extern void mip6_intercept_control + __P((struct in6_addr *, u_int8_t, u_long)); +extern void mip6_intercept_packet + __P((struct in6_addr *, u_long, struct ifnet *)); +extern int mip6_tunnel + __P((struct in6_addr *, struct in6_addr *, int, int, void *)); +extern int mip6_icmp6_input + __P((struct mbuf *, int, int)); +extern void mip6_icmp6_find_addr + __P((u_int8_t *, int, struct in6_addr **, struct in6_addr **)); +extern int mip6_icmp6_ra + __P((struct mbuf *, int, int)); +extern int mip6_icmp6_ra_options + __P((struct ifnet *, struct in6_addr *, + struct nd_router_advert *, int)); +extern int mip6_add_ifaddr + __P((struct in6_addr *, struct ifnet *, int, int)); +extern struct mip6_bc *mip6_bc_find + __P((struct in6_addr *, struct in6_addr *)); +extern struct mip6_bc *mip6_bc_create + __P((struct mbuf *, u_int8_t *, struct in6_addr *, u_int32_t)); +extern void mip6_bc_update + __P((u_int8_t *, struct mip6_bc *, struct in6_addr *, u_int32_t)); +extern int mip6_bc_delete + __P((struct mip6_bc *, struct mip6_bc **)); +extern struct mip6_na *mip6_na_delete + __P((struct mip6_na *)); +extern struct mip6_prefix *mip6_prefix_find + __P((struct ifnet *, struct in6_addr *, u_int8_t)); +extern struct mip6_prefix *mip6_prefix_create + __P((struct ifnet *, struct in6_addr *, u_int8_t, u_int8_t, + u_int32_t, u_int32_t)); +extern void mip6_prefix_update + __P((struct mip6_prefix *, u_int8_t, u_int32_t, u_int32_t)); +extern int mip6_prefix_add_addr + __P((struct mip6_prefix *, struct in6_addr *, struct mip6_halst *)); +extern struct mip6_prefix *mip6_prefix_delete + __P((struct mip6_prefix *)); +extern struct mip6_halst *mip6_hal_find + __P((struct ifnet *, struct in6_addr *)); +extern struct mip6_halst *mip6_hal_create + __P((struct ifnet *, struct in6_addr *, u_int16_t, u_int16_t)); +extern void mip6_hal_sort + __P((struct mip6_halst *)); +extern struct mip6_halst *mip6_hal_delete + __P((struct mip6_halst *)); +extern void mip6_timer_na + __P((void *)); +extern void mip6_timer_bc + __P((void *)); +extern void mip6_timer_prefix + __P((void *)); +extern void mip6_timer_hal + __P((void *)); + +#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) +extern int mip6_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, + struct proc *)); +#else +extern int mip6_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *)); +#endif + +#ifdef MIP6_DEBUG +void mip6_debug __P((char *, ...)); +#endif + +extern void mip6_enable_debug + __P((int)); +extern void mip6_print_sec + __P((u_int32_t)); +extern int mip6_write_config_data + __P((u_long, caddr_t)); +extern int mip6_clear_config_data + __P((u_long, caddr_t)); +extern int mip6_enable_func + __P((u_long, caddr_t)); + + +/* External declaration of function prototypes (mip6_md.c) */ +extern int mip6_is_primhomeprefix + __P((struct nd_prefix *)); +extern void mip6_create_ifid + __P((struct ifnet *, struct in6_addr *, u_int8_t)); +extern struct in6_ifaddr * mip6_pfxaddr_lookup + __P((struct nd_prefix *, int, int)); +extern struct in6_ifaddr * mip6_coa_lookup + __P((struct nd_prefix *)); +extern void mip6_update_home_addrs + __P((struct mbuf *, struct nd_prefix *, int)); +extern int mip6_create_homeaddr + __P((struct mip6_esm *)); +extern void mip6_select_php + __P((struct mip6_esm *)); +extern void mip6_deprecated_addr + __P((struct in6_ifaddr *)); +extern void mip6_md_init + __P((void)); +extern void mip6_select_defrtr + __P((struct nd_prefix *, struct nd_defrouter *)); +extern void mip6_prelist_update + __P((struct nd_prefix *, struct nd_defrouter *, u_char)); +extern void mip6_eager_prefix + __P((struct nd_prefix *, struct nd_defrouter *)); +extern void mip6_eager_md + __P((int enable)); +extern void mip6_expired_defrouter + __P((struct nd_defrouter *dr)); +extern void mip6_probe_defrouter + __P((struct nd_defrouter *dr)); +extern void mip6_probe_pfxrtrs + __P((void)); +extern void mip6_store_advint + __P((struct nd_opt_advinterval *, struct nd_defrouter *)); +extern int mip6_delete_ifaddr + __P((struct in6_addr *addr, struct ifnet *ifp)); +extern struct nd_prefix *mip6_get_home_prefix + __P((void)); +extern int mip6_get_md_state + __P((void)); +extern void mip6_md_exit + __P((void)); + + +/* External declaration of function prototypes (mip6_mn.c) */ +extern void mip6_mn_init + __P((void)); +extern void mip6_mn_exit + __P((void)); +extern int mip6_validate_ba + __P((struct mbuf *, u_int8_t *)); +extern int mip6_process_ba + __P((struct mbuf *, u_int8_t *)); +extern int mip6_ba_error + __P((struct mbuf *, u_int8_t *)); +extern int mip6_process_br + __P((struct mbuf *, u_int8_t *)); +extern int mip6_send_bu + __P((struct mip6_bul *, struct ip6_opt_binding_update *, + struct mip6_buffer *)); +extern void mip6_update_cns + __P((struct ip6_opt_binding_update *, struct mip6_buffer *, + struct in6_addr *, struct in6_addr *, u_int32_t)); +extern struct ip6_opt_binding_update *mip6_create_bu + __P((u_int8_t, u_int8_t, u_int32_t)); +extern void mip6_move + __P((int, struct in6_addr *, u_int8_t, struct nd_prefix *, + struct in6_ifaddr *)); +extern int mip6_move_home + __P((struct in6_addr *, u_int8_t, struct in6_addr *)); +extern int mip6_move_hn2fn + __P((struct in6_addr *, u_int8_t, struct in6_addr *)); +extern int mip6_move_fn2fn + __P((struct in6_addr *, u_int8_t, struct in6_addr *)); +extern int mip6_update_fn + __P((struct in6_addr *, u_int8_t, struct in6_addr *, + struct in6_addr *)); +extern int mip6_send_hadiscov + __P((struct mip6_esm *)); +extern int mip6_icmp6_hadiscov_reply + __P((struct mbuf *, int, int)); +extern u_int32_t mip6_prefix_lifetime + __P((struct in6_addr *, u_int8_t)); +extern struct in6_addr *mip6_in6addr_any + __P((const struct in6_addr *, int)); +extern struct mip6_bul *mip6_bul_find + __P((struct in6_addr *, struct in6_addr *)); +extern struct mip6_bul *mip6_bul_create + __P((struct in6_addr *, struct in6_addr *, struct in6_addr *, + u_int32_t, u_int8_t)); +extern struct mip6_bul *mip6_bul_delete + __P((struct mip6_bul *)); +extern void mip6_bul_clear_state + __P((struct mip6_bul *)); +extern struct mip6_esm *mip6_esm_find + __P((struct in6_addr *, u_int8_t)); +extern struct mip6_esm *mip6_esm_create + __P((struct ifnet *, struct in6_addr *, struct in6_addr *, + struct in6_addr *, struct in6_addr *, + u_int8_t, int, enum esm_type, u_int16_t)); +extern struct mip6_esm *mip6_esm_delete + __P((struct mip6_esm *)); +extern void mip6_timer_bul + __P((void *)); +extern int mip6_bul_retransmit + __P((struct mip6_bul *)); +extern int mip6_bul_refresh + __P((struct mip6_bul *, struct mip6_esm *)); +extern void mip6_timer_esm + __P((void *)); +extern int mip6_write_config_data_mn + __P((u_long, void *)); +extern int mip6_clear_config_data_mn + __P((u_long, caddr_t)); +extern int mip6_enable_func_mn + __P((u_long, caddr_t)); +extern int mip6_incl_br + __P((struct mbuf *)); +extern void mip6_send_rs + __P((struct mip6_esm *,int)); +extern void mip6_rs_output + __P((struct ifnet *)); +extern int mip6_tunneled_rs_output + __P((struct in6_addr *, struct in6_addr *)); +extern void mip6_dhaad_reply + __P((void *)); + + +/* External declaration of function prototypes (mip6_ha.c). */ +extern void mip6_ha_init + __P((void)); +extern void mip6_ha_exit + __P((void)); +extern int mip6_accept_bu + __P((struct mbuf *, u_int8_t *)); +extern int mip6_is_addr_onlink + __P((struct in6_addr *, u_int8_t)); +extern u_int32_t mip6_min_lifetime + __P((struct in6_addr *, u_int8_t)); +extern int mip6_proxy_update + __P((struct in6_addr *, struct in6_addr *, int)); +extern void mip6_proxy_control + __P((struct mip6_bc *, int)); +extern void mip6_icmp6_output + __P((struct mbuf *)); +extern int mip6_write_config_data_ha + __P((u_long, void *)); +extern int mip6_clear_config_data_ha + __P((u_long, void *)); +extern int mip6_enable_func_ha + __P((u_long, caddr_t)); + + +/* External declaration of function prototypes (mip6_hooks.c). */ +extern void mip6_minus_a_case + __P((struct nd_prefix *)); +extern struct nd_prefix *mip6_find_auto_home_addr + __P((struct in6_ifaddr **)); +extern void mip6_enable_hooks + __P((int)); +extern void mip6_disable_hooks + __P((int)); +extern int mip6_attach + __P((int)); +extern int mip6_release + __P((void)); + +#endif /* _KERNEL */ + +#endif /* not _NETINET6_MIP6_H_ */ diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6_common.h kame/kame/sys/netinet6/mip6_common.h --- kame-20010611/kame/sys/netinet6/mip6_common.h Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6_common.h Thu Mar 29 14:34:32 2001 @@ -0,0 +1,145 @@ +/* $KAME: mip6_common.h,v 1.11 2001/03/29 05:34:32 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Authors: Hesham Soliman + * Martti Kuparinen + * Mattias Pettersson + */ + + +#ifndef _NETINET6_MIP6_COMMON_H_ +#define _NETINET6_MIP6_COMMON_H_ + + + +/* SIOCs used for communication between kernel and user space. + * + * SIOCSDEBUG_MIP6 Set MIP6 debug on/off + * + * SIOCSBCFLUSH_MIP6 Remove list of BC + * + * SIOCSDEFCONFIG_MIP6 Restore default configuration + * + * SIOCSBRUPDATE_MIP6 Set time when CN should send Binding request + * + * SIOCSENABLEBR_MIP6 Enable sending BR to the MN + * + * SIOCSHALISTFLUSH_MIP6 Remove list of Home Agents + * + * SIOCSHAPREF_MIP6 HA preference + * + * SIOCSFWDSLUNICAST_MIP6 Enable forwarding of SL Unicast dest addresses + * + * SIOCSFWDSLMULTICAST_MIP6 Enable forwarding of SL Multicast dest addresses + * + * SIOCSFORADDRFLUSH_MIP6 Remove default foreign address from list + * + * SIOCSHADDRFLUSH_MIP6 Remove Home Address + * + * SIOCSBULISTFLUSH_MIP6 Remove Binding Update list + * + * SIOCACOADDR_MIP6 Set Default foreign IP Address + * + * SIOCAHOMEADDR_MIP6 Add home address + * + * SIOCAHOMEPREF_MIP6 Add home prefix + * + * SIOCSBULIFETIME_MIP6 Set default BU lifetime + * + * SIOCSHRLIFETIME_MIP6 Set default lifetime for home registration, not BU + * + * SIOCDCOADDR_MIP6 Remove default foreign address from list + * + * SIOCSPROMMODE_MIP6 Enable link layer promiscuous mode + * + * SIOCSBU2CN_MIP6 Enable sending BU to CN, i.e. Route opt on/off + * + * SIOCSREVTUNNEL_MIP6 Enable tunneling of packets from MN to CN via HA + * + * SIOCSAUTOCONFIG_MIP6 Allow autoconfiguration of Home address + * + * SIOCSEAGERMD_MIP6 Enable eager Movement Detection + * + */ +#define SIOCSDEBUG_MIP6 _IOWR('M', 1, struct mip6_input_data) +#define SIOCSBCFLUSH_MIP6 _IOWR('M', 2, int) +#define SIOCSDEFCONFIG_MIP6 _IOWR('M', 3, int) +#define SIOCSBRUPDATE_MIP6 _IOWR('M', 4, u_int8_t) +#define SIOCSENABLEBR_MIP6 _IOWR('M', 5, u_int8_t) + +#define SIOCSHALISTFLUSH_MIP6 _IOWR('M', 6, int) +#define SIOCSHAPREF_MIP6 _IOWR('M', 7, int) +#define SIOCSFWDSLUNICAST_MIP6 _IOWR('M', 8, int) +#define SIOCSFWDSLMULTICAST_MIP6 _IOWR('M', 9, int) + +#define SIOCSFORADDRFLUSH_MIP6 _IOWR('M', 10, int) +#define SIOCSHADDRFLUSH_MIP6 _IOWR('M', 11, int) +#define SIOCSBULISTFLUSH_MIP6 _IOWR('M', 12, int) +#define SIOCACOADDR_MIP6 _IOWR('M', 13, struct mip6_input_data) +#define SIOCAHOMEADDR_MIP6 _IOWR('M', 14, struct mip6_input_data) +#define SIOCSBULIFETIME_MIP6 _IOWR('M', 15, struct mip6_input_data) +#define SIOCSHRLIFETIME_MIP6 _IOWR('M', 16, struct mip6_input_data) +#define SIOCDCOADDR_MIP6 _IOWR('M', 17, struct mip6_input_data) +#define SIOCSPROMMODE_MIP6 _IOWR('M', 18, struct mip6_input_data) +#define SIOCSBU2CN_MIP6 _IOWR('M', 19, struct mip6_input_data) +#define SIOCSREVTUNNEL_MIP6 _IOWR('M', 20, struct mip6_input_data) +#define SIOCSAUTOCONFIG_MIP6 _IOWR('M', 21, struct mip6_input_data) +#define SIOCSEAGERMD_MIP6 _IOWR('M', 22, struct mip6_input_data) +#define SIOCSATTACH_MIP6 _IOWR('M', 23, struct mip6_input_data) +#define SIOCSRELEASE_MIP6 _IOWR('M', 24, struct mip6_input_data) +#define SIOCAHOMEPREF_MIP6 _IOWR('M', 25, struct mip6_input_data) + + +/* + * Information about which module that has been compiled into the kernel or + * loaded as a module. + */ +#define MIP6_MN_MODULE 0x01 +#define MIP6_HA_MODULE 0x02 + + +/* + * Generic message to pass configuration parameters from mip6config to + * kernel. + */ +struct mip6_input_data { + char if_name[IFNAMSIZ]; /* Interface name */ + u_int8_t prefix_len; /* Prefix length for address */ + struct in6_addr ip6_addr; /* Address */ + struct in6_addr ha_addr; /* Corresponding Home Agent */ + u_int32_t value; /* Value */ +}; + +#endif /* not _NETINET6_MIP6_COMMON_H_ */ diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6_ha.c kame/kame/sys/netinet6/mip6_ha.c --- kame-20010611/kame/sys/netinet6/mip6_ha.c Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6_ha.c Thu Mar 29 14:34:32 2001 @@ -0,0 +1,656 @@ +/* $KAME: mip6_ha.c,v 1.16 2001/03/29 05:34:32 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Author: Conny Larsson + * + */ + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include "opt_inet.h" +#include "opt_inet6.h" +#endif +#ifdef __NetBSD__ +#include "opt_inet.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* + ############################################################################## + # + # INITIALIZATION AND EXIT FUNCTIONS + # These functions are executed when the home agent specific MIPv6 code is + # activated and deactivated respectively. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_ha_init + * Description: Initialization of MIPv6 variables that must be initialized + * before the HA code is executed. + ****************************************************************************** + */ +void +mip6_ha_init(void) +{ + printf("Home Agent initialized\n"); +} + + + +/* + ****************************************************************************** + * Function: mip6_ha_exit + * Description: This function is called when the HA module is unloaded + * (relesed) from the kernel. + ****************************************************************************** + */ +void +mip6_ha_exit() +{ + printf("Home Agent de-activated\n"); +} + + + +/* + ############################################################################## + # + # FUNCTIONS FOR PROCESSING OF INBOUND MIPV6 OPTIONS + # Below are functions used for processing of received MIPv6 options (BU, BA + # and BR) and its sub-options. These options are received by the dest6_input() + # function, which calls the mip6_dstopt() function. The mip6_dstopt() function + # is a dispatcher function. + # As a result of processing an option other functions will be called which + # eventually results in either a response or an action. The functions for + # sending responses are also defined under this section. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_accept_bu + * Description: These checks are performed by the home agent when a Binding + * Update is received as part of accepting a request from a node + * to serve as its home agent (see 9.3). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + * -2 Silently ignore. Process rest of packet. + ****************************************************************************** + */ +int +mip6_accept_bu(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BU option in DH */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6aux *ip6a = NULL; + struct mbuf *n; + int res; + + bu_opt = (struct ip6_opt_binding_update *)opt; + if (!(bu_opt->ip6ou_flags & IP6_BUF_HOME)) { + log(LOG_ERR, + "%s: H-flag must be set in BU to perform this function\n", + __FUNCTION__); + return -2; + } + + n = ip6_findaux(m); + if (!n) return -1; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return -1; + + /* Is the node is a router implementing HA functionality? */ + if (!(ip6_forwarding && MIP6_IS_HA_ACTIVE)) { + res = MIP6_BA_STATUS_HOMEREGNOSUP; + mip6_build_send_ba(m, opt, NULL, NULL, res); + return -2; + } + + /* Verify that the home address is an on-link IPv6 address and + that the prefix length is correct. */ + res = mip6_is_addr_onlink(&ip6a->ip6a_home, bu_opt->ip6ou_prefixlen); + if (res != 0) { + mip6_build_send_ba(m, opt, NULL, NULL, res); + return -2; + } + + /* Must the home agent perform duplicate address detection? */ + if (bu_opt->ip6ou_flags & IP6_BUF_DAD) { + /* 1. Save *m, ip6aux, opt + suboption, coa + 2. Call in6_update_ifa (this function calls start_dad) */ + return 0; + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_is_addr_onlink + * Description: Check if an address is an on-link IPv6 address with respect to + * the home agent's current prefix list (see 9.3). + * Check, if the prefix length for the address is non-zero, that + * the address length is of the same length as the correspondent + * prefix length (see 9.3). + * Ret value: 0 = OK + * 133 = Not home subnet + * 136 = Incorrect interface identifier length + ****************************************************************************** + */ +int +mip6_is_addr_onlink(addr, prefixlen) +struct in6_addr *addr; /* IPv6 address to check */ +u_int8_t prefixlen; /* Prefix length for the address */ +{ + struct mip6_prefix *pr; + + for (pr = mip6_prq; pr; pr = pr->next) { + if (in6_are_prefix_equal(addr, &pr->prefix, pr->prefixlen)) { + if (prefixlen == 0) return 0; + if (pr->prefixlen == prefixlen) + return 0; + else + return MIP6_BA_STATUS_IFLEN; + } + } + return MIP6_BA_STATUS_SUBNET; +} + + + +/* + ****************************************************************************** + * Function: mip6_min_lifetime + * Description: Decide the remaining valid lifetime for a home address. If the + * prefix length is zero the lifetime is the lifetime of the + * prefix list entry for this prefix. + * If the prefix length is non-zero the lifetime is the minimum + * remaining valid lifetime for all subnet prefixes on the mobile + * node's home link. + * Ret value: Lifetime + ****************************************************************************** + */ +u_int32_t +mip6_min_lifetime(addr, prefixlen) +struct in6_addr *addr; /* IPv6 address to check */ +u_int8_t prefixlen; /* Prefix length for the address */ +{ + struct mip6_prefix *pr; /* Ptr to entries in the prexix list */ + u_int32_t min_time; /* Minimum life time */ + + min_time = 0xffffffff; + + for (pr = mip6_prq; pr; pr = pr->next) { + /* Different handling depending on the prefix length. */ + if (prefixlen == 0) { + if (in6_are_prefix_equal(addr, &pr->prefix, + pr->prefixlen)) { + return pr->validtime; + } + } else + min_time = min(min_time, pr->validtime); + } + return min_time; +} + + + +/* + ****************************************************************************** + * Function: mip6_proxy_update + * Description: Update (add or remove) address in the routing table for which + * the home agent is going to act as proxy for. + * Ret value: Standard error codes. + ****************************************************************************** + */ +int +mip6_proxy_update(addr, local, cmd) +struct in6_addr *addr; /* Address to be proxy for */ +struct in6_addr *local; /* Use this address when acting as proxy */ +int cmd; /* RTM_{ADD,DELETE} */ +{ + struct sockaddr_in6 mask; /* = {sizeof(mask), AF_INET6 } */ + struct sockaddr_in6 sa6; + struct sockaddr_dl *sdl; + struct rtentry *rt, *nrt; + struct ifaddr *ifa; + struct ifnet *ifp; + int flags, error; + + if (cmd == RTM_DELETE) { + bzero(&sa6, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + sa6.sin6_addr = *addr; + +#ifdef __FreeBSD__ + 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; +#ifdef MIP6_DEBUG + if (error) + mip6_debug("%s: RTM_DELETE for %s returned " + "error = %d\n", __FUNCTION__, + ip6_sprintf(addr), error); +#endif + return error; + } + + /* + * Case RTM_ADD + */ + + bzero(&sa6, sizeof sa6); + sa6.sin6_len = sizeof(struct sockaddr_in6); + sa6.sin6_family = AF_INET6; + sa6.sin6_addr = *addr; + + rt = rtalloc1((struct sockaddr *)&sa6, 0 +#ifdef __FreeBSD__ + , 0 +#endif /* __FreeBSD__ */ + ); + if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && + rt->rt_gateway->sa_family == AF_LINK) { + /* + * proxy NDP for single entry + */ +#ifdef MIP6_DEBUG + mip6_debug("%s RTM_ADD: we are already proxy for %s\n", + __FUNCTION__, ip6_sprintf(addr)); +#endif + return EEXIST; + } +#if 0 + /* REMOVE THIS */ + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, + IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + if (ifa) { + proxy = 1; + proxydl = SDL(rt->rt_gateway); + } + } + if (rt) + rtfree(rt); + + if (!ifa) { + /* We are not proxy for this address */ + } +#endif /* 0 */ + + /* Create sa6 */ + 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; + + /* Create sdl */ + 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); + + /* Create mask */ + 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); + +#ifdef MIP6_DEBUG + if (error) + mip6_debug("%s: RTM_ADD for %s returned error = %d\n", + __FUNCTION__, ip6_sprintf(addr), error); +#endif + if (error == 0) { + /* Avoid expiration */ + if (nrt) { + nrt->rt_rmx.rmx_expire = 0; + nrt->rt_genmask = NULL; + nrt->rt_refcnt--; + } else + error = EINVAL; + } + + free(sdl, M_IFMADDR); + return error; +} + + + +/* + ****************************************************************************** + * Function: mip6_proxy_control + * Description: While a node is serving as home agent for the mobile node it + * must act as proxy for the mobile node and intercept any packet + * on the home link addressed to the mobile nodes home address, + * including addresses formed from other on-link prefixes, if the + * prefix length field was non-zero in the BU. + * This function adds or removes addresses in the routing table + * for which the home agent act as proxy for. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_proxy_control(bcp, cmd) +struct mip6_bc *bcp; /* BC entry used for proxy generation */ +int cmd; /* RTM_{ADD, DELETE} */ +{ + struct mip6_prefix *prr; + struct in6_addr *laddr, *naddr, *prefix; + u_int8_t plen; + int proxy_for_ll_addr; + + if (!MIP6_IS_HA_ACTIVE) return; + + /* Proxy only for the home address? */ + if (bcp->prefixlen == 0) { + if (mip6_proxy_update(&bcp->peer_home, &bcp->local_home, cmd)){ + log(LOG_INFO, + "%s: Proxy for mobile node %s failed\n", + __FUNCTION__, ip6_sprintf(&bcp->peer_home)); + return; + } + return; + } + + /* Home agent acting as proxy for mobile node (see 9.5) */ + proxy_for_ll_addr = 0; + for (prr = mip6_prq; prr; prr = prr->next) { + prefix = &prr->prefix; + plen = prr->prefixlen; + + /* The prefix length must be equal */ + if (plen != bcp->prefixlen) continue; + + /* Build home address to be proxy for */ + naddr = mip6_in6addr(prefix, &bcp->peer_home, plen); + if (naddr == NULL) continue; + + /* Add MN home address to routing table to be proxy for */ + mip6_proxy_update(naddr, &bcp->local_home, cmd); + + /* Proxy for link-local address if prefix len == 64 */ + if ((plen == 64) && !proxy_for_ll_addr) { + laddr = mip6_in6addr(&in6addr_linklocal, + &bcp->peer_home, plen); + if (laddr == NULL) { + free(laddr, M_TEMP); + continue; + } + + mip6_proxy_update(laddr, &bcp->local_home, cmd); + proxy_for_ll_addr = 1; + free(laddr, M_TEMP); + } + free(naddr, M_TEMP); + } +} + + + +/* + ############################################################################## + # + # IP6 OUTPUT FUNCTIONS + # Functions used for processing of the outgoing IPv6 packet. These functions + # are called by using the mip6_output() function, when necesary, from the + # ip6_output() function. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_icmp6_output + * Description: Takes care of an outgoing Router Advertisement. If the node + * is a home agent it will create/update a home agent list entry + * and a prefix list entry. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_icmp6_output(m) +struct mbuf *m; /* Mbuf chain with IPv6 packet */ +{ + struct ip6_hdr *ip6; /* IPv6 header */ + struct icmp6_hdr *icmp6; /* ICMP6 header */ + struct nd_router_advert *ra = NULL; /* Router Advertisement */ + struct ifnet *ifp = NULL; /* Outgoing interface */ + struct ifaddr *if_addr; /* Interface address */ + struct sockaddr_in6 sin6; + caddr_t icmp6buf; /* Copy of mbuf (consequtive) */ + int icmp6len; + + if (!MIP6_IS_HA_ACTIVE) return; + + ip6 = mtod(m, struct ip6_hdr *); + if (ip6->ip6_nxt != IPPROTO_ICMPV6) return; + + if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) return; + + /* The mbuf data must be stored consequtively to be able to + cast data from it. */ + icmp6len = m->m_pkthdr.len - sizeof(struct ip6_hdr); + icmp6buf = (caddr_t)malloc(icmp6len, M_TEMP, M_NOWAIT); + if (icmp6buf == NULL) return; + + m_copydata(m, sizeof(struct ip6_hdr), icmp6len, icmp6buf); + icmp6 = (struct icmp6_hdr *)icmp6buf; + + /* Check if the packet shall be processed */ + if (icmp6->icmp6_type != ND_ROUTER_ADVERT) { + free(icmp6buf, M_TEMP); + return; + } + + if (icmp6->icmp6_code != 0) { + free(icmp6buf, M_TEMP); + return; + } + + if (icmp6len < sizeof(struct nd_router_advert)) { + free(icmp6buf, M_TEMP); + return; + } + + /* Find the outgoing interface */ + bzero(&sin6, sizeof(struct sockaddr_in6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = ip6->ip6_src; + + if_addr = ifa_ifwithaddr((struct sockaddr *)&sin6); + if (if_addr == NULL) { + free(icmp6buf, M_TEMP); + return; + } + ifp = if_addr->ifa_ifp; + + /* Look through the RA options and do appropriate updates */ + ra = (struct nd_router_advert *)icmp6; + if (mip6_icmp6_ra_options(ifp, &ip6->ip6_src, ra, icmp6len)) { + free(icmp6buf, M_TEMP); + return; + } + free(icmp6buf, M_TEMP); + return; +} + + + +/* + ############################################################################## + # + # IOCTL FUNCTIONS + # These functions are called from mip6_ioctl. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_write_config_data_ha + * Description: This function is called to write certain config values for + * MIPv6. The data is written into the global config structure. + * Ret value: - + ****************************************************************************** + */ +int mip6_write_config_data_ha(u_long cmd, void *arg) +{ + int retval = 0; + + switch (cmd) { + case SIOCSHAPREF_MIP6: + mip6_config.ha_pref = + ((struct mip6_input_data *)arg)->value; + break; + } + return retval; +} + + + +/* + ****************************************************************************** + * Function: mip6_clear_config_data_ha + * Description: This function is called to clear internal lists handled by + * MIPv6. + * Ret value: - + ****************************************************************************** + */ +int mip6_clear_config_data_ha(u_long cmd, void *data) +{ + int retval = 0; + int s; + + s = splnet(); + switch (cmd) { + case SIOCSHALISTFLUSH_MIP6: + break; + } + splx(s); + return retval; +} + + + +/* + ****************************************************************************** + * Function: mip6_enable_func_ha + * Description: This function is called to enable or disable certain functions + * in mip6. The data is written into the global config struct. + * Ret value: - + ****************************************************************************** + */ +int mip6_enable_func_ha(u_long cmd, caddr_t data) +{ + int enable; + int retval = 0; + + enable = ((struct mip6_input_data *)data)->value; + + switch (cmd) { + case SIOCSFWDSLUNICAST_MIP6: + mip6_config.fwd_sl_unicast = enable; + break; + + case SIOCSFWDSLMULTICAST_MIP6: + mip6_config.fwd_sl_multicast = enable; + break; + } + return retval; +} diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6_hooks.c kame/kame/sys/netinet6/mip6_hooks.c --- kame-20010611/kame/sys/netinet6/mip6_hooks.c Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6_hooks.c Mon Jun 4 10:25:01 2001 @@ -0,0 +1,407 @@ +/* $KAME: mip6_hooks.c,v 1.15 2001/06/01 22:54:40 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Authors: Mattias Pettersson + * Hesham Soliman + * Martti Kuparinen + * + */ + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include "opt_inet.h" +#include "opt_inet6.h" +#endif +#ifdef __NetBSD__ +#include "opt_inet.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * These are defined in sys/netinet6/ + */ + +/* Home Agent-specific hooks */ +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); + +/* Mobile Node-specific hooks */ +extern void (*mip6_select_defrtr_hook)(struct nd_prefix *, + struct nd_defrouter *); +extern struct nd_prefix * (*mip6_get_home_prefix_hook)(void); +extern void (*mip6_prelist_update_hook)(struct nd_prefix *, + struct nd_defrouter *, + u_char); +extern void (*mip6_expired_defrouter_hook)(struct nd_defrouter *); +extern void (*mip6_eager_prefix_hook)(struct nd_prefix *pr, + struct nd_defrouter *dr); +extern void (*mip6_probe_pfxrtrs_hook)(void); +extern void (*mip6_store_advint_hook)(struct nd_opt_advinterval *, + struct nd_defrouter *); +extern int (*mip6_get_md_state_hook)(void); +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 *); + + +void +mip6_minus_a_case(struct nd_prefix *pr) +{ + struct in6_addr addr; + struct in6_ifaddr *ia6; + + if ((ia6 = mip6_coa_lookup(pr)) == NULL) { + return; + } + + addr = in6addr_any; + mip6_esm_create(pr->ndpr_ifp, NULL, &addr, &ia6->ia_addr.sin6_addr, + &pr->ndpr_prefix.sin6_addr, + pr->ndpr_plen, MIP6_STATE_UNDEF, PERMANENT, 0xFFFF); +#ifdef MIP6_DEBUG + mip6_debug("Late Home Address %s found for autoconfig'd case. Starting" + " Mobile IPv6.\n", ip6_sprintf(&ia6->ia_addr.sin6_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(struct in6_ifaddr **ia6p) +{ + struct nd_prefix *pr; +#if 0 + struct in6_ifaddr *ia6; +#endif + + if (ia6p == NULL) + return NULL; + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { +#ifdef MIP6_DEBUG + mip6_debug("%s: scanning prefix %s (pr = %p)\n", __FUNCTION__, + ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr); +#endif + if ((*ia6p = mip6_coa_lookup(pr)) == NULL) { + continue; + } +#if 0 + ia6 = in6ifa_ifpwithaddr(pr->ndpr_ifp, &pr->ndpr_addr); + if (ia6 && (ia6->ia6_flags | IN6_IFF_DETACHED)) + continue; + else + break; /* XXXYYY Remove in v2.0. */ +#else +#ifdef 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 /* XXXYYY Add in v2.0 */ + for (pfxrtr = pr->ndpr_advrtrs.lh_first; pfxrtr; + pfxrtr = pfxrtr->pfr_next) { + if ((pfxrtr->router->flags & ND_RA_FLAG_HOME_AGENT) + == ND_RA_FLAG_HOME_AGENT) + break; + } +#endif /* 0 */ + } + if (*ia6p) { +#ifdef MIP6_DEBUG + mip6_debug("Found an autoconfigured home address " + "immediately: %s\n", + ip6_sprintf(&(*ia6p)->ia_addr.sin6_addr)); +#endif + } + else { +#ifdef MIP6_DEBUG + mip6_debug("Couldn't find an autoconfigured home address " + "immediately.\n"); +#endif + } + return pr; +} + + +void +mip6_enable_hooks(int scope) +{ + int s; + + /* + * Activate the hook functions. After this some packets might come + * to the module... + * Note: mip6_minus_a_case_hook() is an exception and is not handled + * here. + */ + s = splimp(); + + if (scope == MIP6_CONFIG_HOOKS) { + /* Activate Home Agent-specific 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; + + /* Activate Mobile Node-specific hooks */ + 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) { + /* Activate Mobile Node-specific hooks */ + if (MIP6_IS_MN_ACTIVE) { + 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; +#ifdef OLDMIP6 + mip6_eager_prefix_hook = mip6_eager_prefix; +#endif + mip6_probe_pfxrtrs_hook = mip6_probe_pfxrtrs; + mip6_store_advint_hook = mip6_store_advint; + mip6_get_md_state_hook = mip6_get_md_state; + } + } + splx(s); + return; +} + + +void +mip6_disable_hooks(int scope) +{ + int s; + + /* + * Deactivate the hook functions. After this some packets might not + * come to the module... + */ + s = splimp(); + + if (scope == MIP6_SPECIFIC_HOOKS) { + /* De-activate Home Agent-specific hooks */ + if (MIP6_IS_HA_ACTIVE) { + mip6_write_config_data_ha_hook = 0; + mip6_clear_config_data_ha_hook = 0; + mip6_enable_func_ha_hook = 0; + } + + /* De-activate Mobile Node-specific hooks */ + if (MIP6_IS_MN_ACTIVE) { + mip6_select_defrtr_hook = 0; + mip6_get_home_prefix_hook = 0; + mip6_prelist_update_hook = 0; + mip6_expired_defrouter_hook = 0; + mip6_eager_prefix_hook = 0; + mip6_probe_pfxrtrs_hook = 0; + mip6_store_advint_hook = 0; + mip6_get_md_state_hook = 0; + mip6_write_config_data_mn_hook = 0; + mip6_clear_config_data_mn_hook = 0; + mip6_enable_func_mn_hook = 0; + mip6_minus_a_case_hook = 0; + } + } + splx(s); + return; +} + + +int +mip6_attach(int module) +{ + /* + * Important that necessary settings have been done _before_ calling + * mip6_attach(), e.g. home address or home prefix specified, or + * autoconfig set. "mip6config" program sees to that. + */ + +/* + No support for modules here yet. XXXYYY + + Old check (not valid any longer): + #if (defined(MIP6_MN) || defined (MIP6_HA) || defined(MIP6_MODULES)) +*/ + if (mip6_module) { +#ifdef MIP6_DEBUG + char *hastr = "Home Agent"; + char *mnstr = "Mobile Node"; + + mip6_debug("Can't switch operation mode from "); + switch (mip6_module) { + case MIP6_HA_MODULE: + mip6_debug("%s", hastr); + break; + case MIP6_MN_MODULE: + mip6_debug("%s", mnstr); + break; + default: + mip6_debug("?"); + } + mip6_debug(" to "); + switch (module) { + case MIP6_HA_MODULE: + mip6_debug("%s", hastr); + break; + case MIP6_MN_MODULE: + mip6_debug("%s", mnstr); + break; + default: + mip6_debug("?"); + } + mip6_debug(" \n" + "- please deactivate first (\"mip6config -x\")\n"); +#endif + return EINVAL; + } + + switch (module) { + case MIP6_HA_MODULE: + printf("%s: attach ha\n", __FUNCTION__); /* RM */ + mip6_module = module; + mip6_ha_init(); + break; + + case MIP6_MN_MODULE: + printf("%s: attach mn\n", __FUNCTION__); /* RM */ + mip6_module = module; + mip6_mn_init(); + break; + + default: +#ifdef 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) /* Test arbitrary hook */ + return 0; + + /* + * If autoconfig state: find a global address to use as Home + * Address. + * - Take first available on any interface, else if no found: + * - Enable hook to wait for a Router Advertisement to give + * us one. + */ + if (mip6_config.autoconfig) { + struct nd_prefix *pr; + struct in6_addr addr; + struct in6_ifaddr *ia6; + + addr = in6addr_any; + if ((pr = mip6_find_auto_home_addr(&ia6)) != NULL) { + mip6_esm_create(pr->ndpr_ifp, &addr, &addr, + &ia6->ia_addr.sin6_addr, + &pr->ndpr_prefix.sin6_addr, + pr->ndpr_plen, + MIP6_STATE_UNDEF, PERMANENT, + 0xFFFF); + mip6_enable_hooks(MIP6_SPECIFIC_HOOKS); + mip6_md_init(); + } + else { +#ifdef 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 { + /* Manual config */ + mip6_enable_hooks(MIP6_SPECIFIC_HOOKS); + mip6_md_init(); + } + } + + if (MIP6_IS_HA_ACTIVE) { + /* XXXYYY Build anycast or is it done? */ + mip6_enable_hooks(MIP6_SPECIFIC_HOOKS); + } + return 0; +} + + +int +mip6_release(void) +{ + /* Disable the hooks */ + 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(); + +/* + Correspondent Node functionality is never terminated. + mip6_disable_hooks(MIP6_GENERIC_HOOKS); + mip6_exit(); +*/ + + mip6_module = 0; /* Make HA or MN inactive */ + + return 0; +} diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6_io.c kame/kame/sys/netinet6/mip6_io.c --- kame-20010611/kame/sys/netinet6/mip6_io.c Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6_io.c Wed Apr 4 18:31:30 2001 @@ -0,0 +1,1550 @@ +/* $KAME: mip6_io.c,v 1.13 2001/04/04 09:31:30 karino Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Authors: Conny Larsson + * Mattias Pettersson + * + */ + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" +#endif + +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ipsec.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef MIP6_DEBUG +#include +#include +#include +#include +#include +#endif + +#include + +/* + ############################################################################## + # + # FUNCTIONS FOR CHECKING OF INCOMING IPV6 PACKETS + # Used for checking of incoming packets, which does not necessarily contains + # any MIP6 options, to make sure that route optimization is done. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_route_optimize + * Description: When a tunneled packet is received a BU shall be sent to the + * CN if no Binding Update List entry exist or if the rate limit + * for sending BUs for an existing BUL entry is not exceded + * (see 10.11). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_route_optimize(m) +struct mbuf *m; /* Mbuf containing the IPv6 packet */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6_hdr *ip6; + struct ip6aux *ip6a = NULL; + struct mbuf *n; + struct mip6_esm *esp; + struct mip6_bul *bulp, *bulp_ha; + struct mip6_buffer subbuf; + struct mip6_subopt_altcoa altcoa; + struct in6_addr src_addr; + u_int8_t bu_flags; + time_t t; + int size, free_bul = 0; + +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + long time_second = time.tv_sec; +#endif + + /* Make sure that all requirements are meet for sending a BU to + the original sender of the packet. */ + if (!MIP6_IS_MN_ACTIVE) + return 0; + + if (!(m->m_flags & M_MIP6TUNNEL)) + return 0; + + ip6 = mtod(m, struct ip6_hdr *); + esp = mip6_esm_find(&ip6->ip6_dst, 0); + if (esp == NULL) + return 0; + + bulp_ha = mip6_bul_find(NULL, &esp->home_addr); + if (bulp_ha == NULL) + return 0; + + /* Find the correct source address */ + n = ip6_findaux(m); + if (!n) return -1; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return -1; + ip6 = mtod(m, struct ip6_hdr *); + + if (ip6a->ip6a_flags & IP6A_HASEEN) + src_addr = ip6a->ip6a_home; + else + src_addr = ip6->ip6_src; + + /* Try to find an existing BUL entry. */ + bu_flags = 0; + bulp = mip6_bul_find(&src_addr, &ip6->ip6_dst); + if (bulp == NULL) { + /* Create Binding Update list entry */ + bulp = mip6_bul_create(&src_addr, &esp->home_addr, &esp->coa, + bulp_ha->lifetime, bu_flags); + if (bulp == NULL) return -1; + free_bul = 1; + } else { + /* If the existing BUL entry is waiting for an ack or + has disabled sending BU, no BU shall be sent. */ + if ((bulp->flags & IP6_BUF_ACK) || (bulp->send_flag == 0)) + return 0; + + /* Check the rate limiting for sending Binding Updates */ + t = (time_t)time_second; + if ((t - bulp->lasttime) < bulp->bul_rate) + return 0; + + /* Update existing BUL entry */ + bulp->peer_home = src_addr; + bulp->local_coa = esp->coa; + bulp->lifetime = bulp_ha->lifetime; + bulp->refresh = bulp_ha->lifetime; + } + + /* Create Binding Update option */ + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, + bulp_ha->lifetime); + if (bu_opt == NULL) { + if (free_bul) mip6_bul_delete(bulp); + return -1; + } + + /* Create necessary sub-options */ + bzero((caddr_t)&subbuf, sizeof(struct mip6_buffer)); + + altcoa.type = IP6SUBOPT_ALTCOA; + altcoa.len = IP6OPT_COALEN; + size = sizeof(struct in6_addr); + bcopy((caddr_t)&esp->coa, &altcoa.coa, size); + mip6_add_subopt2buf((u_int8_t *)&altcoa, &subbuf); + + /* Send BU to CN */ + if (mip6_send_bu(bulp, bu_opt, &subbuf)) { + free(bu_opt, M_TEMP); + return -1; + } + + free(bu_opt, M_TEMP); + return 0; +} + + + +/* + ############################################################################## + # + # FUNCTIONS FOR PROCESSING OF INCOMING MIPV6 OPTIONS + # Below are functions used for processing of received MIPv6 options (BU, BA + # and BR) and its sub-options. These options are received by the dest6_input() + # function, which calls the mip6_dstopt() function. The mip6_dstopt() function + # is a dispatcher function. + # As a result of processing an option other functions will be called which + # eventually results in either a response or an action. The functions for + # sending responses are also defined under this section. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_dstopt + * Description: Decides which MIPv6 option that was received and processes it + * accordingly. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_dstopt(m, dh, opt, dhlen) +struct mbuf *m; /* Ptr to beginning of mbuf */ +struct ip6_dest *dh; /* Ptr to beginning of DH */ +u_int8_t *opt; /* Ptr to current option in DH */ +int dhlen; /* Remaining DH length */ +{ + u_int8_t *subopt; /* Ptr to first sub-option in current option */ + u_int8_t optlen; /* Remaining option length */ + int res; + + optlen = *(opt + 1); + if (dhlen < (optlen + IP6OPT_MINLEN)) { + ip6stat.ip6s_toosmall++; + return -1; + } + + switch (*opt) { + case IP6OPT_BINDING_UPDATE: + /* Verify BU alignment requirement: 4n+2 */ + if ((opt - (u_int8_t *)dh) % 4 != 2) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, "%s: BU alignment failure\n", + __FUNCTION__); + return -1; + } + + if (mip6_validate_bu(m, opt) == -1) return -1; + if (optlen > IP6OPT_BULEN) { + subopt = opt + IP6OPT_MINLEN + IP6OPT_BULEN; + optlen -= IP6OPT_BULEN; + if (mip6_validate_subopt(dh, subopt, + optlen) == -1) + return -1; + } + if (mip6_process_bu(m, opt) == -1) return -1; + break; + + case IP6OPT_BINDING_ACK: + if (!MIP6_IS_MN_ACTIVE) return 0; + + /* Verify BA alignment requirement: 4n+3 */ + if ((opt - (u_int8_t *)dh) % 4 != 3) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, "%s: BA alignment failure\n", + __FUNCTION__); + return -1; + } + + res = mip6_validate_ba(m, opt); + if (res == -1) return -1; + else if (res == -2) return 0; + + if (mip6_process_ba(m, opt) == -1) return -1; + break; + + case IP6OPT_BINDING_REQ: + if (!MIP6_IS_MN_ACTIVE) return 0; + + if (optlen > IP6OPT_BRLEN) { + subopt = opt + IP6OPT_MINLEN + IP6OPT_BRLEN; + optlen -= IP6OPT_BRLEN; + if (mip6_validate_subopt(dh, subopt, + optlen) == -1) + return -1; + } + if (mip6_process_br(m, opt) == -1) return -1; + break; + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_print_subopt + * Description: Print sub-options included in MIPv6 options. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_print_subopt(subopt, optlen) +u_int8_t *subopt; /* Ptr to first sub-option in current option */ +u_int8_t optlen; /* Remaining option length */ +{ + struct mip6_subopt_altcoa *altcoa; + struct mip6_subopt_uid *uid; + struct in6_addr *addr; + + /* Search all sub-options for current option */ + while (optlen > 0) { + switch (*subopt) { + case IP6OPT_PAD1: + optlen -= 1; + subopt += 1; + break; + case IP6OPT_PADN: + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + case IP6SUBOPT_UNIQUEID: + uid = (struct mip6_subopt_uid *)subopt; + mip6_debug("Unique Identifier sub-option\n"); + mip6_debug("Type/Length/Id: %x " + "/ %u / %u\n", + uid->type, uid->len, + ntohs(*(u_int16_t *)uid->uid)); + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + case IP6SUBOPT_ALTCOA: + altcoa = (struct mip6_subopt_altcoa *)subopt; + addr = (struct in6_addr *)altcoa->coa; + mip6_debug("Alternate coa sub-option\n"); + mip6_debug("Type/Length: %x / %u\n", + altcoa->type, altcoa->len); + mip6_debug("Care-of address: %s\n", + ip6_sprintf(addr)); + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + default: + optlen -= *(subopt + 1) + 2; + subopt += *(subopt + 1) + 2; + break; + } + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_print_opt + * Description: Print MIPv6 options included in an incoming destination + * header. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_print_opt(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to MIP6 option in DH */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6_opt_binding_ack *ba_opt; + struct ip6_opt_binding_request *br_opt; + struct ip6_hdr *ip6; + struct ip6aux *ip6a = NULL; + struct mbuf *n; + u_int8_t *subopt; + + ip6 = mtod(m, struct ip6_hdr *); + + /* Search all sub-options for current option */ + if (*opt == IP6OPT_BINDING_UPDATE) { + n = ip6_findaux(m); + if (!n) return; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return; + + bu_opt = (struct ip6_opt_binding_update *)opt; + + mip6_debug("\nReceived Binding Update\n"); + mip6_debug("Src home address %s\n", + ip6_sprintf(&ip6a->ip6a_home)); + mip6_debug("Src Care-of address %s\n", + ip6_sprintf(&ip6a->ip6a_careof)); + mip6_debug("Dst address %s\n", + ip6_sprintf(&ip6->ip6_dst)); + mip6_debug("Type/Length/Flags: %x / %u / ", + bu_opt->ip6ou_type, bu_opt->ip6ou_len); + if (bu_opt->ip6ou_flags & IP6_BUF_ACK) mip6_debug("A "); + if (bu_opt->ip6ou_flags & IP6_BUF_HOME) mip6_debug("H "); + if (bu_opt->ip6ou_flags & IP6_BUF_ROUTER) mip6_debug("R "); + if (bu_opt->ip6ou_flags & IP6_BUF_DAD) mip6_debug("D "); + mip6_debug("\n"); + mip6_debug("Prefix length: %u\n", + bu_opt->ip6ou_prefixlen); + mip6_debug("Sequence number: %u\n", + ntohs(*(u_int16_t *)bu_opt->ip6ou_seqno)); + mip6_debug("Life time: "); + mip6_print_sec(ntohl(*(u_int32_t *)bu_opt->ip6ou_lifetime)); + if (bu_opt->ip6ou_len > IP6OPT_BULEN) { + subopt = opt + IP6OPT_MINLEN + IP6OPT_BULEN; + mip6_print_subopt(subopt , *(opt + 1) - IP6OPT_BULEN); + } + return; + } + + if (*opt == IP6OPT_BINDING_ACK) { + ba_opt = (struct ip6_opt_binding_ack *)opt; + + mip6_debug("\nReceived Binding Acknowledgement\n"); + mip6_debug("IP Header Src: %s\n", + ip6_sprintf(&ip6->ip6_src)); + mip6_debug("IP Header Dst: %s\n", + ip6_sprintf(&ip6->ip6_dst)); + mip6_debug("Type/Length/Status: %x / %u / %u\n", + ba_opt->ip6oa_type, ba_opt->ip6oa_len, + ba_opt->ip6oa_status); + mip6_debug("Sequence number: %u\n", + ntohs(*(u_int16_t *)ba_opt->ip6oa_seqno)); + mip6_debug("Life time: "); + mip6_print_sec(ntohl(*(u_int32_t *)ba_opt->ip6oa_lifetime)); + mip6_debug("Refresh time: "); + mip6_print_sec(ntohl(*(u_int32_t *)ba_opt->ip6oa_refresh)); + if (ba_opt->ip6oa_len > IP6OPT_BALEN) { + subopt = opt + IP6OPT_MINLEN + IP6OPT_BALEN; + mip6_print_subopt(subopt , *(opt + 1) - IP6OPT_BALEN); + } + return; + } + + if (*opt == IP6OPT_BINDING_REQ) { + br_opt = (struct ip6_opt_binding_request *)opt; + + mip6_debug("\nReceived Binding Request\n"); + mip6_debug("IP Header Src: %s\n", + ip6_sprintf(&ip6->ip6_src)); + mip6_debug("IP Header Dst: %s\n", + ip6_sprintf(&ip6->ip6_dst)); + mip6_debug("Type/Length: %x / %u\n", + br_opt->ip6or_type, br_opt->ip6or_len); + if (br_opt->ip6or_len > IP6OPT_BRLEN) { + subopt = opt + IP6OPT_MINLEN + IP6OPT_BRLEN; + mip6_print_subopt(subopt , *(opt + 1) - IP6OPT_BRLEN); + } + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_find_offset + * Description: If the Destination header contains data it may already have + * an 8 octet alignment. The last alignment bytes in the header + * might be possible to remove and instead use it for options. + * This function adjusts the buffer offset, if possible. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_find_offset(buf) +struct mip6_buffer *buf; /* Destination header with options */ +{ + int ii; + u_int8_t new_off; + + /* Verify input */ + if ((buf == NULL) || (buf->off < 2)) return; + + /* Check the buffer for unnecessary padding */ + new_off = 2; + for (ii = 2; ii < buf->off;) { + if (*(buf->buf + ii) == IP6OPT_PAD1) { + new_off = ii; + ii += 1; + } else if (*(buf->buf + ii) == IP6OPT_PADN) { + new_off = ii; + ii += *(buf->buf + ii + 1) + 2; + } else { + ii += *(buf->buf + ii + 1) + 2; + new_off = ii; + } + } + buf->off = new_off; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_subopt2buf + * Description: Add one sub-option to the internal buffer. This buffer may + * include several consecutive sub-options. All sub-options must + * belong to the same MIPv6 option. The sub-options are not + * aligned in this function. Alignment is done in function + * mip6_add_subopt2dh(). + * Ret value: Void + ****************************************************************************** + */ +void +mip6_add_subopt2buf(subopt, buf) +u_int8_t *subopt; /* Sub-option to add */ +struct mip6_buffer *buf; /* Buffer holding all sub-options */ +{ + struct mip6_subopt_altcoa *altcoa; + struct mip6_subopt_uid *uid; + u_int16_t var16; + u_int8_t len; + + /* Verify input */ + if (subopt == NULL || buf == NULL) return; + + /* Add sub-option to the internal sub-option buffer */ + switch (*subopt) { + case IP6SUBOPT_UNIQUEID: + uid = (struct mip6_subopt_uid *)subopt; + if (uid->len != IP6OPT_UIDLEN) return; + + /* Append sub-option to buffer */ + len = IP6OPT_UIDLEN + IP6OPT_MINLEN; + bzero((caddr_t)buf->buf + buf->off, len); + bcopy((caddr_t)uid, (caddr_t)buf->buf + buf->off, len); + + uid = (struct mip6_subopt_uid *)(buf->buf + buf->off); + var16 = htons(*(u_int16_t *)uid->uid); + bcopy((caddr_t)&var16, uid->uid, sizeof(u_int16_t)); + buf->off += len; + break; + case IP6SUBOPT_ALTCOA: + altcoa = (struct mip6_subopt_altcoa *)subopt; + if (altcoa->len != IP6OPT_COALEN) return; + + /* Append sub-option to buffer */ + len = IP6OPT_COALEN + IP6OPT_MINLEN; + bzero((caddr_t)buf->buf + buf->off, len); + bcopy((caddr_t)altcoa, + (caddr_t)buf->buf + buf->off, len); + buf->off += len; + break; + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_opt2dh + * Description: Add Binding Update, Binding Acknowledgement, Binding Request + * or Home Address option to a Destination Header. The option + * must be aligned when added. + * Ret value: Ptr where the MIPv6 option is located in the Destination header + * or NULL. + ****************************************************************************** + */ +u_int8_t * +mip6_add_opt2dh(opt, dh) +u_int8_t *opt; /* BU, BR, BA or Home Address option */ +struct mip6_buffer *dh; /* Buffer containing the IPv6 DH */ +{ + struct ip6_opt_binding_update *bu; + struct ip6_opt_binding_ack *ba; + struct ip6_opt_binding_request *br; + struct ip6_opt_home_address *ha; + u_int8_t *pos, len, padn, off; + u_int16_t seqno; + u_int32_t t; + int rest; + + /* Verify input */ + pos = NULL; + if (opt == NULL || dh == NULL) return pos; + if (dh->off < 2) { + bzero((caddr_t)dh->buf, 2); + dh->off = 2; + } + + /* Add option to Destination header */ + padn = IP6OPT_PADN; + switch (*opt) { + case IP6OPT_BINDING_UPDATE: + /* BU alignment requirement (4n + 2) */ + rest = dh->off % 4; + if (rest == 0) { + /* Add a PADN option with length 0 */ + bzero((caddr_t)dh->buf + dh->off, 2); + bcopy(&padn, (caddr_t)dh->buf + dh->off, 1); + dh->off += 2; + } else if (rest == 1) { + /* Add a PAD1 option */ + bzero((caddr_t)dh->buf + dh->off, 1); + dh->off += 1; + } else if (rest == 3) { + /* Add a PADN option with length 1 */ + len = 1; + bzero((caddr_t)dh->buf + dh->off, 3); + bcopy(&padn, (caddr_t)dh->buf + dh->off, 1); + bcopy(&len, (caddr_t)dh->buf + dh->off + 1, 1); + dh->off += 3; + } + + /* Copy option to DH */ + len = IP6OPT_BULEN + IP6OPT_MINLEN; + off = dh->off; + bu = (struct ip6_opt_binding_update *)opt; + + bzero((caddr_t)dh->buf + off, len); + bcopy((caddr_t)bu, (caddr_t)dh->buf + off, len); + + bu = (struct ip6_opt_binding_update *)(dh->buf + off); +#ifdef DIAGNOSTIC + if (sizeof(seqno) != sizeof(bu->ip6ou_seqno)) + panic("bcopy problem"); +#endif + seqno = htons(*(u_int16_t *)bu->ip6ou_seqno); + bcopy((caddr_t)&seqno, bu->ip6ou_seqno, sizeof(seqno)); +#ifdef DIAGNOSTIC + if (sizeof(t) != sizeof(bu->ip6ou_lifetime)) + panic("bcopy problem"); +#endif + t = htonl(*(u_int32_t *)bu->ip6ou_lifetime); + bcopy((caddr_t)&t, bu->ip6ou_lifetime, sizeof(t)); + + pos = dh->buf + off; + dh->off += len; + break; + case IP6OPT_BINDING_ACK: + /* BA alignment requirement (4n + 3) */ + rest = dh->off % 4; + if (rest == 1) { + /* Add a PADN option with length 0 */ + bzero((caddr_t)dh->buf + dh->off, 2); + bcopy(&padn, (caddr_t)dh->buf + dh->off, 1); + dh->off += 2; + } else if (rest == 2) { + /* Add a PAD1 option */ + bzero((caddr_t)dh->buf + dh->off, 1); + dh->off += 1; + } else if (rest == 0) { + /* Add a PADN option with length 1 */ + len = 1; + bzero((caddr_t)dh->buf + dh->off, 3); + bcopy(&padn, (caddr_t)dh->buf + dh->off, 1); + bcopy(&len, (caddr_t)dh->buf + dh->off + 1, 1); + dh->off += 3; + } + + /* Copy option to DH */ + len = IP6OPT_BALEN + IP6OPT_MINLEN; + off = dh->off; + ba = (struct ip6_opt_binding_ack *)opt; + + bzero((caddr_t)dh->buf + off, len); + bcopy((caddr_t)ba, (caddr_t)dh->buf + off, len); + + ba = (struct ip6_opt_binding_ack *)(dh->buf + off); +#ifdef DIAGNOSTIC + if (sizeof(seqno) != sizeof(ba->ip6oa_seqno)) + panic("bcopy problem"); +#endif + seqno = htons(*(u_int16_t *)ba->ip6oa_seqno); + bcopy((caddr_t)&seqno, ba->ip6oa_seqno, sizeof(seqno)); +#ifdef DIAGNOSTIC + if (sizeof(t) != sizeof(ba->ip6oa_lifetime)) + panic("bcopy problem"); +#endif + t = htonl(*(u_int32_t *)ba->ip6oa_lifetime); + bcopy((caddr_t)&t, ba->ip6oa_lifetime,sizeof(t)); +#ifdef DIAGNOSTIC + if (sizeof(t) != sizeof(ba->ip6oa_refresh)) + panic("bcopy problem"); +#endif + t = htonl(*(u_int32_t *)ba->ip6oa_refresh); + bcopy((caddr_t)&t, ba->ip6oa_refresh, sizeof(t)); + + pos = dh->buf + off; + dh->off += len; + break; + case IP6OPT_BINDING_REQ: + /* Copy option to DH */ + len = IP6OPT_BRLEN + IP6OPT_MINLEN; + off = dh->off; + br = (struct ip6_opt_binding_request *)opt; + + bzero((caddr_t)dh->buf + off, len); + bcopy((caddr_t)br, (caddr_t)dh->buf + off, len); + + pos = dh->buf + off; + dh->off += len; + break; + case IP6OPT_HOME_ADDRESS: + /* HA alignment requirement (8n + 6) */ + rest = dh->off % 8; + if (rest <= 4) { + /* Add a PADN option with length X */ + len = 6 - rest - 2; + bzero((caddr_t)dh->buf + dh->off, len + 2); + bcopy(&padn, (caddr_t)dh->buf + dh->off, 1); + bcopy(&len, (caddr_t)dh->buf + dh->off + 1, 1); + dh->off += len + 2; + } else if (rest == 5) { + /* Add a PAD1 option */ + bzero((caddr_t)dh->buf + dh->off, 1); + dh->off += 1; + } else if (rest == 7) { + /* Add a PADN option with length 5 */ + len = 5; + bzero((caddr_t)dh->buf + dh->off, len + 2); + bcopy(&padn, (caddr_t)dh->buf + dh->off, 1); + bcopy(&len, (caddr_t)dh->buf + dh->off + 1, 1); + dh->off += len + 2; + } + + /* Copy option to DH */ + len = IP6OPT_HALEN + IP6OPT_MINLEN; + off = dh->off; + ha = (struct ip6_opt_home_address *)opt; + + bzero((caddr_t)dh->buf + off, len); + bcopy((caddr_t)ha, (caddr_t)dh->buf + off, len); + + pos = dh->buf + off; + dh->off += len; + break; + } + return pos; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_subopt2dh + * Description: Add the internal list of sub-options to an MIPv6 option. The + * MIPv6 option has been copied to the destination header buffer. + * For each sub-option added, alignment must be done. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_add_subopt2dh(subopt, dh, optpos) +struct mip6_buffer *subopt; /* Buffer including all sub-options */ +struct mip6_buffer *dh; /* Destination header buffer */ +u_int8_t *optpos; /* Position for MIPv6 option */ +{ + struct ip6_opt *opt_hdr; + u_int8_t padn, len, ii, off, added_bytes; + int rest; + + /* Verify input */ + if (subopt == NULL || dh == NULL || optpos == NULL) return; + if (dh->off < 4) return; + + /* Add sub-option to Destination header */ + padn = IP6OPT_PADN; + added_bytes = 0; + for (ii = 0; ii < subopt->off; ii += len) { + switch (*(subopt->buf + ii)) { + case IP6SUBOPT_UNIQUEID: + /* Unique Identifier (2n) */ + rest = dh->off % 2; + if (rest == 1) { + /* Add a PAD1 option */ + bzero((caddr_t)dh->buf + dh->off, 1); + dh->off += 1; + added_bytes += 1; + } + + /* Append sub-option to buffer */ + len = IP6OPT_UIDLEN + IP6OPT_MINLEN; + off = dh->off; + bzero((caddr_t)dh->buf + off, len); + bcopy((caddr_t)subopt->buf + ii, + (caddr_t)dh->buf + off, len); + dh->off += len; + added_bytes += len; + break; + case IP6SUBOPT_ALTCOA: + /* Alternate Care-of Address (8n + 6) */ + rest = dh->off % 8; + if (rest <= 4) { + /* Add a PADN option with length 0 */ + len = 6 - rest - 2; + off = dh->off; + bzero((caddr_t)dh->buf+off, len+2); + bcopy(&padn, (caddr_t)dh->buf+off, 1); + bcopy(&len, (caddr_t)dh->buf+off+1, 1); + dh->off += len + 2; + added_bytes += len + 2; + } else if (rest == 5) { + /* Add a PAD1 option */ + bzero((caddr_t)dh->buf + dh->off, 1); + dh->off += 1; + added_bytes += 1; + } else if (rest == 7) { + /* Add a PADN option with length 5 */ + len = 5; + off = dh->off; + bzero((caddr_t)dh->buf+off, len+2); + bcopy(&padn, (caddr_t)dh->buf+off, 1); + bcopy(&len, (caddr_t)dh->buf+off+1, 1); + dh->off += len + 2; + added_bytes += len + 2; + } + + /* Append sub-option to buffer */ + len = IP6OPT_COALEN + IP6OPT_MINLEN; + off = dh->off; + bzero((caddr_t)dh->buf + off, len); + bcopy((caddr_t)subopt->buf + ii, + (caddr_t)dh->buf + off, len); + dh->off += len; + added_bytes += len; + break; + } + } + + /* Adjust the option length to include the sub-option(s) */ + opt_hdr = (struct ip6_opt *)optpos; + opt_hdr->ip6o_len += added_bytes; + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_align + * Description: Align a destination header to a multiple of 8 octets. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_align(buf) +struct mip6_buffer *buf; /* IPv6 destination header to align */ +{ + struct ip6_ext *ext_hdr; + int rest; /* Rest of modulo division */ + u_int8_t padlen; /* Number of bytes to pad */ + u_int8_t padn; /* Number for option type PADN */ + + padn = IP6OPT_PADN; + rest = buf->off % 8; + + if (rest == 7) { + /* Add a PAD1 option */ + bzero((caddr_t)buf->buf + buf->off, 1); + buf->off += 1; + } else if (rest > 0 && rest < 7) { + /* Add a PADN option */ + padlen = 8 - rest; + bzero((caddr_t)buf->buf + buf->off, padlen); + bcopy(&padn, (caddr_t)buf->buf + buf->off, 1); + padlen = padlen - 2; + bcopy(&padlen, (caddr_t)buf->buf + buf->off + 1, 1); + buf->off += padlen + 2; + } + + /* Adjust the extension header length */ + ext_hdr = (struct ip6_ext *)buf->buf; + ext_hdr->ip6e_len = (buf->off >> 3) - 1; + return; +} + + + +/* + ############################################################################## + # + # IP6 OUTPUT FUNCTIONS + # Functions used for processing of the outgoing IPv6 packet. These functions + # are called by using the mip6_output() function, when necesary, from the + # ip6_output() function. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_output + * Description: This function is always called by function ip6_output(). A Home + * Address option MUST be added if the MN is roaming and a Routing + * Header type 0 MUST be added if the node has a Binding Cache + * entry for the destination node. Otherwise nothing is done. + * Ret value: 0 Everything is OK. + * Otherwise appropriate error code + ****************************************************************************** + */ +int +mip6_output(m, pktopts) +struct mbuf *m; /* Includes IPv6 header */ +struct ip6_pktopts **pktopts; /* Packet Extension headers */ +{ + struct mip6_esm *esp; /* Ptr to entry in event state list */ + struct ip6_hdr *ip6; /* IPv6 header */ + struct mip6_bc *bcp; /* Binding Cache list entry */ + struct in6_addr *peer_home; /* Original dst address for packet */ + int error; + + ip6 = mtod(m, struct ip6_hdr *); + peer_home = &ip6->ip6_dst; + + /* We have to maintain a list of all prefixes announced by the + rtadvd deamon (for on-link determination). */ + if (MIP6_IS_HA_ACTIVE) { + if (ip6->ip6_nxt == IPPROTO_ICMPV6) + mip6_icmp6_output(m); + } + + /* If packet is being sent to a link-local or loop-back addresses, + don't do anything. */ + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst) || + IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) + return 0; + + /* If a COA for the destination address exist, i.e a BC entry + is found, then add a Routing Header. */ + bcp = mip6_bc_find(&ip6->ip6_src, &ip6->ip6_dst); + if (bcp != NULL) { + if ((error = mip6_add_rh(pktopts, bcp)) != 0) + return error; + } + + /* If the MN is roaiming and the source address is one of the + home addresses for the MN then a Home Address option must + be inserted. */ + if (!MIP6_IS_MN_ACTIVE) return 0; + + esp = mip6_esm_find(&ip6->ip6_src, 0); + if ((esp == NULL) || (esp->state < MIP6_STATE_DEREG)) + return 0; + + if ((error = mip6_add_ha(m, pktopts, esp)) != 0) + return error; + + /* If the MN initiate the traffic it should add a BU option + to the packet if no BUL entry exist and there is a BUL + "home registration" entry. */ + if ((error = mip6_add_bu(pktopts, esp, peer_home)) != 0) + return error; + + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_rh + * Description: Add a Routing Header type 0 to the outgoing packet, if its not + * already present, and add the COA for the MN. + * If a Routing Header type 0 exist, but contains no data, or the + * COA for the MN is missing it is added to the Routing Header. + * If the Routing Header is not of type 0 the function returns. + * Note: The destination address for the outgoing packet is not changed + * since this is taken care of in the ip6_output function. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_add_rh(pktopts, bcp) +struct ip6_pktopts **pktopts; /* Packet Ext headers, options and data */ +struct mip6_bc *bcp; /* Binding Cache list entry */ +{ + struct ip6_pktopts *opts; /* Pkt Ext headers, options & data */ + struct ip6_rthdr0 *rthdr0; /* Routing header type 0 */ + struct in6_addr *ip6rt_addr; /* IPv6 routing address(es) */ + caddr_t ptr; /* Temporary pointer */ + int size, len, new_len, idx; + + /* A Multicast address must not appear in a Routing Header. */ + if (IN6_IS_ADDR_MULTICAST(&bcp->peer_coa)) return 0; + + opts = *pktopts; + if (opts == NULL) { + /* No Packet options present at all. */ + opts = (struct ip6_pktopts *)malloc(sizeof(struct ip6_pktopts), + M_TEMP, M_NOWAIT); + if (opts == NULL) return -1; + init_ip6pktopts(opts); + opts->ip6po_flags |= IP6PO_MIP6OPT; + + opts->ip6po_rthdr = mip6_create_rh(&bcp->peer_coa, + IPPROTO_DSTOPTS); + if(opts->ip6po_rthdr == NULL) { + free(opts, M_TEMP); + return -1; + } + opts->ip6po_flags |= IP6PO_NEWRH0; + opts->ip6po_orgrh0 = NULL; + } else if (opts->ip6po_rthdr == NULL) { + /* Packet extension header allocated but no RH present */ + opts->ip6po_rthdr = mip6_create_rh(&bcp->peer_coa, + IPPROTO_DSTOPTS); + if(opts->ip6po_rthdr == NULL) return -1; + opts->ip6po_flags |= IP6PO_NEWRH0; + opts->ip6po_orgrh0 = NULL; + } else { + /* A RH exist. Don't do anything if the type is not 0. */ + if (opts->ip6po_rthdr->ip6r_type != IPV6_RTHDR_TYPE_0) + return 0; + + if (opts->ip6po_rthdr->ip6r_len % 2) + return 0; + + /* A routing header exist. If the last segment is not + equal to the MN's COA, add it. */ + len = opts->ip6po_rthdr->ip6r_len; + if (len == 0) + new_len = 2; + else { + new_len = len + 2; + idx = (len / 2) - 1; + rthdr0 = (struct ip6_rthdr0 *)opts->ip6po_rthdr; + ptr = (caddr_t)rthdr0 + sizeof(struct ip6_rthdr0); + ip6rt_addr = (struct in6_addr *)ptr + idx; + if (IN6_ARE_ADDR_EQUAL(&bcp->peer_coa, ip6rt_addr)) + return 0; + } + + /* Save pointer to original header */ + opts->ip6po_orgrh0 = opts->ip6po_rthdr; + + /* Allocate new RH and add one extra address */ + size = sizeof(struct ip6_rthdr0); + size += (new_len / 2) * sizeof(struct in6_addr); + rthdr0 = (struct ip6_rthdr0 *)malloc(size, M_TEMP, M_NOWAIT); + if (rthdr0 == NULL) return -1; + + bcopy((caddr_t)opts->ip6po_rthdr, (caddr_t)rthdr0, (len+1)*8); + bcopy((caddr_t)&bcp->peer_coa, (caddr_t)rthdr0 + (len+1)*8, + sizeof(struct in6_addr)); + rthdr0->ip6r0_len = new_len; + rthdr0->ip6r0_segleft = new_len / 2; + + opts->ip6po_rthdr = (struct ip6_rthdr *)rthdr0; + opts->ip6po_flags |= IP6PO_NEWRH0; + } + + *pktopts = opts; + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_ha + * Description: Add Home Address option to the Destination Header. + * Note: According to 10.2, IPsec processing of outbound packets, the + * IPv6 source address in the IPv6 header must contain the MNs + * home address and the Home Address option must include the + * care-of address. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_add_ha(m, pktopts, esp) +struct mbuf *m; /* Includes IPv6 header */ +struct ip6_pktopts **pktopts; /* Packet Ext headers, options and data */ +struct mip6_esm *esp; /* Event-state machine */ +{ + struct ip6_opt_home_address *ha_opt; + struct mip6_buffer *dh1; + struct ip6_pktopts *opts; + int size; + + size = sizeof(struct mip6_buffer); + dh1 = (struct mip6_buffer *)malloc(size, M_TEMP, M_NOWAIT); + if (dh1 == NULL) return -1; + bzero((caddr_t)dh1, size); + + size = sizeof(struct ip6_opt_home_address); + ha_opt = (struct ip6_opt_home_address *)malloc(size, M_TEMP, M_NOWAIT); + if (ha_opt == NULL) { + free(dh1, M_TEMP); + return -1; + } + ha_opt->ip6oh_type = IP6OPT_HOME_ADDRESS; + ha_opt->ip6oh_len = IP6OPT_HALEN; + + size = sizeof(struct in6_addr); + bcopy((u_int8_t *)&esp->coa, ha_opt->ip6oh_addr, size); + + opts = *pktopts; + if (opts == NULL) { + /* No Packet options present at all. */ + opts = (struct ip6_pktopts *)malloc(sizeof(struct ip6_pktopts), + M_TEMP, M_NOWAIT); + if (opts == NULL) { + free(dh1, M_TEMP); + free(ha_opt, M_TEMP); + return -1; + } + init_ip6pktopts(opts); + opts->ip6po_flags |= IP6PO_MIP6OPT; + opts->ip6po_orgdh1 = NULL; + } else if (opts->ip6po_dest1 == NULL) { + /* Packet extension header allocated but no DH present */ + opts->ip6po_orgdh1 = NULL; + } else { + /* Destination Header exist */ + opts->ip6po_orgdh1 = opts->ip6po_dest1; + size = (opts->ip6po_dest1->ip6d_len + 1) << 3; + bcopy((caddr_t)opts->ip6po_dest1, (caddr_t)dh1->buf, size); + + dh1->off = size; + mip6_find_offset(dh1); + } + + /* Add Home Address option to DH1 */ + mip6_add_opt2dh((u_int8_t *)ha_opt, dh1); + mip6_align(dh1); + + opts->ip6po_dest1 = (struct ip6_dest *)dh1->buf; + opts->ip6po_flags |= IP6PO_NEWDH1; + + free(ha_opt, M_TEMP); + *pktopts = opts; + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_addr_exchange + * Description: Exchange IPv6 header source address with contents in Home + * Address option address field. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_addr_exchange(m, dstm) +struct mbuf *m; /* Includes IPv6 header */ +struct mbuf *dstm; /* Includes Destination Header 1 */ +{ + struct ip6_opt_home_address *ha_opt; + struct ip6_dest *dh; + struct ip6_hdr *ip6; + struct in6_addr ip6_src; + u_int8_t *opt; + int ii, len; + + /* Sanity check */ + if (!MIP6_IS_MN_ACTIVE) + return; + + if (dstm == NULL) + return; + + /* Find Home Address option */ + dh = mtod(dstm, struct ip6_dest *); + len = (dh->ip6d_len + 1) << 3; + if (len > dstm->m_len) + return; + + ha_opt = NULL; + ii = 2; + + opt = (u_int8_t *)dh + ii; + while (ii < len) { + switch (*opt) { + case IP6OPT_PAD1: + ii += 1; + opt += 1; + break; + case IP6OPT_HOME_ADDRESS: + ha_opt = (struct ip6_opt_home_address *)opt; + break; + default: + ii += *(opt + 1) + 2; + opt += *(opt + 1) + 2; + break; + } + if (ha_opt) break; + } + + if (ha_opt == NULL) return; + + /* Change the IP6 source address to the care-of address */ + ip6 = mtod(m, struct ip6_hdr *); + ip6_src = ip6->ip6_src; + + ip6->ip6_src = *(struct in6_addr *)ha_opt->ip6oh_addr; + bcopy((caddr_t)&ip6_src, ha_opt->ip6oh_addr, sizeof(struct in6_addr)); + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_add_bu + * Description: Add Binding Update option to outgoing packet if we are + * initiating the traffic and there exist no Binding Update + * list entry already. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_add_bu(pktopts, esp, peer_home) +struct ip6_pktopts **pktopts; /* Packet Ext headers, options and data */ +struct mip6_esm *esp; /* Event-state machine */ +struct in6_addr *peer_home; /* Original packet destination */ +{ + + struct ip6_opt_binding_update *bu_opt; + struct ip6_pktopts *opts; + struct mip6_bul *bulp_cn, *bulp_ha; + struct mip6_buffer *dh2; + u_int16_t seqno; + u_int8_t bu_flags; + int size; + + bulp_cn = mip6_bul_find(peer_home, &esp->home_addr); + bulp_ha = mip6_bul_find(NULL, &esp->home_addr); + if ((bulp_cn == NULL) && (bulp_ha != NULL)) { + /* Create BU option and BUL entry. */ + bu_flags = 0; + bu_opt = mip6_create_bu(0, bu_flags, bulp_ha->lifetime); + if (bu_opt == NULL) return -1; + + bulp_cn = mip6_bul_create(peer_home, &esp->home_addr, + &esp->coa, bulp_ha->lifetime, + bu_flags); + if (bulp_cn == NULL) return -1; + + seqno = 1; + bcopy((caddr_t)&seqno, bu_opt->ip6ou_seqno, sizeof(seqno)); + bulp_cn->seqno = seqno; + } else + return 0; + + /* Allocate new memory for DH2. Copy existing data */ + size = sizeof(struct mip6_buffer); + dh2 = (struct mip6_buffer *)malloc(size, M_TEMP, M_NOWAIT); + if (dh2 == NULL) { + free(bu_opt, M_TEMP); + mip6_bul_delete(bulp_cn); + return -1; + } + bzero((caddr_t)dh2, sizeof(struct mip6_buffer)); + dh2->off = 2; + + opts = *pktopts; + if (opts == NULL) { + /* No Packet options present at all. */ + opts = (struct ip6_pktopts *)malloc(sizeof(struct ip6_pktopts), + M_TEMP, M_NOWAIT); + if (opts == NULL) { + free(bu_opt, M_TEMP); + mip6_bul_delete(bulp_cn); + free(dh2, M_TEMP); + return -1; + } + init_ip6pktopts(opts); + opts->ip6po_flags |= IP6PO_MIP6OPT; + opts->ip6po_orgdh2 = NULL; + } else if (opts->ip6po_dest2 == NULL) { + /* Packet extension header allocated but no DH present */ + opts->ip6po_orgdh2 = NULL; + } else { + /* Destination Header exist */ + opts->ip6po_orgdh2 = opts->ip6po_dest2; + size = (opts->ip6po_dest2->ip6d_len + 1) << 3; + bcopy((caddr_t)opts->ip6po_dest2, (caddr_t)dh2->buf, size); + + dh2->off = size; + mip6_find_offset(dh2); + } + + /* Add Binding Update option to DH2 */ + mip6_add_opt2dh((u_int8_t *)bu_opt, dh2); + mip6_align(dh2); + + opts->ip6po_dest2 = (struct ip6_dest *)dh2->buf; + opts->ip6po_flags |= IP6PO_NEWDH2; + free(bu_opt, M_TEMP); + *pktopts = opts; + return 0; +} + + + +/* + ############################################################################## + # + # MIP6 TUNNELLING FUNCTIONS + # Functions used for tunnelling of packets. The mip6_tunnel_output() function + # encapsulate an IPv6 header in a new IPv6 header and the mip6_tunnel_input() + # function decapsulate the packet. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_tunnel_input + * Description: similar to gif_input() and in6_gif_input(). + * Ret value: standard error codes. + ****************************************************************************** + */ +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; /* Tell MN that this packet + was tunnelled. */ + ip6 = mtod(m, struct ip6_hdr *); + + s = splimp(); + if (IF_QFULL(&ip6intrq)) { + IF_DROP(&ip6intrq); /* update statistics */ + m_freem(m); + splx(s); + return IPPROTO_DONE; + } + IF_ENQUEUE(&ip6intrq, m); +#if 0 + /* we don't need it as we tunnel IPv6 in IPv6 only. */ + schednetisr(NETISR_IPV6); +#endif + splx(s); + break; + } + default: +#ifdef MIP6_DEBUG + mip6_debug("%s: protocol %d not supported.\n", __FUNCTION__, + proto); +#endif + m_freem(m); + return IPPROTO_DONE; + } + + return IPPROTO_DONE; +} + + + +/* + ****************************************************************************** + * Function: mip6_tunnel_output + * Description: Encapsulates packet in an outer header which is determined + * of the Binding Cache entry provided. Note that packet is + * (currently) not sent here, but should be sent by the caller. + * Ret value: != 0 if failure. It's up to the caller to free the mbuf chain. + ****************************************************************************** + */ +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->peer_coa; + + if (ep->af != AF_INET6 || ep->dst.ss_len != dst.sin6_len || + bcmp(&ep->dst, &dst, dst.sin6_len) != 0 ) + return EFAULT; + + /* Recursion problems? */ + + 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; + + + /* prepend new IP header */ + 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) { +#ifdef 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; /* Same? */ + ip6->ip6_src = sin6_src->sin6_addr; + + /* bidirectional configured tunnel mode */ + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) + ip6->ip6_dst = sin6_dst->sin6_addr; + else { + m_freem(m); + return ENETUNREACH; + } +#ifdef IPV6_MINMTU + /* + * force fragmentation to minimum MTU, to avoid path MTU discovery. + * it is too painful to ask for resend of inner packet, to achieve + * path MTU discovery for encapsulated packets. + */ + return(ip6_output(m, 0, 0, IPV6_MINMTU, 0, NULL)); +#else + return(ip6_output(m, 0, 0, 0, 0, NULL)); +#endif +} + +#ifdef OLDMIP6 +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->peer_coa; + + if (ep->af != AF_INET6 || ep->dst.ss_len != dst.sin6_len || + bcmp(&ep->dst, &dst, dst.sin6_len) != 0 ) + return EFAULT; + + /* Recursion problems? */ + + 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; + + + /* prepend new IP header */ + 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) { +#ifdef 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; /* Same? */ + ip6->ip6_src = sin6_src->sin6_addr; + + /* bidirectional configured tunnel mode */ + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) + ip6->ip6_dst = sin6_dst->sin6_addr; + else + return ENETUNREACH; + + *mp = m; + return 0; +} +#endif /* OLDMIP6 */ diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6_md.c kame/kame/sys/netinet6/mip6_md.c --- kame-20010611/kame/sys/netinet6/mip6_md.c Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6_md.c Mon Jun 11 13:39:12 2001 @@ -0,0 +1,2343 @@ +/* $KAME: mip6_md.c,v 1.30 2001/05/31 01:01:25 suz Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Author: Mattias Pettersson + * + */ + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" +#endif +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ipsec.h" +#endif + +/* + * Mobile IPv6 Movement Detection for Mobile Nodes + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include +#endif +#if !defined(__OpenBSD__) && !defined(__bsdi__) +#include +#endif +#include + +#ifdef IPSEC +#include +#include +#endif + +#include + +struct in6_addr mip6_php; /* Primary Home Prefix */ +u_int8_t mip6_phpl; /* Primary Home Prefix Length */ +struct nd_prefix *mip6_phpp = NULL; /* Primary Home Prefix Pointer */ +struct nd_prefix *mip6_pp = NULL; /* Primary (Care-of) Prefix */ +struct in6_addr mip6_pdr; /* Primary Default Router */ +struct ifnet *mip6_hifp = NULL; /* ifp holding all Home Addresses */ +int mip6_md_state = MIP6_MD_UNDEFINED; +int mip6_new_homeaddr; +/* + * Mobile IPv6 Home Address route state for the Mobile Node. + * route_state NET == MD_HOME == network route. + * route_state HOST == MD_FOREIGN|UNDEFINED == host route. + */ +int mip6_route_state = MIP6_ROUTE_NET; /* According to MD_UNDEFINED state. */ +int mip6_max_lost_advints = MIP6_MAX_LOST_ADVINTS; +int mip6_nd6_delay = 0; +int mip6_nd6_umaxtries = 0; + + +/* + ****************************************************************************** + * Function: mip6_tell_em + * Description: Print state change and tell event-state machine. + * Ret value: - + ****************************************************************************** + */ +static void +mip6_tell_em(int state, + struct in6_addr *hp, + u_int8_t hpl, + struct nd_prefix *pp, + struct in6_ifaddr *coa, + struct nd_defrouter *dr) /* Phased out. Just print. */ +{ +#ifdef 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/%d\n", hp ? ip6_sprintf(hp) : "NULL", + hpl); + mip6_debug("Primary Prefix = %s\n", pp ? ip6_sprintf( + &pp->ndpr_prefix.sin6_addr) : "NULL"); + mip6_debug("Primary COA = %s\n", coa ? ip6_sprintf( + &coa->ia_addr.sin6_addr) : "NULL"); + mip6_debug("Default Router = %s\n", dr ? ip6_sprintf(&dr->rtaddr) + : "NULL"); +#endif + mip6_move(state, hp, hpl, pp, coa); +} + + + +/* + ****************************************************************************** + * Function: mip6_php_lookup + * Description: Find an nd_prefix that matches the primary home prefix. + * + * Side effect: also sets global lookup cache pointer mip6_phpp. + * Ret value: A pointer to the nd_prefix, or NULL if there is no such + * entry in the prefix list. + ****************************************************************************** + */ +static struct nd_prefix * +mip6_php_lookup(void) +{ + struct nd_prefix *p; + + /* + * Check if cached mip6_phpp really points to an nd_prefix + * that contains mip6_php and mip6_phpl. + */ + if (mip6_phpp != NULL && mip6_phpp->ndpr_stateflags & NDPRF_HOME && + mip6_phpl == mip6_phpp->ndpr_plen && + in6_are_prefix_equal(&mip6_php, + &mip6_phpp->ndpr_prefix.sin6_addr, + mip6_phpl)) { + return (mip6_phpp); + } else { + if (IN6_IS_ADDR_UNSPECIFIED(&mip6_php) || mip6_phpl == 0) + /* XXX error? */ + return NULL; + + for (p = nd_prefix.lh_first; p; p = p->ndpr_next) { + if (p->ndpr_stateflags & NDPRF_HOME && + mip6_phpl == p->ndpr_plen && + in6_are_prefix_equal(&mip6_php, + &p->ndpr_prefix.sin6_addr, + mip6_phpl)) { + break; + } + } + mip6_phpp = p; + return p; + } +} + + + +/* + ****************************************************************************** + * Function: mip6_is_primhomeprefix + * Description: Check if this nd_prefix matches the Primary Home Prefix. + * Ret value: 1 for yes, 0 for no. + ****************************************************************************** + */ +/* XXX Can we merge this into php_lookup() function? */ +int +mip6_is_primhomeprefix(struct nd_prefix *pr) +{ + struct nd_prefix *p; + + /* + * XXX Important: keep mip6_phpp consistent with mip6_php. If + * uncertain, set mip6_phpp to NULL. + */ + if (mip6_phpp != NULL && mip6_phpp->ndpr_stateflags & NDPRF_HOME) { + return ((mip6_phpp == pr) ? 1 : 0); + } else { + if (IN6_IS_ADDR_UNSPECIFIED(&mip6_php) || mip6_phpl == 0) + return 0; + + for (p = nd_prefix.lh_first; p; p = p->ndpr_next) { + if (p->ndpr_stateflags & NDPRF_HOME && + mip6_phpl == p->ndpr_plen && + in6_are_prefix_equal(&mip6_php, + &p->ndpr_prefix.sin6_addr, + mip6_phpl)) + /* XXX also set mip6_php? */ + return 1; + } + return 0; + } +} + + + +/* + ****************************************************************************** + * Function: mip6_create_ifid + * Description: Sets the field "ifid" in the event-state machine based on the + * which interface the home prefix is received. + * Does not create a home address in the esm. + * Ret value: - + ****************************************************************************** + */ +void +mip6_create_ifid(struct ifnet *ifp, + struct in6_addr *prefix, + u_int8_t prefixlen) +{ + struct mip6_esm *esp; + struct ifaddr *ifa; + struct in6_ifaddr *ib; + u_int8_t plen0; + + for (esp = mip6_esmq; esp; esp = esp->next) { + if ((in6_are_prefix_equal(&esp->home_pref, prefix, + esp->prefixlen)) && + esp->prefixlen == prefixlen) + break; + } + if (esp == NULL) + return; + if (!IN6_IS_ADDR_UNSPECIFIED(&esp->ifid)) + return; + + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0);/* 0 is OK? */ + if (ifa) + ib = (struct in6_ifaddr *)ifa; + else + return; + +#if 0 /* don't care link local addr state, and always do DAD */ + /* if link-local address is not eligible, do not autoconfigure. */ + if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) { + printf("in6_ifadd: link-local address not ready\n"); + return NULL; + } +#endif + + /* prefixlen + ifidlen must be equal to 128 */ + plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL); + if (prefixlen != plen0) { + log(LOG_INFO, "%s: wrong prefixlen for %s " + "(prefix=%d ifid=%d)\n", + __FUNCTION__, if_name(ifp), prefixlen, 128 - plen0); + return; + } + + /* interface ID */ +#define mask ib->ia_prefixmask.sin6_addr + esp->ifid.s6_addr32[0] = (ib->ia_addr.sin6_addr.s6_addr32[0] & + ~mask.s6_addr32[0]); + esp->ifid.s6_addr32[1] = (ib->ia_addr.sin6_addr.s6_addr32[1] & + ~mask.s6_addr32[1]); + esp->ifid.s6_addr32[2] = (ib->ia_addr.sin6_addr.s6_addr32[2] & + ~mask.s6_addr32[2]); + esp->ifid.s6_addr32[3] = (ib->ia_addr.sin6_addr.s6_addr32[3] & + ~mask.s6_addr32[3]); +#undef mask + +#ifdef MIP6_DEBUG + mip6_debug("%s: will use this ifid: %s\n",__FUNCTION__, + ip6_sprintf(&esp->ifid)); +#endif +} + + + +/* + ****************************************************************************** + * Function: mip6_pfxaddr_lookup + * Description: Find a good interface address that is associated with the + * nd_prefix and obeys the flags. + * Example of flags: + * - IN6_IFF_AUTOCONF + * Example of negflags: + * - IN6_IFF_TEMPORARY + * Ret value: The interface address found or NULL. + ****************************************************************************** + */ +struct in6_ifaddr * +mip6_pfxaddr_lookup(struct nd_prefix *pr, int flags, int negflags) +{ + struct in6_ifaddr *ifa6 = NULL; + struct ifaddr *ifa; + struct ifnet *ifp; + + if (pr == NULL) + return NULL; + + if ((ifp = pr->ndpr_ifp) == NULL) + return NULL; + +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#elif defined(__FreeBSD__) && __FreeBSD__ >= 4 + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif + { + int ifa_plen; + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ifa6 = (struct in6_ifaddr *)ifa; + + /* + * Spec is not clear here, but I believe we should concentrate + * on unicast (i.e. not anycast) addresses. + * XXX: other ia6_flags? detached or duplicated? + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL); + if (ifa_plen != pr->ndpr_plen || + !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, + &pr->ndpr_prefix.sin6_addr, + ifa_plen)) + continue; + + if ((ifa6->ia6_flags & flags) != flags) + continue; + + if ((ifa6->ia6_flags & negflags) != 0) + continue; + + if (IFA6_IS_INVALID(ifa6)) + continue; + + /* + * This behaviour could be improved. + */ + if (IFA6_IS_DEPRECATED(ifa6)) + continue; + + /* + * At least one matched address. + */ + return ifa6; + } + return NULL; +} + + + +/* + ****************************************************************************** + * Function: mip6_coa_lookup + * Description: Find a usable interface address associated with this + * nd_prefix. + * Ret value: The first interface address with same prefix or NULL. + ****************************************************************************** + */ +struct in6_ifaddr * +mip6_coa_lookup(struct nd_prefix *pr) +{ + struct in6_ifaddr *ia6; + + /* + * Filter out unwanted prefix types. + */ + if (IN6_IS_ADDR_MULTICAST(&pr->ndpr_prefix.sin6_addr) || + IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) + return NULL; + + /* Other flags??? */ + ia6 = mip6_pfxaddr_lookup(pr, IN6_IFF_AUTOCONF, + IN6_IFF_ANYCAST | IN6_IFF_TEMPORARY); + + return ia6; +} + + + +/* + ****************************************************************************** + * Function: mip6_update_home_addrs + * Description: Update home addresses. This prefix is already determined to + * be a home prefix. Update lifetimes etc on already existing + * home addresses. If no associated home address exists for this + * prefix, create a new home address on loopback. + * + * Note: home addresses are only updated here. They are bound + * to loopback (lo0) and have flag IN6_IFF_HOME set. + * Ret value: - + ****************************************************************************** + */ +void +mip6_update_home_addrs(struct mbuf *m, + struct nd_prefix *pr, + int auth) +{ + struct ifnet *ifp = mip6_hifp; + struct ifaddr *ifa; + struct in6_ifaddr *ia6_match = NULL, *ia6; + struct in6_addrlifetime lt6_tmp; + struct mip6_esm *esp; + int error; +#define new pr + + if ((!m) || (!pr)) + return; + + /* + * Outline below recycled from nd6_prelist_update(): addrconf: + */ + + + /* + * Address autoconfiguration based on Section 5.5.3 of RFC 2462. + * Note that pr must be non NULL at this point. + */ + + /* 5.5.3 (a). Ignore the prefix without the A bit set. */ +/* if (!new->ndpr_raf_auto) */ +/* goto end; */ + + /* + * 5.5.3 (b). the link-local prefix should have been ignored in + * nd6_ra_input. + */ + + /* + * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. + * This should have been done in nd6_ra_input. + */ + + /* + * 5.5.3 (d). If the prefix advertised does not match the prefix of an + * address already in the list, and the Valid Lifetime is not 0, + * form an address. Note that even a manually configured address + * should reject autoconfiguration of a new address. + */ +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) +#elif defined(__FreeBSD__) && __FreeBSD__ >= 4 + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) +#else + for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) +#endif + { + struct in6_ifaddr *ifa6; + int ifa_plen; + u_int32_t storedlifetime; +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + long time_second = time.tv_sec; +#endif + + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; + + ifa6 = (struct in6_ifaddr *)ifa; + + /* + * Spec is not clear here, but I believe we should concentrate + * on unicast (i.e. not anycast) addresses. + * XXX: other ia6_flags? detached or duplicated? + */ + if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0) + continue; + + /* + * Only update addresses marked as home addresses. + */ + if ((ifa6->ia6_flags & IN6_IFF_HOME) == 0) + continue; + + ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL); + if (ifa_plen != new->ndpr_plen || + !in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr, + &new->ndpr_prefix.sin6_addr, + ifa_plen)) + continue; + + if (ia6_match == NULL) /* remember the first one */ + ia6_match = ifa6; + + /* Home addresses are regarded not autoconfigured. */ +/* if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0) */ +/* continue; */ + + /* + * An already autoconfigured address matched. Now that we + * are sure there is at least one matched address, we can + * proceed to 5.5.3. (e): update the lifetimes according to the + * "two hours" rule and the privacy extension. + */ +#define TWOHOUR (120*60) + lt6_tmp = ifa6->ia6_lifetime; + + storedlifetime = IFA6_IS_INVALID(ifa6) ? 0 : + (lt6_tmp.ia6t_expire - time_second); + + if (TWOHOUR < new->ndpr_vltime || + storedlifetime < new->ndpr_vltime) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } else if (storedlifetime <= TWOHOUR +#if 0 + /* + * This condition is logically redundant, so we just + * omit it. + * See IPng 6712, 6717, and 6721. + */ + && new->ndpr_vltime <= storedlifetime +#endif + ) { + if (auth) { + lt6_tmp.ia6t_vltime = new->ndpr_vltime; + } + } else { + /* + * new->ndpr_vltime <= TWOHOUR && + * TWOHOUR < storedlifetime + */ + lt6_tmp.ia6t_vltime = TWOHOUR; + } + + /* The 2 hour rule is not imposed for preferred lifetime. */ + lt6_tmp.ia6t_pltime = new->ndpr_pltime; + + in6_init_address_ltimes(pr, <6_tmp); + + /* + * When adjusting the lifetimes of an existing temporary + * address, only lower the lifetimes. + * RFC 3041 3.3. (1). + * XXX: how should we modify ia6t_[pv]ltime? + */ + if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) { + if (lt6_tmp.ia6t_expire == 0 || /* no expire */ + lt6_tmp.ia6t_expire > + ifa6->ia6_lifetime.ia6t_expire) { + lt6_tmp.ia6t_expire = + ifa6->ia6_lifetime.ia6t_expire; + } + if (lt6_tmp.ia6t_preferred == 0 || /* no expire */ + lt6_tmp.ia6t_preferred > + ifa6->ia6_lifetime.ia6t_preferred) { + lt6_tmp.ia6t_preferred = + ifa6->ia6_lifetime.ia6t_preferred; + } + } + + ifa6->ia6_lifetime = lt6_tmp; + } + if (ia6_match == NULL && new->ndpr_vltime) { + /* + * No address matched and the valid lifetime is non-zero. + * Create a new address. + */ + + /* XXX Same result every time. Only one esm. */ + for (esp = mip6_esmq; esp; esp = esp->next) { + if (esp->prefixlen == new->ndpr_plen && + in6_are_prefix_equal(&esp->home_pref, &mip6_php, + esp->prefixlen)) + break; + } + if (esp == NULL) + return; + if (IN6_IS_ADDR_UNSPECIFIED(&esp->ifid)) { + log(LOG_ERR, "%s: can't create home address, no " + "ifid available\n", __FUNCTION__); + return; + } + + if ((ia6 = in6_ifadd(new, &esp->ifid)) != NULL) { + /* + * note that we should use pr (not new) for reference. + */ +/* pr->ndpr_refcnt++; */ +/* ia6->ia6_ndpr = pr; */ + /* + * Home Addresses are regarded as not autoconfigured, + * since we don't have one single nd_prefix that + * has associated lifetimes. + */ + ia6->ia6_ndpr = NULL; + ia6->ia6_flags &= ~IN6_IFF_AUTOCONF; + + ia6->ia6_flags |= IN6_IFF_HOME; + + /* + * If this is first address built based on the + * preconfigured home prefix, save it in the esm. + * This is actually our primary home address. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)) { + esp->home_addr = ia6->ia_addr.sin6_addr; +#ifdef MIP6_DEBUG + mip6_debug("%s: esm home address set to %s\n", + __FUNCTION__, + ip6_sprintf(&esp->home_addr)); +#endif + } + + /* + * Remember to register whenever a new address is + * constructed. + */ + if (mip6_incl_br(m)) + mip6_new_homeaddr = 1; + + /* + * RFC 3041 3.3 (2). + * When a new public address is created as described + * in RFC2462, also create a new temporary address. + * + * RFC 3041 3.5. + * When an interface connects to a new link, a new + * randomized interface identifier should be generated + * immediately together with a new set of temporary + * addresses. Thus, we specifiy 1 as the 2nd arg of + * in6_tmpifadd(). + */ + if (ip6_use_tempaddr) { + int e; + if ((e = in6_tmpifadd(ia6, 1)) != 0) { + log(LOG_NOTICE, "prelist_update: " + "failed to create a temporary " + "address, errno=%d\n", + e); + } + } + + /* + * A newly added address might affect the status + * of other addresses, so we check and update it. + * XXX: what if address duplication happens? + */ +/* pfxlist_onlink_check(); */ + } else { + /* just set an error. do not bark here. */ + error = EADDRNOTAVAIL; /* XXX: might be unused. */ + } + } + +} + + + +/* + ****************************************************************************** + * Function: mip6_create_homeaddr + * Description: Create a home address that is static on lo0. Used in backwards + * compatible start-up scenario when home address and possibly the + * home agent's unicast address are specfified. + * Ret value: Standard error codes. + ****************************************************************************** + */ +int +mip6_create_homeaddr(struct mip6_esm *esp) +{ + struct ifnet *ifp = esp->ifp; + struct in6_aliasreq ifra; + struct in6_ifaddr *ia; + int error; + int prefixlen = esp->prefixlen; + + /* + * Code recycled from in6_ifadd(). + */ + + if (esp == NULL) + return EINVAL; + + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)) { + log(LOG_ERR, "%s: error - unspecified home address\n", + __FUNCTION__); + return EINVAL; + } + + if (esp->prefixlen > 64 && esp->prefixlen != 128) { + log(LOG_ERR, "%s: error - invalid prefix length %d\n", + __FUNCTION__, esp->prefixlen); + return EINVAL; + } + + + /* make ifaddr */ + + bzero(&ifra, sizeof(ifra)); + /* + * in6_update_ifa() does not use ifra_name, but we accurately set it + * for safety. + */ + strncpy(ifra.ifra_name, if_name(ifp), sizeof(ifra.ifra_name)); + ifra.ifra_addr.sin6_family = AF_INET6; + ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_addr.sin6_addr = esp->home_addr; + + ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); + ifra.ifra_prefixmask.sin6_family = AF_INET6; + in6_len2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); + + /* + * lifetime. + * XXX: in6_init_address_ltimes would override these values later. + * We should reconsider this logic. + */ + ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; +#ifdef MIP6_DEBUG + mip6_debug("%s: note - home address %s on lo0 is set to infinite " + "lifetime\n", __FUNCTION__, ip6_sprintf(&esp->home_addr)); +#endif + + /* XXX: scope zone ID? */ + +#if 0 + ifra.ifra_flags |= IN6_IFF_AUTOCONF; /* obey autoconf */ +#endif + /* + * temporarily set the nopfx flag to avoid conflict. + * XXX: we should reconsider the entire mechanism about prefix + * manipulation. + */ + ifra.ifra_flags |= IN6_IFF_NOPFX; + + /* allocate ifaddr structure, link into chain, etc. */ + if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { + log(LOG_ERR, + "in6_ifadd: failed to make ifaddr %s on %s (errno=%d)\n", + ip6_sprintf(&ifra.ifra_addr.sin6_addr), if_name(ifp), + error); + return(NULL); /* ifaddr must not have been allocated. */ + } + + ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); + +/* pr->ndpr_refcnt++; */ +/* ia6->ia6_ndpr = pr; */ + /* + * Home Addresses are regarded as not autoconfigured, + * since we don't have one single nd_prefix that + * has associated lifetimes. + */ + ia->ia6_ndpr = NULL; + ia->ia6_flags &= ~IN6_IFF_AUTOCONF; + + ia->ia6_flags |= IN6_IFF_HOME; + +/* return(ia);*/ /* this must NOT be NULL. */ + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_select_php + * Description: Select a new primary home prefix from the valid home addresses. + * This is due to expiration of the previous primary home prefix. + * Ret value: - + ****************************************************************************** + */ +void +mip6_select_php(struct mip6_esm *esp) +{ + struct in6_ifaddr *ia6; + struct in6_addrlifetime *lt6; + + if (esp == NULL) + return; + + for (ia6 = in6_ifaddr; ia6; ia6 = ia6->ia_next) { + /* check address lifetime */ + + if ((ia6->ia6_flags & IN6_IFF_HOME) == 0) + continue; + + lt6 = &ia6->ia6_lifetime; + if (IFA6_IS_INVALID(ia6)) + continue; + + if (ia6->ia_ifp != esp->ifp) + continue; + + break; + } + + if (ia6 == NULL) { +#ifdef MIP6_DEBUG + mip6_debug("%s: could not find a new primary home address.\n", + __FUNCTION__); +#endif + log(LOG_ERR, "%s: could not find a new primary home " + "address.\n", __FUNCTION__); + return; + } + + bcopy(&ia6->ia_addr.sin6_addr, &mip6_php, sizeof(mip6_php)); + mip6_php.s6_addr32[0] &= ia6->ia_prefixmask.sin6_addr.s6_addr32[0]; + mip6_php.s6_addr32[1] &= ia6->ia_prefixmask.sin6_addr.s6_addr32[1]; + mip6_php.s6_addr32[2] &= ia6->ia_prefixmask.sin6_addr.s6_addr32[2]; + mip6_php.s6_addr32[3] &= ia6->ia_prefixmask.sin6_addr.s6_addr32[3]; + + mip6_phpl = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); + + mip6_phpp = NULL; + + esp->home_pref = mip6_php; + esp->prefixlen = mip6_phpl; + esp->home_addr = ia6->ia_addr.sin6_addr; + /* XXX ifid? */ +} + + + +/* + ****************************************************************************** + * Function: mip6_deprecated_addr + * Description: If this depracated address is a home address and corresponds + * to the primary home prefix, select a new primary home prefix. + * Ret value: - + ****************************************************************************** + */ +void +mip6_deprecated_addr(struct in6_ifaddr *ia6) +{ + struct mip6_esm *esp; + + if (ia6 == NULL) + return; + + if ((ia6->ia6_flags & IN6_IFF_HOME) == 0) + return; + + /* Only find the primary home addresses. */ + for (esp = mip6_esmq; esp; esp = esp->next) { + if (!IN6_ARE_ADDR_EQUAL(&esp->home_addr, + &ia6->ia_addr.sin6_addr)) + continue; + if (esp->type == TEMPORARY) + continue; + if (ia6->ia_ifp == esp->ifp) + break; + } + if (esp == NULL) + return; + + mip6_select_php(esp); +} + + + +/* + ****************************************************************************** + * Function: mip6_md_init_with_prefix + * Description: Given an event-state machine and one home prefix, create + * a home address if we are at home. Determine whether we are + * at home, at foreign or undefined and take appropriate action. + * Ret value: Standard error codes. + ****************************************************************************** + */ +static int +mip6_md_init_with_prefix(struct mip6_esm *esp) +{ + struct in6_ifaddr *ia6 = NULL; + struct nd_prefix *pr = NULL; + struct nd_defrouter *dr; + + if (esp == NULL) + return EINVAL; + + /* + * Look if preconfigured home prefix already exists. Don't care + * about which ifp. + */ + if (esp->prefixlen == 0) { + log(LOG_ERR, "%s: home prefix length == 0\n", __FUNCTION__); + return EINVAL; + } + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (esp->prefixlen == pr->ndpr_plen && + in6_are_prefix_equal(&esp->home_pref, + &pr->ndpr_prefix.sin6_addr, + esp->prefixlen)) + break; + } + if (pr) { + if ((ia6 = mip6_pfxaddr_lookup(pr, IN6_IFF_AUTOCONF, + IN6_IFF_ANYCAST | + IN6_IFF_TEMPORARY)) != NULL) { + /* + * This is a good home address. + */ + esp->home_addr = ia6->ia_addr.sin6_addr; + + /* + * Store the interface ID from this already existing + * address of the home prefix. + */ + +#define orig ia6->ia_addr.sin6_addr +#define mask ia6->ia_prefixmask.sin6_addr + esp->ifid.s6_addr32[0] = (orig.s6_addr32[0] & + ~mask.s6_addr32[0]); + esp->ifid.s6_addr32[1] = (orig.s6_addr32[1] & + ~mask.s6_addr32[1]); + esp->ifid.s6_addr32[2] = (orig.s6_addr32[2] & + ~mask.s6_addr32[2]); + esp->ifid.s6_addr32[3] = (orig.s6_addr32[3] & + ~mask.s6_addr32[3]); +#undef orig +#undef mask + +#ifdef MIP6_DEBUG + mip6_debug("%s: will use this ifid: %s\n",__FUNCTION__, + ip6_sprintf(&esp->ifid)); +#endif + } + + /* + * We can be HOME, UNDEFINED or FOREIGN, with or without + * an address. + */ + } + mip6_phpp = pr; + mip6_php = esp->home_pref; + mip6_phpl = esp->prefixlen; + + /* XXX Do something about defrouter? */ + + /* XXX Should we have or not have an address associated with pr here? + * => Well, if we are home, we should have one. On the other hand, if + * we are undef or foreign, we should not have one. + * Also important, we need to preserve good interface IDs or create + * new good ones. We really should stick to one ID all way through, + * also for multiple addresses or prefixes and during renumbering. + */ + + /* + * XXX + * We may need to revise the procedure below, when movement + * detection is written. + */ + + /* + * XXXYYY Is this line actually correct? Don't we need to check + * more than just first dr? + */ + dr = nd_defrouter_primary; + /* + * XXXYYY + * Add check for probably reachable router here as well. Mattias + */ + if (pr && pr->ndpr_advrtrs.lh_first && dr && + pfxrtr_lookup(pr, dr)) { + /* If we have home pfxrtrs and defrtr is one of these, then + we're home. */ + mip6_md_state = MIP6_MD_HOME; +/* mip6_route_state = MIP6_ROUTE_NET; */ + + mip6_send_rs(esp, 0); + + mip6_pp = mip6_phpp; + mip6_pdr = dr->rtaddr; + mip6_tell_em(MIP6_MD_HOME, &mip6_php, mip6_phpl, NULL, + NULL, dr); + } + else { + if (dr) { + mip6_md_state = MIP6_MD_FOREIGN; +/* mip6_route_state = MIP6_ROUTE_HOST; */ + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if ((pfxrtr_lookup(pr, dr) != NULL) && + (ia6 = mip6_coa_lookup(pr)) != NULL) { + break; + } + } + if (pr) { + /* + * We can't send tunneled RS here; we don't + * know our Home Agent yet. + */ + mip6_pp = pr; + mip6_pdr = dr->rtaddr; + mip6_tell_em(MIP6_MD_FOREIGN, &mip6_php, + mip6_phpl, pr, ia6, dr); + } + else { +#ifdef 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; +/* mip6_route_state = MIP6_ROUTE_NET; */ + + /* We can always try... */ + mip6_send_rs(esp, 0); + + mip6_pdr = in6addr_any; + mip6_pp = NULL; + mip6_tell_em(MIP6_MD_UNDEFINED, &mip6_php, mip6_phpl, + NULL, NULL, NULL); + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_md_init_with_addr + * Description: Given an event-state machine and one home address, create + * a home address on loopback. Determine whether we are at + * home, at foreign or undefined and take appropriate action. + * Ret value: Standard error codes. + ****************************************************************************** + */ +static int +mip6_md_init_with_addr(struct mip6_esm *esp) +{ + struct in6_ifaddr *ia6 = NULL; + struct nd_prefix *pr = NULL; + struct nd_defrouter *dr; + int error = 0; + + if (esp == NULL) + return EINVAL; + + /* + * Look if preconfigured home prefix already exists. Don't care + * about which ifp. + */ + if (esp->prefixlen == 0) { + log(LOG_ERR, "%s: home prefix length == 0\n", __FUNCTION__); + return EINVAL; + } + + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (esp->prefixlen == pr->ndpr_plen && + in6_are_prefix_equal(&esp->home_pref, + &pr->ndpr_prefix.sin6_addr, + esp->prefixlen)) + break; + } + + if ((error = mip6_create_homeaddr(esp)) != 0) + return error; + + mip6_phpp = pr; + mip6_php = esp->home_pref; + mip6_phpl = esp->prefixlen; + + /* XXX Do something about defrouter? */ + + /* + * XXX + * We may need to revise the procedure below, when movement + * detection is written. + */ + + /* + * XXXYYY Is this line actually correct? Don't we need to check + * more than just first dr? + */ +#ifdef MIP6_DEBUG + mip6_debug("Defrouter list:\n"); + for (dr = TAILQ_FIRST(&nd_defrouter); dr; + dr = TAILQ_NEXT(dr, dr_entry)) { + mip6_debug(" %s\n", ip6_sprintf(&dr->rtaddr)); + } +#endif + + dr = nd_defrouter_primary; + /* + * XXXYYY + * Add check for probably reachable router here as well. Mattias + */ + if (pr && pr->ndpr_advrtrs.lh_first && dr && + pfxrtr_lookup(pr, dr)) { + /* If we have home pfxrtrs and defrtr is one of these, then + we're home. */ + mip6_md_state = MIP6_MD_HOME; +/* mip6_route_state = MIP6_ROUTE_NET; */ + + mip6_send_rs(esp, 0); + + mip6_pp = mip6_phpp; + mip6_pdr = dr->rtaddr; + mip6_tell_em(MIP6_MD_HOME, &mip6_php, mip6_phpl, NULL, + NULL, dr); + } + else { + if (dr) { + mip6_md_state = MIP6_MD_FOREIGN; +/* mip6_route_state = MIP6_ROUTE_HOST; */ + + +#ifdef MIP6_DEBUG + mip6_debug("Prefix list: (break at first hit)\n"); +#endif + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { +#ifdef MIP6_DEBUG + struct nd_pfxrouter *search; + mip6_debug(" P %s\n", + ip6_sprintf(&pr-> + ndpr_prefix.sin6_addr)); + for (search = pr->ndpr_advrtrs.lh_first; + search; search = search->pfr_next) { + mip6_debug(" R %s\n", + (search->router) + ? ip6_sprintf( + &search->router-> + rtaddr) : "NULL"); + } + mip6_debug(" lookup = %s\n", + pfxrtr_lookup(pr, dr) ? + "yes" : "NULL"); + /* Phase out!!! */ + mip6_debug(" addr = %s\n", + ip6_sprintf(&pr->ndpr_addr)); +#endif + if ((pfxrtr_lookup(pr, dr) != NULL) && + (ia6 = mip6_coa_lookup(pr)) != NULL) { + break; + } + } + if (pr) { + /* + * Send a tunneled RS here if we + * know our Home Agent. + */ + if (!IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) + mip6_send_rs(esp, 1); + + mip6_pp = pr; + mip6_pdr = dr->rtaddr; + mip6_tell_em(MIP6_MD_FOREIGN, &mip6_php, + mip6_phpl, pr, ia6, dr); + } + else { +#ifdef 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; +/* mip6_route_state = MIP6_ROUTE_NET; */ + + /* We can always try... */ + mip6_send_rs(esp, 0); + + mip6_pdr = in6addr_any; + mip6_pp = NULL; + mip6_tell_em(MIP6_MD_UNDEFINED, &mip6_php, mip6_phpl, + NULL, NULL, NULL); + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_md_init + * Description: Scan through the Event-State Machine List. + * Initialize every Event-State Machine depending on if it + * is configured with a home prefix or a full home address. + * Ret value: - + ****************************************************************************** + */ +void +mip6_md_init() +{ + struct mip6_esm *esp; /* Entry in the Event State machine list */ +#ifdef OLDMIP6 + struct nd_prefix *pr, *existing_pr = NULL; + struct nd_defrouter *dr; + struct in6_ifaddr *ia; + int i, s, error; +#endif /* OLDMIP6 */ + + for (esp = mip6_esmq; esp; esp = esp->next) { + +#ifdef MIP6_DEBUG + if (esp != mip6_esmq) + mip6_debug("%s: Only supporting one home " + "prefix in this version.\n", __FUNCTION__); +#endif + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)){ + /* No home address given. Only home prefix. */ + mip6_md_init_with_prefix(esp); + } + else { + printf("%s: WARNING, this mode is being phased out " + "from Feb 2001 and on. \nYou should try " + "specifying only home prefix.\n", + __FUNCTION__); + mip6_md_init_with_addr(esp); + } + } +} + + +/* + ****************************************************************************** + * Function: mip6_select_defrtr + * Description: Usually called as an extension to defrtrlist_del() when the + * previous primary default router times out. Tries to select a + * new default router that announces the Primary Home Prefix if + * available. + * Manages the Movement Detection state transitions. + * Finally informs the event-state machine about any transitions + * and new default routers. + * Hints to a good prefix and default router to choose can be + * provided, which is currently used for Eager Movement Detection + * level 2. A disadvantage of level 2 is that the new default + * router is chosen before it's two-way reachability is confirmed. + * Only use when you need fast handoffs. + * This function is tightly coupled with mip6_prelist_update(). + * Ret value: - + ****************************************************************************** + */ +void +mip6_select_defrtr(prhint, drhint) + struct nd_prefix *prhint; + struct nd_defrouter *drhint; +{ + struct nd_prefix *pr = NULL, *phpp; + struct nd_defrouter *dr, anydr; + struct nd_pfxrouter *pfxrtr; + struct in6_ifaddr *ia6 = NULL; + struct rtentry *rt = NULL; + struct llinfo_nd6 *ln = NULL; + int s = splnet(), state; + + pr = mip6_pp; + /* Only for sanity check */ + dr = mip6_pp ? + defrouter_lookup(&mip6_pdr, mip6_pp->ndpr_ifp) : NULL; + state = mip6_md_state; + +#ifdef MIP6_DEBUG + mip6_debug("\n"); +#endif +#ifdef MIP6_DEBUG + mip6_debug("%s: previous primary dr = %s.\n", __FUNCTION__, + ip6_sprintf(&mip6_pdr)); + mip6_debug("%s: dr = %s.\n", __FUNCTION__, + dr ? ip6_sprintf(&dr->rtaddr) : "NULL"); +#endif + + if (MIP6_EAGER_PREFIX && prhint && drhint) { + if (drhint != dr && + (prhint = nd6_prefix_lookup(prhint)) != pr && + pfxrtr_lookup(prhint, drhint)) { + /* + * Check if hints are ok as the new defualt router + * and primary prefix. Otherwise use ordinary + * selection. + */ + dr = drhint; + pr = prhint; + + /* + * Check Care-of Address of the prefix + */ + if ((ia6 = mip6_coa_lookup(pr)) != NULL) { + state = MIP6_MD_FOREIGN; + +#ifdef MIP6_DEBUG + mip6_debug("%s: new probably reachable " + "defrtr %s on foreign subnet " + "selected in eager mode.\n", + __FUNCTION__, + ip6_sprintf(&dr->rtaddr)); +#endif + + /* + * Place dr first since it's prim. + */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry); + goto found; + } + } + } + + if ( (mip6_md_state == MIP6_MD_HOME) || + (mip6_md_state == MIP6_MD_UNDEFINED) ) { + if ((pr = mip6_php_lookup()) == NULL){ +#ifdef MIP6_DEBUG + mip6_debug("%s: tried home, but no onlink home " + "prefix.\n", __FUNCTION__); +#endif + goto nothome; + } + + if ((MIP6_EAGER_PREFIX && + ((pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs)) != NULL)) || + (!MIP6_EAGER_PREFIX && + ((pfxrtr = find_pfxlist_reachable_router(pr)) != NULL))) { +#ifdef MIP6_DEBUG + mip6_debug("%s: there are (reachable) pfxrtrs at " + "home.\n", __FUNCTION__); +#endif + if ((ia6 = mip6_coa_lookup(pr)) != NULL) { + /* Pick first reachable pfxrtr. */ + state = MIP6_MD_HOME; + + dr = pfxrtr->router; + + /* Place dr first since its prim. */ + TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); + TAILQ_INSERT_HEAD(&nd_defrouter, dr, dr_entry); + +#ifdef 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) { +#ifdef MIP6_DEBUG + mip6_debug("%s: there are no pfxrtrs at home, trying " + "non-home instead.\n", __FUNCTION__); +#endif + } + + /* + * No home prefix defrtr found, just drop through and pick + * one by the ordinary procedure below. + */ +#ifdef MIP6_DEBUG + mip6_debug("%s: no home prefix router found.\n", __FUNCTION__); +#endif + } + nothome: + /* + * Go through the Default Router List in search for a (probably) + * reachable router that advertises a prefix and with an associated + * Care-of Address. This is a merge from defrouter_select(). + */ + 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)) { + + /* + * Find a Care-of Address from a prefix + * announced by this router. + + */ + for (pr = nd_prefix.lh_first; pr; + pr = pr->ndpr_next) { + if ((pfxrtr_lookup(pr, dr) != NULL) && + (ia6 = mip6_coa_lookup(pr)) + != NULL) { + state = MIP6_MD_FOREIGN; + +#ifdef MIP6_DEBUG + mip6_debug("%s: new probably reachable defrtr %s on foreign subnet selected.\n", __FUNCTION__, ip6_sprintf(&dr->rtaddr)); +#endif + +#ifdef RTPREF + nd_defrouter_primary = dr; +#else + /* + * Place dr first since + * it's prim. + */ + TAILQ_REMOVE(&nd_defrouter, + dr, dr_entry); + TAILQ_INSERT_HEAD( + &nd_defrouter, + dr, dr_entry); +#endif + + goto found; + } + } + } + } + +#ifdef OLDMIP6 +/* + * XXX + * Don't use this at the moment. It might be a bad idea to try to + * select an unreachable router due to Kame changes. On the other hand, + * can we now move quickly upon detection of new prefixes? /Mattias 20010221 + * + * Or do this only at eager 2 for instance? But what about priority of + * home prefix...? Same thing actually. + */ + /* + * No (probably) reachable router found that matched our requirements. + * Go through the Default Router List again in search for any + * router that advertises a prefix and with an associated + * Care-of Address. This is a merge from defrouter_select(). + */ + for(dr = TAILQ_FIRST(&nd_defrouter); dr; dr = TAILQ_NEXT(dr, dr_entry)){ + /* + * Find a Care-of Address from a prefix announced by + * this router. + */ + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if ((pfxrtr_lookup(pr, dr) != NULL) && + ((ia6 = mip6_coa_lookup(pr)) != NULL)) { + state = MIP6_MD_FOREIGN; + +#ifdef MIP6_DEBUG + mip6_debug("%s: new (unreachable?) " + "defrtr %s on foreign subnet " + "selected.\n", __FUNCTION__, + ip6_sprintf(&dr->rtaddr)); +#endif + + /* Place dr first since its prim. */ + TAILQ_REMOVE(&nd_defrouter, dr, + dr_entry); + TAILQ_INSERT_HEAD(&nd_defrouter, dr, + dr_entry); + goto found; + } + } + } +#endif /* OLDMIP6 */ + } + + /* + * No new defrtr or no with an associated Care-of Address found + * -> State = undefined + */ + pr = NULL; + dr = NULL; + ia6 = NULL; + state = MIP6_MD_UNDEFINED; +#ifdef MIP6_DEBUG + mip6_debug("%s: no new good defrtr found.\n", __FUNCTION__); +#endif + + found: +#ifdef MIP6_DEBUG + mip6_debug("%s: found: dr = %s.\n", __FUNCTION__, dr ? ip6_sprintf(&dr->rtaddr) : "NULL"); +#endif + if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { +#ifdef MIP6_DEBUG + mip6_debug("%s: TAILQ: dr = %s.\n", __FUNCTION__, dr ? ip6_sprintf(&dr->rtaddr) : "NULL"); +#endif + /* + * De-install the previous default gateway and install + * a new one. + * Note that if there is no reachable router in the list, + * the head entry will be used anyway. + * XXX: do we have to check the current routing table entry? + */ + bzero(&anydr, sizeof(anydr)); + defrouter_delreq(&anydr, 0); + defrouter_addreq(dr); + } + else { + /* + * The Default Router List is empty, so install the default + * route to an inteface. + * XXX: The specification does not say this mechanism should + * be restricted to hosts, but this would be not useful + * (even harmful) for routers. + */ + if (!ip6_forwarding) { + /* + * De-install the current default route + * in advance. + */ + bzero(&anydr, sizeof(anydr)); + defrouter_delreq(&anydr, 0); + if (nd6_defifp) { + /* + * Install a route to the default interface + * as default route. + */ + defrouter_addifreq(nd6_defifp); + } + else /* noisy log? */ + log(LOG_INFO, "defrouter_select: " + "there's no default router and no default" + " interface\n"); + } + } + + + /* + * If we grab a (unreachable) defrouter that actually is a home + * prefix router, we should consider ourself at home rather than + * default foreign. + */ + if (dr && ((phpp = mip6_php_lookup()) != NULL)) { + struct nd_pfxrouter *pfxrtr; + + pfxrtr = pfxrtr_lookup(phpp, dr); + if (pfxrtr && dr == pfxrtr->router) { +#ifdef 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_phpp; + } + } + + /* + * First case: same router as last time. + * Second case: coming from UNDEFINED, we might have had a router, but + * we didn't have a care-of address. + */ + if (IN6_ARE_ADDR_EQUAL(&mip6_pdr, + (dr ? &dr->rtaddr : &in6addr_any)) && + !(dr && mip6_pp == NULL)) { +#ifdef MIP6_DEBUG + mip6_debug("%s: Warning: Primary default router hasn't " + "changed! No action taken.\n", __FUNCTION__); +#endif + return; + } + +#ifdef OLDMIP6 + /* + * Switch between network and host route for the Home Address + * in the following cases: + * + * md_state route_state + * + * HOME -> FOREIGN NET -> HOST + * UNDEFINED -> FOREIGN NET -> HOST + * FOREIGN -> HOME HOST -> NET + * FOREIGN -> UNDEFINED HOST -> NET + */ + + if ((state == MIP6_MD_HOME || state == MIP6_MD_UNDEFINED) + && mip6_route_state == MIP6_ROUTE_HOST) { + error = mip6_add_ifaddr(&mip6_phpp->ndpr_addr, + mip6_phpp->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_phpp->ndpr_addr, + mip6_phpp->ndpr_ifp, 128, + IN6_IFF_NODAD); + if (error) + printf("%s: address assignment error (errno = %d).\n", + __FUNCTION__, error); + mip6_route_state = MIP6_ROUTE_HOST; + } +#endif /* OLDMIP6 */ + /* + * If the Mobile Node has changed its primary prefix (probably due to + * a move to a different subnet), clear the Neighbor Cache from entries + * cloned from the previous primary prefix. This does not happen when + * we keep the same prefix but change default router. + */ +#ifdef MIP6_DEBUG + mip6_debug("mip6_pp = %s\n", mip6_pp ? ip6_sprintf(&mip6_pp->ndpr_prefix.sin6_addr) : "NULL"); + mip6_debug("pr = %s\n", pr ? ip6_sprintf(&pr->ndpr_prefix.sin6_addr) : "NULL"); +#endif + if (mip6_pp && (pr != mip6_pp)) { + struct llinfo_nd6 *ln; + + /* Taken from nd6_timer() */ + ln = llinfo_nd6.ln_next; + /* XXX BSD/OS separates this code -- itojun */ + 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); + /* sanity check */ + if (!rt) + panic("rt=0 in %s(ln=%p)\n", __FUNCTION__, ln); + if (!dst) + panic("dst=0 in %s(ln=%p)\n", __FUNCTION__, ln); + + /* Skip if the address belongs to us */ + if (ln->ln_expire == 0) { + ln = next; + continue; + } + +#ifdef 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_pp-> + ndpr_prefix.sin6_addr, + mip6_pp->ndpr_plen)) { + + /* Fake an INCOMPLETE neighbor that we're giving up */ + if (ln->ln_hold) { + m_freem(ln->ln_hold); + ln->ln_hold = NULL; + } + +#ifdef MIP6_DEBUG + mip6_debug("Deleting Neighbor %s.\n", + ip6_sprintf(&(satosin6( + rt_key(rt))->sin6_addr))); +#endif + +#ifdef IPSEC +#ifndef __OpenBSD__ + key_sa_routechange(rt_key(rt)); +#endif +#endif + +#ifdef MIP6_DEBUG + mip6_debug("Ref count = %d, now pfctlinput\n", + rt->rt_refcnt); +#endif + + /* New era */ + pfctlinput(PRC_REDIRECT_HOST, rt_key(rt)); + +#ifdef MIP6_DEBUG + mip6_debug("Ref count = %d, now RTM_DELETE\n", + rt->rt_refcnt); +#endif + next = nd6_free(rt); + } + ln = next; + /* + * XXX Also remove the link-local addresses which + * aren't ours? + */ + } + + 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); + /* sanity check */ + if (!rt) + panic("rt=0 in %s(ln=%p)\n", __FUNCTION__, ln); + if (!dst) + panic("dst=0 in %s(ln=%p)\n", __FUNCTION__, ln); + + /* Skip if the address belongs to us */ + if (ln->ln_expire == 0) { + ln = next; + continue; + } + +#ifdef 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_pp-> + ndpr_prefix.sin6_addr, + mip6_pp->ndpr_plen)) { + +#ifdef MIP6_DEBUG + mip6_debug("Deleting Neighbor %s round 2.\n", + ip6_sprintf(&(satosin6( + rt_key(rt))->sin6_addr))); +#endif + +#ifdef 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) { + rtrequest(RTM_DELETE, rt_key(rt), + (struct sockaddr *)0, + rt_mask(rt), 0, + (struct rtentry **)0); + } + } + ln = next; + /* + * XXX Also remove the link-local addresses which + * aren't ours? + */ + } + } + + /* + * Make decision permanent. + * Primary Default Router is already set above. + */ + mip6_md_state = state; + mip6_pp = pr; /* Other depend on this */ + /* + * Save rtaddr for next mip6_select_defrtr session. + */ + mip6_pdr = dr ? dr->rtaddr : in6addr_any; + + /* + * Assumptions made below: + * - dr is the chosen Default Router + * - pr is the new Primary Prefix if we're not home + * - ia6 is the new Care-of Address if we're not home + */ + switch (mip6_md_state) { + case MIP6_MD_HOME: + mip6_tell_em(mip6_md_state, &mip6_php, mip6_phpl, NULL, NULL, + dr); + break; + + case MIP6_MD_FOREIGN: + mip6_tell_em(mip6_md_state, &mip6_php, mip6_phpl, pr, ia6, dr); + break; + case MIP6_MD_UNDEFINED: + /* + * Note: we pass dr == NULL, but we might have a Default + * Router anyway, but with no prefix/Care-of Address + * associated. + */ + mip6_tell_em(mip6_md_state, &mip6_php, mip6_phpl, NULL, NULL, + NULL); + break; + } + splx(s); + return; +} + + +/* + ****************************************************************************** + * Function: mip6_prelist_update(pr, dr, was_onlink) + * Description: A hook to ND's prelist_update(). Checks if the Home Prefix + * was announced and in that case tries to force the Mobile Node + * to select that default router. If the Mobile Node was in + * UNDEFINED state we want to select that router immediately, no + * matter what the prefix was. + * Finally, if we are in eager 2 mode, we select any new + * prefix or prefix becoming attached and associating router. + * Ret value: - + ****************************************************************************** + */ +void +mip6_prelist_update(pr, dr, was_onlink) + struct nd_prefix *pr; + struct nd_defrouter *dr; + u_char was_onlink; +{ + if (dr == NULL) { + return; + } + if (mip6_is_primhomeprefix(pr)) { + /* + * It was the Primary Home Prefix that was advertised. + * Note: we don't want to go into default router selection + * during RA processing for other than the primary home + * prefix. Drawback: we won't move to home if RA actually + * contains some secondary home prefixes but not the primary. + * Currently, we consider such a RA configuration corrupt. + */ + if (mip6_md_state != MIP6_MD_HOME) { + /* + * We're not home but here's a router advertising + * our home prefix => make it primary defrtr and + * we're home! + */ +#ifdef MIP6_DEBUG + mip6_debug("%s: returning home.\n", __FUNCTION__); +#endif + mip6_md_state = MIP6_MD_HOME; + + /* State must be home before call. */ + if (TAILQ_FIRST(&nd_defrouter) != NULL) { + defrouter_select(); + } + else { +#ifdef MIP6_DEBUG + mip6_debug("%s: Undef -> Home: no previous " + "router available " + "at this stage.\n", __FUNCTION__); +#endif + /* XXXYYY or use defrouter_select()? */ + mip6_select_defrtr(NULL, NULL); + } + } + } + else if (mip6_md_state == MIP6_MD_UNDEFINED) { + /* + * Take care of transitions from UNDEFINED to FOREIGN, when the + * prefix is already known. XXX Now also when the prefix is + * new. + */ + if (TAILQ_FIRST(&nd_defrouter) != NULL) { + defrouter_select(); + } + else { +#ifdef MIP6_DEBUG + mip6_debug("%s: Strange, no default router available" + "at this stage.\n", __FUNCTION__); +#endif + /* XXXYYY or use defrouter_select()? */ + mip6_select_defrtr(NULL, NULL); + } + } + else if (MIP6_EAGER_PREFIX) + /* + * Note that transistions from any to home is taken care of at + * code above, even in eager 2 mode. + * Also note that in eager mode we consider a prefix to be + * onlink as soon as we hear it, so onlink flag can't be used + * here. + * was_onlink == 0 for re-attached prefixes or for completetly + * new prefixes. + */ + if (!was_onlink && LIST_FIRST(&pr->ndpr_advrtrs)) { +#ifdef MIP6_DEBUG + mip6_debug("%s: eager at re-attached or new prefix.\n", + __FUNCTION__); +#endif + mip6_select_defrtr(pr, dr); + } +} + + +#ifdef OLDMIP6 +/* + ****************************************************************************** + * Function: mip6_eager_prefix(pr, dr) + * Description: New prefix is heard. If Eager Movement Detection level 2 is + * activated, try to make it the primary one. + * Ret value: - + ****************************************************************************** + */ +void +mip6_eager_prefix(pr, dr) + struct nd_prefix *pr; + struct nd_defrouter *dr; +{ + if (!MIP6_EAGER_PREFIX) + return; + + if (dr == NULL || pr == NULL) { + return; + } +#ifdef MIP6_DEBUG + mip6_debug("%s: eager at new prefix.\n", __FUNCTION__); +#endif + mip6_select_defrtr(pr, dr); +} +#endif /* OLDMIP6 */ + + +/* + ****************************************************************************** + * Function: mip6_eager_md() + * Description: If eager Movement Detection is chosen, trim parameters to a + * really fast hand-off. The disadvantage is that the detection + * becomes very exposed to go into state UNDEFINED if one single + * packet is lost. Even more eager Movement Detection will make + * the Mobile Node choose new prefixes as the Primary Prefix, even + * before the previous Default Router disappears. + * Level 0: eager Movement Detection off + * Level >= 1: eager Movement Detection on, aggressive parameters + * Level >= 2: same, plus handoff as soon as new prefixes appears + * Ret value: - + ****************************************************************************** + */ +void +mip6_eager_md(int enable) +{ + mip6_config.eager_md = enable; + if (enable) { + mip6_max_lost_advints = 1; /* Aggressive values */ + if (!mip6_nd6_delay) { + mip6_nd6_delay = nd6_delay; /* Store */ + mip6_nd6_umaxtries = nd6_umaxtries; /* Store */ + } + nd6_delay = 1; /* Aggressive values */ + nd6_umaxtries = 1; + } + else { + mip6_max_lost_advints = MIP6_MAX_LOST_ADVINTS; + if (mip6_nd6_delay) { + nd6_delay = mip6_nd6_delay; /* Restore */ + nd6_umaxtries = mip6_nd6_umaxtries; /* Restore */ + mip6_nd6_delay = 0; + mip6_nd6_umaxtries = 0; + } + } +} + + +/* + ****************************************************************************** + * Function: mip6_expired_defrouter() + * Description: If the field advint_expire (which is parallel to field + * expire for router lifetime) times out, allow a small number + * of lost Router Advertisements before doubting if this + * particular default router is still reachable. + * Ret value: - + ****************************************************************************** + */ +void +mip6_expired_defrouter(struct nd_defrouter *dr) +{ +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + 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) { + /* advints_lost starts at 0. max = 1 (or more). */ + dr->advint_expire = time_second + dr->advint / 1000; +#ifdef 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; +#ifdef MIP6_DEBUG + mip6_debug("Adv Int #%d lost from router %s.\n", + dr->advints_lost, ip6_sprintf(&dr->rtaddr)); +#endif + mip6_probe_defrouter(dr); + } + } +} + + +/* + ****************************************************************************** + * Function: mip6_probe_defrouter() + * Description: Probes a default router to see if it is still reachable. + * Ordinary Neigbor Discovery routines (NUD) takes care of the + * rest. Puts this router into ND state PROBE. + * Ret value: - + ****************************************************************************** + */ +void +mip6_probe_defrouter(struct nd_defrouter *dr) +{ +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + 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) { + /* This is not a host route. */ + 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_NOSTATE)) + return; + + /* Force state to PROBE, simulate DELAY->PROBE */ + 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); +#ifdef MIP6_DEBUG + mip6_debug("Probing defrouter %s\n", ip6_sprintf(&dr->rtaddr)); +#endif +} + + +/* + ****************************************************************************** + * Function: mip6_probe_pfxrtrs() + * Description: If a new or previously detached prefix is heard, probe (NUD) + * all prefix routers on the current primary prefix in order to + * quickly detect if we have moved. This is only enabled in + * eager Movement Detection (level 1 and 2). + * Ret value: - + ****************************************************************************** + */ +void +mip6_probe_pfxrtrs() +{ + struct nd_pfxrouter *pfr; + if (!mip6_config.eager_md) + return; + + if (!mip6_pp) + return; + +#ifdef MIP6_DEBUG + mip6_debug("New or detached prefix received, probe old routers:\n"); +#endif + for (pfr = mip6_pp->ndpr_advrtrs.lh_first; + pfr; pfr = pfr->pfr_next) { + mip6_probe_defrouter(pfr->router); + } +} + + +/* + ****************************************************************************** + * Function: mip6_store_advint(ai, dr) + * Description: If Advertisement Interval option is available in Router + * Advertisements, keep a timer for this expiry parallel to the + * ordinary Router lifetime timer. + * Ret value: - + ****************************************************************************** + */ +void +mip6_store_advint(struct nd_opt_advinterval *ai, + struct nd_defrouter *dr) +{ +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + long time_second = time.tv_sec; +#endif + + /* Check the advertisement interval option */ + if (ai->nd_opt_adv_len != 1) { + log(LOG_INFO, "%s: bad Advertisement Interval Option " + "length\n", __FUNCTION__); + } + else if (dr) { + dr->advint = ntohl(ai->nd_opt_adv_interval); /* milliseconds */ + + /* Sorry for delay between reception and this setting */ + dr->advint_expire = time_second + dr->advint / 1000; + dr->advints_lost = 0; + } +} + + +/* + ****************************************************************************** + * Function: mip6_delete_ifaddr + * Description: Similar to "ifconfig delete". + * Ret value: - + ****************************************************************************** + */ +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) + 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(); + + /* + * Code recycled from in6_control(). + */ + + /* + * Find address for this interface, if it exists. + */ + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + if (sa6->sin6_addr.s6_addr16[1] == 0) { + /* interface ID is not embedded by the user */ + 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); /* ifid is contradict */ + } + 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; /* XXX: good way? */ + } + } + ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); + + /* + * for IPv4, we look for existing in6_ifaddr here to allow + * "ifconfig if0 delete" to remove first IPv4 address on the + * interface. For IPv6, as the spec allow multiple interface + * address from the day one, we consider "remove the first one" + * semantics to be not preferrable. + */ + if (ia == 0) { + splx(s); + return(EADDRNOTAVAIL); + } + /* FALLTHROUGH */ + + 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; + /* gain a refcnt for the link from in6_ifaddr */ + IFAREF(&ia->ia_ifa); + +#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 + /* gain another refcnt for the link from if_addrlist */ + IFAREF(&ia->ia_ifa); + } + + in6_purgeaddr(&ia->ia_ifa); + + splx(s); + return(0); +} + + +#if 0 +/* + ****************************************************************************** + * Function: mip6_delete_ifaddr + * Description: Similar to "ifconfig delete". + * Ret value: - + ****************************************************************************** + */ +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) + , NULL +#endif + ); + splx(s); + if (error) { +#ifdef MIP6_DEBUG + mip6_debug("%s: Attempt to delete addr %s failed.\n", __FUNCTION__, + ip6_sprintf(addr)); +#endif + } +} +#endif /* 0 */ + +struct nd_prefix * +mip6_get_home_prefix(void) +{ + return(mip6_phpp); /* XXX */ +} + + +int +mip6_get_md_state(void) +{ + return(mip6_md_state); +} + + +/* + ****************************************************************************** + * Function: mip6_md_exit + * Description: Tidy up after the Mobile IPv6 Movement Detection. This is + * used when releasing the kernel module. All Home Addresses + * on loopback are released. If at home,the prefix and address + * will be automagically configured as specified by ND. + * Ret value: - + ****************************************************************************** + */ +void +mip6_md_exit() +{ +/* struct nd_prefix *pr; */ + +#if 1 + panic("mip6_md_exit(): this function is broken"); +#else +#warning This function is broken. +#endif + /* + * XXXYYY Should use mip6_esmq when multiple Home Addresses are + * supported. + */ + + mip6_phpp = NULL; + mip6_php = in6addr_any; + mip6_phpl = 0; + mip6_pp = NULL; +#if 0 +/* + * Todo: go through all ESMs and delete all home addresses on lo0 for each + * esm. + * + * Clear NDPRF_HOME on prefixes. + */ +/*XXX*/ pr = mip6_phpp; + 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_phpp = NULL; + mip6_php = in6addr_any; +#ifdef MIP6_DEBUG + mip6_debug("Home Prefix and Home Address removed.\n"); +#endif + } +#endif /* 0 */ +} diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/mip6_mn.c kame/kame/sys/netinet6/mip6_mn.c --- kame-20010611/kame/sys/netinet6/mip6_mn.c Thu Jan 1 09:00:00 1970 +++ kame/kame/sys/netinet6/mip6_mn.c Thu May 3 23:51:48 2001 @@ -0,0 +1,3201 @@ +/* $KAME: mip6_mn.c,v 1.25 2001/05/03 14:51:48 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999, 2000 and 2001 Ericsson Radio Systems AB + * All rights reserved. + * + * Authors: Conny Larsson + * Mattias Pettersson + * + */ + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_ipsec.h" +#endif + +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ipsec.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) +#include +#elif defined(__OpenBSD__) +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Declaration of Global variables. */ +struct mip6_bul *mip6_bulq = NULL; /* First entry in Binding Update list */ +struct mip6_esm *mip6_esmq = NULL; /* List of event-state machines */ + +#ifdef __NetBSD__ +struct callout mip6_timer_bul_ch = CALLOUT_INITIALIZER; +struct callout mip6_timer_esm_ch = CALLOUT_INITIALIZER; +#elif (defined(__FreeBSD__) && __FreeBSD__ >= 3) +struct callout mip6_timer_bul_ch; +struct callout mip6_timer_esm_ch; +#endif + + +/* + ############################################################################## + # + # INITIALIZATION AND EXIT FUNCTIONS + # These functions are executed when the mobile node specific MIPv6 code is + # activated and deactivated respectively. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_mn_init + * Description: Initialization of MIPv6 variables that must be initialized + * before the MN code is executed. + ****************************************************************************** + */ +void +mip6_mn_init(void) +{ + mip6_hadiscov_id = 0; + +#if defined(__FreeBSD__) && __FreeBSD__ >= 3 + /* Initialize handle for timer functions. */ + callout_init(&mip6_timer_bul_ch); + callout_init(&mip6_timer_esm_ch); +#endif + printf("Mobile Node initialized\n"); +} + + + +/* + ****************************************************************************** + * Function: mip6_mn_exit + * Description: This function is called when the MN module is unloaded + * (relesed) from the kernel. + ****************************************************************************** + */ +void +mip6_mn_exit() +{ + struct mip6_bul *bulp; + struct mip6_esm *esp; + int s; + + /* Cancel outstanding timeout function calls. */ +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_bul_ch); + callout_stop(&mip6_timer_esm_ch); +#else + untimeout(mip6_timer_bul, (void *)NULL); + untimeout(mip6_timer_esm, (void *)NULL); +#endif + + /* Remove each entry in every queue. */ + s = splnet(); + 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); +} + + + +/* + ############################################################################## + # + # FUNCTIONS FOR PROCESSING OF INBOUND MIPV6 OPTIONS + # Below are functions used for processing of received MIPv6 options (BU, BA + # and BR) and its sub-options. These options are received by the dest6_input() + # function, which calls the mip6_dstopt() function. The mip6_dstopt() function + # is a dispatcher function. + # As a result of processing an option other functions will be called which + # eventually results in either a response or an action. The functions for + # sending responses are also defined under this section. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_validate_ba + * Description: Validate received Binding Acknowledgement option (see 10.12). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + * -2 Silently ignore. Process rest of packet. + ****************************************************************************** + */ +int +mip6_validate_ba(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BA option in DH */ +{ + struct ip6_opt_binding_ack *ba_opt; + struct ip6_hdr *ip6; + struct mip6_bul *bulp; + + ba_opt = (struct ip6_opt_binding_ack *)(opt); + ip6 = mtod(m, struct ip6_hdr *); + + /* Make sure that the BA is protected by an AH (see 4.4). */ +#ifdef IPSEC +#ifndef __OpenBSD__ + if ( !(m->m_flags & M_AUTHIPHDR && m->m_flags & M_AUTHIPDGM)) { + log(LOG_ERR, + "%s: BA not protected by AH from host %s\n", + __FUNCTION__, ip6_sprintf(&ip6->ip6_src)); + return -2; + } +#endif +#endif + + /* Make sure that the length field in the BA is >= IP6OPT_BALEN. */ + if (ba_opt->ip6oa_len < IP6OPT_BALEN) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, + "%s: Length field to short (%d) in BA from host %s\n", + __FUNCTION__, ba_opt->ip6oa_len, + ip6_sprintf(&ip6->ip6_src)); + return -2; + } + + /* The sent BU sequence number == received BA sequence number. */ + bulp = mip6_bul_find(&ip6->ip6_src, &ip6->ip6_dst); + if (bulp == NULL) { + log(LOG_ERR, "%s: No Binding Update List entry found\n", + __FUNCTION__); + return -2; + } + + if (ntohs(*(u_int16_t *)ba_opt->ip6oa_seqno) != bulp->seqno) { + ip6stat.ip6s_badoptions++; + log(LOG_ERR, + "%s: Received sequence # (%d) not equal to sent (%d)\n", + __FUNCTION__, ntohs(*(u_int16_t *)ba_opt->ip6oa_seqno), + bulp->seqno); + return -2; + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_process_ba + * Description: Process a received Binding Acknowledgement option, see 10.12. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_process_ba(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BA option in DH */ +{ + struct ip6_opt_binding_update *bu_opt; + struct ip6_opt_binding_ack *ba_opt; + struct ip6_hdr *ip6; + struct mip6_esm *esp; + struct mip6_bul *bulp; + u_int32_t lt_update, lt_ack; + u_int32_t lt_remain, lt_refresh; + u_int8_t flags; + int res; + +#ifdef MIP6_DEBUG + mip6_print_opt(m, opt); +#endif + + ba_opt = (struct ip6_opt_binding_ack *)opt; + ip6 = mtod(m, struct ip6_hdr *); + + bulp = mip6_bul_find(&ip6->ip6_src, &ip6->ip6_dst); + if (bulp == NULL) return -1; + + /* Check the status field in the BA. */ + if (ba_opt->ip6oa_status >= MIP6_BA_STATUS_UNSPEC) { + /* Remove BUL entry. Process error (order is important). */ + mip6_bul_delete(bulp); + res = mip6_ba_error(m, opt); + if (res == -1) return -1; + return 0; + } + + /* BA was accepted. Update corresponding entry in the BUL. + Stop retransmitting the BU. */ + mip6_bul_clear_state(bulp); + + lt_update = bulp->sent_lifetime; + lt_ack = ntohl(*(u_int32_t *)ba_opt->ip6oa_lifetime); + lt_remain = bulp->lifetime; + if (lt_ack < lt_update) + lt_remain = max(lt_remain - (lt_update - lt_ack), 0); + else + lt_remain = ntohl(*(u_int32_t *)ba_opt->ip6oa_lifetime); + + bulp->lifetime = lt_remain; + bulp->refresh = lt_remain; + + if (bulp->flags & IP6_BUF_HOME) { + lt_refresh = ntohl(*(u_int32_t *)ba_opt->ip6oa_refresh); + if ((lt_refresh > 0) && (lt_refresh < lt_remain)) + bulp->refresh = lt_refresh; + } + + /* If the BA was received from the Home Agent the state + of the event state machine shall be updated. */ + if (bulp->flags & IP6_BUF_HOME) { + esp = mip6_esm_find(&bulp->local_home, 0); + if (esp == NULL) { + log(LOG_ERR, "%s: No ESM found\n", __FUNCTION__); + return -1; + } + + if (esp->state == MIP6_STATE_DEREG) { + /* Returning home (see 10.20) */ + mip6_bul_delete(bulp); + + /* Remove tunnel from MN to HA */ + mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL, + MIP6_NODE_MN, (void *)esp); + + /* Send BU to each CN in the BUL to remove its + BC entry. */ + flags = 0; + bu_opt = mip6_create_bu(0, flags, 0); + if (bu_opt == NULL) return 0; + + mip6_update_cns(bu_opt, NULL, &esp->home_addr, + &esp->home_addr, 0); + + /* Don't set the state until BUs have been sent to + all CNs, otherwise the Home Address option will + not be added for the outgoing packet. */ + esp->state = MIP6_STATE_HOME; + esp->coa = in6addr_any; + } else { + esp->state = MIP6_STATE_REG; + + /* Create or modify a tunnel used by the MN to + receive incoming tunneled packets. */ + if (mip6_tunnel(&esp->coa, &esp->ha_hn, + MIP6_TUNNEL_MOVE, MIP6_NODE_MN, + (void *)esp)) + return -1; + + /* Send BU to each CN in the BUL to update BC entry. */ + flags = 0; + bu_opt = mip6_create_bu(0, flags, bulp->lifetime); + if (bu_opt == NULL) return -1; + + mip6_update_cns(bu_opt, NULL, &esp->home_addr, + &esp->coa, bulp->lifetime); + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_ba_error + * Description: Each incoming BA error is taken care of by this function. + * If a registration to the Home Agent failed then dynamic home + * agent address discovery shall be performed. If a de-regi- + * stration failed then perform the same actions as when a + * BA with status equals to 0 is received. + * If a registration or de-registration to the CN failed then + * the error is logged, no further action is taken. + * If dynamic home agent address discovery already has been + * done then take the next entry in the list. If its just one + * entry in the list discard it and send a BU with destination + * address equals to Home Agents anycast address. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_ba_error(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BA option in DH */ +{ + struct ip6_opt_binding_ack *ba_opt; + struct ip6_hdr *ip6; + + ba_opt = (struct ip6_opt_binding_ack *)opt; + ip6 = mtod(m, struct ip6_hdr *); + + if (ba_opt->ip6oa_status == MIP6_BA_STATUS_UNSPEC) { + /* Reason unspecified + Received when either a Home Agent or Correspondent Node + was not able to process the BU. */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Reason unspecified) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_PROHIBIT) { + /* Administratively prohibited */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Administratively prohibited) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_RESOURCE) { + /* Insufficient resources + Received when a Home Agent receives a BU with the H-bit + set and insufficient space exist or can be reclaimed + (sec. 8.7). */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Insufficient resources) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_HOMEREGNOSUP) { + /* Home registration not supported + Received when a primary care-of address registration + (sec. 9.3) is done and the node is not a router + implementing Home Agent functionality. */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Home registration not supported) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_SUBNET) { + /* Not home subnet + Received when a primary care-of address registration + (sec. 9.3) is done and the home address for the binding + is not an on-link IPv6 address with respect to the Home + Agent's current prefix list. */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Not home subnet) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_IFLEN) { + /* Incorrect subnet prefix length + Received when a primary care-of address registration + (sec. 9.3) is done and the prefix length in the BU + differs from the length of the home agent's own knowledge + of the subnet prefix length on the home link. */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Incorrect subnet prefix length) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_NOTHA) { + /* Not Home Agent for this Mobile Node + Received when a primary care-of address de-registration + (sec. 9.4) is done and the Home Agent has no entry for + this mobil node marked as "home registration" in its + Binding Cache. */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Not Home Agent for this Mobile Node) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else if (ba_opt->ip6oa_status == MIP6_BA_STATUS_DAD) { + /* Duplicate Address Detection failed + Received when the Mobile Node's home address already is + in use at the home network (see X.X). */ + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Duplicate Address Detection failed) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } else { + log(LOG_INFO, + "\nBinding Acknowledgement error = %d " + "(Unknown) from host %s\n", + ba_opt->ip6oa_status, ip6_sprintf(&ip6->ip6_src)); + } + + /* Furthr processing according to the desription in the header. */ + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_process_br + * Description: Process a Binding Request option (see 10.13). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_process_br(m, opt) +struct mbuf *m; /* Ptr to beginning of mbuf */ +u_int8_t *opt; /* Ptr to BR option in DH */ +{ + struct ip6_opt_binding_request *br_opt; + struct ip6_opt_binding_update *bu_opt; + struct ip6_hdr *ip6; + struct ip6aux *ip6a = NULL; + struct mbuf *n; + struct mip6_bul *bulp_cn; /* CN entry in BU list */ + struct mip6_bul *bulp_ha; /* HA entry in BU list */ + struct mip6_buffer *subbuf; /* Sub-options for BU */ + struct mip6_subopt_altcoa altcoa; + struct mip6_subopt_uid *uid = NULL, bruid; + struct mip6_esm *esp; + u_int16_t var16; + u_int8_t *subopt, flags; + int size; + +#ifdef MIP6_DEBUG + mip6_print_opt(m, opt); +#endif + + br_opt = (struct ip6_opt_binding_request *)opt; + ip6 = mtod(m, struct ip6_hdr *); + + n = ip6_findaux(m); + if (!n) return -1; + ip6a = mtod(n, struct ip6aux *); + if (ip6a == NULL) return -1; + + /* If the BR came from the home agent it was included in a RA + and is processed by the MIPv6 icmp code. */ + esp = mip6_esm_find(&ip6->ip6_dst, 0); + if (esp == NULL) return -1; + + if (br_opt->ip6or_len > IP6OPT_BRLEN) { + subopt = opt + IP6OPT_MINLEN + IP6OPT_BRLEN; + uid = mip6_find_subopt_uid(subopt, *(opt + 1) - IP6OPT_BRLEN); + } + + if (IN6_ARE_ADDR_EQUAL(&esp->ha_hn, &ip6->ip6_dst)) { + if (uid == NULL) return -1; + + ip6a->ip6a_flags |= IP6A_BRUID; + ip6a->ip6a_bruid = ntohs(*(u_int16_t *)uid->uid); + return 0; + } + + /* A CN is requesting the MN to send a BU to update its BC. + Find out which lifetime to use in the BU */ + bulp_cn = mip6_bul_find(&ip6->ip6_src, &ip6->ip6_dst); + if (bulp_cn == NULL) return -1; + + bulp_ha = mip6_bul_find(&esp->ha_hn, &ip6->ip6_dst); + if (bulp_ha == NULL) return -1; + + if (bulp_ha->lifetime > bulp_cn->lifetime) { + size = sizeof(struct mip6_buffer); + subbuf = (struct mip6_buffer *)malloc(size, M_TEMP, M_NOWAIT); + if (subbuf == NULL) return -1; + bzero((caddr_t)subbuf, sizeof(struct mip6_buffer)); + + flags = 0; + bu_opt = mip6_create_bu(esp->prefixlen, flags, + bulp_ha->lifetime); + if (bu_opt == NULL) { + free(subbuf, M_TEMP); + return 0; + } + + altcoa.type = IP6SUBOPT_ALTCOA; + altcoa.len = IP6OPT_COALEN; + size = sizeof(struct in6_addr); + bcopy((caddr_t)&bulp_cn->local_coa, altcoa.coa, size); + mip6_add_subopt2buf((u_int8_t *)&altcoa, subbuf); + + if (uid != NULL) { + bruid.type = IP6SUBOPT_UNIQUEID; + bruid.len = IP6OPT_UIDLEN; + var16 = ntohs(*(u_int16_t *)uid->uid); + bcopy((caddr_t)&var16, &bruid.uid, sizeof(var16)); + mip6_add_subopt2buf((u_int8_t *)&bruid, subbuf); + } + + /* Send BU to CN */ + if (mip6_send_bu(bulp_cn, bu_opt, subbuf)) { + free(subbuf, M_TEMP); + free(bu_opt, M_TEMP); + return -1; + } + + /* Update BUL entry */ + bulp_cn->sent_lifetime = bulp_ha->lifetime; + bulp_cn->lifetime = bulp_ha->lifetime; + bulp_cn->refresh = bulp_ha->lifetime; + bulp_cn->flags = 0; + mip6_bul_clear_state(bulp_cn); + free(subbuf, M_TEMP); + free(bu_opt, M_TEMP); + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_send_bu + * Description: Send a Binding Update option to a node (CN, HA or MN). A new + * IPv6 packet is built including an IPv6 header and a Destination + * header (where the BU is stored). + * Arguments: bulp - BUL entry for which the BU is sent. + * bu_opt - BU option to send. NULL if the BU option stored in + * the BUL entry is used. + * subbuf - Sub-options for the BU. NULL if the BU sub-options + * stored in the BUL entry is used. + * Note: The following combinations of indata are possible: + * bu_opt == NULL && subbuf == NULL Use existing data, i.e used + * for retransmission + * bu_opt != NULL && subbuf == NULL Clear existing data and send + * a new BU without sub-options + * bu_opt != NULL && subbuf != NULL Clear existing data and send + * a new BU with new sub-options + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_send_bu(bulp, bu_opt, subbuf) +struct mip6_bul *bulp; /* BUL entry used when sending BU */ +struct ip6_opt_binding_update *bu_opt; /* Binding Update option */ +struct mip6_buffer *subbuf; /* Buffer with BU options or NULL */ +{ + struct mbuf *mo; /* IPv6 header stored in a mbuf */ + struct ip6_pktopts *pktopts; /* Options for IPv6 packet */ + struct mip6_esm *esp; /* Home address entry */ + struct ip6_ext *ext_hdr; + struct mip6_buffer dh2; + u_int8_t *bu_pos, *ptr; + int error, ii, len, size; + +#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) + long time_second = time.tv_sec; +#endif + + /* Make sure that it's allowed to send a BU */ + if (bulp == NULL) return 0; + + if (!bulp->send_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->peer_home)); + return 0; + } + + /* Only send BU if we are not in state UNDEFINED */ + esp = mip6_esm_find(&bulp->local_home, 0); + 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->local_home)); + return 0; + } + + /* Evaluate parameters according to the note in the function header */ + if ((bu_opt == NULL) && (subbuf == NULL)) { + if (!(bulp->flags & IP6_BUF_ACK) && bulp->bul_opt == NULL) { + log(LOG_ERR, + "%s: No existing BU option to send\n", + __FUNCTION__); + return 0; + } + + bulp->seqno += 1; + bcopy((caddr_t)&bulp->seqno, bulp->bul_opt->ip6ou_seqno, + sizeof(bulp->seqno)); + bcopy((caddr_t)&bulp->lifetime, bulp->bul_opt->ip6ou_lifetime, + sizeof(bulp->lifetime)); + bu_opt = bulp->bul_opt; + subbuf = bulp->bul_subopt; + } else if (bu_opt != NULL) { + mip6_bul_clear_state(bulp); + bulp->seqno += 1; + bcopy((caddr_t)&bulp->seqno, bu_opt->ip6ou_seqno, + sizeof(bulp->seqno)); + bcopy((caddr_t)&bulp->lifetime, bu_opt->ip6ou_lifetime, + sizeof(bulp->lifetime)); + + if (bu_opt->ip6ou_flags & IP6_BUF_ACK) { + size = sizeof(struct ip6_opt_binding_update); + bulp->bul_opt = (struct ip6_opt_binding_update *) + malloc(size, M_TEMP, M_NOWAIT); + if (bulp->bul_opt == NULL) return -1; + bcopy((caddr_t)bu_opt, (caddr_t)bulp->bul_opt, size); + + if (subbuf != NULL) { + size = sizeof(struct mip6_buffer); + bulp->bul_subopt = (struct mip6_buffer *) + malloc(size, M_TEMP, M_NOWAIT); + if (bulp->bul_subopt == NULL) { + free(bulp->bul_opt, M_TEMP); + return -1; + } + bcopy((caddr_t)subbuf, + (caddr_t)bulp->bul_subopt,size); + } + + bulp->flags |= IP6_BUF_ACK; + if (bu_opt->ip6ou_flags & IP6_BUF_DAD) { + bulp->bul_timeout = 4; + bulp->bul_timeleft = 4; + } else { + bulp->bul_timeout = 2; + bulp->bul_timeleft = 2; + } + bu_opt = bulp->bul_opt; + subbuf = bulp->bul_subopt; + } + } else { + log(LOG_ERR, + "%s: Function parameter error. We should not come here\n", + __FUNCTION__); + return 0; + } + + /* Allocate necessary memory and send the BU */ + pktopts = (struct ip6_pktopts *)malloc(sizeof(struct ip6_pktopts), + M_TEMP, M_NOWAIT); + if (pktopts == NULL) return -1; + init_ip6pktopts(pktopts); + + mo = mip6_create_ip6hdr(&bulp->local_home, &bulp->peer_home, + IPPROTO_NONE, 0); + if (mo == NULL) { + free(pktopts, M_TEMP); + return -1; + } + + bzero((caddr_t)&dh2, sizeof(dh2)); + bu_pos = mip6_add_opt2dh((u_int8_t *)bu_opt, &dh2); + mip6_add_subopt2dh(subbuf, &dh2, bu_pos); + mip6_align(&dh2); + ext_hdr = (struct ip6_ext *)dh2.buf; + ext_hdr->ip6e_nxt = IPPROTO_NONE; + pktopts->ip6po_dest2 = (struct ip6_dest *)dh2.buf; + + error = ip6_output(mo, pktopts, NULL, 0, NULL, NULL); + if (error) { + free(pktopts, M_TEMP); + log(LOG_ERR, + "%s: ip6_output function failed to send BU, error = %d\n", + __FUNCTION__, error); + return -1; + } + + /* Update Binding Update List variables. */ + bulp->lasttime = time_second; + + if (!(bu_opt->ip6ou_flags & IP6_BUF_ACK)) { + bulp->bul_sent += 1; + if (bulp->bul_sent >= MIP6_MAX_FAST_UPDATES) + bulp->bul_rate = MIP6_SLOW_UPDATE_RATE; + } + +#ifdef MIP6_DEBUG + mip6_debug("\nSent Binding Update option (0x%x)\n", bu_opt); + mip6_debug("IP Header Src: %s\n", ip6_sprintf(&bulp->local_home)); + mip6_debug("IP Header Dst: %s\n", ip6_sprintf(&bulp->peer_home)); + mip6_debug("Type/Length/Flags: %x / %u / ", + bu_opt->ip6ou_type, bu_opt->ip6ou_len); + if (bu_opt->ip6ou_flags & IP6_BUF_ACK) mip6_debug("A "); + if (bu_opt->ip6ou_flags & IP6_BUF_HOME) mip6_debug("H "); + if (bu_opt->ip6ou_flags & IP6_BUF_ROUTER) mip6_debug("R "); + if (bu_opt->ip6ou_flags & IP6_BUF_DAD) mip6_debug("D "); + mip6_debug("\n"); + mip6_debug("Prefix length: %u\n", bu_opt->ip6ou_prefixlen); + mip6_debug("Sequence number: %u\n", + *(u_int16_t *)bu_opt->ip6ou_seqno); + mip6_debug("Life time: "); + mip6_print_sec(*(u_int32_t *)bu_opt->ip6ou_lifetime); + mip6_debug("Destination Header 2 Contents\n"); + + ptr = (u_int8_t *)dh2.buf; + len = (*(ptr + 1) + 1) << 3; + for (ii = 0; ii < len; ii++, ptr++) { + if (ii % 16 == 0) mip6_debug("\t0x:"); + if (ii % 4 == 0) mip6_debug(" "); + mip6_debug("%02x ", *ptr); + if ((ii + 1) % 16 == 0) mip6_debug("\n"); + } + if (ii % 16) mip6_debug("\n"); +#endif + + /* Remove allocated memory (mo is removed by ip6_output). */ + free(pktopts, M_TEMP); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_update_cns + * Description: Search the BUL for each entry with a matching home address for + * which no Binding Update has been sent for the new COA. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_update_cns(bu_opt, subbuf, local_home, local_coa, lifetime) +struct ip6_opt_binding_update *bu_opt; /* BU option */ +struct mip6_buffer *subbuf; /* List of sub-options or NULL */ +struct in6_addr *local_home; /* Home address for MN */ +struct in6_addr *local_coa; /* New coa for MN */ +u_int32_t lifetime; +{ + struct mip6_bul *bulp; + + /* Search the Binding Update list for entries for which a BU + option have to be sent. */ + for (bulp = mip6_bulq; bulp;) { + if (IN6_ARE_ADDR_EQUAL(local_home, &bulp->local_home) && + !IN6_ARE_ADDR_EQUAL(local_coa, &bulp->local_coa)) { + bulp->lifetime = lifetime; + bulp->refresh = lifetime; + bulp->sent_lifetime = lifetime; + if (mip6_send_bu(bulp, bu_opt, subbuf) == -1) + return; + + /* Remove BUL entry if de-registration and A-bit + was not set. */ + if (!(bu_opt->ip6ou_flags & IP6_BUF_ACK) && + (IN6_ARE_ADDR_EQUAL(local_home, local_coa) || + (bu_opt->ip6ou_lifetime == 0))) + bulp = mip6_bul_delete(bulp); + else + bulp = bulp->next; + } else + bulp = bulp->next; + } +} + + + +/* + ****************************************************************************** + * Function: mip6_create_bu + * Description: Create a Binding Update option for transmission. + * Ret value: Pointer to the BU option or NULL. + * Note: Variable seqno is set in function mip6_update_bul_entry(). + * Variables are stored in host byte order. + ****************************************************************************** + */ +struct ip6_opt_binding_update * +mip6_create_bu(prefixlen, flags, lifetime) +u_int8_t prefixlen; /* Prefix length for Home Address */ +u_int8_t flags; /* Flags for BU option */ +u_int32_t lifetime; /* Suggested lifetime for the BU registration */ +{ + struct ip6_opt_binding_update *bu_opt; + int len; + + /* Allocate and store Binding Update option data */ + len = sizeof(struct ip6_opt_binding_update); + bu_opt = (struct ip6_opt_binding_update *)malloc(len,M_TEMP,M_NOWAIT); + if (bu_opt == NULL) return NULL; + bzero(bu_opt, sizeof(struct ip6_opt_binding_update)); + + bu_opt->ip6ou_type = IP6OPT_BINDING_UPDATE; + bu_opt->ip6ou_len = IP6OPT_BULEN; + bu_opt->ip6ou_flags = flags; + bcopy((caddr_t)&lifetime, bu_opt->ip6ou_lifetime, sizeof(lifetime)); + + /* Validate semantics according to 5.1 */ + if (bu_opt->ip6ou_flags & IP6_BUF_HOME) + bu_opt->ip6ou_prefixlen = prefixlen; + else { + bu_opt->ip6ou_prefixlen = 0; + bu_opt->ip6ou_flags &= ~IP6_BUF_ROUTER; + } + + if (!(bu_opt->ip6ou_flags & IP6_BUF_HOME && + bu_opt->ip6ou_flags & IP6_BUF_ACK)) + bu_opt->ip6ou_flags &= ~IP6_BUF_DAD; + + return bu_opt; +} + + + +/* + ############################################################################## + # + # EVENT TRIGGED FUNCTIONS + # These functions are called when a mobile node change its point of attach- + # ment, i.e. it moves from a home network to a foreign network or from one + # foreign network to another or from a foreign network back to the home + # network. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_move + * Description: Called from the move detection algorithm when it has decided + * to change default router, i.e the network that we were + * connected to has changed. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_move(state, home_prefix, home_plen, prim_prefix, prim_coa) +int state; /* State from move detection algorithm */ +struct in6_addr *home_prefix; /* Prefix for home address for MN */ +u_int8_t home_plen; /* Prefix length for home address */ +struct nd_prefix *prim_prefix; /* Prefix for primary care-of address */ +struct in6_ifaddr *prim_coa; /* Primary care-of address */ +{ + struct in6_addr *prim_addr; /* Primary Care-of Adress for MN */ + struct mip6_esm *esp; + +#if 0 + /* Check incoming parameters */ + if (prim_prefix == NULL) + prim_addr = NULL; + else + prim_addr = &prim_prefix->ndpr_addr; +#else + /* Check incoming parameters */ + if (prim_coa == NULL) + prim_addr = NULL; + else + prim_addr = &prim_coa->ia_addr.sin6_addr; +#endif /* 0 */ + + /* Find event-state machine and update it */ + esp = mip6_esm_find(home_prefix, home_plen); + if (esp == NULL) { + log(LOG_ERR, + "%s: No event-state machine found\n", __FUNCTION__); + return; + } + + /* Decide how the mobile node has moved. */ + if ((prim_prefix == NULL) && (state == MIP6_MD_UNDEFINED)) { + /* The Mobile Node is not connected to a network */ + esp->state = MIP6_STATE_UNDEF; + esp->coa = in6addr_any; + if (mip6_tunnel(NULL, NULL, MIP6_TUNNEL_DEL, + MIP6_NODE_MN, (void *)esp)) + return; + } else if ((prim_prefix == NULL) && (state == MIP6_MD_HOME)) { + /* The Mobile Node is returning to the home link. */ + mip6_move_home(home_prefix, home_plen, prim_addr); + } else if ((prim_prefix != NULL) && (state == MIP6_MD_FOREIGN)) { + if ((esp->state == MIP6_STATE_UNDEF) || + (esp->state == MIP6_STATE_HOME) || + (esp->state == MIP6_STATE_DEREG)) + /* Home Network --> Foreign Network */ + mip6_move_hn2fn(home_prefix, home_plen, prim_addr); + else if (esp->state == MIP6_STATE_REG || + esp->state == MIP6_STATE_REREG || + esp->state == MIP6_STATE_REGNEWCOA || + esp->state == MIP6_STATE_NOTREG) + /* Foreign Network --> New Foreign Network */ + mip6_move_fn2fn(home_prefix, home_plen, prim_addr); + } else + esp->state = MIP6_STATE_UNDEF; + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_move_home + * Description: Called from the move detection function when a mobile node is + * returning to its home network (see 10.6, 10.20). + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_move_home(home_prefix, home_plen, prim_addr) +struct in6_addr *home_prefix; /* Prefix for home address for MN */ +u_int8_t home_plen; /* Prefix length for home address */ +struct in6_addr *prim_addr; /* Primary Care-of Adress for MN */ +{ + struct ip6_opt_binding_update *bu_opt; /* BU option */ + struct mip6_esm *esp; /* Home address entry */ + struct mip6_bul *bulp; /* Entry in the BU list */ + struct ifaddr *if_addr; /* Interface address */ + struct in6_addr old_coa; + struct sockaddr_in6 sin6; + u_int8_t bu_flags; /* Flags for BU */ + u_long na_flags; /* Flags for NA */ + + /* Find event-state machine and update it */ + esp = mip6_esm_find(home_prefix, home_plen); + if (esp == NULL) { + log(LOG_ERR, + "%s: No event-state machine found\n", __FUNCTION__); + return -1; + } + + esp->state = MIP6_STATE_DEREG; + old_coa = esp->coa; + esp->coa = esp->home_addr; + + /* Send a BU de-registration to the Home Agent. */ + bulp = mip6_bul_find(NULL, &esp->home_addr); + if (bulp == NULL) { + /* The event-state machine was in state undefined. */ + esp->state = MIP6_STATE_HOME; + + /* When returning home and no home registration exist + we can not assume the home address to be unique. + Perform DAD, but find the i/f address first. */ + 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 -1; + + ((struct in6_ifaddr *)if_addr)->ia6_flags |= IN6_IFF_TENTATIVE; + nd6_dad_start(if_addr, NULL); + return 0; + } + + /* Update BUL entry and send BU to home agent */ + bulp->lifetime = mip6_prefix_lifetime(&esp->home_addr, esp->prefixlen); + bulp->refresh = bulp->lifetime; + bulp->sent_lifetime = bulp->lifetime; + bulp->local_coa = bulp->local_home; + bulp->peer_home = esp->ha_hn; + + bu_flags = 0; + bu_flags |= IP6_BUF_HOME; + bu_flags |= IP6_BUF_ACK; + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, bulp->lifetime); + if (bu_opt == NULL) return -1; + + if (mip6_send_bu(bulp, bu_opt, NULL)) return -1; + + /* Update home agent on previous foreign network. */ + mip6_update_fn(home_prefix, home_plen, prim_addr, &old_coa); + + /* Make the HA stop intercepting packets */ + na_flags = 0; + na_flags |= ND_NA_FLAG_OVERRIDE; + mip6_intercept_control(home_prefix, esp->prefixlen, na_flags); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_move_hn2fn + * Description: Called from the move detection algorithm when a mobile node + * moves from the home network to a foreign network, see 10.6. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_move_hn2fn(home_prefix, home_plen, prim_addr) +struct in6_addr *home_prefix; /* Prefix for home address for MN */ +u_int8_t home_plen; /* Prefix length for home address */ +struct in6_addr *prim_addr; /* Primary Care-of Adress for MN */ +{ + struct ip6_opt_binding_update *bu_opt; /* BU option */ + struct mip6_esm *esp; /* Home address entry */ + struct mip6_bul *bulp; /* Entry in the BU list */ + u_int32_t lifetime; /* Lifetime used in BU */ + u_int8_t bu_flags; /* Flags for BU */ + + /* Find event-state machine and update it */ + esp = mip6_esm_find(home_prefix, home_plen); + if (esp == NULL) { + log(LOG_ERR, + "%s: No event-state machine found\n", __FUNCTION__); + return -1; + } + + esp->state = MIP6_STATE_NOTREG; + esp->coa = *prim_addr; + + /* There are three different ways of sending the packet. + 1. HA address unspecified --> Dynamic HA Address Discovery + 2. Home Address unspecified --> Send tunneled RS to HA + 3. Otherwise --> Send BU to Home agent + */ + if (IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) { + /* Perform Dynamic Home Agent Address Discovery */ + if (mip6_send_hadiscov(esp)) return -1; + return 0; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)) { + /* Send tunneled Router Solicitation to home agent */ + mip6_send_rs(esp, 1); + return 0; + } + + /* Make sure that the lifetime is correct + - Less or equal to lifetime for home address + - Less or equal to lifetime for coa + */ + lifetime = mip6_prefix_lifetime(&esp->home_addr, esp->prefixlen); + lifetime = min(lifetime, mip6_prefix_lifetime(&esp->coa, + esp->prefixlen)); +#ifdef MIP6_DEBUG + lifetime = min(lifetime, MIP6_BU_LIFETIME); +#endif + + /* Create or Update BUL entry and send BU to home agent */ + bu_flags = 0; + bu_flags |= IP6_BUF_HOME; + bu_flags |= IP6_BUF_ACK; + + bulp = mip6_bul_find(NULL, &esp->home_addr); + if (bulp == NULL) { + bu_flags |= IP6_BUF_DAD; + bulp = mip6_bul_create(&esp->ha_hn, &esp->home_addr, + &esp->coa, lifetime, bu_flags); + if (bulp == NULL) return -1; + } + + bulp->peer_home = esp->ha_hn; + bulp->local_coa = esp->coa; + bulp->lifetime = lifetime; + bulp->refresh = lifetime; + bulp->sent_lifetime = lifetime; + + if (ip6_forwarding) bu_flags |= IP6_BUF_ROUTER; + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, lifetime); + if (bu_opt == NULL) return -1; + + /* Send a BU registration to the Home Agent. */ + if (mip6_send_bu(bulp, bu_opt, NULL)) { + free(bu_opt, M_TEMP); + return -1; + } + + free(bu_opt, M_TEMP); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_move_fn2fn + * Description: Called from the move detection algorithm when a mobile node + * moves from one foreign network to another foreign network, + * see 10.6. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_move_fn2fn(home_prefix, home_plen, prim_addr) +struct in6_addr *home_prefix; /* Prefix for home address for MN */ +u_int8_t home_plen; /* Prefix length for home address */ +struct in6_addr *prim_addr; /* Primary Care-of Adress for MN */ +{ + struct ip6_opt_binding_update *bu_opt; /* BU option */ + struct mip6_esm *esp; /* Home address entry */ + struct mip6_bul *bulp; /* Entry in the BU list */ + struct in6_addr old_coa; + u_int32_t lifetime; /* Lifetime used in BU */ + u_int8_t bu_flags; /* Flags for BU */ + + /* Find event-state machine and update it */ + esp = mip6_esm_find(home_prefix, home_plen); + if (esp == NULL) { + log(LOG_ERR, + "%s: No event-state machine found\n", __FUNCTION__); + return -1; + } + + esp->state = MIP6_STATE_REGNEWCOA; + old_coa = esp->coa; + esp->coa = *prim_addr; + + /* There are three different ways of sending the packet. + 1. HA address unspecified --> Dynamic HA Address Discovery + 2. Home Address unspecified --> Send tunneled RS to HA + 3. Otherwise --> Send BU to Home agent + */ + if (IN6_IS_ADDR_UNSPECIFIED(&esp->ha_hn)) { + /* Perform Dynamic Home Agent Address Discovery */ + if (mip6_send_hadiscov(esp)) return -1; + return 0; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)) { + /* Send tunneled Router Solicitation to home agent */ + mip6_send_rs(esp, 1); + return 0; + } + + /* Make sure that the lifetime is correct + - Less or equal to lifetime for home address + - Less or equal to lifetime for coa + */ + lifetime = mip6_prefix_lifetime(&esp->home_addr, esp->prefixlen); + lifetime = min(lifetime, mip6_prefix_lifetime(&esp->coa, + esp->prefixlen)); +#ifdef MIP6_DEBUG + lifetime = min(lifetime, MIP6_BU_LIFETIME); +#endif + + /* Create or Update BUL entry and send BU to home agent */ + bu_flags = 0; + bu_flags |= IP6_BUF_HOME; + bu_flags |= IP6_BUF_ACK; + + bulp = mip6_bul_find(NULL, &esp->home_addr); + if (bulp == NULL) { + bulp = mip6_bul_create(&esp->ha_hn, + &esp->home_addr, + prim_addr, + lifetime, + bu_flags); + if (bulp == NULL) return -1; + bu_flags |= IP6_BUF_DAD; + } + + bulp->peer_home = esp->ha_hn; + bulp->local_coa = esp->coa; + bulp->lifetime = lifetime; + bulp->refresh = lifetime; + bulp->sent_lifetime = lifetime; + + if (ip6_forwarding) bu_flags |= IP6_BUF_ROUTER; + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, lifetime); + if (bu_opt == NULL) return -1; + + /* Send a BU registration to the Home Agent. */ + if (mip6_send_bu(bulp, bu_opt, NULL)) return -1; + + /* Update home agent on previous foreign network. */ + mip6_update_fn(home_prefix, home_plen, prim_addr, &old_coa); + + /* Do not remove bu_opt. Needed for retransmission of BU option */ + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_update_fn + * Description: When a mobile node connects to a new link it sends a Binding + * Update to its previous link to establish forwarding of packets + * from a previous care-of address to the new care-of address, + * see 10.9. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_update_fn(home_prefix, home_plen, prim_addr, old_coa) +struct in6_addr *home_prefix; /* Prefix for home address for MN */ +u_int8_t home_plen; /* Prefix length for home address */ +struct in6_addr *prim_addr; /* Primary Care-of Adress for MN */ +struct in6_addr *old_coa; /* Previous care-of address */ +{ + struct ip6_opt_binding_update *bu_opt; /* BU option */ + struct mip6_prefix *pfxp; /* MIP6 prefix entry */ + struct mip6_halst *oldha; /* Old home agent */ + struct mip6_esm *esp; /* Home address entry */ + struct mip6_bul *bulp; /* Entry in the BU list */ + struct ifaddr *if_addr; /* Interface address */ + struct in6_addr *oldha_addr; /* Address for old HA */ + struct mip6_addrlst *addrp; + struct sockaddr_in6 sin6; + u_int32_t lifetime; /* Lifetime used in BU */ + u_int8_t bu_flags; /* Flags for BU */ + + if (IN6_IS_ADDR_UNSPECIFIED(old_coa)) return 0; + + /* Find event-state machine for home address */ + esp = mip6_esm_find(home_prefix, home_plen); + if (esp == NULL) { + log(LOG_ERR, + "%s: No event-state machine found\n", __FUNCTION__); + return -1; + } + + /* Find interface where the previous coa is stored */ + bzero(&sin6, sizeof(struct sockaddr_in6)); + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = *old_coa; + + if_addr = ifa_ifwithaddr((struct sockaddr *)&sin6); + if (if_addr == NULL) return -1; + + /* Find a home agent on previous foreign network */ + pfxp = mip6_prefix_find(if_addr->ifa_ifp, old_coa, home_plen); + if (pfxp == NULL) return -1; + + oldha = NULL; + oldha_addr = NULL; + for (addrp = pfxp->addrlst; addrp; addrp = addrp->next) { + if (addrp->hap == NULL) continue; + oldha_addr = &addrp->ip6_addr; + oldha = addrp->hap; + } + + if ((oldha_addr == NULL) || (oldha == NULL)) return -1; + + /* Make sure that the lifetime is correct + 1. Less or equal to lifetime for home address (here old_coa) + 2. Less or equal to lifetime for coa (here esp->coa) + 3. Less or equal to lifetime for home agent. + */ + lifetime = mip6_prefix_lifetime(old_coa, esp->prefixlen); + lifetime = min(lifetime, mip6_prefix_lifetime(&esp->coa, + esp->prefixlen)); + lifetime = min(lifetime, oldha->lifetime); +#ifdef MIP6_DEBUG + lifetime = min(lifetime, MIP6_BU_LIFETIME_HAFN); +#endif + + /* Create or Update BUL entry and send BU to home agent */ + bu_flags = 0; + bu_flags |= IP6_BUF_HOME; + + bulp = mip6_bul_find(NULL, old_coa); + if (bulp == NULL) { + bulp = mip6_bul_create(oldha_addr, old_coa, &esp->coa, + lifetime, bu_flags); + if (bulp == NULL) return -1; + } + + bulp->peer_home = *oldha_addr; + bulp->local_coa = esp->coa; + bulp->lifetime = lifetime; + bulp->refresh = lifetime; + bulp->sent_lifetime = lifetime; + + if (ip6_forwarding) bu_flags |= IP6_BUF_ROUTER; + bu_opt = mip6_create_bu(0, bu_flags, lifetime); + if (bu_opt == NULL) return -1; + + /* Create an event-state machine to be used when the home address + option is created for outgoing packets. The event-state machine + must be removed when the BUL entry is removed. */ + esp = mip6_esm_create(if_addr->ifa_ifp, oldha_addr, &esp->coa, + prim_addr, prim_addr, home_plen, + MIP6_STATE_NOTREG, TEMPORARY, lifetime); + if (esp == NULL) { + free(bu_opt, M_TEMP); + return -1; + } + + /* Create a tunnel used by the MN to receive + incoming tunneled packets. */ + if (mip6_tunnel(prim_addr, oldha_addr, MIP6_TUNNEL_ADD, + MIP6_NODE_MN, (void *)esp)) { + free(bu_opt, M_TEMP); + return -1; + } + + /* Send a BU registration to the Home Agent. */ + if (mip6_send_bu(bulp, bu_opt, NULL)) return -1; + + free(bu_opt, M_TEMP); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_send_hadiscov + * Description: When the home agents unicast address is unknown an ICMP6 + * "Dynamic Home Agent Address Discovery", packet must be sent + * from the mobile node to the home agents anycast address, see + * 10.7. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_send_hadiscov(esp) +struct mip6_esm *esp; /* Event-state machine for MN home address */ +{ + struct ha_discov_req *hadiscov; + struct in6_addr *ha_anyaddr; + struct ip6_hdr *ip6; + struct mbuf *mo; + u_int32_t icmp6len, off; + int res, size; + + /* Build home agents anycast address */ + ha_anyaddr = mip6_in6addr_any(&esp->home_pref, esp->prefixlen); + if (ha_anyaddr == NULL) return -1; + esp->ha_hn = *ha_anyaddr; + + /* Create the mbuf and copy the ICMP6 message to the mbuf */ + icmp6len = sizeof(struct ha_discov_req); + mo = mip6_create_ip6hdr(&esp->coa, ha_anyaddr, + IPPROTO_ICMPV6, icmp6len); + if (mo == NULL) { + log(LOG_ERR, "%s: mbuf allocation failure\n", __FUNCTION__); + return -1; + } + + /* Allocate memory to hold HA Discovery information. */ + if (esp->hadiscov) { + if (esp->hadiscov->hal) + free(esp->hadiscov->hal, M_TEMP); + free(esp->hadiscov, M_TEMP); + } + size = sizeof(struct mip6_hadiscov); + esp->hadiscov = (struct mip6_hadiscov *)malloc(size, M_TEMP, M_NOWAIT); + bzero((caddr_t)esp->hadiscov, size); + mip6_hadiscov_id += 1; + esp->hadiscov->sent_hadiscov_id = mip6_hadiscov_id; + + /* Build the ICMP6 message. */ + ip6 = mtod(mo, struct ip6_hdr *); + hadiscov = (struct ha_discov_req *)(ip6 + 1); + bzero((caddr_t)hadiscov, sizeof(struct ha_discov_req)); + hadiscov->discov_req_type = ICMP6_HADISCOV_REQUEST; + hadiscov->discov_req_code = 0; + + hadiscov->discov_req_id = htons(esp->hadiscov->sent_hadiscov_id); + hadiscov->ha_dreq_home = esp->home_pref; + + /* Calculate checksum for ICMP6 packet */ + off = sizeof(struct ip6_hdr); + hadiscov->discov_req_cksum = in6_cksum(mo, IPPROTO_ICMPV6, + off, icmp6len); + + /* Send the ICMP6 packet to the home agent */ + res = ip6_output(mo, NULL, NULL, 0, NULL, NULL); + if (res) { + log(LOG_ERR, + "%s: ip6_output function failed to send ICMP6 " + "Dynamic Home Agent Address Discovery request message, " + "error = %d\n", + __FUNCTION__, res); + return -1; + } + + /* mo is removed by ip6_output() */ + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_icmp6_hadiscov_reply + * Description: Processing of an incoming ICMP6 message replying to a + * previously sent "Dynamic Home Agent Address Discovery", + * see 10.7. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_icmp6_hadiscov_reply(m, off, icmp6len) +struct mbuf *m; /* Ptr to beginning of mbuf */ +int off; /* Offset from start of mbuf to ICMP6 message */ +int icmp6len; /* Total ICMP6 payload length */ +{ + struct ip6_opt_binding_update *bu_opt; /* BU option */ + struct ha_discov_rep *hadiscov; + struct mip6_esm *esp; /* Home address entry */ + struct mip6_bul *bulp; /* Entry in the BU list */ + struct ip6_hdr *ip6; /* IPv6 header */ + struct in6_addr *addr; + u_int8_t *ptr; + u_int32_t lifetime; /* Lifetime used in BU */ + u_int8_t bu_flags; /* Flags for BU */ + u_int16_t sum, id; + u_int16_t size, offset; + int found; + + ip6 = mtod(m, struct ip6_hdr *); + hadiscov = (struct ha_discov_rep *)(ip6 + 1); + + /* Find event-state machine */ + esp = mip6_esm_find(&ip6->ip6_dst, 0); + if (esp == NULL) { + log(LOG_ERR, + "%s: No event-state machine found\n", __FUNCTION__); + return -1; + } + + /* Validation of ICMP6 HA Discovery message */ + if (hadiscov->discov_rep_type != ICMP6_HADISCOV_REPLY) { + log(LOG_ERR, + "%s: Wrong reply type (%d) for ICMP6 HA Discovery\n", + __FUNCTION__, hadiscov->discov_rep_type); + return -1; + } + + if (hadiscov->discov_rep_code != 0) { + log(LOG_ERR, + "%s: Wrong reply code (%d) for ICMP6 HA Discovery\n", + __FUNCTION__, hadiscov->discov_rep_code); + return -1; + } + + if (icmp6len < sizeof(struct ha_discov_rep)) { + log(LOG_ERR, + "%s: Size (%d) to short for ICMP6 HA Discovery reply\n", + __FUNCTION__, icmp6len); + return -1; + } + + if (icmp6len % 16) { + log(LOG_ERR, + "%s: Size (%d) must be a multiple of 16\n", + __FUNCTION__, icmp6len); + return -1; + } + + off = sizeof(struct ip6_hdr); + if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { + log(LOG_ERR, + "%s: ICMP6 checksum error(%d|%x) %s\n", + __FUNCTION__, hadiscov->discov_rep_type, sum, + ip6_sprintf(&ip6->ip6_src)); + icmp6stat.icp6s_checksum++; + return -1; + } + + if (esp->hadiscov == NULL) { + log(LOG_ERR, + "%s: MN did not expect ICMP6 HA Discovery reply\n", + __FUNCTION__); + return -1; + } + + id = ntohs(hadiscov->discov_rep_id); + if (id != esp->hadiscov->sent_hadiscov_id) { + log(LOG_ERR, + "%s: Wrong id (%d) for ICMP6 HA Discovery reply\n", + __FUNCTION__, id); + return -1; + } + + /* Allocate memory for holding the HA addresses */ + if (esp->hadiscov->hal != NULL) + free(esp->hadiscov->hal, M_TEMP); + + size = sizeof(struct mip6_buffer); + esp->hadiscov->hal = (struct mip6_buffer *)malloc(size, M_TEMP, + M_NOWAIT); + if (esp->hadiscov->hal == NULL) return -1; + bzero((caddr_t)esp->hadiscov->hal, size); + + /* Save the received home address(es) */ + if (icmp6len == sizeof(struct ha_discov_rep)) { + /* Use the source address of the packet */ + size = sizeof(struct in6_addr); + bcopy((caddr_t)&ip6->ip6_src, esp->hadiscov->hal->buf, size); + esp->hadiscov->hal->off = size; + } else { + /* See if the packet source address is included in the + list of home agent addresses. */ + found = 0; + offset = sizeof(struct ha_discov_rep); + while (offset < icmp6len) { + ptr = (u_int8_t *)hadiscov + offset; + addr = (struct in6_addr *)ptr; + if (IN6_ARE_ADDR_EQUAL(addr, &ip6->ip6_src)) { + found = 1; + break; + } + offset += sizeof(struct in6_addr); + } + + if (!found) { + /* Add the source address of the packet */ + size = sizeof(struct in6_addr); + bcopy((caddr_t)&ip6->ip6_src, + esp->hadiscov->hal->buf, size); + esp->hadiscov->hal->off = size; + } + + /* Copy received addresses to the buffer */ + offset = sizeof(struct ha_discov_rep); + bcopy((caddr_t)hadiscov + offset, + esp->hadiscov->hal->buf + esp->hadiscov->hal->off, + icmp6len - offset); + + esp->hadiscov->hal->off += icmp6len - offset; + } + esp->hadiscov->pos = 0; + + /* If no home address available, send Router Solicitation */ + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)) { + mip6_send_rs(esp, 1); + return 0; + } + + /* Create a BUL entry and a BU option. */ + bulp = mip6_bul_find(NULL, &esp->home_addr); + 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 -1; + } + + bu_flags = 0; + bu_flags |= IP6_BUF_HOME; + bu_flags |= IP6_BUF_ACK; + bu_flags |= IP6_BUF_DAD; + if (ip6_forwarding) bu_flags |= IP6_BUF_ROUTER; + + lifetime = MIP6_BU_LIFETIME_HADISCOV; + esp->ha_hn = *(struct in6_addr *)(esp->hadiscov->hal->buf); + bulp = mip6_bul_create(&esp->ha_hn, &esp->home_addr, + &esp->coa, lifetime, bu_flags); + if (bulp == NULL) return -1; + + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, lifetime); + if (bu_opt == NULL) return -1; + + /* Send a BU registration to the Home Agent. */ + if (mip6_send_bu(bulp, bu_opt, NULL)) { + free(bu_opt, M_TEMP); + return -1; + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_send_rs + * Description: Sends a tunneled Router Solicitation to the home agent, see + * 10.16. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ + + + +/* + ****************************************************************************** + * Function: mip6_prefix_lifetime + * Description: Decide the remaining valid lifetime for a home address. Search + * the prefix list for a match and use this lifetime value. + * Note: This function is used by the MN since no test of the on-link + * flag is done. + * Ret value: Lifetime + ****************************************************************************** + */ +u_int32_t +mip6_prefix_lifetime(addr, prefixlen) +struct in6_addr *addr; /* IPv6 address to check */ +u_int8_t prefixlen; /* Prefix length for address */ +{ + struct nd_prefix *pr; /* Entries in the prexix list */ + u_int32_t min_time; /* Minimum life time */ + + min_time = 0xffffffff; + for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + if (pr->ndpr_plen != prefixlen) continue; + +#if 0 + if (IN6_ARE_ADDRESS_EQUAL(x,y)); +#endif + + if (in6_are_prefix_equal(addr, &pr->ndpr_prefix.sin6_addr, + pr->ndpr_plen)) { + + return pr->ndpr_vltime; + } + } + return min_time; +} + + + +/* + ****************************************************************************** + * Function: mip6_in6addr_any + * Description: Build a mobile IPv6 Home Agents anycast address from a prefix + * and the prefix length. The interface id is according to + * RFC2526. + * Ret value: Pointer to HA anycast address or NULL. + ****************************************************************************** + */ +struct in6_addr * +mip6_in6addr_any(prefix, prefixlen) +const struct in6_addr *prefix; /* Prefix part of the address */ +int prefixlen; /* Prefix length (bits) */ +{ + struct in6_addr *new_addr; /* New address built in this function */ + struct in6_addr id; /* ID part of address */ + + if (prefix->s6_addr8[0] == 0xff) return NULL; + + if (((prefix->s6_addr8[0] & 0xe0) != 0) && (prefixlen != 64)) + return NULL; + + if (((prefix->s6_addr8[0] & 0xe0) != 0) && (prefixlen == 64)) + id = in6addr_aha_64; + else + id = in6addr_aha_nn; + + new_addr = mip6_in6addr(prefix, &id, prefixlen); + return new_addr; +} + + + +/* + ############################################################################## + # + # LIST FUNCTIONS + # The Mobile Node maintains a Bindig Update List (BUL) for each node to which + # a BU has been sent. + # Besides from this a list of event-state machines, one for each home address + # is handled by the Mobile Node and the Correspondent Node since it may + # become mobile at any time. + # An output queue for piggybacking of options (BU, BA, BR) on the first + # outgoing packet sent to the node is also maintained. If the option has not + # been sent with a packet within MIP6_OUTQ_LIFETIME it will be sent in a + # separate packet. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_bul_find + * Description: Find a Binding Update List entry for which a matching can be + * found for both the local and peer home address. + * If variable peer_home is NULL an entry for home registration + * will be searched for. + * Ret value: Pointer to Binding Update List entry or NULL + ****************************************************************************** + */ +struct mip6_bul * +mip6_bul_find(peer_home, local_home) +struct in6_addr *peer_home; /* Destination Address for Binding Update */ +struct in6_addr *local_home; /* Home Address for MN or previous COA */ +{ + struct mip6_bul *bulp; /* Entry in the Binding Update list */ + + if (peer_home == NULL) { + for (bulp = mip6_bulq; bulp; bulp = bulp->next) { + if (IN6_ARE_ADDR_EQUAL(local_home,&bulp->local_home) && + (bulp->flags & IP6_BUF_HOME)) + break; + } + } else { + for (bulp = mip6_bulq; bulp; bulp = bulp->next) { + if (IN6_ARE_ADDR_EQUAL(peer_home, &bulp->peer_home) && + IN6_ARE_ADDR_EQUAL(local_home, &bulp->local_home)) + break; + } + if (bulp != NULL) return bulp; + + /* It might be that the dest address for the BU was the Home + Agent anycast address and in that case we try to find it. */ + for (bulp = mip6_bulq; bulp; bulp = bulp->next) { + if ((bulp->peer_home.s6_addr8[15] & 0x7f) == + MIP6_ADDR_ANYCAST_HA && + IN6_ARE_ADDR_EQUAL(local_home, &bulp->local_home)){ + break; + } + } + } + return bulp; +} + + + + +/* + ****************************************************************************** + * Function: mip6_bul_create + * Description: Create a new Binding Update List entry and insert it as the + * first entry in the list. + * Ret value: Pointer to Binding Update List entry or NULL. + * Note: If the BUL timeout function has not been started it is started. + * The BUL timeout function will be called once every second until + * there are no more entries in the BUL. + ****************************************************************************** + */ +struct mip6_bul * +mip6_bul_create(peer_home, local_home, local_coa, lifetime, flags) +struct in6_addr *peer_home; /* Dst address for Binding Update */ +struct in6_addr *local_home; /* Home Address for MN or previous COA */ +struct in6_addr *local_coa; /* Primary COA for MN */ +u_int32_t lifetime; /* Lifetime for BU */ +u_int8_t flags; /* Flags for sent BU */ +{ + struct mip6_bul *bulp; /* New Binding Update list entry */ + int s; + + bulp = (struct mip6_bul *)malloc(sizeof(struct mip6_bul), + M_TEMP, M_NOWAIT); + if (bulp == NULL) return NULL; + bzero(bulp, sizeof(struct mip6_bul)); + + bulp->next = NULL; + bulp->peer_home = *peer_home; + bulp->local_home = *local_home; + bulp->local_coa = *local_coa; + bulp->sent_lifetime = lifetime; + bulp->lifetime = lifetime; + bulp->refresh = lifetime; + bulp->seqno = 0; + bulp->lasttime = 0; + bulp->send_flag = 1; + bulp->flags = flags; + + if (bulp->flags & IP6_BUF_ACK) { + bulp->bul_opt = NULL; + bulp->bul_subopt = NULL; + bulp->bul_timeout = 2; + bulp->bul_timeleft = 2; + } else { + bulp->bul_sent = 0; + bulp->bul_rate = MIP6_MAX_UPDATE_RATE; + } + + /* Insert the entry as the first entry in the BUL. */ + s = splnet(); + if (mip6_bulq == NULL) { + mip6_bulq = bulp; + } else { + bulp->next = mip6_bulq; + mip6_bulq = bulp; + } + splx(s); + +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_bul_ch, hz, mip6_timer_bul, NULL); +#else + timeout(mip6_timer_bul, (void *)0, hz); +#endif + +#ifdef MIP6_DEBUG + mip6_debug("\nBinding Update List Entry created (0x%x)\n", bulp); + mip6_debug("Dst Address: %s\n", ip6_sprintf(&bulp->peer_home)); + mip6_debug("Home Address: %s\n", ip6_sprintf(&bulp->local_home)); + mip6_debug("Care-of Address: %s\n", ip6_sprintf(&bulp->local_coa)); + mip6_debug("Lifetime: "); + mip6_print_sec(bulp->lifetime); + mip6_debug("Refresh time: "); + mip6_print_sec(bulp->refresh); + mip6_debug("Seq no/Flags: %u / ", bulp->seqno); + if (bulp->flags & IP6_BUF_HOME) mip6_debug("H "); + if (bulp->flags & IP6_BUF_ACK) mip6_debug("A "); + mip6_debug("\n"); +#endif + return bulp; +} + + + +/* + ****************************************************************************** + * Function: mip6_bul_delete + * Description: Delete the requested Binding Update list entry. + * Ret value: Ptr to next entry in list or NULL if last entry removed. + ****************************************************************************** + */ +struct mip6_bul * +mip6_bul_delete(bul_remove) +struct mip6_bul *bul_remove; /* BUL entry to be deleted */ +{ + struct mip6_bul *bulp; /* Current entry in the BU list */ + struct mip6_bul *bulp_prev; /* Previous entry in the BU list */ + struct mip6_bul *bulp_next; /* Next entry in the BU list */ + int s; + + /* Find the requested entry in the BUL. */ + 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; +#ifdef MIP6_DEBUG + mip6_debug("\nBU List Entry deleted (0x%x)\n", bulp); +#endif + mip6_bul_clear_state(bulp); + free(bulp, M_TEMP); + + /* Remove the timer if the BUL queue is empty */ + if (mip6_bulq == NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_bul_ch); +#else + untimeout(mip6_timer_bul, (void *)NULL); +#endif + } + break; + } + bulp_prev = bulp; + } + splx(s); + return bulp_next; +} + + + +/* + ****************************************************************************** + * Function: mip6_bul_clear_state + * Description: Removes the current content of the bulp->state variable. + * Ret value: Void + ****************************************************************************** + */ +void +mip6_bul_clear_state(bulp) +struct mip6_bul *bulp; +{ + if (bulp == NULL) return; + + if (bulp->flags & IP6_BUF_ACK) { + if (bulp->bul_opt) free(bulp->bul_opt, M_TEMP); + if (bulp->bul_subopt) free(bulp->bul_subopt, M_TEMP); + bulp->flags &= ~IP6_BUF_ACK; + bulp->bul_opt = NULL; + bulp->bul_subopt = NULL; + bulp->bul_timeout = 2; + bulp->bul_timeleft = 2; + } else { + bulp->bul_sent = 0; + bulp->bul_rate = MIP6_MAX_UPDATE_RATE; + } + return; +} + + + +/* + ****************************************************************************** + * Function: mip6_esm_find + * Description: Find an event-state machine. If the home address is known, set + * the prefix variable equal to the home address and prefixlen + * equals to 0. Otherwise, if the home address is not known, set + * the prefixlen to the length of the prefix and the prefix + * variable equal to the prefix. + * Ret value: Pointer to event-state machine entry or NULL + ****************************************************************************** + */ +struct mip6_esm * +mip6_esm_find(prefix, prefixlen) +struct in6_addr *prefix; /* Mobile nodes home prefix */ +u_int8_t prefixlen; +{ + struct mip6_esm *esp; + + for (esp = mip6_esmq; esp; esp = esp->next) { + if (prefixlen == 0) { + if (IN6_ARE_ADDR_EQUAL(prefix, &esp->home_addr)) + return esp; + else + continue; + } + + if (esp->prefixlen != prefixlen) continue; + if (in6_are_prefix_equal(&esp->home_pref, prefix, prefixlen)) + return esp; + } + return NULL; +} + + + +/* + ****************************************************************************** + * Function: mip6_esm_create + * Description: Create an event-state machine entry and add it first to the + * list. If type is PERMANENT the lifetime will be set to 0xFFFF, + * otherwise it will be set to the specified lifetime. If type is + * TEMPORARY the timer will be started if not already started. + * Ret value: Pointer to an event-state machine or NULL. + ****************************************************************************** + */ +struct mip6_esm * +mip6_esm_create(ifp, ha_hn, coa, home_addr, home_pref, prefixlen, state, + type, lifetime) +struct ifnet *ifp; /* Physical i/f used by this home address */ +struct in6_addr *ha_hn; /* Home agent address (home network) */ +struct in6_addr *coa; /* Current care-of address */ +struct in6_addr *home_addr; /* Home address */ +struct in6_addr *home_pref; /* Home prefix */ +u_int8_t prefixlen; /* Prefix length for the home address */ +int state; /* State of the home address */ +enum esm_type type; /* Permanent or Temporary esm */ +u_int16_t lifetime; /* Lifetime for event-state machine */ +{ + 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->home_pref = *home_pref; + esp->prefixlen = prefixlen; + esp->ha_hn = *ha_hn; + esp->coa = *coa; + esp->hadiscov = NULL; + + if (type == PERMANENT) { + esp->lifetime = 0xFFFF; + start_timer = 0; + } else { + esp->lifetime = lifetime; + start_timer = 1; + } + + /* If no TEMPORARY already exist and the new is TEMPORARY, start + the timer. */ + for (esp_tmp = mip6_esmq; esp_tmp; esp_tmp = esp_tmp->next) { + if (esp_tmp->type == TEMPORARY) + start_timer = 0; + } + + /* Insert entry as the first entry in the event-state machine list */ + s = splnet(); + if (mip6_esmq == NULL) + mip6_esmq = esp; + else { + esp->next = mip6_esmq; + mip6_esmq = esp; + } + splx(s); + + if (start_timer) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_esm_ch, hz, mip6_timer_esm, NULL); +#else + timeout(mip6_timer_esm, (void *)0, hz); +#endif + } + return esp; +} + + + +/* + ****************************************************************************** + * Function: mip6_esm_delete + * Description: Delete the requested event-state machine. + * Ret value: Ptr to next entry in list or NULL if last entry removed. + ****************************************************************************** + */ +struct mip6_esm * +mip6_esm_delete(esm_remove) +struct mip6_esm *esm_remove; /* Event-state machine to be deleted */ +{ + struct mip6_esm *esp; /* Current entry in event-state list */ + struct mip6_esm *esp_prev; /* Previous entry in event-state list */ + struct mip6_esm *esp_next; /* Next entry in the event-state list */ + int s; + + /* Find the requested entry in the event-state list. */ + 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->hadiscov) { + if (esp->hadiscov->hal) + free(esp->hadiscov->hal, M_TEMP); + free(esp->hadiscov, M_TEMP); + } + +#ifdef MIP6_DEBUG + mip6_debug("\nEvent-state machine deleted (0x%x)\n", + esp); +#endif + free(esp, M_TEMP); + + /* Remove the timer if the ESM queue is empty */ + if (mip6_esmq == NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_stop(&mip6_timer_esm_ch); +#else + untimeout(mip6_timer_esm, (void *)NULL); +#endif + } + break; + } + esp_prev = esp; + } + splx(s); + return esp_next; +} + + + +/* + ############################################################################## + # + # TIMER FUNCTIONS + # These functions are called at regular basis. They operate on the lists, e.g. + # reducing timer counters and removing entries from the list if needed. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_timer_bul + * Description: Search the Binding Update list for entries for which the life- + * time or refresh time has expired. + * If there are more entries left in the output queue, call this + * fuction again once every second until the queue is empty. + * Ret value: - + ****************************************************************************** + */ +void +mip6_timer_bul(arg) +void *arg; /* Not used */ +{ + struct mip6_bul *bulp; /* Current BUL element */ + struct mip6_bul *new_bulp; /* New BUL entry */ + struct mip6_esm *esp; /* Home address entry */ + struct ip6_opt_binding_update *bu_opt; /* BU option to be sent */ + struct in6_addr *dst_addr; /* Dst address for BU */ + struct mip6_buffer *subbuf; /* Buffer BU sub-options */ + u_int32_t ltime; + u_int8_t bu_flags; + int s; + + /* Go through the entire BUL and check if any BU have to be sent. */ + esp = NULL; + subbuf = NULL; + s = splnet(); + for (bulp = mip6_bulq; bulp;) { + /* Find the correct event-state machine */ + esp = mip6_esm_find(&bulp->local_home, 0); + if (esp == NULL) { + bulp = bulp->next; + continue; + } + + /* If infinity lifetime, don't decrement it. */ + if (bulp->lifetime == 0xffffffff) { + bulp = bulp->next; + continue; + } + + bulp->lifetime -= 1; + if (bulp->lifetime <= 0) { + /* If the BUL entry is associated with a none + permanent ESM or not a home registration it + MUST be deleted. */ + if ((esp->type != PERMANENT) || + !(bulp->flags & IP6_BUF_HOME)) { + bulp = mip6_bul_delete(bulp); + continue; + } + + /* This BUL entry is for a Home Agent. Create a new + BUL entry and remove the existing. */ + 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 Dynamic Home Agent Address Discovery, + pick the dst address from the esp->dad list + and set index. */ +#if 0 + if (esp->hadiscov && esp->hadiscov->hal) { + /* Set position to next entry to be used + in the list. */ + max_pos = esp->hadiscov->hal->off; + if ((esp->hadiscov->hal->off / 16) == 1) + esp->hadiscov->pos = 0; + else + esp->hadiscov->pos += 16; + + pos += esp->hadiscov->pos; + if (esp->hadiscov->hal->off == pos + + dst_addr = esp->hadiscov->hal->buf + pos + 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; + ltime = MIP6_BU_LIFETIME_DHAAD; + } else +#endif + { + dst_addr = &esp->ha_hn; + ltime = mip6_prefix_lifetime(&esp->home_addr, + esp->prefixlen); + } + + /* Send BU to the decided destination */ + bu_flags = 0; + bu_flags |= IP6_BUF_ACK; + bu_flags |= IP6_BUF_HOME; + bu_flags |= IP6_BUF_DAD; + if (ip6_forwarding) bu_flags |= IP6_BUF_ROUTER; + + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, + ltime); + if (bu_opt == NULL) break; + + new_bulp = mip6_bul_create(dst_addr, &esp->home_addr, + &bulp->local_coa, + ltime, bu_flags); + if (new_bulp == NULL) { + free(bu_opt, M_TEMP); + break; + } + + if (mip6_send_bu(new_bulp, bu_opt, NULL)) break; + + bulp = mip6_bul_delete(bulp); + continue; + } + + if (bulp->refresh > 0) + bulp->refresh -= 1; + + /* Skip the bul entry if its not allowed to send any further + BUs to the host. */ + if (bulp->send_flag == 0) { + bulp = bulp->next; + continue; + } + + /* Check if a BU has already been sent to the destination. */ + if (bulp->flags & IP6_BUF_ACK) { + if (mip6_bul_retransmit(bulp)) + break; + else + bulp = bulp->next; + continue; + } + + /* Refreshtime has expired and no BU has been sent to the HA + so far. Then we do it. */ + if (bulp->refresh <= 0) { + if (mip6_bul_refresh(bulp, esp)) + break; + else + bulp = bulp->next; + continue; + } + bulp = bulp->next; + } + + /* Set the timer if there are more entries in the list */ + if (mip6_bulq != NULL) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_bul_ch, hz, mip6_timer_bul, NULL); +#else + timeout(mip6_timer_bul, (void *)0, hz); +#endif + } + splx(s); +} + + + +/* + ****************************************************************************** + * Function: mip6_bul_retransmit + * Description: This function is called by mip6_timer_bul() function for + * retransmission of Binding Updates that have not been + * acknowledged yet + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_bul_retransmit(bulp) +struct mip6_bul *bulp; +{ + /* Check if a BU has already been sent to the destination. */ + if (!(bulp->flags & IP6_BUF_ACK)) + return 0; + + bulp->bul_timeleft -= 1; + if (bulp->bul_timeleft == 0) { + if (bulp->flags & IP6_BUF_HOME) { + /* This is a BUL entry for the HA */ + if (mip6_send_bu(bulp, NULL, NULL)) return -1; + + if (bulp->bul_timeout < MIP6_MAX_BINDACK_TIMEOUT) + bulp->bul_timeout = 2 * bulp->bul_timeout; + else + bulp->bul_timeout = MIP6_MAX_BINDACK_TIMEOUT; + + bulp->bul_timeleft = bulp->bul_timeout; + } else { + /* This is a BUL entry for a Correspondent Node */ + if (bulp->bul_timeout >= MIP6_MAX_BINDACK_TIMEOUT) { + /* Do NOT continue to retransmit the BU */ + mip6_bul_clear_state(bulp); + } else { + if (mip6_send_bu(bulp, NULL, NULL)) return -1; + + bulp->bul_timeout = 2 * bulp->bul_timeout; + bulp->bul_timeleft = bulp->bul_timeout; + } + } + } + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_bul_refresh + * Description: This function is called by mip6_timer_bul() function for + * refresh of an existing binding before it times out. + * Ret value: 0 Everything is OK. + * -1 Error code used when something went wrong. + ****************************************************************************** + */ +int +mip6_bul_refresh(bulp, esp) +struct mip6_bul *bulp; +struct mip6_esm *esp; +{ + struct ip6_opt_binding_update *bu_opt; /* BU option to be sent */ + struct mip6_subopt_altcoa altcoa; + struct mip6_buffer *subbuf; + u_int32_t lifetime; + u_int8_t bu_flags; + int size; + + if (bulp->refresh > 0) return 0; + + /* Store sub-option for BU option. */ + size = sizeof(struct mip6_buffer); + subbuf = (struct mip6_buffer *)malloc(size, M_TEMP, M_NOWAIT); + if (subbuf == NULL) return -1; + bzero((caddr_t)subbuf, sizeof(struct mip6_buffer)); + + altcoa.type = IP6SUBOPT_ALTCOA; + altcoa.len = IP6OPT_COALEN; + size = sizeof(struct in6_addr); + bcopy((caddr_t)&bulp->local_coa, altcoa.coa, size); + mip6_add_subopt2buf((u_int8_t *)&altcoa, subbuf); + + lifetime = mip6_prefix_lifetime(&esp->home_addr, esp->prefixlen); + bu_flags = 0; + + if (bulp->flags & IP6_BUF_HOME) { + /* Since this is an entry for the Home Agent a new BU + is being sent for which we require the receiver to + respond with a BA. */ + bu_flags |= IP6_BUF_ACK; + bu_flags |= IP6_BUF_HOME; + if (ip6_forwarding) bu_flags |= IP6_BUF_ROUTER; + } + + bu_opt = mip6_create_bu(esp->prefixlen, bu_flags, lifetime); + if (bu_opt == NULL) { + free(subbuf, M_TEMP); + return -1; + } + + if (mip6_send_bu(bulp, bu_opt, subbuf)) { + free(bu_opt, M_TEMP); + free(subbuf, M_TEMP); + return -1; + } + + free(bu_opt, M_TEMP); + free(subbuf, M_TEMP); + return 0; +} + + + +/* + ****************************************************************************** + * Function: mip6_timer_esm + * Description: This function is called when an event-state machine has been + * created for sending a BU to the previous default router. The + * event-state machine entry is needed for the correct addition + * of the home address option for outgoing packets. + * When the life time for the BU expires the event-state machine + * is removed as well. + * Ret value: - + ****************************************************************************** + */ +void +mip6_timer_esm(arg) +void *arg; /* Not used */ +{ + struct mip6_esm *esp; /* Current event-state machine entry */ + int s, start_timer; + + /* Go through the entire list of event-state machines. */ + 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; + } + + /* Only start the timer if there is a TEMPORARY machine in the list. */ + start_timer = 0; + for (esp = mip6_esmq; esp; esp = esp->next) { + if (esp->type == TEMPORARY) { + start_timer = 1; + break; + } + } + + if (start_timer) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&mip6_timer_esm_ch, hz, mip6_timer_esm, NULL); +#else + timeout(mip6_timer_esm, (void *)0, hz); +#endif + } + splx(s); +} + + + +/* + ############################################################################## + # + # IOCTL FUNCTIONS + # These functions are called from mip6_ioctl. + # + ############################################################################## + */ + +/* + ****************************************************************************** + * Function: mip6_write_config_data_mn + * Description: This function is called to write certain config values for + * MIPv6. The data is written into the global config structure. + * Ret value: - + ****************************************************************************** + */ +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 i, retval = 0; + struct in6_addr any = in6addr_any; + struct in6_addr mask, pfx; + + 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 = mip6_hifp; +#ifdef MIP6_DEBUG + if (ifp != ifunit(input->if_name)) + mip6_debug("%s: warning - home addresses must be on " + "lo0. Using lo0, ignoring %s.\n", + __FUNCTION__, input->if_name); +#endif + if (ifp == NULL) + return EINVAL; + + in6_prefixlen2mask(&mask, input->prefix_len); + /* make prefix in the canonical form */ + pfx = input->ip6_addr; + for (i = 0; i < 4; i++) + pfx.s6_addr32[i] &= + mask.s6_addr32[i]; + + /* + * Home address is given, home prefix is derived from that. + * Home agent's address can be given or be unspecified. + */ + p = mip6_esm_create(ifp, &input->ha_addr, &any, + &input->ip6_addr, &pfx, + input->prefix_len, + MIP6_STATE_UNDEF, PERMANENT, 0xFFFF); + if (p == NULL) + return EINVAL; /*XXX*/ + + /* Set interface ID */ + bzero(&p->ifid, sizeof(p->ifid)); + p->ifid.s6_addr32[0] |= (p->home_addr.s6_addr32[0] & + ~mask.s6_addr32[0]); + p->ifid.s6_addr32[1] |= (p->home_addr.s6_addr32[1] & + ~mask.s6_addr32[1]); + p->ifid.s6_addr32[2] |= (p->home_addr.s6_addr32[2] & + ~mask.s6_addr32[2]); + p->ifid.s6_addr32[3] |= (p->home_addr.s6_addr32[3] & + ~mask.s6_addr32[3]); +#ifdef MIP6_DEBUG + mip6_debug("%s: will use this ifid: %s\n",__FUNCTION__, + ip6_sprintf(&p->ifid)); +#endif + break; + + case SIOCAHOMEPREF_MIP6: + input = (struct mip6_input_data *) arg; + ifp = mip6_hifp; + if (ifp == NULL) + return EINVAL; + + in6_prefixlen2mask(&mask, input->prefix_len); +#define prefix input->ip6_addr + /* make prefix in the canonical form */ + for (i = 0; i < 4; i++) + prefix.s6_addr32[i] &= + mask.s6_addr32[i]; + + /* + * Note: input->ha_addr should be empty. + */ + p = mip6_esm_create(ifp, &input->ha_addr, &any, &any, + &prefix, input->prefix_len, + MIP6_STATE_UNDEF, PERMANENT, 0xFFFF); + if (p == NULL) + return EINVAL; /*XXX*/ + + 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; + + case SIOCSEAGERMD_MIP6: + /* Note: value = 0, 1 or 2. */ + mip6_eager_md(((struct mip6_input_data *)arg)->value); + break; + } + return retval; +} + + + +/* + ****************************************************************************** + * Function: mip6_clear_config_data_mn + * Description: This function is called to clear internal lists handled by + * MIPv6. + * Ret value: - + ****************************************************************************** + */ +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; +} + + + +/* + ****************************************************************************** + * Function: mip6_enable_func_mn + * Description: This function is called to enable or disable certain functions + * in mip6. The data is written into the global config struct. + * Ret value: - + ****************************************************************************** + */ +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; + } + return retval; +} + + + +/* + ############################################################################## + # + # XXXXXXXXXXX + # These functions are functioning but some further is required. + # + ############################################################################## + */ +int +mip6_incl_br(struct mbuf *m) +{ + struct mbuf *n; + + if (MIP6_IS_MN_ACTIVE) { + n = ip6_findaux(m); + if (n && (mtod(n, struct ip6aux *)->ip6a_flags & + IP6A_BRUID) == IP6A_BRUID) return 1; + } + return 0; +} + + + +void +mip6_send_rs(struct mip6_esm *esp, + int tunneled) +{ + struct ifnet *ifp; + +/* + Called from: + - mip6_md_init_with_prefix() + - mip6_select_defrtr() ? + - mip6_timer_list() ? + - ... + + From an esp, there might be pending RSes to be sent, both local and + tunneled. + If timer says so, send RSes. + Also check against overall policy of max transmission (static variable). + + Local RS: + Create packet. Nothing special. Update timers. + + Tunneled RS: + Create packet. Take information for addresses from esp. Update timers. +*/ + +/* + * TODO: Rate limit this. + */ + + if (!tunneled) { + /* + * Find all useful outgoing interfaces. + */ +#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) + for (ifp = ifnet; ifp; ifp = ifp->if_next) +#else + for (ifp = TAILQ_FIRST(&ifnet); ifp; + ifp = TAILQ_NEXT(ifp, if_list)) +#endif + { + if (ifp->if_flags & IFF_LOOPBACK) + continue; + + if ((ifp->if_flags & IFF_UP) == 0) + continue; + +#ifdef MIP6_DEBUG + mip6_debug("%s: sending RS on %s\n", __FUNCTION__, + if_name(ifp)); +#endif + mip6_rs_output(ifp); + } + } + else { + /* Send tunneled RS */ + } +} + + + +/* + * Output a Router Solicitation Message. Caller specifies: + * - ifp for outgoing interface + * + * No rate limiting is done here. + * Based on RFC 2461 + */ +void +mip6_rs_output(ifp) + struct ifnet *ifp; +{ + struct mbuf *m; + struct ip6_hdr *ip6; + struct nd_router_solicit *nd_rs; + struct in6_ifaddr *ia6 = NULL; + struct ip6_moptions im6o; + int icmp6len; + int maxlen; +/* caddr_t mac; */ + struct ifnet *outif = NULL; + int error; + +/* TODO: add support for tunneled RSes. */ + + if (ifp == NULL) + return; + + /* estimate the size of message */ + maxlen = sizeof(*ip6) + sizeof(*nd_rs); + maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; + if (max_linkhdr + maxlen >= MCLBYTES) { +#ifdef MIP6_DEBUG + mip6_debug("%s: max_linkhdr + maxlen >= MCLBYTES " + "(%d + %d > %d)\n", __FUNCTION__, max_linkhdr, + maxlen, MCLBYTES); +#endif + return; + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) { +#ifdef MIP6_DEBUG + mip6_debug("%s: error - no mbuf\n", __FUNCTION__); +#endif + return; + } + m->m_pkthdr.rcvif = NULL; + + m->m_flags |= M_MCAST; + im6o.im6o_multicast_ifp = ifp; + im6o.im6o_multicast_hlim = 255; + im6o.im6o_multicast_loop = 0; + + icmp6len = sizeof(*nd_rs); + m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; + m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ + + /* fill router solicitation packet */ + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; + /* ip6->ip6_plen will be set later */ + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 255; + + ip6->ip6_dst = in6addr_linklocal_allrouters; + ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); + + ia6 = in6ifa_ifpforlinklocal(ifp, 0); + + if (ia6 == NULL) { +#ifdef MIP6_DEBUG + mip6_debug("%s: error - no ifa for source addr found.\n", + __FUNCTION__); + return; +#endif + } + ip6->ip6_src = ia6->ia_addr.sin6_addr; + + nd_rs = (struct nd_router_solicit *)(ip6 + 1); + nd_rs->nd_rs_type = ND_ROUTER_SOLICIT; + nd_rs->nd_rs_code = 0; + nd_rs->nd_rs_reserved = 0; + +#if 0 +/* Will we ever add source link-layer address option? /Mattias */ + /* + * Add source link-layer address option. + * + * spec implementation + * --- --- + * DAD packet MUST NOT do not add the option + * there's no link layer address: + * impossible do not add the option + * there's link layer address: + * Multicast NS MUST add one add the option + * Unicast NS SHOULD add one add the option + */ + if (!dad && (mac = nd6_ifptomac(ifp))) { + int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); + /* 8 byte alignments... */ + optlen = (optlen + 7) & ~7; + + m->m_pkthdr.len += optlen; + m->m_len += optlen; + icmp6len += optlen; + bzero((caddr_t)nd_opt, optlen); + nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; + nd_opt->nd_opt_len = optlen >> 3; + bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + } +#endif /* 0 */ + + ip6->ip6_plen = htons((u_short)icmp6len); + nd_rs->nd_rs_cksum = 0; + nd_rs->nd_rs_cksum + = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); + +#ifdef IPSEC + /* Don't lookup socket */ + (void)ipsec_setsocket(m, NULL); +#endif + error = ip6_output(m, NULL, NULL, 0, &im6o, &outif); + + if (error) { +#ifdef MIP6_DEBUG + mip6_debug("%s: ip6_output failed (errno = %d)\n", + __FUNCTION__, error); +#endif + return; + } + if (outif) { + icmp6_ifstat_inc(outif, ifs6_out_msg); + icmp6_ifstat_inc(outif, ifs6_out_routersolicit); + } + icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]++; +} + + + +/* + * Output a tunneled Router Solicitation Message. Caller specifies: + * - Outer source IP address + * - Outer and inner (identical) destination IP address + * + * Used for a Mobile Node to send tunneled Router Solicitation according + * to section 10.16 in draft-ietf-mobileip-ipv6-13.txt. + * + * Based on RFC 2461 and Mobile IPv6. + */ +int +mip6_tunneled_rs_output(src, dst) + struct in6_addr *src, *dst; +{ + struct mbuf *m; + struct ip6_hdr *ip6; + struct nd_router_solicit *nd_rs; +/* struct in6_ifaddr *ia = NULL; */ +/* struct ip6_moptions im6o; */ + int icmp6len; + int maxlen; +/* caddr_t mac; */ +/* struct ifnet *outif = NULL; */ + +/* TODO: add support for tunneled RSes. */ + + /* estimate the size of message */ + maxlen = 2 * sizeof(*ip6) + sizeof(*nd_rs); + maxlen += (sizeof(struct nd_opt_hdr) + 6 + 7) & ~7; + if (max_linkhdr + maxlen >= MCLBYTES) { +#ifdef DIAGNOSTIC + printf("%s: max_linkhdr + maxlen >= MCLBYTES " + "(%d + %d > %d)\n", __FUNCTION__, + max_linkhdr, maxlen, MCLBYTES); +#endif + return ENOMEM; + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m && max_linkhdr + maxlen >= MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m = NULL; + } + } + if (m == NULL) + return ENOBUFS; + m->m_pkthdr.rcvif = NULL; + +/* m->m_flags |= M_MCAST; + im6o.im6o_multicast_ifp = ifp; + im6o.im6o_multicast_hlim = 255; + im6o.im6o_multicast_loop = 0; +*/ + icmp6len = sizeof(*nd_rs); + m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; + m->m_data += max_linkhdr; /*or MH_ALIGN() equivalent?*/ + + /* fill router solicitation packet */ + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_flow = 0; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; + /* ip6->ip6_plen will be set later */ + ip6->ip6_nxt = IPPROTO_ICMPV6; + ip6->ip6_hlim = 255; + + ip6->ip6_dst = *dst; /* Inner dst and src */ + ip6->ip6_src = in6addr_any; + + nd_rs = (struct nd_router_solicit *)(ip6 + 1); + nd_rs->nd_rs_type = ND_ROUTER_SOLICIT; + nd_rs->nd_rs_code = 0; + nd_rs->nd_rs_reserved = 0; + +#if 0 +/* Will we ever add source link-layer address option? /Mattias */ + /* + * Add source link-layer address option. + * + * spec implementation + * --- --- + * DAD packet MUST NOT do not add the option + * there's no link layer address: + * impossible do not add the option + * there's link layer address: + * Multicast NS MUST add one add the option + * Unicast NS SHOULD add one add the option + */ + if (!dad && (mac = nd6_ifptomac(ifp))) { + int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); + /* 8 byte alignments... */ + optlen = (optlen + 7) & ~7; + + m->m_pkthdr.len += optlen; + m->m_len += optlen; + icmp6len += optlen; + bzero((caddr_t)nd_opt, optlen); + nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; + nd_opt->nd_opt_len = optlen >> 3; + bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + } +#endif /* 0 */ + + ip6->ip6_plen = htons((u_short)icmp6len); + nd_rs->nd_rs_cksum = 0; + nd_rs->nd_rs_cksum + = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); + +#ifdef IPSEC + /* Don't lookup socket */ + (void)ipsec_setsocket(m, NULL); +#endif +/* ip6_output(m, NULL, NULL, 0, &im6o, &outif); + if (outif) { + icmp6_ifstat_inc(outif, ifs6_out_msg); + icmp6_ifstat_inc(outif, ifs6_out_routersolicit); + } + icmp6stat.icp6s_outhist[ND_ROUTER_SOLICIT]++; +*/ + +/* if (sin6_src == NULL || sin6_dst == NULL || + sin6_src->sin6_family != AF_INET6 || + sin6_dst->sin6_family != AF_INET6) { + m_freem(m); + return EAFNOSUPPORT; + } +*/ +/* struct ip6_hdr *ip6; + proto = IPPROTO_IPV6; + 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; +*/ + + /* prepend new IP header */ + 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) { + printf("ENOBUFS in %s %d\n", __FUNCTION__, __LINE__); + 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)m->m_pkthdr.len); + ip6->ip6_nxt = IPPROTO_IPV6; + ip6->ip6_hlim = ip6_gif_hlim; + ip6->ip6_src = *src; /* Outer src and dst */ + ip6->ip6_dst = *dst; +/* if (ifp->if_flags & IFF_LINK0) { + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) + ip6->ip6_dst = sin6_dst->sin6_addr; + else if (rt) { + if (family != AF_INET6) { + m_freem(m); + return EINVAL; + } + ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; + } else { + m_freem(m); + return ENETUNREACH; + } + } else { +*/ /* bidirectional configured tunnel mode */ +/* if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) + ip6->ip6_dst = sin6_dst->sin6_addr; + else { + m_freem(m); + return ENETUNREACH; + } + } +*/ +/* + if (ifp->if_flags & IFF_LINK1) + ip_ecn_ingress(ECN_ALLOWED, &otos, &itos); + else + ip_ecn_ingress(ECN_NOCARE, &otos, &itos); + ip6->ip6_flow &= ~htonl(0x0ff00000); + ip6->ip6_flow |= htonl((u_int32_t)otos << 20); +*/ +/* if (dst->sin6_family != sin6_dst->sin6_family || + !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { + bzero(dst, sizeof(*dst)); + dst->sin6_family = sin6_dst->sin6_family; + dst->sin6_len = sizeof(struct sockaddr_in6); + dst->sin6_addr = sin6_dst->sin6_addr; + if (sc->gif_ro6.ro_rt) { + RTFREE(sc->gif_ro6.ro_rt); + sc->gif_ro6.ro_rt = NULL; + } +#if 0 + sc->gif_if.if_mtu = GIF_MTU; +#endif + } +*/ +/* if (sc->gif_ro6.ro_rt == NULL) { + rtalloc((struct route *)&sc->gif_ro6); + if (sc->gif_ro6.ro_rt == NULL) { + m_freem(m); + return ENETUNREACH; + } +*/ + /* if it constitutes infinite encapsulation, punt. */ +/* if (sc->gif_ro.ro_rt->rt_ifp == ifp) { + m_freem(m); + return ENETUNREACH; + } +#if 0 + ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu + - sizeof(struct ip6_hdr); +#endif + } +*/ +#ifdef IPV6_MINMTU + /* + * force fragmentation to minimum MTU, to avoid path MTU discovery. + * it is too painful to ask for resend of inner packet, to achieve + * path MTU discovery for encapsulated packets. + */ + return(ip6_output(m, 0, 0, IPV6_MINMTU, 0, NULL)); +#else + return(ip6_output(m, 0, 0, 0, 0, NULL)); +#endif +} + + + +#if 0 /* no more */ +void +mip6_tunneled_ra_input() +{ +/* + Find esp. + Stop peding outgoing tunneled RSes in the esp. + + See if we can reuse prelist_update(). + RtrAdvInt should be saved if included. + Do we have a default router from this RA? Probably no. +*/ +} +#endif + + +/* + * Todo: This is a conceptual function. May be implemented elsewhere. + */ +void +mip6_dhaad_reply(void *arg) +{ + struct mip6_esm *esp; + + /* Todo: Find esp */ + esp = NULL; + + if (IN6_IS_ADDR_UNSPECIFIED(&esp->home_addr)) { + mip6_send_rs(esp, 1); + } +} diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/nd6.c kame/kame/sys/netinet6/nd6.c --- kame-20010611/kame/sys/netinet6/nd6.c Mon Jun 4 21:03:43 2001 +++ kame/kame/sys/netinet6/nd6.c Mon Jun 11 11:12:41 2001 @@ -101,6 +101,11 @@ #include #include +#ifdef MIP6 +#include +#include +#endif + #if !defined(__bsdi__) && !defined(__OpenBSD__) #include "loop.h" #endif @@ -153,6 +158,10 @@ static void nd6_slowtimo __P((void *)); static int regen_tmpaddr __P((struct in6_ifaddr *)); +#ifdef MIP6 +void (*mip6_expired_defrouter_hook)(struct nd_defrouter *dr) = 0; +#endif + #ifdef __NetBSD__ struct callout nd6_slowtimo_ch = CALLOUT_INITIALIZER; struct callout nd6_timer_ch = CALLOUT_INITIALIZER; @@ -500,6 +509,20 @@ #else s = splnet(); #endif +#ifdef MIP6 + if (MIP6_EAGER_PREFIX) { +#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) + callout_reset(&nd6_timer_ch, nd6_prune * hz / MIP6_EAGER_FREQ, + nd6_timer, NULL); +#elif defined(__OpenBSD__) + timeout_set(&nd6_timer_ch, nd6_timer, NULL); + timeout_add(&nd6_timer_ch, nd6_prune * hz / MIP6_EAGER_FREQ); +#else + timeout(nd6_timer, (caddr_t)0, + nd6_prune * hz / MIP6_EAGER_FREQ); +#endif + } else +#endif #if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3) callout_reset(&nd6_timer_ch, nd6_prune * hz, nd6_timer, NULL); @@ -550,11 +573,22 @@ ln->ln_asked++; ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].retrans / 1000; +#if defined(MIP6) && defined(MIP6_DEBUG) + printf("INCOMPLETE send ns #%lu to %s\n", + ln->ln_asked, ip6_sprintf(&dst->sin6_addr)); +#endif nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); } else { struct mbuf *m = ln->ln_hold; +#if defined(MIP6) && defined(MIP6_DEBUG) + printf("INCOMPLETE free %s\n", + ip6_sprintf(&dst->sin6_addr)); +#endif if (m) { +#if defined(MIP6) && defined(MIP6_DEBUG) + printf("Also free mbuf and send icmp6 error...\n"); +#endif if (rt->rt_ifp) { /* * Fake rcvif to make ICMP error @@ -592,6 +626,10 @@ ln->ln_state = ND6_LLINFO_PROBE; ln->ln_expire = time_second + ndi->retrans / 1000; +#if defined(MIP6) && defined(MIP6_DEBUG) + printf("DELAY send ns #%lu to %s\n", + ln->ln_asked, ip6_sprintf(&dst->sin6_addr)); +#endif nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); @@ -605,9 +643,17 @@ ln->ln_asked++; ln->ln_expire = time_second + nd_ifinfo[ifp->if_index].retrans / 1000; +#if defined(MIP6) && defined(MIP6_DEBUG) + printf("PROBE send ns #%lu to %s\n", + ln->ln_asked, ip6_sprintf(&dst->sin6_addr)); +#endif nd6_ns_output(ifp, &dst->sin6_addr, &dst->sin6_addr, ln, 0); } else { +#if defined(MIP6) && defined(MIP6_DEBUG) + printf("PROBE now free %s\n", + ip6_sprintf(&dst->sin6_addr)); +#endif next = nd6_free(rt); } break; @@ -624,6 +670,10 @@ defrtrlist_del(dr); dr = t; } else { +#ifdef MIP6 + if (mip6_expired_defrouter_hook) + (*mip6_expired_defrouter_hook)(dr); +#endif /* MIP6 */ dr = TAILQ_NEXT(dr, dr_entry); } } @@ -667,6 +717,10 @@ ia6->ia6_flags |= IN6_IFF_DEPRECATED; +#ifdef MIP6 + if (MIP6_IS_MN_ACTIVE) + mip6_deprecated_addr(ia6); +#endif /* MIP6 */ /* * If a temporary address has just become deprecated, * regenerate a new one if possible. diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/nd6.h kame/kame/sys/netinet6/nd6.h --- kame-20010611/kame/sys/netinet6/nd6.h Mon Jun 4 18:04:03 2001 +++ kame/kame/sys/netinet6/nd6.h Thu May 31 10:01:25 2001 @@ -1,4 +1,4 @@ -/* $KAME: nd6.h,v 1.57 2001/06/04 09:04:03 keiichi Exp $ */ +/* $KAME: nd6.h,v 1.56 2001/05/31 01:01:25 suz Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -320,6 +320,9 @@ #endif /* nd6_rtr.c */ +#ifdef MIP6 +extern struct ifnet *nd6_defifp; +#endif extern int nd6_defifindex; extern int ip6_desync_factor; /* seconds */ extern u_int32_t ip6_temp_preferred_lifetime; /* seconds */ @@ -410,17 +413,37 @@ void defrouter_select __P((void)); void defrtrlist_del __P((struct nd_defrouter *)); void prelist_remove __P((struct nd_prefix *)); +#ifdef MIP6 +int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, + struct mbuf *, int)); +#else int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, struct mbuf *)); +#endif int nd6_prelist_add __P((struct nd_prefix *, struct nd_defrouter *, struct nd_prefix **)); int nd6_prefix_onlink __P((struct nd_prefix *)); int nd6_prefix_offlink __P((struct nd_prefix *)); +#ifdef MIP6 +struct in6_ifaddr *in6_ifadd __P((struct nd_prefix *, struct in6_addr *)); +struct nd_pfxrouter *find_pfxlist_reachable_router __P((struct nd_prefix *)); +#endif void pfxlist_onlink_check __P((void)); +#ifdef MIP6 +void defrouter_addifreq __P((struct ifnet *)); +#endif struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, struct ifnet *)); struct nd_prefix *nd6_prefix_lookup __P((struct nd_prefix *)); +#ifdef MIP6 +struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, + struct nd_defrouter *)); +#endif int in6_init_prefix_ltimes __P((struct nd_prefix *ndpr)); +#ifdef MIP6 +void in6_init_address_ltimes __P((struct nd_prefix *ndpr, + struct in6_addrlifetime *lt6)); +#endif void rt6_flush __P((struct in6_addr *, struct ifnet *)); int nd6_setdefaultiface __P((int)); int in6_tmpifadd __P((const struct in6_ifaddr *, int)); diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/nd6_rtr.c kame/kame/sys/netinet6/nd6_rtr.c --- kame-20010611/kame/sys/netinet6/nd6_rtr.c Mon Jun 4 18:07:28 2001 +++ kame/kame/sys/netinet6/nd6_rtr.c Fri Jun 1 12:02:50 2001 @@ -1,4 +1,4 @@ -/* $KAME: nd6_rtr.c,v 1.119 2001/06/04 09:07:28 keiichi Exp $ */ +/* $KAME: nd6_rtr.c,v 1.118 2001/05/31 22:20:48 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -70,30 +70,45 @@ #include #include +#ifdef MIP6 +#include +#include +#endif + #include #define SDL(s) ((struct sockaddr_dl *)s) static struct nd_defrouter *defrtrlist_update __P((struct nd_defrouter *)); +#ifndef MIP6 static struct in6_ifaddr *in6_ifadd __P((struct nd_prefix *, struct in6_addr *)); static struct nd_pfxrouter *pfxrtr_lookup __P((struct nd_prefix *, struct nd_defrouter *)); +#endif static void pfxrtr_add __P((struct nd_prefix *, struct nd_defrouter *)); static void pfxrtr_del __P((struct nd_pfxrouter *)); +#ifndef MIP6 static struct nd_pfxrouter *find_pfxlist_reachable_router __P((struct nd_prefix *)); static void defrouter_addifreq __P((struct ifnet *)); +#endif static void nd6_rtmsg __P((int, struct rtentry *)); +#ifndef MIP6 static void in6_init_address_ltimes __P((struct nd_prefix *ndpr, struct in6_addrlifetime *lt6)); +#endif static int rt6_deleteroute __P((struct radix_node *, void *)); extern int nd6_recalc_reachtm_interval; +#ifdef MIP6 +struct ifnet *nd6_defifp; +#else static struct ifnet *nd6_defifp; +#endif int nd6_defifindex; int ip6_use_tempaddr = 0; @@ -108,6 +123,22 @@ */ int ip6_temp_regen_advance = TEMPADDR_REGEN_ADVANCE; +#ifdef MIP6 +void (*mip6_select_defrtr_hook)(struct nd_prefix *, + struct nd_defrouter *) = NULL; +struct nd_prefix * (*mip6_get_home_prefix_hook)(void) = NULL; +void (*mip6_prelist_update_hook)(struct nd_prefix *pr, + struct nd_defrouter *dr, + u_char onlink) = NULL; +void (*mip6_eager_prefix_hook)(struct nd_prefix *pr, + struct nd_defrouter *dr) = NULL; +void (*mip6_probe_pfxrtrs_hook)(void) = NULL; +void (*mip6_store_advint_hook)(struct nd_opt_advinterval *ai, + struct nd_defrouter *dr) = NULL; +int (*mip6_get_md_state_hook)(void) = 0; +void (*mip6_minus_a_case_hook)(struct nd_prefix *) = NULL; +#endif /* MIP6 */ + /* * Receive Router Solicitation Message - just for routers. * Router solicitation/advertisement is mostly managed by userland program @@ -226,6 +257,9 @@ #endif union nd_opts ndopts; struct nd_defrouter *dr; +#ifdef MIP6 + int home = 0; +#endif if (ip6_accept_rtadv == 0) goto freeit; @@ -238,7 +272,11 @@ goto bad; } - if (!IN6_IS_ADDR_LINKLOCAL(&saddr6)) { + if (!IN6_IS_ADDR_LINKLOCAL(&saddr6) +#ifdef MIP6 + && MIP6_IS_MN_ACTIVE && !mip6_incl_br(m) +#endif /* MIP6 */ + ) { nd6log((LOG_ERR, "nd6_ra_input: src %s is not link-local\n", ip6_sprintf(&saddr6))); @@ -265,6 +303,17 @@ goto freeit; } +#ifdef MIP6 + if (MIP6_IS_MN_ACTIVE && mip6_incl_br(m)) { +#ifdef MIP6_DEBUG + mip6_debug("%s: tunneled RA from %s\n", + __FUNCTION__, + ip6_sprintf(&saddr6)); +#endif + dr = NULL; + goto prefix; + } +#endif /* MIP6 */ { struct nd_defrouter dr0; u_int32_t advreachable = nd_ra->nd_ra_reachable; @@ -297,6 +346,9 @@ dr = defrtrlist_update(&dr0); } +#ifdef MIP6 + prefix: +#endif /* MIP6 */ /* * prefix */ @@ -305,6 +357,75 @@ struct nd_opt_prefix_info *pi = NULL; struct nd_prefix pr; +#ifdef MIP6 + mip6_new_homeaddr = 0; + + /* + * We can be at home and hear primary home prefix plus + * other home prefixes. Some of them are new to us or + * old but not marked home prefixes. Make all of them + * home prefixes. + * We can also be at foreign and get an RA tunneled + * (with RH + BR + Id). Do the same, but remember + * - not to store the dr + * - to store the advint option in prefix or esm + * - to send a BU + Id to HA after parsing all the prefixes + * + * Note that this is not a test for Movement Detection. + */ + if (MIP6_IS_MN_ACTIVE) { + /* XXX Add hook later? */ + + for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; + pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; + pt = (struct nd_opt_hdr *)((caddr_t)pt + + (pt->nd_opt_len << 3))) { + if (pt->nd_opt_type != + ND_OPT_PREFIX_INFORMATION) + continue; + pi = (struct nd_opt_prefix_info *)pt; + + /* + * Strong enough test? We could also + * test if other non-primary home + * prefixes are included, in case + * the primary for some reason isn't. + */ + if (in6_are_prefix_equal( + &pi->nd_opt_pi_prefix, + &mip6_php, + pi->nd_opt_pi_prefix_len)) { + + if (home == 0) + /* + * This interface is a good generator + * of our long-lasting interface ID + * for the home addresses. + */ + mip6_create_ifid(ifp, + &pi->nd_opt_pi_prefix, + pi-> + nd_opt_pi_prefix_len); + home = 1; + } + } +#ifdef MIP6_DEBUG + if (!home && mip6_incl_br(m)) + /* + * XXX We could set home=1 if we know this + * is from HA, due to the BR. The HA could + * possibly send RAs without the primary + * home prefix even when there is no + * renumbering taking place. + */ + mip6_debug("%s: warning, tunneled RA received," + " but can't recognize any " + "prefixes\n (badly formed " + "renumbering?). Skipping... \n", + __FUNCTION__); +#endif + } +#endif /* MIP6 */ for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; pt = (struct nd_opt_hdr *)((caddr_t)pt + @@ -367,7 +488,11 @@ if (in6_init_prefix_ltimes(&pr)) continue; /* prefix lifetime init failed */ - (void)prelist_update(&pr, dr, m); + (void)prelist_update(&pr, dr, m +#ifdef MIP6 + , home +#endif + ); } } @@ -440,6 +565,21 @@ pfxlist_onlink_check(); } +#ifdef MIP6 + if (mip6_store_advint_hook) { + if (ndopts.nd_opts_adv) + (*mip6_store_advint_hook)(ndopts.nd_opts_adv, dr); + } + + if (MIP6_IS_MN_ACTIVE && home && mip6_incl_br(m)) + if (mip6_new_homeaddr) { +#if 0 +#warning mip6_send_bu(); /* Merge with Conny */ +#endif + printf("blah...\n");; /*RM*/ + } +#endif + freeit: m_freem(m); return; @@ -514,7 +654,11 @@ } /* Add a route to a given interface as default */ +#ifdef MIP6 +void +#else static void +#endif defrouter_addifreq(ifp) struct ifnet *ifp; { @@ -693,6 +837,16 @@ struct rtentry *rt = NULL; struct llinfo_nd6 *ln = NULL; +#ifdef MIP6 + /* Mobile IPv6 alternative routine */ + if (mip6_select_defrtr_hook) { + (*mip6_select_defrtr_hook)(NULL, NULL); /* XXXYYY Temporary? */ + splx(s); + return; + } + /* End of Mobile IPv6 */ +#endif /* MIP6 */ + /* * Search for a (probably) reachable router from the list. * XXX: nd_defrouter_primary is initialized to point to the first entry @@ -903,7 +1057,11 @@ return(n); } +#ifdef MIP6 +struct nd_pfxrouter * +#else static struct nd_pfxrouter * +#endif pfxrtr_lookup(pr, dr) struct nd_prefix *pr; struct nd_defrouter *dr; @@ -1012,6 +1170,24 @@ if (dr) { pfxrtr_add(new, dr); +#ifdef MIP6 + if (mip6_get_md_state_hook) { + /* + * If we are in UNDEFINED state and a router appears, + * select that router and change state. + * This case takes care of transitions from UNDEFINED + * to FOREIGN when the prefix is not known from before. + */ + if ((*mip6_get_md_state_hook)() == MIP6_MD_UNDEFINED) { +#ifdef MIP6_DEBUG + mip6_debug("%s: WARNING, this case (hook on prelist_add) is broken. XXX\n", __FUNCTION__); + /* XXX No ndpr_addr available yet. */ +#endif + if (mip6_select_defrtr_hook) + (*mip6_select_defrtr_hook)(NULL, NULL); + } + } +#endif /* MIP6 */ } return 0; @@ -1053,6 +1229,12 @@ s = splnet(); #endif +#ifdef MIP6 + /* Reset Mobile IPv6 prefix pointers */ + mip6_phpp = NULL; + mip6_pp = NULL; +#endif /* MIP6 */ + /* unlink ndpr_entry from nd_prefix list */ LIST_REMOVE(pr, ndpr_entry); @@ -1070,10 +1252,18 @@ } int +#ifdef MIP6 +prelist_update(new, dr, m, home) + struct nd_prefix *new; + struct nd_defrouter *dr; /* may be NULL */ + struct mbuf *m; + int home; +#else prelist_update(new, dr, m) struct nd_prefix *new; struct nd_defrouter *dr; /* may be NULL */ struct mbuf *m; +#endif { struct in6_ifaddr *ia6 = NULL, *ia6_match = NULL; struct ifaddr *ifa; @@ -1088,6 +1278,9 @@ int newprefix = 0; int auth; struct in6_addrlifetime lt6_tmp; +#ifdef MIP6 + int was_onlink = 0; +#endif /* MIP6 */ auth = 0; if (m) { @@ -1101,11 +1294,32 @@ #endif } + +#ifdef MIP6 + if (MIP6_IS_MN_ACTIVE && home && mip6_incl_br(m)) { + /* + * Tunneled RA from HA. No associated prefix. + */ + pr = nd6_prefix_lookup(new); + goto afteraddrconf; + } +#endif /* MIP6 */ if ((pr = nd6_prefix_lookup(new)) != NULL) { /* * nd6_prefix_lookup() ensures that pr and new have the same * prefix on a same interface. */ +#ifdef OLDMIP6 + if (mip6_get_home_prefix_hook) { + /* + * The home prefix should be kept away from updates. + * XXXYYY Tunneled RA? New Home Prefix? Unless + * configured, the code below will be executed. + */ + if (pr == (*mip6_get_home_prefix_hook)()) + goto afteraddrconf; + } +#endif /* OLDMIP6 */ /* * Update prefix information. Note that the on-link (L) bit @@ -1123,6 +1337,11 @@ pr->ndpr_expire = new->ndpr_expire; } +#ifdef MIP6 + /* Remember old state for MIP6 */ + was_onlink = (pr->ndpr_stateflags & NDPRF_ONLINK); +#endif + if (new->ndpr_raf_onlink && (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { int e; @@ -1179,6 +1398,18 @@ pr = newpr; } +#ifdef MIP6 + if (MIP6_IS_MN_ACTIVE && home) + if ((pr->ndpr_stateflags & NDPRF_HOME) == 0) { + pr->ndpr_stateflags |= NDPRF_HOME; +#ifdef MIP6_DEBUG + mip6_debug("%s: making %s a home prefix\n", + __FUNCTION__, + ip6_sprintf(&pr->ndpr_prefix.sin6_addr)); +#endif + } +#endif /* MIP6 */ + /* * Address autoconfiguration based on Section 5.5.3 of RFC 2462. * Note that pr must be non NULL at this point. @@ -1361,6 +1592,71 @@ } afteraddrconf: +#ifdef MIP6 + if (MIP6_IS_MN_ACTIVE && home) + /* + * Mobile IPv6 home addresses are not updated in the + * procedure above. + */ + mip6_update_home_addrs(m, new, auth); + + if (mip6_incl_br(m)) + /* + * Tunneled RAs from HA must not trigger movement + * detection. + */ + goto end; + + if (mip6_prelist_update_hook) { + /* + * Check for if this prefix can trigger + * movement home (eager 0) or any movement + * (eager 2). + */ + (*mip6_prelist_update_hook)(pr, dr, was_onlink); + } + + if (!newprefix) { + /* + * XXXYYY + * Really need to check this section here. I don't know + * if it's doing everything right. Mattias, 20010210. + */ + + if (mip6_probe_pfxrtrs_hook) { + /* + * If this prefix previously was detached, maybe we + * have moved. + */ + if (!was_onlink) + (*mip6_probe_pfxrtrs_hook)(); + } + } else { +#ifdef OLDMIP6 + /* XXX This is not needed in new MIP6. mip6_prelist_update() + does the work. */ + if (mip6_eager_prefix_hook) + /* New prefix: if eager, try to move */ + (*mip6_eager_prefix_hook)(pr, dr); +#endif + if (mip6_probe_pfxrtrs_hook) { + /* This is a new prefix, maybe we have moved. */ + (*mip6_probe_pfxrtrs_hook)(); + } + + if (mip6_minus_a_case_hook) { + /* + * If we are still looking for an autoconfigured home + * address when we are in "minus a" case, here's a new + * prefix and hopefully we can use the address derived + * from that. + */ + if (ia6) + (*mip6_minus_a_case_hook)(pr); + } + } +#endif /* MIP6 */ + end: splx(s); return error; @@ -1371,7 +1667,11 @@ * detect if a given prefix has a (probably) reachable advertising router. * XXX: lengthy function name... */ +#ifdef MIP6 +struct nd_pfxrouter * +#else static struct nd_pfxrouter * +#endif find_pfxlist_reachable_router(pr) struct nd_prefix *pr; { @@ -1686,6 +1986,16 @@ return(EEXIST); } +#ifdef MIP6 + if (pr == mip6_phpp) { + /* Keep consistent. Don't cache off-link pr. */ + mip6_phpp = NULL; +#ifdef MIP6_DEBUG + mip6_debug("%s: primary home prefix is detached.\n", + __FUNCTION__); +#endif + } +#endif bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); @@ -1763,7 +2073,11 @@ return(error); } +#ifdef MIP6 +struct in6_ifaddr * +#else static struct in6_ifaddr * +#endif in6_ifadd(pr, ifid) struct nd_prefix *pr; struct in6_addr *ifid; /* Mobile IPv6 addition */ @@ -2031,7 +2345,11 @@ return 0; } +#ifdef MIP6 +void +#else static void +#endif in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) { #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/kame/sys/netinet6/route6.c kame/kame/sys/netinet6/route6.c --- kame-20010611/kame/sys/netinet6/route6.c Mon Jun 4 18:07:56 2001 +++ kame/kame/sys/netinet6/route6.c Wed Mar 14 12:07:05 2001 @@ -1,4 +1,4 @@ -/* $KAME: route6.c,v 1.25 2001/06/04 09:07:56 keiichi Exp $ */ +/* $KAME: route6.c,v 1.24 2001/03/14 03:07:05 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -54,6 +54,11 @@ #include #include + +#ifdef MIP6 +#include +#include +#endif static int ip6_rthdr0 __P((struct mbuf *, struct ip6_hdr *, struct ip6_rthdr0 *)); diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/freebsd4/sys/conf/files kame/freebsd4/sys/conf/files --- kame-20010611/freebsd4/sys/conf/files Mon Jun 4 17:45:51 2001 +++ kame/freebsd4/sys/conf/files Mon Jun 4 20:22:11 2001 @@ -95,6 +95,13 @@ crypto/sha2/sha2.c optional ipsec crypto/twofish/twofish2.c optional ipsec_esp +netinet6/mip6.c optional mip6 +netinet6/mip6_hooks.c optional mip6 +netinet6/mip6_io.c optional mip6 +netinet6/mip6_mn.c optional mip6 +netinet6/mip6_md.c optional mip6 +netinet6/mip6_ha.c optional mip6 + ddb/db_access.c optional ddb ddb/db_kld.c optional ddb ddb/db_break.c optional ddb diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/freebsd4/sys/conf/options kame/freebsd4/sys/conf/options --- kame-20010611/freebsd4/sys/conf/options Mon Jun 4 17:46:26 2001 +++ kame/freebsd4/sys/conf/options Mon Jun 4 20:22:11 2001 @@ -246,6 +246,8 @@ INET6 opt_inet6.h ENABLE_DEFAULT_SCOPE opt_inet6.h IP6_AUTO_LINKLOCAL opt_inet6.h +MIP6 opt_inet.h +MIP6_DEBUG opt_inet.h NATPT opt_natpt.h NATPT_NAT opt_natpt.h NEW_STRUCT_ROUTE opt_global.h diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/freebsd4/sys/i386/conf/GENERIC.KAME kame/freebsd4/sys/i386/conf/GENERIC.KAME --- kame-20010611/freebsd4/sys/i386/conf/GENERIC.KAME Mon Jun 4 17:47:11 2001 +++ kame/freebsd4/sys/i386/conf/GENERIC.KAME Mon Jun 4 20:22:22 2001 @@ -294,6 +294,10 @@ # Network Address Translation - Protocol Translation (NAT-PT) #options NATPT +# mobile-ip6 options +#options "MIP6" +##options "MIP6_DEBUG" + #pseudo-device atm #pseudo-device dummy 1 #pseudo-device stf diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/freebsd4/usr.sbin/Makefile kame/freebsd4/usr.sbin/Makefile --- kame-20010611/freebsd4/usr.sbin/Makefile Mon Jun 4 17:48:20 2001 +++ kame/freebsd4/usr.sbin/Makefile Mon Jun 4 20:22:35 2001 @@ -4,5 +4,6 @@ SUBDIR+=altqd natptconfig natptd natptlog pvcbridge pvcsif pvctxctl tbrconfig #SUBDIR+=ifmcstat SUBDIR+=racoon +SUBDIR+=mip6config mip6stat .include diff -ruN --exclude=CVS --exclude=MIP6BAK kame-20010611/freebsd4/usr.sbin/rtadvd/Makefile kame/freebsd4/usr.sbin/rtadvd/Makefile --- kame-20010611/freebsd4/usr.sbin/rtadvd/Makefile Mon Jun 4 17:50:31 2001 +++ kame/freebsd4/usr.sbin/rtadvd/Makefile Mon Jun 4 20:22:44 2001 @@ -3,7 +3,7 @@ PROG= rtadvd SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c -CFLAGS+=-DINET6 +CFLAGS+=-DINET6 -DMIP6 LDADD+= -lcompat DPADD+= ${LIBCOMPAT} LDADD+= -L${.OBJDIR}/../../lib/libinet6 \