summaryrefslogtreecommitdiff
path: root/sys/netinet6/ipv6_rtrequest.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1999-01-06 23:26:59 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1999-01-06 23:26:59 +0000
commit003294c571f5404cf3bb2ab3cf33eba8643b932b (patch)
tree0efc8463ff2d3c2555c4a6a830fe508568cc5e07 /sys/netinet6/ipv6_rtrequest.c
parent21e642825078d715499c3110ace9c6e61f6c6db4 (diff)
first few files of NRL ipv6. This NRL release was officially exported
to me by US DOD officials, with the crypto already removed.
Diffstat (limited to 'sys/netinet6/ipv6_rtrequest.c')
-rw-r--r--sys/netinet6/ipv6_rtrequest.c1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/sys/netinet6/ipv6_rtrequest.c b/sys/netinet6/ipv6_rtrequest.c
new file mode 100644
index 00000000000..7e0bb20e27a
--- /dev/null
+++ b/sys/netinet6/ipv6_rtrequest.c
@@ -0,0 +1,1005 @@
+/*
+%%% copyright-nrl-95
+This software is Copyright 1995-1998 by Randall Atkinson, Ronald Lee,
+Daniel McDonald, Bao Phan, and Chris Winters. All Rights Reserved. All
+rights under this copyright have been assigned to the US Naval Research
+Laboratory (NRL). The NRL Copyright Notice and License Agreement Version
+1.1 (January 17, 1995) applies to this software.
+You should have received a copy of the license with this software. If you
+didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
+
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+/*#include <sys/ioctl.h>*/
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <netinet6/in6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ipv6.h>
+#include <netinet6/ipv6_var.h>
+#include <netinet6/ipv6_icmp.h>
+
+#include <sys/debug.h>
+
+/*
+ * Globals (and forward function declarations).
+ */
+
+/*
+ * External globals.
+ */
+extern struct sockaddr_in6 in6_allones;
+extern struct discq dqhead;
+extern struct v6router defrtr;
+extern struct v6router nondefrtr;
+
+extern struct in6_ifaddr *in6_ifaddr;
+
+extern int ipv6forwarding;
+
+/*
+ * External function prototypes.
+ */
+
+/*
+ * General notes:
+ *
+ * These functions are mainly called from ipv6_discovery.c but are
+ * fairly generic and generally useful and so live in their own file.
+ * danmcd rja
+ */
+void tunnel_parent __P((struct rtentry *));
+struct v6router *ipv6_add_defrouter_rtrequest(struct rtentry *);
+void tunnel_child __P((struct rtentry *));
+void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *));
+void tunnel_parent_clean __P((struct rtentry *));
+int ipv6_delete_defrouter __P((struct v6router *));
+void tunnel_child_clean __P((struct rtentry *));
+
+static int add_defchild __P((struct rtentry *));
+static void add_netchild __P((struct rtentry *));
+void ipv6_setrtifa __P((struct rtentry *));
+static void add_non_default __P((struct rtentry *));
+
+/*
+ * Functions and macros.
+ */
+
+
+/*----------------------------------------------------------------------
+ * add_defchild():
+ * Find the best default router out of our list and use it
+ * for this destination route.
+ ----------------------------------------------------------------------*/
+static int
+add_defchild(rt)
+ struct rtentry *rt;
+{
+ struct v6router *v6r = defrtr.v6r_next;
+ struct sockaddr_in6 *dst;
+
+ /*
+ * What if this child turns out to be a tunneling route? OUCH!
+ *
+ * One other thing: If rt_key(rt) is link-local, but it hit from
+ * the default router, then perhaps it should
+ * go with the ipv6_onlink_query() route.
+ */
+
+ dst = (struct sockaddr_in6 *)rt_key(rt);
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
+ /*
+ * Since there are no priorities, just pick one. For now, I guess I'll
+ * Just pick v6r_next.
+ */
+ if (v6r != &defrtr)
+ {
+ struct v6child *v6c;
+ int rc = 0;
+
+ DPRINTF(IDL_EVENT,("About to do rt_setgate. rt before is:\n"));
+ DDO(IDL_EVENT,dump_rtentry(rt));
+ DPRINTF(IDL_EVENT,("Args are (dst, parent->rt_gateway, v6r_rt):\n"));
+ DDO(IDL_EVENT,dump_smart_sockaddr((struct sockaddr *)dst);\
+ dump_smart_sockaddr(rt->rt_parent->rt_gateway);\
+ dump_rtentry(v6r->v6r_rt));
+
+ if (rt_key(v6r->v6r_rt)->sa_family != AF_INET6)
+ {
+ /*
+ * Hmmm, we must be a tunneling default route. Current
+ * conventional wisdom is that we can only see one in our
+ * "default router list," and that it was added manually. Using
+ * that assumption, get things right, including chasing the
+ * parent and using its rt_gateway sockaddr for rt_setgate.
+ */
+ rt_setgate(rt,(struct sockaddr *)dst,rt->rt_parent->rt_gateway);
+ rc = 1;
+ }
+ else
+ {
+ rt_setgate(rt,(struct sockaddr *)dst,rt_key(v6r->v6r_rt));
+ rt->rt_flags &= ~RTF_TUNNEL; /* In case cloned off an initially
+ tunnelling default route. */
+ }
+
+ DDO(IDL_GROSS_EVENT,\
+ printf("After rt_setgate, rt is:\n");dump_rtentry(rt));
+
+ rt->rt_llinfo = malloc(sizeof(*v6c),M_DISCQ,M_NOWAIT);
+ if (rt->rt_llinfo == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("add_defchild(): malloc failed.\n"));
+ /* Perhaps route should be freed here */
+ }
+ else
+ {
+ v6c = (struct v6child *)rt->rt_llinfo;
+ insque(v6c,&v6r->v6r_children);
+ v6c->v6c_route = rt;
+ }
+ return rc;
+ }
+ else
+ {
+ /*
+ * Default route hit, but no default router children.
+ *
+ * Perhaps delete it?
+ */
+ DPRINTF(IDL_ERROR,\
+ ("Default route hit, but no default routers in list.\n"));
+ }
+
+ /* Either on-link or in trouble. Needs to be coded. */
+ DPRINTF(IDL_ERROR, ("On-link or in trouble in ipv6_rtrequest\n"));
+
+ /*
+ * Convert off-link entry to indeterminate on-link entry.
+ * Subsequent code after a call to this function will do the right thing.
+ * (ipv6_rtrequest code to handle new on-link neighbors follows this.)
+ */
+ rt->rt_flags = RTF_UP|RTF_LLINFO|RTF_HOST;
+ rt->rt_llinfo = NULL;
+ rt->rt_ifa->ifa_refcnt--;
+ rt->rt_ifa = NULL;
+ rt->rt_ifp = NULL;
+ bzero(rt->rt_gateway,rt->rt_gateway->sa_len);
+ rt->rt_gateway->sa_family = AF_LINK;
+ rt->rt_gateway->sa_len = sizeof(struct sockaddr_dl);
+ rt->rt_rmx.rmx_mtu = 0;
+
+ /*
+ * QUESTION: If I do this, should I call ipv6_nsolicit() to complete
+ * this conversion from default route child to non-deterministic
+ * on-link neighbor?
+ *
+ * ANSWER: Yes, but not here. Just return 1, and let later code take
+ * care of the nsolicit.
+ */
+ return 1;
+}
+
+/*----------------------------------------------------------------------
+ * add_netchild():
+ * Given a network route child (i.e. non-default),
+ * put this child in the parent's descendant list.
+ ----------------------------------------------------------------------*/
+
+static void
+add_netchild(rt)
+ struct rtentry *rt;
+{
+ struct v6router *v6r;
+ struct v6child *v6c;
+
+ if (rt->rt_parent == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("add_netchild: No parent route. (Must be manually added.)\n"));
+ return;
+ }
+ v6r = (struct v6router *)rt->rt_parent->rt_llinfo;
+
+ v6c = malloc(sizeof(*v6c),M_DISCQ,M_NOWAIT);
+ if (v6c == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("add_netchild(): malloc failed.\n"));
+ /* Perhaps should free route here */
+ return;
+ }
+ insque(v6c,v6r->v6r_children.v6c_next);
+ v6c->v6c_route = rt;
+ v6c->v6c_parent = v6r;
+ rt->rt_llinfo = (caddr_t)v6c;
+}
+
+/*----------------------------------------------------------------------
+ * add_non_default():
+ * add a non-default routing entry.
+ *
+ ----------------------------------------------------------------------*/
+static void
+add_non_default(rt)
+ struct rtentry *rt;
+{
+ struct v6router *newbie;
+
+ if (rt_key(rt)->sa_family != AF_INET6)
+ {
+ DPRINTF(IDL_ERROR,
+ ("IPv6 off-net non-tunnel route w/o IPv6 gwaddr.\n"));
+ return;
+ }
+
+ newbie = malloc(sizeof(*newbie),M_DISCQ,M_NOWAIT);
+ if (newbie == NULL)
+ {
+ DPRINTF(IDL_ERROR, ("add_non_default(): malloc failed.\n"));
+ /* Should probably free route */
+ return;
+ }
+ bzero(newbie,sizeof(*newbie));
+ newbie->v6r_children.v6c_next = newbie->v6r_children.v6c_prev =
+ &newbie->v6r_children;
+ insque(newbie,&nondefrtr);
+
+ /*
+ * On creation of rt, rt_setgate() was called, therefore we take on blind
+ * faith that an appropriate neighbor cache entry was created. If not,
+ * we're in deep trouble.
+ */
+ newbie->v6r_rt = rt->rt_gwroute;
+ rt->rt_llinfo = (caddr_t)newbie;
+}
+
+
+/*----------------------------------------------------------------------
+ * ipv6_setrtifa():
+ * Set route's interface address. Source address selection for
+ * a route.
+ *
+ ----------------------------------------------------------------------*/
+void
+ipv6_setrtifa(rt)
+ struct rtentry *rt;
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)rt_key(rt);
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *)rt->rt_ifa->ifa_addr;
+ struct in6_ifaddr *src_in6ifa = (struct in6_ifaddr *)rt->rt_ifa;
+ struct sockaddr_in6 *mask = (struct sockaddr_in6 *)rt_mask(rt);
+ struct ifaddr *ifa = NULL;
+
+ DPRINTF(IDL_EVENT,("Entering ipv6_setrtifa.\n"));
+
+ /*
+ * If I can't find a "better" source address, stick with the one I got.
+ *
+ * ASSUMES: rt_ifp is set PROPERLY. This NEEDS to be true.
+ */
+
+ /*
+ * If link-local, use link-local source.
+ */
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ {
+ if (IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr))
+ return;
+#if __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if ((current->sin6_family != AF_INET6) ||
+ IS_DEPRECATED((struct in6_ifaddr *)ifa))
+ continue; /* For loop. */
+ if (IN6_IS_ADDR_LINKLOCAL(&current->sin6_addr))
+ break; /* For loop. */
+ }
+ if (ifa == NULL)
+ return; /* We're in real trouble. */
+ }
+
+ /*
+ * If v4-compatible, use v4-compatible source address.
+ */
+ if (ifa == NULL && (sin6->sin6_addr.in6a_words[0] == 0 &&
+ sin6->sin6_addr.in6a_words[1] == 0 &&
+ sin6->sin6_addr.in6a_words[2] == 0 &&
+ sin6->sin6_addr.in6a_words[3] != htonl(1) &&
+ (mask == NULL || mask->sin6_len >=
+ sizeof(*mask) - sizeof(struct in_addr))))
+ {
+ if (IN6_IS_ADDR_V4COMPAT(&src->sin6_addr) && !IS_DEPRECATED(src_in6ifa))
+ return;
+#if __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if ((current->sin6_family != AF_INET6)
+ || IS_DEPRECATED((struct in6_ifaddr *)ifa))
+ continue; /* For loop. */
+ if (IN6_IS_ADDR_V4COMPAT(&current->sin6_addr))
+ break; /* For loop. */
+ }
+ /*if (ifa == NULL)
+ return;*/ /* if ifa == NULL here, pretend it's global, because
+ it is global! */
+ }
+
+ /*
+ * If site-local, use a site-local source address.
+ */
+
+ if (ifa == NULL && IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+ {
+ if (IN6_IS_ADDR_SITELOCAL(&src->sin6_addr) && !IS_DEPRECATED(src_in6ifa))
+ return;
+#ifdef __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ {
+ struct sockaddr_in6 *current = (struct sockaddr_in6 *)ifa->ifa_addr;
+
+ if ((current->sin6_family != AF_INET6)
+ || IS_DEPRECATED((struct in6_ifaddr *)ifa))
+ continue; /* For loop. */
+ if (IN6_IS_ADDR_SITELOCAL(&current->sin6_addr))
+ break; /* For loop. */
+ }
+ if (ifa == NULL)
+ return; /* We don't want to potentially pollute the global
+ internet with site-local traffic. If you feel
+ differently, comment out this ifa == NULL check
+ and fallthrough. */
+ }
+
+ if (ifa == NULL)
+ {
+ /*
+ * At this point, the address _could_ be anything, but is most likely
+ * a globally routable address that didn't fit into the previous
+ * categories.
+ *
+ * Do gyrations iff rt->rt_ifa's address is link-local and the dest
+ * isn't or the src address previously picked is deprecated.
+ */
+ DPRINTF(IDL_GROSS_EVENT,("In default case of ipv6_setrtifa().\n"));
+ if ((!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
+ IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr)) || IS_DEPRECATED(src_in6ifa))
+ {
+ struct ifaddr *ifa_compat = 0, *ifa_site = 0;
+
+ /*
+ * For now, pick a non-link-local address using the following
+ * order of preference: global, compatible, site-local.
+ *
+ */
+#ifdef __FreeBSD__
+ for (ifa = rt->rt_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next)
+#else /* __FreeBSD__ */
+#if __NetBSD__ || __OpenBSD__
+ for (ifa = rt->rt_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
+#else /* __NetBSD__ || __OpenBSD__ */
+ for (ifa = rt->rt_ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ */
+ if (ifa->ifa_addr->sa_family == AF_INET6 &&
+ !IS_DEPRECATED((struct in6_ifaddr *)ifa) &&
+ !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
+ if (IN6_IS_ADDR_V4COMPAT(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
+ ifa_compat = ifa;
+ else if (IN6_IS_ADDR_SITELOCAL(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
+ ifa_site = ifa;
+ else /* globally routable address */
+ break;
+ if (!ifa)
+ ifa = ifa_compat ? ifa_compat : ifa_site;
+ }
+ }
+
+ if (ifa != NULL)
+ {
+ /*
+ * Q: Do I call ipv6_rtrequest (through ifa_rtrequest) if I
+ * change ifa's on a route?
+ */
+ /*if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
+ rt->rt_ifa->ifa_rtrequest(RTM_DELETE,rt,rt->rt_gateway);*/
+ IFAFREE(rt->rt_ifa);
+ ifa->ifa_refcnt++;
+ rt->rt_ifa = ifa;
+ rt->rt_ifp = ifa->ifa_ifp; /* Is this desirable? */
+ /*if (ifa->ifa_rtrequest)
+ ifa->ifa_rtrequest(RTM_ADD, rt, rt->rt_gateway);*/
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ * ipv6_rtrequest():
+ * IPv6-specific route ADD, RESOLVE, and DELETE function.
+ * Typically called from route.c's rtrequest() function to handle
+ * the IPv6 unique things.
+ ----------------------------------------------------------------------*/
+
+void
+ipv6_rtrequest(req, rt, sa)
+ int req;
+ register struct rtentry *rt;
+ struct sockaddr *sa; /* Ignored parameter, I believe. */
+{
+ static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}, *sdlp;
+ int spray = 0;
+
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0000-Starting.\n"));
+ DPRINTF(IDL_EVENT,("req = %d, rt = 0x%lx, sa = 0x%lx\n",req,\
+ (unsigned long)rt, (unsigned long)sa));
+ DDO(IDL_EVENT,dump_rtentry(rt));
+
+ switch (req)
+ {
+ case RTM_ADD:
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0100-case RTM_ADD:.\n"));
+
+ /*
+ * Set route Path MTU if not already set on brand new route.
+ */
+ if (rt->rt_rmx.rmx_mtu == 0)
+ rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
+
+ /*
+ * An explicitly added "all-ones" mask or no mask at all should be
+ * a host route.
+ */
+
+ if (rt_mask(rt) == NULL ||
+ (rt_mask(rt)->sa_len == sizeof(struct sockaddr_in6) &&
+ IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)rt_mask(rt))->sin6_addr,
+ &in6_allones.sin6_addr))
+ )
+ rt->rt_flags |= RTF_HOST;
+
+ /*
+ * Explicitly add the cloning bit to the route entry.
+ * (Do we still want to do this?)
+ *
+ * One fallout from this is that if someone wants to disable cloning,
+ * the disabling will have to be explicitly done after adding the route.
+ */
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ {
+ /*
+ * All non-host routes that aren't m-cast are cloning!
+ */
+ if (!IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr))
+ rt->rt_flags |= RTF_CLONING;
+
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0150-Calling tunnel_parent().\n"));
+ tunnel_parent(rt);
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ if (rt_mask(rt)->sa_len == 0)
+ {
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ /*
+ * Add tunnel to default router list.
+ */
+ ipv6_add_defrouter_rtrequest(rt);
+ }
+ else
+ {
+ struct v6router *v6r;
+ /*
+ * The "default" route has just been added.
+ * Do default router stuff here.
+ *
+ * Search router list to see if I'm already there.
+ * this avoids double-adding.
+ */
+
+ for (v6r = defrtr.v6r_next; v6r != &defrtr;
+ v6r = v6r->v6r_next)
+ if (v6r->v6r_rt == rt->rt_gwroute)
+ break;
+
+ if (v6r == &defrtr)
+ {
+ DPRINTF(IDL_ERROR,
+ ("Calling ipv6_add_defrouter from RTM_ADD.\n"));
+ ipv6_add_defrouter_rtrequest(rt);
+ }
+
+ rt->rt_gwroute->rt_flags |= RTF_ISAROUTER;
+ }
+ rt->rt_flags |= RTF_DEFAULT;
+ }
+ else
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ /*
+ * Perhaps if the tunnel bit is already set here, don't
+ * do anything.
+ */
+ }
+ else
+ {
+ /*
+ * A non-default network route has just been added.
+ * Do non-default router stuff here.
+ */
+ add_non_default(rt);
+ }
+ else if (!(rt->rt_flags & RTF_TUNNEL))
+ {
+ /*
+ * Interface route (i.e. on-link non-host prefix).
+ */
+ DPRINTF(IDL_EVENT,("Setting up i/f route.\n"));
+ rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl);
+ sdlp = (struct sockaddr_dl *)rt->rt_gateway;
+ sdlp->sdl_type = rt->rt_ifp->if_type;
+ sdlp->sdl_index = rt->rt_ifp->if_index;
+ }
+ }
+
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0199-Falling out of RTM_ADD:.\n"));
+ /* FALLTHROUGH... */
+ case RTM_RESOLVE:
+ {
+#ifdef __FreeBSD__
+ extern struct ifnet loif[];
+ struct ifnet *loifp = loif;
+#else /* __FreeBSD__ */
+#if (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802))
+ extern struct ifnet loif;
+#else /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+ extern struct ifnet *loifp;
+#endif /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+#endif /* __FreeBSD__ */
+
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0200-Now in case RTM_RESOLVE:.\n"));
+
+ /*
+ * First, check if ifa addr is same as route addr,
+ * If yes, then wire the loopback interface as the destination.
+ */
+ if ((rt->rt_flags & RTF_HOST) && rt->rt_ifa &&
+ IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
+ &I6A_SIN(rt->rt_ifa)->sin6_addr)
+#if (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802))
+ && loifp
+#endif /* (defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)) */
+ )
+ {
+ /* Change ifp to loopback and gateway to itself. */
+#ifdef __FreeBSD__
+ rt->rt_ifp = loifp;
+ rt->rt_gateway = rt_key(rt);
+ rt->rt_rmx.rmx_mtu = loifp->if_mtu;
+#else /* __FreeBSD__ */
+#if (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802))
+ rt->rt_ifp = &loif;
+ rt->rt_gateway = rt_key(rt);
+ rt->rt_rmx.rmx_mtu = loif.if_mtu;
+#else /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+ rt->rt_ifp = loifp;
+ rt->rt_gateway = rt_key(rt);
+ rt->rt_rmx.rmx_mtu = loifp->if_mtu;
+#endif /* (!defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802)) */
+#endif /* __FreeBSD__ */
+
+ if (rt->rt_parent)
+ rt->rt_flags &= ~(RTF_STATIC | RTF_TUNNEL);
+
+ break;
+ };
+ }
+
+ /*
+ * Second, check the accuracy of the route's interface address (ifa).
+ * If possible, update the route's ifa such that it is the "best"
+ * source address for packets bound for this destination.
+ *
+ * This is done to any routes and is subtle enough
+ * to merit its own separate function:
+ */
+
+ ipv6_setrtifa(rt);
+
+ if (!(rt->rt_flags & RTF_HOST))
+ {
+ /*
+ * This should be the "none of the above" case. For now,
+ * do nothing and just return.
+ */
+ DPRINTF(IDL_GROSS_EVENT,("In RESOLVE/lateADD, but not RTF_HOST.\n"));
+ break;
+ }
+
+ /*
+ * At this point, I know (1) I'm a host route which has either been
+ * added manually or (2) I have cloned off to become either:
+ *
+ * a tunnelling route child,
+ * RTF_TUNNEL is set on this entry.
+ * I should be okay, although tunnel state may become necessary.
+ * I'm a next-hop cache entry.
+ *
+ * a default router child,
+ * a non-default network route child,
+ * RTF_GATEWAY is set on this entry.
+ * I have to insert this child into the router's children list.
+ * My child entry hangs off rt_llinfo. I'm a next-hop cache
+ * entry.
+ *
+ * an interface (i.e. neighbor) route child,
+ * a link-local (i.e. off link-local mask) route child
+ * rt->rt_gateway is AF_LINK
+ * I should set RTF_LLINFO, and set up a discq entry because I'm
+ * a neighbor cache entry. (Neighbor caches double as next-hop
+ * cache entries.)
+ */
+
+ if (rt->rt_gateway == NULL)
+ panic("ipv6_rtrequest(): No rt_gateway at the RTM_RESOLVE code.\n");
+
+ /*
+ * Order of bit checking is very important. I check TUNNEL first,
+ * because it's REALLY special. Then I check GATEWAY or not.
+ */
+
+ if (rt->rt_parent != NULL && rt_mask(rt->rt_parent)->sa_len == 0)
+ {
+ DPRINTF(IDL_EVENT,("Cloning off default route!\n"));
+ /*
+ * Find a default router out of the list, and assign it to this
+ * child. Clear or add tunnel bits if necessary.
+ */
+
+ rt->rt_flags &= ~RTF_STATIC;
+ /*
+ * If add_defchild returns 0, then either it's a tunneling
+ * default route, or a link-local converted to a neighbor entry.
+ *
+ * Set 'spray' to the result of add_defchild, because if it is
+ * a neighbor entry (see AF_LINK case below), then spray will
+ * indicate the need to do some pre-emptive neighbor adverts.
+ */
+ if (!(spray = add_defchild(rt))) /* If an actual off-net default
+ route... */
+ break;
+DPRINTF(IDL_ERROR,("add_defchild returned 1, either on-link or tunnel.\n"));
+ /* Otherwise, either tunnel or converted into LINK address. */
+ }
+ else rt->rt_llinfo = NULL; /* Is this already done? */
+
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("ipv6_rtrequest():0250-Calling tunnel_child().\n"));
+ tunnel_child(rt); /* Be careful if manually added RTF_TUNNEL. */
+ break; /* We're done with this Tunnel host route. */
+ }
+
+ if (rt->rt_flags & RTF_GATEWAY)
+ {
+ add_netchild(rt);
+ if (req == RTM_RESOLVE)
+ rt->rt_flags &= ~RTF_STATIC;
+ break;
+ }
+
+ if (rt->rt_gateway->sa_family == AF_LINK)
+ {
+ /*
+ * I may enter here even after RTF_GATEWAY check because
+ * get_defrouter() may convert the route entry into an on-link one!
+ */
+ struct discq *dq;
+
+ rt->rt_llinfo = malloc(sizeof(*dq),M_DISCQ,M_NOWAIT);
+ dq = (struct discq *)rt->rt_llinfo;
+ if (dq == NULL)
+ {
+ DPRINTF(IDL_ERROR,
+ ("ipv6_rtrequest(): malloc for rt_llinfo failed.\n"));
+ /* Probably should free route */
+ break;
+ }
+ bzero(dq,sizeof(*dq));
+ dq->dq_rt = rt;
+ dq->dq_unanswered = -1;
+ rt->rt_flags |= RTF_LLINFO;
+ insque(dq,&dqhead);
+ /*
+ * State is already INCOMPLETE, because of link stuff.
+ *
+ * If this neigbor entry is caused by add_defchild (i.e. link-local)
+ * spray nsolicit out all interfaces.
+ */
+
+ if (spray)
+ ipv6_nsolicit(NULL,NULL,rt);
+
+ break;
+ }
+ else
+#if 0
+ /*
+ * support for PPP goes here.
+ * Frame Relay, and ATM will probably probably be handled by the
+ * AF_LINK case. Other AF_* will be covered later.
+ */
+ panic("ipv6_rtrequest: Not tunnel, not off-net, and not neighbor.");
+#else /* 0 */
+ {
+ DPRINTF(IDL_ERROR, ("ipv6_rtrequest: Not tunnel, not off-net, and not neighbor. rt is:\n"));
+ DDO(IDL_ERROR, dump_rtentry(rt));
+ }
+#endif /* 0 */
+
+ break;
+ case RTM_DELETE:
+ DPRINTF(IDL_GROSS_EVENT,("ipv6_rtrequest():0300-Now in case RTM_DELETE.\n"));
+ /*
+ * The FLUSH call ('route flush...') checks the ifp, perhaps I should
+ * fill that just in case.
+ */
+
+ if (rt->rt_ifp == NULL)
+ rt->rt_ifp = rt->rt_ifa->ifa_ifp;
+
+ if ((rt->rt_flags & RTF_HOST) == 0)
+ {
+ /*
+ * Clean up after network routes.
+ */
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("Cleaning up tunnel route.\n"));
+ tunnel_parent_clean(rt);
+ if (rt_mask(rt)->sa_len != 0)
+ return; /* If default route is RTF_TUNNEL, then continue. */
+ }
+ if (rt->rt_flags & RTF_GATEWAY)
+ if (rt_mask(rt)->sa_len == 0)
+ {
+ struct v6router *v6r;
+
+ DPRINTF(IDL_GROSS_EVENT,\
+ ("Cleaning up THE default route.\n"));
+ /*
+ * Find manually added default route thing, and clean up that
+ * entry.
+ *
+ * If user deletes default route added by receiving router
+ * adverts, then the router is still on the default router
+ * list, and needs to be added back.
+ */
+ for (v6r = defrtr.v6r_next; v6r != &defrtr;
+ v6r = v6r->v6r_next)
+ {
+ /*
+ * PROBLEM: rt->rt_gwroute, which is what I REALLY
+ * want to check against, is gone at this
+ * point.
+ *
+ * POSSIBLE SOLUTION: Do a check with rt_key(v6r->v6r_rt)
+ * and rt->rt_gateway, but there
+ * is the question of tunneling
+ * default routes, etc., which may
+ * mean doing a masked match.
+ */
+#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
+ /*
+ * PRESENTED SOLUTION: Try checking the expiration
+ * (easy giveaway for manually
+ * added default routes), followed
+ * by blatant sockaddr compares
+ * (will nail all other normal
+ * cases), followed by sa_family
+ * check (may be unneccesary).
+ */
+ if (v6r->v6r_expire == 0 ||
+ equal(rt_key(v6r->v6r_rt),rt->rt_gateway) ||
+ (rt->rt_gateway->sa_family != AF_INET6 &&
+ rt->rt_gateway->sa_family ==
+ rt_key(v6r->v6r_rt)->sa_family))
+ break;
+ }
+ if (v6r != &defrtr)
+ (void)ipv6_delete_defrouter(v6r);
+ if (defrtr.v6r_next != &defrtr)
+ {
+ /*
+ * Somehow re-add the default route.
+ *
+ * The default route has been deleted from the radix
+ * tree already, so re-adding should be relatively
+ * straightforward.
+ */
+ DPRINTF(IDL_ERROR,
+ ("Auto-added default routers still there!\n"));
+ }
+ }
+ else
+ {
+ struct v6router *v6r = (struct v6router *)rt->rt_llinfo;
+ struct v6child *v6c;
+
+ /* Non-default router. */
+ DPRINTF(IDL_GROSS_EVENT,\
+ ("Cleaning up a non-{default,host} route.\n"));
+
+ if (v6r == NULL)
+ panic("Non-default w/o v6router entry.");
+
+ v6c = v6r->v6r_children.v6c_next;
+ while (v6c != v6c->v6c_next)
+ {
+ DPRINTF(IDL_ERROR,("Calling RTM_DELETE of child.\n"));
+ /* rtrequest() should remove child from the linked list */
+ rtrequest(RTM_DELETE,rt_key(v6c->v6c_route),NULL,NULL,0,
+ NULL);
+ v6c = v6r->v6r_children.v6c_next;
+ }
+ remque(v6r);
+ free(v6r,M_DISCQ);
+ }
+ /* Anything else that isn't a HOST needs no work, so return. */
+ return;
+ }
+
+ DPRINTF(IDL_EVENT,("v6_discov_rtrequest() deleting a host\n"));
+ DPRINTF(IDL_EVENT,("I'm at a host-route point.\n"));
+
+ if (rt->rt_flags & RTF_TUNNEL)
+ {
+ DPRINTF(IDL_EVENT,("Tunneling child.\n"));
+ /*
+ * PROBLEM: Following check will die if parent went bye-bye.
+ * See if you can fix in another way.
+ */
+ /*if (rt_mask(rt->rt_parent)->sa_len == 0)*/
+ if (rt->rt_flags & RTF_DEFAULT)
+ {
+ /*
+ * Tunneling default route child. Clean off meta-state.
+ */
+ struct v6child *v6c = (struct v6child *)rt->rt_llinfo;
+
+ DPRINTF(IDL_ERROR,("Cleaning tunnel-default child.\n"));
+ remque(v6c);
+ rt->rt_llinfo = NULL;
+ free(v6c,M_DISCQ);
+ }
+ tunnel_child_clean(rt);
+ }
+ else if (rt->rt_flags & RTF_GATEWAY)
+ {
+ struct v6child *v6c = (struct v6child *) rt->rt_llinfo;
+
+ if (v6c == NULL)
+ {
+ if (rt->rt_flags & RTF_HOST)
+ DPRINTF(IDL_ERROR,
+ ("no v6c in RTM_DELETE of RTF_GATEWAY.\n"));
+ }
+ else
+ {
+ remque(v6c);
+ rt->rt_llinfo = NULL;
+ free(v6c,M_DISCQ);
+ }
+ }
+ else if (rt->rt_flags & RTF_LLINFO)
+ {
+ struct discq *dq;
+
+ /* Neighbor cache entry. */
+ if (rt->rt_flags & RTF_ISAROUTER)
+ {
+ struct v6router *v6r,*head;
+
+ /* Clean up all children of this router. */
+
+ DPRINTF(IDL_GROSS_EVENT,("Cleaning up router neighbor.\n"));
+ if (rt->rt_flags & RTF_DEFAULT)
+ head = &defrtr;
+ else
+ {
+ head = &nondefrtr;
+ /* Q: Do I want to delete the actual network route too? */
+ /* Q2: Do I want to delete the children? */
+ }
+
+ for (v6r = head->v6r_next; v6r != head; v6r = v6r->v6r_next)
+ if (v6r->v6r_rt == rt)
+ break;
+ if (v6r == head)
+ {
+ /*
+ * At many addresses per interface, this isn't a huge
+ * problem, because a router's on-link address might not
+ * be tied to a router entry.
+ */
+ DPRINTF(IDL_EVENT,
+ ("Router neighbor inconsistency.\n"));
+ }
+ else
+ if (head == &defrtr)
+ (void) ipv6_delete_defrouter(v6r); /* This gets rid of
+ children, too! */
+ }
+
+ dq = (struct discq *)rt->rt_llinfo;
+ rt->rt_flags &= ~RTF_LLINFO;
+ if (dq == NULL)
+ panic("No discq or other rt_llinfo in RTM_DELETE");
+ remque(dq);
+ rt->rt_llinfo = NULL;
+ if (dq->dq_queue)
+ {
+ /* Send ICMP unreachable error. */
+ ipv6_icmp_error(dq->dq_queue, ICMPV6_UNREACH,
+ ICMPV6_UNREACH_ADDRESS, 0);
+ /* m_freem(dq->dq_queue);*/
+ }
+ free(dq,M_DISCQ);
+ }
+ else
+ {
+ DPRINTF(IDL_GROSS_EVENT,\
+ ("Freeing self-wired address. Doing nothing.\n"));
+ }
+ break;
+ }
+ DPRINTF(IDL_GROSS_EVENT,("ipv6_rtrequest():1000-Finished.\n"));
+}
+
+/* EOF */