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 <netinet/icmp6.h>
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#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 <net/if_faith.h>
 #endif
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#endif
+
 #include <net/net_osdep.h>
 
 #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 <net/if_gif.h>
 #endif
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+
+struct nd_prefix *(*mip6_get_home_prefix_hook) __P((void));
+#endif /* MIP6 */
+
 #include <net/net_osdep.h>
 
 #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 <netinet6/in6_gif.h>
 #endif
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#endif
+
 #include <net/net_osdep.h>
 
 #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 <sys/errno.h>
 #include <sys/time.h>
 #include <sys/proc.h>
+#ifdef MIP6
+#include <sys/kernel.h>
+#endif
 
 #include <net/if.h>
 #include <net/route.h>
@@ -114,6 +118,13 @@
 extern struct ifnet loif[NLOOP];
 #endif
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+
+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 <netinet6/ip6_fw.h>
 #endif
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#endif
+
 #include <net/net_osdep.h>
 
 #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 <netinet6/ipsec.h>
 #endif
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#endif
+
 #if defined(IPV6FIREWALL) || (defined(__FreeBSD__) && __FreeBSD__ >= 4)
 #include <netinet6/ip6_fw.h>
 #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 <net/net_osdep.h>
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#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 <Conny.Larsson@era.ericsson.se>
+ *          Mattias Pettersson <Mattias.Pettersson@era.ericsson.se>
+ *
+ */
+
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/ioccom.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if_gif.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_encap.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/callout.h>
+#elif defined(__OpenBSD__)
+#include <sys/timeout.h>
+#endif
+
+#ifdef MIP6_DEBUG
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <machine/stdarg.h>
+#include <sys/syslog.h>
+#endif
+
+#include <net/net_osdep.h>
+
+int  (*mip6_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 <ifp> <addr> prefixlen <plen>".
+ * 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 <Conny.Larsson@era.ericsson.se>
+ *          Mattias Pettersson <Mattias.Pettersson@era.ericsson.se>
+ *
+ */
+
+#ifndef _NETINET6_MIP6_H_
+#define _NETINET6_MIP6_H_
+
+#include <netinet6/nd6.h>
+#include <netinet/icmp6.h>
+
+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 <hesham.soliman@ericsson.com.au>
+ *          Martti Kuparinen <martti.kuparinen@ericsson.com>
+ *          Mattias Pettersson <mattias.pettersson@era.ericsson.se>
+ */
+
+
+#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
+ *                          <mip6config -d>
+ * SIOCSBCFLUSH_MIP6        Remove list of BC
+ *                          <mip6stat -C>
+ * SIOCSDEFCONFIG_MIP6      Restore default configuration
+ *                          <mip6stat -P>
+ * SIOCSBRUPDATE_MIP6       Set time when CN should send Binding request
+ *                          <mip6config -w>
+ * SIOCSENABLEBR_MIP6       Enable sending BR to the MN
+ *                          <mip6config -q>
+ * SIOCSHALISTFLUSH_MIP6    Remove list of Home Agents
+ *                          <mip6stat -A>
+ * SIOCSHAPREF_MIP6         HA preference
+ *                          <mip6config -y>
+ * SIOCSFWDSLUNICAST_MIP6   Enable forwarding of SL Unicast dest addresses
+ *                          <mip6config -u>
+ * SIOCSFWDSLMULTICAST_MIP6 Enable forwarding of SL Multicast dest addresses
+ *                          <mip6config -m>
+ * SIOCSFORADDRFLUSH_MIP6   Remove default foreign address from list
+ *                          <mip6stat -F>
+ * SIOCSHADDRFLUSH_MIP6     Remove Home Address
+ *                          <mip6stat -M>
+ * SIOCSBULISTFLUSH_MIP6    Remove Binding Update list
+ *                          <mip6stat -U>
+ * SIOCACOADDR_MIP6         Set Default foreign IP Address
+ *                          <mip6config -F>
+ * SIOCAHOMEADDR_MIP6       Add home address
+ *                          <mip6config -H>
+ * SIOCAHOMEPREF_MIP6       Add home prefix
+ *                          <mip6config -P>
+ * SIOCSBULIFETIME_MIP6     Set default BU lifetime
+ *                          <mip6config -b>
+ * SIOCSHRLIFETIME_MIP6     Set default lifetime for home registration, not BU
+ *                          <mip6config -l>
+ * SIOCDCOADDR_MIP6         Remove default foreign address from list
+ *                          <mip6config -E>
+ * SIOCSPROMMODE_MIP6       Enable link layer promiscuous mode
+ *                          <mip6config -p>
+ * SIOCSBU2CN_MIP6          Enable sending BU to CN, i.e. Route opt on/off
+ *                          <mip6config -r>
+ * SIOCSREVTUNNEL_MIP6      Enable tunneling of packets from MN to CN via HA
+ *                          <mip6config -t>
+ * SIOCSAUTOCONFIG_MIP6     Allow autoconfiguration of Home address
+ *                          <mip6config -a>
+ * SIOCSEAGERMD_MIP6        Enable eager Movement Detection
+ *                          <mip6config -e>
+ */
+#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 <Conny.Larsson@era.ericsson.se>
+ *
+ */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#endif
+#ifdef __NetBSD__
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/ioccom.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/nd6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+#include <machine/limits.h>
+
+#include <net/net_osdep.h>
+
+
+/*
+ ##############################################################################
+ #
+ # 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 <mattias.pettersson@era.ericsson.se>
+ *          Hesham Soliman <hesham.soliman@ericsson.com.au>
+ *          Martti Kuparinen <martti.kuparinen@ericsson.com>
+ *
+ */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include "opt_inet.h"
+#include "opt_inet6.h"
+#endif
+#ifdef __NetBSD__
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/kernel.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+
+/*
+ * 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 <Conny.Larsson@era.ericsson.se>
+ *          Mattias Pettersson <Mattias.Pettersson@era.ericsson.se>
+ *
+ */
+
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if_gif.h>
+#include <net/if_dl.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_encap.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+
+#ifdef MIP6_DEBUG
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <machine/stdarg.h>
+#include <sys/syslog.h>
+#endif
+
+#include <net/net_osdep.h>
+
+/*
+ ##############################################################################
+ #
+ # 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 <mattias.pettersson@era.ericsson.se>
+ *
+ */
+
+#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 <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/nd6.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <netinet/in_pcb.h>
+#endif
+#if !defined(__OpenBSD__) && !defined(__bsdi__)
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet6/mip6.h>
+
+#ifdef IPSEC
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#endif
+
+#include <net/net_osdep.h>
+
+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, &lt6_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 <ifp> <addr> 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 <ifp> <addr> 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 <Conny.Larsson@era.ericsson.se>
+ *	    Mattias Pettersson <Mattias.Pettersson@era.ericsson.se>
+ *
+ */
+
+#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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/ioccom.h>
+
+#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/callout.h>
+#elif defined(__OpenBSD__)
+#include <sys/timeout.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/net_osdep.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/nd6.h>
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+
+/* 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 <netinet6/in6_prefix.h>
 #include <netinet/icmp6.h>
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+#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 <netinet/icmp6.h>
 #include <netinet6/scope6_var.h>
 
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#include <netinet6/mip6_common.h>
+#endif
+
 #include <net/net_osdep.h>
 
 #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 <netinet6/ip6_var.h>
 
 #include <netinet/icmp6.h>
+
+#ifdef MIP6
+#include <netinet6/mip6.h>
+#include <net/if_types.h>
+#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 <bsd.subdir.mk>
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 \
