summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/ipv6_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/ipv6_input.c')
-rw-r--r--sys/netinet6/ipv6_input.c1449
1 files changed, 0 insertions, 1449 deletions
diff --git a/sys/netinet6/ipv6_input.c b/sys/netinet6/ipv6_input.c
deleted file mode 100644
index a0e01faf481..00000000000
--- a/sys/netinet6/ipv6_input.c
+++ /dev/null
@@ -1,1449 +0,0 @@
-/*
-%%% 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/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/route.h>
-#ifdef __FreeBSD__
-#include <net/netisr.h>
-#endif /* __FreeBSD__ */
-
-#include <netinet/in.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 <netinet6/ipv6_addrconf.h>
-
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/in_pcb.h>
-
-#if __OpenBSD__
-#undef IPSEC
-#ifdef NRL_IPSEC
-#define IPSEC 1
-#endif /* NRL_IPSEC */
-#endif /* __OpenBSD__ */
-
-#ifdef IPSEC
-#include <sys/osdep.h>
-#include <net/netproc.h>
-#include <net/netproc_var.h>
-
-#include <netsec/ipsec.h>
-#endif /* IPSEC */
-
-#if __FreeBSD__
-#include <sys/sysctl.h>
-#endif /* __FreeBSD__ */
-
-#ifdef DEBUG_NRL
-#include <sys/debug.h>
-#else /* DEBUG_NRL */
-#if __OpenBSD__
-#include <netinet6/debug.h>
-#else /* __OpenBSD__ */
-#include <sys/debug.h>
-#endif /* __OpenBSD__ */
-#endif /* DEBUG_NRL */
-
-/*
- * Globals
- */
-
-u_char ipv6_protox[IPPROTO_MAX]; /* For easy demuxing to HLP's. */
-struct ipv6stat ipv6stat; /* Stats. */
-struct in6_ifaddr *in6_ifaddr = NULL; /* List of IPv6 addresses. */
-struct in6_ifnet *in6_ifnet = NULL; /* List of IPv6 interfaces. */
-struct ipv6_fragment *ipv6_fragmentq = NULL; /* Fragment reassembly queue */
-struct ifqueue ipv6intrq;
-struct route6 ipv6forward_rt;
-
-/*
- * External globals
- */
-
-extern int ipv6forwarding; /* See in6_proto.c */
-extern int ipv6rsolicit; /* See in6_proto.c */
-extern int ipv6_defhoplmt; /* See in6_proto.c */
-extern int ipv6qmaxlen; /* See in6_proto.c */
-extern struct protosw inet6sw[]; /* See in6_proto.c */
-extern struct domain inet6domain; /* See in6_proto.c */
-extern struct discq dqhead; /* See ipv6_discovery.c */
-
-/*
- * Funct. prototypes.
- */
-
-void ipv6_forward __P((struct mbuf *));
-void ipv6_discovery_init __P((void));
-extern int ipv6_clean_nexthop __P((struct radix_node *,void *));
-#if !__FreeBSD__
-int sysctl_int __P((void *, size_t *, void *, size_t, int *));
-/* For reasons somewhat unknown, <sys/mbuf.h> doesn't prototype this */
-struct mbuf *m_split __P((register struct mbuf *, int, int));
-#endif /* !__FreeBSD__ */
-
-void ipv6intr __P((void));
-int ipv6_enabled __P((struct ifnet *));
-
-static struct mbuf *ipv6_saveopt(caddr_t p, int size, int type, int level);
-#if 0
-static struct mbuf *ipv6_savebag(struct mbuf *m, int level);
-#endif /* 0 */
-
-/*----------------------------------------------------------------------
- * IPv6 initialization function.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_init()
-{
- register struct protosw *pr;
- register int i;
-
- DPRINTF(GROSSEVENT,("IPv6 initializing..."));
-
- bzero(&ipv6stat,sizeof(struct ipv6stat));
-
- pr = pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW);
- if (pr == 0)
- panic("ipv6_init");
-
- /*
- * To call the right IPv6 next header function off next header type, have
- * next header numbers index the protocol switch, like protocols in IP
- * (default the switch for this). Otherwise, just switch off into normal
- * (TCP,UDP) stuff.
- *
- * Initialize ipv6_protox[].
- */
-
- for (i = 0; i < IPPROTO_MAX; i++)
- ipv6_protox[i] = pr - inet6sw;
- for (pr = inet6domain.dom_protosw;
- pr < inet6domain.dom_protoswNPROTOSW; pr++)
- if (pr->pr_domain->dom_family == PF_INET6
- && pr->pr_protocol != IPPROTO_RAW )
- ipv6_protox[pr->pr_protocol] = pr - inet6sw;
-
- /*
- * Initialize discovery stuff.
- */
-
- ipv6_discovery_init();
-
- /*
- * Initialize addrconf stuff.
- */
-
- addrconf_init();
-
- /*
- * Initialize IPv6 i/f queue.
- */
-
- ipv6intrq.ifq_maxlen = ipv6qmaxlen;
-
-#if 0 /* defined(INET6) && defined(IPSEC) */
- /*
- * Initialise IPsec
- */
- ipsec_init();
-#endif /* defined(INET6) && defined(IPSEC) */
-
- DPRINTF(GROSSEVENT,("...done\n"));
-}
-
-/*----------------------------------------------------------------------
- * IPv6 input queue interrupt handler.
- ----------------------------------------------------------------------*/
-
-void
-ipv6intr()
-{
- struct mbuf *m;
- int s;
-
- while (1) /* Keep yanking off packets until I hit... */
- {
- s = splimp();
- IF_DEQUEUE(&ipv6intrq, m);
- splx(s);
-
- if (m == NULL)
- {
- return; /* ...HERE. THIS is how I exit this endless loop. */
- }
-
-#if 0 /* def IPSEC */
- m->m_flags &= ~(M_AUTHENTIC | M_DECRYPTED);
-#endif /* IPSEC */
-
- ipv6_input(m, 0);
- }
-}
-
-#if __FreeBSD__
-NETISR_SET(NETISR_IPV6, ipv6intr);
-#endif /* __FreeBSD__ */
-
-/*----------------------------------------------------------------------
- * Actual inbound (up the protocol graph from the device to the user)
- * IPv6 processing.
- ----------------------------------------------------------------------*/
-
-#if __OpenBSD__
-void
-#if __STDC__
-ipv6_input(struct mbuf *incoming, ...)
-#else /* __STDC__ */
-ipv6_input(incoming, va_alist)
- struct mbuf *m;
- va_dcl
-#endif /* __STDC__ */
-#else /* __OpenBSD__ */
-void
-ipv6_input(incoming, extra)
- struct mbuf *incoming;
- int extra;
-#endif /* __OpenBSD__ */
-{
- struct ipv6 *header;
- struct in6_ifaddr *i6a = NULL;
- struct in6_multi *in6m = NULL;
- int jumbogram = 0;
- uint8_t nexthdr;
- int payload_len;
-#if __OpenBSD__
- va_list ap;
- int extra;
-#endif /* __OpenBSD__ */
-
- DPRINTF(GROSSEVENT,("ipv6_input(struct mbuf *incoming=%08lx, int extra=%x)\n", (unsigned long)incoming, extra));
- DPRINTF(IDL_FINISHED,("incoming->m_data = %08lx, & 3 = %lx\n", (unsigned long)incoming->m_data, (unsigned long)incoming->m_data & 3));
-
-#if __OpenBSD__
- va_start(ap, incoming);
- extra = va_arg(ap, int);
- va_end(ap);
-#endif /* __OpenBSD__ */
-
- /*
- * Can't do anything until at least an interface is marked as
- * ready for IPv6.
- */
-
- if (in6_ifaddr == NULL)
- {
- m_freem(incoming);
- return;
- }
-
- ipv6stat.ips_total++;
-
- /*
- * IPv6 inside something else. Discard encapsulating header(s) and
- * (maybe try to) make it look like virgin IPv6 packet.
- *
- * As with other stupid mbuf tricks, there probably is a better way.
- */
-
- if (extra) {
- struct mbuf *newpacket;
-
- DP(FINISHED, extra, d);
-
-#if IPSEC
- /* Perform input-side policy check. Drop packet if policy says to drop
- it.
-
- Note that the gist of this check is that every decapsulation
- requires a trip to input policy. For packets that end up locally,
- this is probably bad. For packets that go off-host, this is probably
- good.
-
- Right now, we err on the side of security and always check. -cmetz*/
- {
- struct sockaddr_in6 srcsa, dstsa;
-
- bzero(&srcsa, sizeof(struct sockaddr_in6));
- srcsa.sin6_family = AF_INET6;
- srcsa.sin6_len = sizeof(struct sockaddr_in6);
- srcsa.sin6_addr = mtod(incoming, struct ipv6 *)->ipv6_src;
-
- bzero(&dstsa, sizeof(struct sockaddr_in6));
- dstsa.sin6_family = AF_INET6;
- dstsa.sin6_len = sizeof(struct sockaddr_in6);
- dstsa.sin6_addr = mtod(incoming, struct ipv6 *)->ipv6_dst;
-
- /* XXX - state arg should NOT be NULL, it should be the netproc state
- carried up the stack - cmetz */
- if (netproc_inputpolicy(NULL, (struct sockaddr *)&srcsa,
- (struct sockaddr *)&dstsa, IPPROTO_IPV6,
- incoming, NULL, NULL)) {
- m_freem(incoming);
- return;
- }
- }
-#endif /* IPSEC */
-
- /*
- * Split packet into what I need, and what was encapsulating what I
- * need. Discard the encapsulating portion.
- */
-
- if (!(newpacket = m_split(incoming, extra, M_DONTWAIT))) {
- printf("ipv6_input() couldn't trim extra off.\n");
- m_freem(incoming);
- return;
- }
- newpacket->m_flags |= incoming->m_flags;
- m_freem(incoming);
- incoming = newpacket;
- extra = 0;
- }
-
- /*
- * Even before preparsing (see vitriolic comments later),
- * I need to have the whole IPv6 header at my disposal.
- */
-
-
- if (incoming->m_len < sizeof (struct ipv6) &&
- (incoming = m_pullup(incoming, sizeof(struct ipv6))) == NULL)
- {
- ipv6stat.ips_toosmall++;
- return;
- }
-
- /*
- * Check version bits immediately.
- */
-
- if ((incoming->m_data[0] >> 4) != IPV6VERSION)
- {
- ipv6stat.ips_badvers++;
- m_freem(incoming);
- return;
- }
-
- header = mtod(incoming, struct ipv6 *);
-
- DDO(IDL_FINISHED,dump_ipv6(header));
-
- /*
- * Save off payload_len because of munging later...
- */
- payload_len = ntohs(header->ipv6_length);
-
- /*
- * Check that the amount of data in the buffers
- * is as at least much as the IPv6 header would have us expect.
- * Trim mbufs if longer than we expect.
- * Drop packet if shorter than we expect.
- */
-
- if (incoming->m_pkthdr.len < payload_len + sizeof(struct ipv6))
- {
- ipv6stat.ips_tooshort++;
- m_freem(incoming);
- return;
- }
-
- if (incoming->m_pkthdr.len > payload_len + sizeof(struct ipv6)) {
- if (!payload_len) {
- jumbogram = 1; /* We might have a jumbogram here! */
- } else {
- if (incoming->m_len == incoming->m_pkthdr.len) {
- incoming->m_len = payload_len + sizeof(struct ipv6);
- incoming->m_pkthdr.len = payload_len + sizeof(struct ipv6);
- } else {
- m_adj(incoming,
- (payload_len + sizeof(struct ipv6)) - incoming->m_pkthdr.len );
- }
- }
- }
-
- /*
- * See if it's for me by checking list of i6a's. I may want to convert
- * this into a routing lookup and see if rt->rt_ifp is loopback.
- * (Might be quicker. :)
- */
-
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (IN6_ARE_ADDR_EQUAL(&I6A_SIN(i6a)->sin6_addr, &header->ipv6_dst))
- break;
- /*
- * Check for inbound multicast datagram is for us.
- */
-
- if (i6a == NULL && IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
- {
-#ifdef MROUTING
- /*
- * Do IPv6 mcast routing stuff here. This means even if I'm in the
- * m-cast group, I may have to forward the packet too. Hence, I put
- * the m-cast routing stuff HERE.
- */
-#endif
- DPRINTF(IDL_EVENT,("In multicast case. Looking up..."));
- IN6_LOOKUP_MULTI(&header->ipv6_dst,incoming->m_pkthdr.rcvif,in6m);
- if (!in6m) {
- DPRINTF(IDL_EVENT,("mcast lookup failed.\n"));
- DDO(IDL_EVENT, dump_ipv6(header));
- ipv6stat.ips_cantforward++;
- m_freem(incoming);
- return;
- }
- DPRINTF(IDL_EVENT,("succeeded.\n"));
- }
-
- /* If this datagram is for me, I'll either have one of my addresses in
- i6a, or one of my multicast groups in in6m. From the code given
- above, the condition (in6m && i6a) is always false. */
-
- if (i6a || in6m)
- {
- if (i6a) {
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- /* Don't increment stats for encapsulated pkt */
- if (!extra) {
- i6a->i6a_ifa.ifa_ipackets++;
- i6a->i6a_ifa.ifa_ibytes += incoming->m_pkthdr.len;
- };
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
-
- if (i6a->i6a_addrflags & I6AF_NOTSURE) {
- /*
- * Don't want to unicast receive packets for me unless I know this
- * is a verified unique address.
- *
- * Multicast packets will still come in and be handled.
- */
- m_freem(incoming);
- return;
- }
- };
-
- nexthdr = (mtod(incoming, struct ipv6 *))->ipv6_nexthdr;
-
- DPRINTF(IDL_EVENT, ("nexthdr = %d.\n", nexthdr));
-
- DPRINTF(IDL_EVENT, ("Calling protoswitch for %d\n", nexthdr));
-
- ipv6stat.ips_delivered++;
- (*inet6sw[ipv6_protox[nexthdr]].pr_input)
- (incoming, extra + sizeof(struct ipv6));
-
- DPRINTF(IDL_EVENT, ("Back from protoswitch for %d\n", nexthdr));
- return;
- }
-
- DDO(IDL_EVENT,printf("Might forward for dest: ");\
- dump_in6_addr(&header->ipv6_dst););
-
- if (ipv6forwarding == 0)
- {
- DPRINTF(IDL_ERROR,("ipv6_input: Would call ipv6_forward if on.\n"));
- ipv6stat.ips_cantforward++;
- m_freem(incoming);
- return;
- }
- else
- {
- /*
- * Perform hop-by-hop options, if present. Dest opt. bags, and source
- * routes SHOULD be handled by previous protosw call.
- */
- DPRINTF(IDL_FINISHED,("Calling ipv6_forward.\n"));
- ipv6_forward(incoming);
- }
-}
-
-/*----------------------------------------------------------------------
- * IPv6 reassembly code. Returns an mbuf chain if the fragment completes
- * a message, otherwise it returns NULL.
- *
- * Assumptions:
- * * The IPv6 header is at the beginning of the incoming structure
- * and has already been pulled up. I don't expect this to be
- * a problem.
- * * Fragments don't overlap (or, if they do, we can discard them).
- * They can be duplicates (which we drop), but they cannot overlap.
- * If we have IPv4->IPv6 header translation, this assumption could
- * be incorrect because IPv4 has intermediate fragmentation (which
- * you have to have in order for fragments to overlap). This is yet
- * another reason why header translation is a Bad Thing.
- * * The slowtimeo()-based routine that frees fragments will not
- * get called during the middle of this routine. This assumption
- * seems to be made in the IPv4 reassembly code. How this is so
- * actually so I have yet to discover. Requiring splnet() or some
- * sort of resource arbitration to handle this assumption being
- * incorrect could be a major hassle.
- *
- * Other comments:
- *
- * * m_split(), a valuable call that gets no press, does much magic here.
- * * Security against denial-of-service attacks on reassembly cannot be
- * provided.
- * * frag_id is more or less used as a 32-bit cookie. We don't need to
- * htonl() it, because it's either equal or it's not, no matter what
- * byte order you use.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_reasm(incoming, extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6_fraghdr *fraghdr;
- struct ipv6_fragment *fragment, **pfragment;
-
- if (incoming->m_len < extra + sizeof(struct ipv6_fraghdr))
- if (!(incoming = m_pullup(incoming, extra + sizeof(struct ipv6_fraghdr))))
- return;
-
- DP(FINISHED, OSDEP_PCAST(incoming), 08x);
-
- fraghdr = (struct ipv6_fraghdr *)(mtod(incoming, caddr_t) + extra);
- fraghdr->frag_bitsoffset = htons(fraghdr->frag_bitsoffset);
-
- /*
- * Locate reassembly queue for incoming fragment.
- */
- {
- struct ipv6 *ipv6i, *ipv6f;
-
- ipv6i = mtod(incoming, struct ipv6 *);
-
- for (pfragment = &ipv6_fragmentq; *pfragment;
- pfragment = &((*pfragment)->next)) {
-
- ipv6f = mtod((*pfragment)->prefix, struct ipv6 *);
-
- if (IN6_ARE_ADDR_EQUAL(&ipv6i->ipv6_src, &ipv6f->ipv6_src)) {
- if (IN6_ARE_ADDR_EQUAL(&ipv6i->ipv6_dst, &ipv6f->ipv6_dst)) {
- register struct ipv6_fraghdr *fhf = mtod((*pfragment)->data,
- struct ipv6_fraghdr *);
-
- if ((fraghdr->frag_id == fhf->frag_id) &&
- (fraghdr->frag_nexthdr == fhf->frag_nexthdr)) {
- break;
- }
- }
- }
- }
- }
-
- fragment = *pfragment;
-
- if (!fragment) {
- /*
- * Create a new fragment queue entry - this one looks new.
- *
- * Notice that the order of insertion is such that the newest queues
- * are at the head of the one-way list. The entry aging code takes
- * advantage of this.
- */
- MALLOC(fragment, struct ipv6_fragment *, sizeof(*fragment), M_FRAGQ,
- M_NOWAIT);
- if (!fragment) {
- DPRINTF(IDL_ERROR, ("MALLOC fragment queue entry failed.\n"));
- goto reasm_cleanup;
- };
- fragment->ttl = 60; /* 30 seconds */
- if (!(fragment->data = m_split(incoming, extra, M_DONTWAIT))) {
- free(fragment, M_FRAGQ);
- goto reasm_cleanup;
- }
-
- fragment->prefix = incoming;
-
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
-
- fragment->flags = (~fraghdr->frag_bitsoffset) & 1;
-
- fragment->next = ipv6_fragmentq;
- ipv6_fragmentq = fragment;
- return;
- }
-
- /*
- * If two packets have claimed to be the beginning or the end, we don't
- * know which is right. The easiest solution is to drop this packet.
- */
-
- if (!(fraghdr->frag_bitsoffset & 1)) {
- if (fragment->flags & 1) /* i.e. we already have the end... */
- {
- /*
- * Duplicate (ending) fragment.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- }
- else
- fragment->flags |= 1;
- }
-
- if (!(fraghdr->frag_bitsoffset & 0xFFF8)) {
- /*
- * We want to end up with the part before the frag header for the packet
- * at offset zero. (RFC1123?)
- */
- if (!(mtod(fragment->data, struct ipv6_fraghdr *)->frag_bitsoffset & 0xFFF8)) {
- /*
- * Duplicate (initial) packet.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- } else {
- struct mbuf *mbuf;
-
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
-
- m_freem(fragment->prefix);
-
- /* Save everything before the frag header */
- if (!(mbuf = m_split(incoming, extra, M_DONTWAIT))) {
- /* should probably toss whole fragment queue */
- m_freem(incoming);
- return;
- }
-
- fragment->prefix = incoming;
- incoming = mbuf;
-
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
- }
- } else
- m_adj(incoming, extra); /* Discard everything before the frag header */
-
- {
- struct mbuf *hm[3];
- int i;
-
- {
- struct mbuf *m, *pm;
-
- /*
- * Find where this fragment fits in.
- */
- for (pm = NULL, m = fragment->data; m; pm = m, m = m->m_nextpkt) {
- if (((mtod(m, struct ipv6_fraghdr *))->frag_bitsoffset & 0xFFF8) >
- (fraghdr->frag_bitsoffset & 0xFFF8)) {
- break;
- }
-
- if ((mtod(m, struct ipv6_fraghdr *))->frag_bitsoffset ==
- fraghdr->frag_bitsoffset) {
- /*
- * Duplicate fragment.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- }
- }
-
- /*
- * Right here, pm will contain the preceeding fragment to the incoming
- * one, and m will contain the succeeding fragment to the incoming one.
- *
- * This is somewhat non-obvious. hm[] is a vector of pointers to the
- * mbufs containing ipv6_fraghdrs and dm[] is a vector of pointers to
- * the mbufs at the head of each of their associated data lists. [0]
- * is the mbuf in the main chain to the immediate left of where our
- * new data should go, [1] is our new data, and [2] is the mbuf in
- * the main chain to the immediate right of where our new data should
- * go. One of [0] or [2] may be NULL.
- *
- * Each dm[n]->m_nextpkt will have that fragment's length stored.
- *
- * The reason why we do this is so we can bubble together the [0] and
- * [1] elements if possible, make the [1] = [0] if we do, then we can
- * bubble the [1] and the [2] together and it'll do the right thing.
- * We could theoretically do this for the rest of the list except that
- * it is made deliberately unecessary (we bubble on insertion until we
- * have a known-done big bubble so we don't have to do a O(N/2)
- * rescan of the list every time just to figure out whether or not
- * we're done.
- *
- * This seems really ugly, but it does the job and it may even be
- * somewhat efficient.
- */
-
- hm[0] = pm;
- hm[1] = incoming;
- hm[2] = m;
-
- if (!pm) {
- incoming->m_nextpkt = fragment->data;
- fragment->data = incoming;
- } else {
- pm->m_nextpkt = incoming;
- incoming->m_nextpkt = m;
- }
- }
-
- for (i = 0; i < 2; i++) {
- if (!hm[i] || !hm[i+1]) {
- DP(FINISHED, i, d);
- continue;
- }
-
- if ((((mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8) + hm[i]->m_pkthdr.len - sizeof(struct ipv6_fraghdr)) >
- ((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8)) {
- /*
- * Overlapping fragment.
- */
- DPRINTF(IDL_FINISHED, ("Got a dupe/overlap fragment"));
- goto reasm_cleanup;
- }
-
- if ((((mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8) + hm[i]->m_pkthdr.len - sizeof(struct ipv6_fraghdr)) ==
- ((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset
- & 0xFFF8)) {
- /*
- * If the fragments are contiguous, bubble them together:
- * Combine the data chains and increase the appropriate
- * chain data length. The second fragment header is now
- * unnecessary.
- */
- if (!((mtod(hm[i+1], struct ipv6_fraghdr *))->frag_bitsoffset & 1)){
- (mtod(hm[i], struct ipv6_fraghdr *))->frag_bitsoffset &= (~1);
- }
-
- DDO(FINISHED, dump_mchain(hm[i]));
- DDO(FINISHED, dump_mchain(hm[i+1]));
-
- /* Trim the second fragment header */
- m_adj(hm[i+1], sizeof(struct ipv6_fraghdr));
- /* Append the second fragment's data */
- m_cat(hm[i], hm[i+1]);
- /* And update the first fragment's length */
- hm[i]->m_pkthdr.len += hm[i+1]->m_pkthdr.len;
-
- hm[i]->m_nextpkt = hm[i+1]->m_nextpkt;
-
- DDO(FINISHED, dump_mchain(hm[i]));
-
- /* Hack to make the bubble happen with the m found above if we
- just bubbled with the pm found above */
- if (!i)
- hm[i+1] = hm[i];
- }
- }
- }
-
- /*
- * Now, the moment of truth. Do we have a complete packet?
- * To be done, we have to have only one packet left in the queue now that
- * we've bubbled together (i.e., one complete packet), have it at offset
- * zero (i.e., there's nothing before it), and not have its more bit set
- * (i.e., there's nothing after it). If we meet these conditions, we are
- * DONE!
- *
- * Remember when we htons()ed frag_bitsoffset? If we're done, it contains
- * a zero. I don't know of any architecture in which a zero in network
- * byte order isn't a zero in host byte order, do you?
- */
-
- if (!fragment->data->m_nextpkt &&
- !(mtod(fragment->data, struct ipv6_fraghdr *))->frag_bitsoffset) {
- uint8_t nexthdr = mtod(fragment->data, struct ipv6_fraghdr *)->frag_nexthdr;
-
- if (*pfragment)
- *pfragment = fragment->next;
-
- /*
- * Pain time.
- *
- * The fragmentation header must be removed. This requires me to rescan
- * prefix, going through each header until I figure out where the last
- * header before the fragmentation header is. Then I set that header's
- * next header to the fragmentation header's next header.
- *
- * N.B. that this means people adding new random header processing code
- * to this IPv6 implementation need to make the appropriate mods below.
- * Failure to do so will really hose you if your header appears before
- * a fragment header. It is a Good Thing to mod the code below even if
- * you don't *think* it will ever appear before a fragment header, just
- * because it *could*.
- *
- * This is really annoying and somewhat expensive. On the other hand,
- * it might prove itself to be yet another reason for higher level
- * protocols to work at avoiding fragmentation where possible.
- */
-
- /*
- Since we start out by doing a m_pullup() of extra + sizeof(struct
- ipv6_fraghdr, we can treat this as a straight linear buffer.
-
- This could easily be implemented as a fast and slow path, but
- reassembly is an inherently slow path anyway. -cmetz
- */
-
- DDO(FINISHED, dump_mchain(fragment->prefix));
- DDO(FINISHED, dump_mchain(fragment->data));
-
- {
- caddr_t data;
- uint8_t *type;
-
- DDO(FINISHED, dump_ipv6(mtod(fragment->prefix, struct ipv6 *)));
-
- data = mtod(fragment->prefix, caddr_t) + sizeof(struct ipv6);
- type = &(mtod(fragment->prefix, struct ipv6 *)->ipv6_nexthdr);
-
- while(*type != IPPROTO_FRAGMENT) {
- switch(*type) {
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS:
- {
- struct ipv6_opthdr *ipv6_opthdr = (struct ipv6_opthdr *)data;
-
- type = &(ipv6_opthdr->oh_nexthdr);
- data += sizeof(struct ipv6_opthdr) +
- ipv6_opthdr->oh_extlen * sizeof(uint64_t);
- };
- break;
- case IPPROTO_ROUTING:
- {
- struct ipv6_srcroute0 *ipv6_srcroute0 =
- (struct ipv6_srcroute0 *)data;
-
- type = &(ipv6_srcroute0->i6sr_nexthdr);
- data += sizeof(struct ipv6_srcroute0) +
- ipv6_srcroute0->i6sr_len * sizeof(uint64_t);
- };
- break;
- default:
- DPRINTF(ERROR, ("ipv6_reasm: Received a header (%d) that isn't allowed before a fragment header", *type));
-
- DP(FINISHED, OSDEP_PCAST(fragment->data), 08x);
- DP(FINISHED, OSDEP_PCAST(fragment->prefix), 08x);
-
- m_freem(fragment->data);
- m_freem(fragment->prefix);
- FREE(fragment, M_FRAGQ);
-
- return;
- }
- }
- *type = nexthdr;
- }
-
- extra = fragment->prefix->m_pkthdr.len;
-
- incoming = fragment->prefix;
- m_adj(fragment->data, sizeof(struct ipv6_fraghdr));
- m_cat(incoming, fragment->data);
- incoming->m_pkthdr.len += fragment->data->m_pkthdr.len;
-
- FREE(fragment, M_FRAGQ);
-
- /* Can't reassemble into a jumbogram */
- if (incoming->m_pkthdr.len > 0xffff) {
- m_freem(incoming);
- return; /* no other cleanup needed */
- }
-
- /* Dummy up length */
- (mtod(incoming, struct ipv6 *))->ipv6_length =
- htons(incoming->m_pkthdr.len - sizeof(struct ipv6));
-
- DDO(FINISHED, dump_mchain(incoming));
-
- (*inet6sw[ipv6_protox[nexthdr]].pr_input)(incoming, extra);
- }
- return;
-
-reasm_cleanup:
- if (incoming)
- m_freem(incoming);
-
- return;
-};
-
-/*----------------------------------------------------------------------
- * IPv6 forwarding engine.
- *
- * Assumes IPv6 header is already pulled up and ready-to-read.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_forward(outbound)
- struct mbuf *outbound;
-{
- struct ipv6 *ipv6 = mtod(outbound, struct ipv6 *);
- struct sockaddr_in6 *sin6;
- struct rtentry *rt;
- struct mbuf *ocopy;
- int type = 0, code = 0, pptr = 0, error;
-
- /*
- * Check hop limit.
- */
-
- if (ipv6->ipv6_hoplimit <= 1)
- {
- ipv6_icmp_error(outbound,ICMPV6_TIMXCEED,ICMPV6_TIMXCEED_INTRANS,0);
- return;
- }
-
- /*
- * Check link-local nature of source and dest. (Thanks to rja!)
- */
- if (IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_src) || IN6_IS_ADDR_LINKLOCAL(&ipv6->ipv6_dst))
- {
- printf("Can't forward packet with link-locals in it!\n");
- m_freem(outbound);
- return;
- }
- ipv6->ipv6_hoplimit--;
- sin6 = &ipv6forward_rt.ro_dst;
- if ((rt = ipv6forward_rt.ro_rt) == NULL ||
- !IN6_ARE_ADDR_EQUAL(&ipv6->ipv6_dst, &sin6->sin6_addr))
- {
- if (ipv6forward_rt.ro_rt != NULL)
- {
- RTFREE(ipv6forward_rt.ro_rt);
- ipv6forward_rt.ro_rt = NULL;
- }
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_addr = ipv6->ipv6_dst;
-
- rtalloc_noclone((struct route *)&ipv6forward_rt,ONNET_CLONING);
- if (ipv6forward_rt.ro_rt == NULL)
- {
- ipv6_icmp_error(outbound,ICMPV6_UNREACH, ICMPV6_UNREACH_NOROUTE, 0);
- return;
- }
- rt = ipv6forward_rt.ro_rt;
- }
-
- /*
- * Save 576 bytes of the packet in case we need to generate and ICMPv6
- * message to the sender.
- */
-
- ocopy = m_copy(outbound,0, min(ntohs(ipv6->ipv6_length),IPV6_MINMTU));
-
- /*
- * ip_forward() keeps some statistics here if GATEWAY is defined. We
- * skip that for now.
- */
-
- if (rt->rt_ifp == outbound->m_pkthdr.rcvif && rt_mask(rt)
- && rt_mask(rt)->sa_len
- && (rt->rt_flags & (RTF_TUNNEL|RTF_DYNAMIC|RTF_MODIFIED)) == 0 )
- {
- DPRINTF(IDL_EVENT, ("WARNING: May be cause for a redirect in ipv6_forward().\n"));
- }
-
- /*
- * Perform hop-by-hop options since we're ready to go!
- */
-
- if (!ipv6->ipv6_nexthdr)
- {
- /*
- * If bad hop-by-hop, return.
- */
- printf("Hop-by-hop options present in packet to be forwarded!\n");
- }
-
- error = ipv6_output(outbound, &ipv6forward_rt, IPV6_FORWARDING, NULL, NULL, NULL);
-
- if (error)
- ipv6stat.ips_cantforward++;
- else
- {
- /*
- * Check for redirect flags that should've been set in the redirect
- * code, otherwise...
- */
- m_freem(ocopy);
- return;
- }
-
- switch (error)
- {
- case 0: /* type and code should've been set by redirect code. */
- break;
-
- case ENETUNREACH: /* These two should've been checked above. */
- case EHOSTUNREACH:
- type = ICMPV6_UNREACH;
- code = ICMPV6_UNREACH_NOROUTE;
- break;
-
- case EHOSTDOWN:
- type = ICMPV6_UNREACH;
- code = ICMPV6_UNREACH_ADDRESS;
- break;
-
- case EMSGSIZE:
- type = ICMPV6_TOOBIG;
- code = 0;
- ipv6stat.ips_cantfrag++;
- pptr = rt->rt_rmx.rmx_mtu;
- break;
- }
-
- ipv6_icmp_error(ocopy, type, code, pptr);
-}
-
-/*----------------------------------------------------------------------
- * IPv6 hop-by-hop option handler.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_hop(incoming, extra)
- struct mbuf *incoming;
- int extra;
-{
- struct ipv6 *header;
- struct ipv6_opthdr *hopopt;
- struct ipv6_option *option;
- uint8_t *tmp;
-
- if (incoming->m_len < extra + sizeof(struct ipv6_opthdr))
- if (!(incoming = m_pullup2(incoming, extra + sizeof(struct ipv6_opthdr))))
- return;
-
- hopopt = (struct ipv6_opthdr *)(mtod(incoming, caddr_t) + extra);
-
- if (incoming->m_len < extra + sizeof(struct ipv6_opthdr) +
- (hopopt->oh_extlen * sizeof(uint64_t))) {
- if (!(incoming = m_pullup2(incoming, extra + sizeof(struct ipv6_opthdr) +
- (hopopt->oh_extlen * sizeof(uint64_t)))))
- return;
-
- hopopt = (struct ipv6_opthdr *)(mtod(incoming, caddr_t) + extra);
- }
-
- header = mtod(incoming, struct ipv6 *);
-
- tmp = hopopt->oh_data;
- /*
- * Slide the char pointer tmp along, parsing each option in the "bag" of
- * hop-by-hop options.
- */
- while (tmp < hopopt->oh_data + (hopopt->oh_extlen << 2))
- {
- option = (struct ipv6_option *)tmp;
- switch (option->opt_type)
- {
- case OPT_PAD1:
- tmp++;
- break;
- case OPT_PADN:
- tmp += option->opt_datalen + 2;
- break;
- case OPT_JUMBO:
- tmp += 2;
- /*
- * Confirm that the packet header field matches the jumbogram size.
- */
- if (incoming->m_pkthdr.len != ntohl(*(uint32_t *)tmp) + sizeof(*header))
- {
- /*
- * For now, bail. Add instrumenting code here, too.
- */
- m_freem(incoming);
- return;
- }
- break;
- default:
- /*
- * Handle unknown option by taking appropriate action based on
- * high bit values. With this code, this first non-skipping
- * unknown option will cause the packet to be dropped.
- */
- switch (option->opt_type & 0xC0)
- {
- case 0: /* Skip over */
- tmp += option->opt_datalen + 2;
- break;
- case 0xC0: /* Only if not multicast... */
- if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst))
- /* FALLTHROUGH */
- case 0x80: /* Send ICMP Unrecognized type. */
- {
- /* Issue ICMP parameter problem. */
- ipv6_icmp_error(incoming,ICMPV6_PARAMPROB,
- ICMPV6_PARAMPROB_BADOPT,
-#ifdef __alpha__
- (u_long)option - (u_long)hopopt->oh_data +
-#else
- (uint32_t)option - (uint32_t)hopopt->oh_data +
-#endif
- sizeof(struct ipv6));
- }
- return; /* incoming has already been freed. */
-
- case 0x40: /* Discard packet */
- m_freem(incoming);
- return;
- }
- }
- }
-
- DPRINTF(GROSSEVENT, ("ipv6_hop calling protoswitch for %d\n", \
- hopopt->oh_nexthdr));
-
- (*inet6sw[ipv6_protox[hopopt->oh_nexthdr]].pr_input)
- (incoming, extra + sizeof(struct ipv6_opthdr) +
- (hopopt->oh_extlen * sizeof(uint64_t)));
-
- DPRINTF(GROSSEVENT, ("Leaving ipv6_hop\n"));
-}
-
-
-/*----------------------------------------------------------------------
- * If timers expires on reassembly queues, discard them.
- * Also update any discovery queues.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_slowtimo()
-{
- struct ipv6_fragment *fragment, *fragmentprev;
- int s = splnet();
-
- /*
- * Age reasssembly fragments.
- *
- * (Since fragments are inserted at the beginning of the queue, once we've
- * found the first timed-out fragment, we know that everything beyond is
- * also timed-out since it must be older.)
- *
- */
- for (fragmentprev = NULL, fragment = ipv6_fragmentq; fragment;
- fragmentprev = fragment, fragment = fragment->next)
- if (fragment->ttl <= 1)
- break;
- else
- fragment->ttl--;
-
- if (fragment) {
- struct mbuf *m, *m2;
-
- if (fragmentprev)
- fragmentprev->next = NULL;
- else
- ipv6_fragmentq = NULL;
-
- /*
- * This loop does most of the work and doesn't require splnet()...?
- */
- splx(s);
-
- while(fragment) {
- /*
- * We "should" (says the spec) send an ICMP time exceeded here.
- * However, among other headaches, we may not actually have a copy
- * of the real packet sent to us (if we bubbled, we now have a frag
- * header that never really came from the sender). The solution taken
- * for now is to continue the BSD tradition of not bothering to send
- * these messages.
- */
- m = fragment->data;
- ipv6stat.ips_fragtimeout++;
- while(m) {
- m2 = m;
- m = m->m_nextpkt;
- m_freem(m2);
- }
- m_freem(fragment->prefix);
- fragmentprev = fragment;
- fragment = fragment->next;
- FREE(fragmentprev, M_FRAGQ);
- }
- } else
- splx(s);
-}
-
-/*----------------------------------------------------------------------
- * Drain all fragments & possibly discovery structures.
- ----------------------------------------------------------------------*/
-
-void
-ipv6_drain()
-{
- struct ipv6_fragment *totrash;
- struct mbuf *m,*m2;
- struct radix_node_head *rnh = rt_tables[AF_INET6];
-
- while (ipv6_fragmentq != NULL)
- {
- ipv6stat.ips_fragdropped++;
- totrash = ipv6_fragmentq;
- ipv6_fragmentq = totrash->next;
- m = totrash->data;
- while (m)
- {
- m2 = m;
- m = m->m_nextpkt;
- m_freem(m2);
- }
- if (totrash->prefix)
- m_freem(totrash->prefix);
- FREE(totrash, M_FRAGQ);
- }
-
- /*
- * Might want to delete all off-net host routes,
- * and maybe even on-net host routes.
- *
- * For now, do only the off-net host routes.
- */
- (void) rnh->rnh_walktree(rnh, ipv6_clean_nexthop, (void *)1);
-}
-
-/*----------------------------------------------------------------------
- * sysctl(2) handler for IPv6. Not yet implemented.
- ----------------------------------------------------------------------*/
-int *ipv6_sysvars[] = IPV6CTL_VARS;
-
-#if __FreeBSD__
-SYSCTL_STRUCT(_net_inet_ipv6, IPV6CTL_STATS, ipv6stats, CTLFLAG_RD, &ipv6stat, ipv6stat, "");
-#else /* __FreeBSD__ */
-int
-ipv6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
- int *name;
- uint namelen;
- void *oldp;
- size_t *oldlenp;
- void *newp;
- size_t newlen;
-{
- if (namelen != 1)
- return ENOTDIR;
-
- switch (name[0])
- {
-#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802)
- case IPV6CTL_STATS:
- return sysctl_rdtrunc(oldp, oldlenp, newp, &ipv6stat, sizeof(ipv6stat));
- default:
- return (sysctl_int_arr(ipv6_sysvars, name, namelen, oldp, oldlenp, newp, newlen));
-#else /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- default:
- return EOPNOTSUPP;
-#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */
- }
-}
-#endif __FreeBSD__
-
-/*----------------------------------------------------------------------
- * Should be a macro, this function determines if IPv6 is running on a
- * given interface.
- ----------------------------------------------------------------------*/
-
-int
-ipv6_enabled(ifp)
- struct ifnet *ifp;
-{
- struct in6_ifaddr *i6a;
-
- for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next)
- if (i6a->i6a_ifp == ifp)
- return 1;
-
- return 0;
-}
-
-/*----------------------------------------------------------------------
- * Strips IPv6 options for TCP or UDP.
- *
- * This function assumes that the input chain (incoming) has been
- * munged by ipv6_preparse() and starts with an IPv6 header.
- * The header index is invalid after this call.
- * The IPv6 header is not updated EXCEPT for the length, which must be
- * in HOST order.
- * Note that the API for this function is NOT THE SAME as its IPv4
- * counterpart.
- *
- * Often called like:
- *
- * ipv6_stripoptions(incoming, extra, nexthdr);
- *
- * Where ihi and ihioff are the header index arrays, passed up after
- * ipv6_preparse(). If preparse is eliminated or altered, this code will
- * be too.
- ----------------------------------------------------------------------*/
-
-void ipv6_stripoptions(incoming, extra)
-register struct mbuf *incoming;
-int extra;
-{
- struct mbuf *optm, *m;
- int optlen;
-
- if (extra == sizeof(struct ipv6)) {
- /* i.e. If there are no options... */
- optm = NULL;
- optlen = 0;
- return;
- }
-
- if (!(optm = m_split(incoming, sizeof(struct ipv6), M_DONTWAIT)))
- return;
-
- if (!(m = m_split(optm, extra - sizeof(struct ipv6), M_DONTWAIT))) {
- m_cat(incoming, optm);
- return;
- }
-
- m_cat(incoming, m);
- incoming->m_pkthdr.len -= (extra - sizeof(struct ipv6));
-
- (mtod(incoming, struct ipv6 *))->ipv6_length =
- (incoming->m_pkthdr.len - sizeof(struct ipv6) > 0xffff) ? 0 :
- htons(incoming->m_pkthdr.len - sizeof(struct ipv6));
-
- /*
- * XXX - We should be saving these stripped options somewhere...
- */
- m_freem(optm);
-}
-
-static struct mbuf *ipv6_saveopt(caddr_t p, int size, int type, int level)
-{
- register struct cmsghdr *cp;
- struct mbuf *m;
-
- if ((m = m_get(M_DONTWAIT, MT_CONTROL)) == NULL)
- return ((struct mbuf *) NULL);
- cp = (struct cmsghdr *) mtod(m, struct cmsghdr *);
- bcopy(p, CMSG_DATA(cp), size);
- size += sizeof(*cp);
- m->m_len = size;
- cp->cmsg_len = size;
- cp->cmsg_level = level;
- cp->cmsg_type = type;
- return m;
-}
-
-#if 0
-static struct mbuf *ipv6_savebag(struct mbuf *m, int level)
-{
- uint8_t *p = mtod(m, uint8_t *) + 1;
- int len = (*(p++) << 3) + 6;
- struct mbuf *opts = NULL, **mp = &opts;
-
- while(len > 0) {
- if (!*p) {
- p++; len--;
- continue;
- }
-
- if (len <= 1)
- return opts;
-
- if (*p != 1)
- if ((*mp = ipv6_saveopt((caddr_t)(p + 2), *(p + 1), *p, level)))
- mp = &(*mp)->m_next;
-
- len -= *(p + 1) + 2;
- p += *(p + 1) + 2;
- };
-
- return opts;
-};
-#endif /* 0 */
-
-struct mbuf *ipv6_headertocontrol(struct mbuf *m, int extra, int inp_flags)
-{
- struct mbuf *opts = NULL, **mp = &opts;
-
- if (inp_flags & INP_RXINFO) {
- struct in6_pktinfo pktinfo;
-
- if (m->m_pkthdr.rcvif) {
- pktinfo.ipi6_ifindex = m->m_pkthdr.rcvif->if_index;
- } else {
- DPRINTF(IDL_ERROR, ("ipv6_controldata: m->m_pkthdr.rcvif = NULL!\n"));
- pktinfo.ipi6_ifindex = 0;
- }
-
- bcopy(&(mtod(m, struct ipv6 *)->ipv6_dst), &pktinfo.ipi6_addr, sizeof(struct in6_addr));
- if ((*mp = ipv6_saveopt((caddr_t)&pktinfo, sizeof(struct in6_pktinfo), IPV6_PKTINFO, IPPROTO_IPV6)))
- mp = &(*mp)->m_next;
- };
-
- if (inp_flags & INP_HOPLIMIT) {
- int hoplimit = mtod(m, struct ipv6 *)->ipv6_hoplimit;
- if ((*mp = ipv6_saveopt((caddr_t)&hoplimit, sizeof(int), IPV6_HOPLIMIT, IPPROTO_IPV6)))
- mp = &(*mp)->m_next;
- };
-
-#if 0
- /* Since there's not any immediate need for these options anyway, we'll
- worry about reimplementing them later. - cmetz */
-
- if (inp_flags & INP_RXHOPOPTS) {
- int i;
- for (i = 1; i < ihioff; i++)
- if (ihi[i].ihi_nexthdr == IPPROTO_HOPOPTS) {
- if ((*mp = ipv6_savebag(ihi[i].ihi_mbuf, IPPROTO_HOPOPTS)))
- mp = &(*mp)->m_next;
- break;
- };
- };
-
- if (inp_flags & INP_RXDSTOPTS) {
- int i;
- for (i = 1; i < ihioff; i++)
- if (ihi[i].ihi_nexthdr == IPPROTO_DSTOPTS) {
- if ((*mp = ipv6_savebag(ihi[i].ihi_mbuf, IPPROTO_DSTOPTS)))
- mp = &(*mp)->m_next;
- break;
- };
- };
-
- if (inp_flags & INP_RXSRCRT) {
- int i;
- for (i = 1; i < ihioff; i++)
- if (ihi[i].ihi_nexthdr == IPPROTO_ROUTING) {
- if ((*mp = ipv6_saveopt((caddr_t)(mtod(ihi[i].ihi_mbuf, uint8_t *) + 3), (*mtod(ihi[i].ihi_mbuf, uint8_t *) << 3) + 5, *(mtod(ihi[i].ihi_mbuf, uint8_t *) + 2), IPPROTO_ROUTING)))
- mp = &(*mp)->m_next;
- break;
- };
- };
-#endif /* 0 */
-
- return opts;
-};