diff options
Diffstat (limited to 'sys/netinet6/ipv6_output.c')
-rw-r--r-- | sys/netinet6/ipv6_output.c | 2107 |
1 files changed, 0 insertions, 2107 deletions
diff --git a/sys/netinet6/ipv6_output.c b/sys/netinet6/ipv6_output.c deleted file mode 100644 index fca449a4215..00000000000 --- a/sys/netinet6/ipv6_output.c +++ /dev/null @@ -1,2107 +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/protosw.h> -#include <sys/domain.h> -#include <sys/mbuf.h> -#include <sys/socket.h> -#include <sys/socketvar.h> -#include <sys/systm.h> - -#include <net/if.h> -#include <net/route.h> - -#include <netinet/in.h> -#include <netinet6/in6_var.h> -#include <netinet6/ipv6.h> -#include <netinet6/ipv6_var.h> - -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/in_pcb.h> -#include <netinet/tcp.h> -#include <netinet/tcp_timer.h> -#include <netinet/ip_var.h> -#include <netinet/tcpip.h> -#include <netinet/tcp_var.h> - -#if __OpenBSD__ -#undef IPSEC -#ifdef NRL_IPSEC -#define IPSEC 1 -#endif /* NRL_IPSEC */ -#endif /* __OpenBSD__ */ - -#if IPSEC -#include <sys/osdep.h> -#include <net/netproc.h> -#include <net/netproc_var.h> -#include <sys/nbuf.h> -#endif /* IPSEC */ - -#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 and function definitions. - */ - -uint32_t outfragid = 0; /* Outbound fragment groups have unique id's. */ -struct mbuf *ipv6_fragment __P((struct mbuf *,int)); - -/* - * External globals. - */ - -extern struct ipv6stat ipv6stat; -extern struct in6_ifaddr *in6_ifaddr; -extern struct in6_ifnet *in6_ifnet; -extern struct ifnet *mcastdefault; -extern int ipv6_defhoplmt; - -#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) -extern struct ifnet *loifp; -#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */ - -void ipv6_nsolicit __P((struct ifnet *, struct mbuf *, struct rtentry *)); -int ipv6_tunnel_output __P((struct mbuf *, struct sockaddr_in6 *, struct rtentry *)); - -int ipv6_setmoptions __P((int, struct inpcb *, void *, size_t)); -int ipv6_getmoptions __P((int, struct ipv6_moptions *, int *)); -void ipv6_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in6 *)); -int ipv6_finddivpoint __P((struct mbuf *, uint8_t *)); - -/*---------------------------------------------------------------------- - * IPv6 output routine. The mbuf chain contains a near-complete IPv6 header, - * and an already-inserted list of options. (I figure it's something for - * the code with PCB access to handle.) The options should have their - * fields in network order. The header should have its fields in host order. - * (Save the addresses, which IMHO are always in network order. Weird.) - ----------------------------------------------------------------------*/ - -int -#ifdef __OpenBSD__ -ipv6_output(struct mbuf *outgoing, ...) -#else /* __OpenBSD__ */ -ipv6_output(outgoing,ro,flags,i6mo, forceifp, socket) - struct mbuf *outgoing; - struct route6 *ro; - int flags; - struct ipv6_moptions *i6mo; - struct ifnet *forceifp; - struct socket *socket; -#endif /* __OpenBSD__ */ -{ - struct ipv6 *header; - struct route6 ipv6route; - struct sockaddr_in6 *dst; - struct in6_ifaddr *i6a = NULL; - struct ifnet *ifp = NULL; - int error=0; - uint32_t outmtu = 0; -#if __OpenBSD__ - va_list ap; - struct route6 *ro; - int flags; - struct ipv6_moptions *i6mo; - struct ifnet *forceifp; - struct socket *socket; - - va_start(ap, outgoing); - ro = va_arg(ap, struct route6 *); - flags = va_arg(ap, int); - i6mo = va_arg(ap, struct ipv6_moptions *); - forceifp = va_arg(ap, struct ifnet *); - socket = va_arg(ap, struct socket *); - va_end(ap); -#endif /* __OpenBSD__ */ - -#ifdef DIAGNOSTIC - if ((outgoing->m_flags & M_PKTHDR) == 0) - panic("ipv6_output() no HDR"); -#endif - - /* - * Assume the IPv6 header is already contiguous. - */ - header = mtod(outgoing, struct ipv6 *); - - DDO(IDL_FINISHED,printf("ipv6_output:\n");dump_ipv6(header)); - DPRINTF(IDL_FINISHED,("\n")); - - /* - * Fill in v6 header. Assume flow id/version field is in network order, - * and that the high 4 bits are 0's. - */ - - if ((flags & (IPV6_FORWARDING|IPV6_RAWOUTPUT)) == 0) - { - header->ipv6_versfl = htonl(0x60000000) | - (header->ipv6_versfl & htonl(0x0fffffff)); - ipv6stat.ips_localout++; - } - - /* - * Determine interface and physical destination to send datagram out - * towards. Do this by looking up a route, or using the route we were - * passed. - */ - - DPRINTF(IDL_FINISHED,("route passed to ipv6_output is:\n")); - DDO(IDL_FINISHED,if (ro) dump_rtentry(ro->ro_rt)); - if (ro == 0) - { - ro = &ipv6route; - bzero((caddr_t)ro,sizeof(struct route6)); - } - dst = &ro->ro_dst; - - if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || - !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &header->ipv6_dst))) - { - RTFREE(ro->ro_rt); - ro->ro_rt = NULL; - } - - if (ro->ro_rt == NULL) - { - dst->sin6_family = AF_INET6; - dst->sin6_len = sizeof(struct sockaddr_in6); - dst->sin6_addr = header->ipv6_dst; - dst->sin6_port = 0; - dst->sin6_flowinfo = 0; - } - -#define ifatoi6a(ifa) ((struct in6_ifaddr *)(ifa)) -#define sin6tosa(sin6) ((struct sockaddr *)(sin6)) - - if (flags & IPV6_ROUTETOIF) - { - /* - * Check for route to interface only. (i.e. the user doesn't want - * to rely on the routing tables, so send it out an interface). - */ - if ((i6a = ifatoi6a(ifa_ifwithdstaddr(sin6tosa(dst)))) == 0 && - (i6a = ifatoi6a(ifa_ifwithnet(sin6tosa(dst)))) == 0 ) - { - /* - * Q: Do we want to assume that if a user specifies this option, - * the user doesn't want ANYTHING to do with the routing tables? - */ - - ipv6stat.ips_noroute++; - error = ENETUNREACH; - goto bad; - } - ifp = i6a->i6a_ifp; - header->ipv6_hoplimit = 1; - outmtu = ifp->if_mtu; - } - else - { - /* - * Do normal next-hop determination with the help of the routing tree. - */ - if (ro->ro_rt == 0) - rtalloc((struct route *)ro); /* Initial route lookup. */ - - if (ro->ro_rt == 0) - { - /* - * No route of any kind, so spray neighbor solicits out all - * interfaces, unless it's a multicast address. - */ - if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst)) - goto mcast; - DPRINTF(IDL_FINISHED, ("v6_output doesn't have a route...calling onlink_query!\n")); - ipv6_onlink_query(dst); - rtalloc((struct route *)ro); - } - if (ro->ro_rt == NULL) - { - /* - * ipv6_onlink_query() should've added a route. Probably - * failed. - */ - DPRINTF(IDL_GROSS_EVENT, ("v6_output: onlink_query didn't add route!\n")); - ipv6stat.ips_noroute++; - error = ENETUNREACH; - goto bad; - } - - if (ro->ro_rt->rt_ifa == NULL) - { - /* - * We have a route where we don't quite know which interface - * the neighbor belongs to yet. If I get here, I know that this - * route is not pre-allocated (such as done by in6_pcbconnect()), - * because those pre-allocators will do the same ipv6_onlink_query() - * and ipv6_verify_onlink() in advance. - * - * I can therefore free the route, and get it again. - * Multicast dgrams should NEVER be in this code path. - */ - - RTFREE(ro->ro_rt); - ro->ro_rt = NULL; - DPRINTF(IDL_FINISHED,("v6_output calling ipv6_verify_onlink\n")); - if ((error = ipv6_verify_onlink(dst)) != 0) - { - if (error == -1) - { - DPRINTF(IDL_ERROR,("verify_onlink() failed in v6_out.\n")); - error = ENETUNREACH; - } - ipv6stat.ips_noroute++; /* Better stat needed, because - error might not be - E{NET,HOST}UNREACH. */ - - goto bad; - } - rtalloc((struct route *)ro); - if (ro->ro_rt == NULL || ro->ro_rt->rt_ifa == NULL) - panic("Oops, I'm forgetting something after verify_onlink()."); - } - - /* - * Exploit properties of route. - */ - - ifp = ro->ro_rt->rt_ifp; /* Q: Is this REALLY the ifp - for the route? - - A: Maybe. If multi-homed, - and we attempt to - intelligently figure out - where link-locals are - destined, then we're - in trouble. */ - /* - * On-net route exists, but no destination as of yet. This can - * be snipped if an ifp is just selected. (Depends on multihomed - * experience.) - * - * Currently, this code never executes, because we guarantee rt_ifp is - * set. This may, however, change in later versions of this code as - * we gain multihomed experience. - */ - if (ifp == NULL && ro->ro_rt->rt_gateway->sa_family == AF_LINK) - { - DPRINTF(IDL_EVENT,\ - ("ipv6_output() calling ipv6_nsolicit(2)\n")); - ipv6_nsolicit(NULL, outgoing, ro->ro_rt); - DPRINTF(IDL_EVENT,\ - ("ipv6_output() attempted to send neighbor solicit(2), returning.\n")); - goto done; - } - - /* - * Q: What if address has expired? Perhaps I should use ifp to - * obtain optimal i6a value. There's also the question of using - * link-local source addresses for off-link communication. (or for - * that matter, on-link, but not link-local communication. - * - * Q2: Perhaps use this time to reset the route's ifa? - * Q3: Perhaps it is better to use the ipv6_rtrequest()? - * - * Regardless, i6a's only use in this function is to determine the - * source address of the packet. - * - * Currently, ipv6_rtrequest() attempts to store a decent in6_ifaddr - * in rt_ifa. This also may change with experience. - */ - - i6a = ifatoi6a(ro->ro_rt->rt_ifa); - if (i6a->i6a_addrflags & I6AF_NOTSURE) { - if (!(outgoing->m_flags & M_DAD)) - { - /* - * 1. Think of a better error. - * - * 2. Keep some sort of statistic. - */ - DPRINTF(IDL_ERROR,("Using NOTSURE source address.\n")); - error = EADDRNOTAVAIL; - goto bad; - } - else i6a = NULL; - } - - /* - * More source address selection goes here. - */ - - ro->ro_rt->rt_use++; - /* - * Path MTU comes from the route entry. - */ - outmtu = ro->ro_rt->rt_rmx.rmx_mtu; - - if (ro->ro_rt->rt_flags & RTF_GATEWAY) /* Gateway/router/whatever. */ - dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway; - } - - if (forceifp) { - DPRINTF(IDL_EVENT, ("ipv6_output: in forceifp case\n")); - ifp = forceifp; - if (outmtu > ifp->if_mtu) - outmtu = ifp->if_mtu; - }; - - /* - * Handle case of a multicast destination. - */ - mcast: - if (IN6_IS_ADDR_MULTICAST(&header->ipv6_dst)) - { - struct in6_multi *in6m; - - outgoing->m_flags |= M_MCAST; - - dst = &ro->ro_dst; - - if (i6mo != NULL) - { - /* - * As we gain more multicast experience, use i6mo fields to alter - * properties of outgoing packet. (Including, quite possibly, - * the source address.) - */ - if (i6mo->i6mo_multicast_ifp != NULL) - ifp = i6mo->i6mo_multicast_ifp; - header->ipv6_hoplimit = i6mo->i6mo_multicast_ttl; - } - else - { - /* - * Use default values, since there are no multicast options to - * use. - */ - if (ifp == NULL) - ifp = mcastdefault; - header->ipv6_hoplimit = IPV6_DEFAULT_MCAST_HOPS; - } - - if (outmtu == 0) /* But what about mcast Path MTU? */ - outmtu = ifp->if_mtu; - - if ((ifp->if_flags & IFF_MULTICAST) == 0) - { - ipv6stat.ips_noroute++; - error = ENETUNREACH; - goto bad; - } - - if ((IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src) && !(outgoing->m_flags & M_DAD)) || - (IN6_IS_ADDR_LINKLOCAL(&header->ipv6_src) && - IN6_MSCOPE(&header->ipv6_dst) > IN6_LINK_LOCAL)) - { - register struct in6_ifaddr *i6a; - - /* - * Source address selection for multicast datagrams. - * If link-local source, get in here too, because you don't want - * link-local addresses going on non-local multicast. - * - * Eventually should fix this to perform best source address - * selection. Probably should separate this out into a function. - */ - for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next) - if (i6a->i6a_ifp == ifp) - { - header->ipv6_src = I6A_SIN(i6a)->sin6_addr; - break; - } - } - - IN6_LOOKUP_MULTI(&header->ipv6_dst, ifp, in6m); - DPRINTF(IDL_GROSS_EVENT,("in6m == 0x%lx, i6mo == 0x%lx\n", (unsigned long)in6m, (unsigned long)i6mo)); - if (in6m != NULL && - (i6mo == NULL || i6mo->i6mo_multicast_loop)) - { - - /* - * See ipv6_mloopback for details, but that function will tag - * the packet with the actual interface the multicast is - * supposed to go out. This makes duplicate address detection - * harder to implement, because the inbound mbuf SHOULD be tagged - * as coming from me for the case of solicits. (Perhaps burning - * another flag...) - */ - DPRINTF(IDL_GROSS_EVENT,("Calling ipv6_mloopback().\n")); - ipv6_mloopback(ifp, outgoing, dst); - } - -#ifdef MROUTING - /* - * Do m-cast routing even if I can't find it in my m-cast list. - */ -#endif - - /* - * If intra-node scope. I've already hit it with ipv6_mloopback above. - */ - - if (IN6_MSCOPE(&header->ipv6_dst) == IN6_NODE_LOCAL || - (ifp->if_flags & IFF_LOOPBACK)) - goto bad; /* Not really bad, y'know, just getting out of here. */ - } - - if (ro->ro_rt == NULL && outmtu == 0) - panic("ipv6_output: How did I get here without a route or MTU?"); - - /* - * Specify source address. Use i6a, for now. - */ - - if (IN6_IS_ADDR_UNSPECIFIED(&header->ipv6_src) && i6a != NULL && - !(outgoing->m_flags & M_DAD)) - header->ipv6_src = I6A_SIN(i6a)->sin6_addr; - - DPRINTF(IDL_FINISHED,("header & chain before security check are:\n")); - DDO(IDL_FINISHED,dump_ipv6(header)); - DDO(IDL_FINISHED,dump_mchain(outgoing)); - -#ifdef IPSEC - if (!(flags & IPV6_FORWARDING)) { - size_t preoverhead, postoverhead; - void *state; - - /* NB: If there exists a configured secure tunnel, then - the packet being tunneled will have been encapsulated - inside an IP packet with (src=me, dest=tunnel-end-point) - PRIOR to ip_output() being called, so the above - check doesn't preclude secure tunnelling. rja */ - /* - * I would like to just pass in &ia->ia_addr, but there is a small - * chance that the source address doesn't match ia->ia_addr. - * - * Also, if you need a dest. port, fill in ro->ro_dst with it. - */ - { - struct sockaddr_in6 srcsa, dstsa; - - bzero(&srcsa, sizeof(struct sockaddr_in6)); - srcsa.sin6_family = AF_INET6; - srcsa.sin6_len = sizeof(struct sockaddr_in6); - /* XXX - port */ - srcsa.sin6_addr = header->ipv6_src; - - bzero(&dstsa, sizeof(struct sockaddr_in6)); - dstsa.sin6_family = AF_INET6; - dstsa.sin6_len = sizeof(struct sockaddr_in6); - /* XXX - port */ - dstsa.sin6_addr = header->ipv6_dst; - - /* XXX - get the ULP protocol number */ - if (error = netproc_outputpolicy(socket, (struct sockaddr *)&srcsa, - (struct sockaddr *)&dstsa, header->ipv6_nexthdr, &preoverhead, - &postoverhead, &state)) { - if (error == EACCES) /* XXX - means fail silently */ - error = 0; - goto bad; - } - } - - if (state) { - struct nbuf *nbuf; - - DP(FINISHED, preoverhead, d); - DP(FINISHED, postoverhead, d); - - header->ipv6_length = htons(outgoing->m_pkthdr.len - - sizeof(struct ipv6)); - - if (!(nbuf = mton(outgoing, preoverhead, postoverhead))) { - netproc_outputfree(state); - error = ENOMEM; - goto bad; - } - - outgoing = NULL; - - if (error = netproc_output(state, nbuf)) { - if (error == EACCES) - error = 0; - } - - /* If successful, netproc_output actually does the output. - Either way, it frees the nbuf. */ - goto done; - } - } -#endif /* defined(IPSEC) || defined(NRL_IPSEC) */ - - /* - * Assume above three return a contiguous and UPDATED IPv6 header. - */ - header = mtod(outgoing,struct ipv6 *); - - /* - * Determine the outbound i6a to record statistics. - */ - if (flags & IPV6_FORWARDING) - i6a = NULL; - else if (i6a == NULL || - !IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &header->ipv6_src)) - { - for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next) - if (i6a->i6a_ifp == ifp && - IN6_ARE_ADDR_EQUAL(&i6a->i6a_addr.sin6_addr, &header->ipv6_src)) - break; - } - - /* - * If small enough for path MTU, send, otherwise, fragment. - */ - - DPRINTF(IDL_FINISHED,("Before output, path mtu = %d, header is:\n",\ - (int)outmtu)); - DDO(IDL_FINISHED,dump_ipv6(header)); - DDO(IDL_FINISHED,printf("Chain is:\n");dump_mchain(outgoing)); - -#if 0 -/* DEBUG tunnel */ - DPRINTF(IDL_EVENT,("ROUTE passed to ipv6_output is:\n")); - DDO(IDL_EVENT,if (ro) dump_rtentry(ro->ro_rt)); - if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) - DPRINTF(IDL_FINISHED,("HEY !! I see the tunnel!!!\n")); - else { - DPRINTF(IDL_FINISHED,("HEY !! I can't see the tunnel!!!\n")); - if (ro->ro_rt == NULL) - DPRINTF(IDL_FINISHED,("ro->ro_rt is null!!\n")); - else - { - DPRINTF(IDL_FINISHED,("ro->ro_rt is not null!!\n")); - if (ro->ro_rt->rt_flags & RTF_TUNNEL) - DPRINTF(IDL_FINISHED,("HEY, I can see RTFTUNNEL!\n")); - else - DPRINTF(IDL_FINISHED,("HEY, I can't see RTFTUNNEL!\n")); - } - } -/* END OF DEBUG tunnel */ -#endif /* 0 */ - - if (outgoing->m_pkthdr.len <= outmtu) - { -DPRINTF(IDL_EVENT,("IPV6_OUTPUT(): Not entering fragmenting engine.\n")); - header->ipv6_length = htons(outgoing->m_pkthdr.len - - sizeof(struct ipv6)); - - /* - * If there is a route, and its TUNNEL bit is turned on, do not send - * out the interface, but send through a tunneling routine, which will, - * given information from the route, encapsulate the packet accordingly. - * - * Keith Sklower suggested a "rt_output() method" which would save - * the checking here. - */ - if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) { - DPRINTF(IDL_EVENT,("ipv6_output():-Sending out IPV6 in IPV4/6 tunnel.\n")); - error = ipv6_tunnel_output(outgoing, dst, ro->ro_rt); - } else { -#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) - if (i6a) { - i6a->i6a_ifa.ifa_opackets++; - i6a->i6a_ifa.ifa_obytes += outgoing->m_pkthdr.len; - } -#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */ - error = (*ifp->if_output)(ifp, outgoing, (struct sockaddr *)dst, ro->ro_rt); - } - DPRINTF(IDL_FINISHED,("Lone IPv6 went out if (error = %d).\n",error)); - goto done; - } - - - /* - * If I make it here, then the packet is too big for the path MTU, and must - * be fragmented. - */ - - DPRINTF(IDL_EVENT,("IPV6_OUTPUT(): Entering fragmenting engine.\n")); - - if (flags & IPV6_FORWARDING) - { - error = EMSGSIZE; - goto bad; - } - - if (outgoing->m_pkthdr.len > 0xffff) { - DPRINTF(IDL_ERROR,("Jumbogram needs fragmentation, something that can't be done\n")); - ipv6stat.ips_odropped++; /* ?!? */ - error = EINVAL; - goto bad; - } - - /* - * The following check should never really take place. - */ -#ifdef DIAGNOSTIC - if (outmtu < IPV6_MINMTU) - { - DPRINTF(IDL_ERROR,("Outbound MTU is less than IPV6_MINMTU (%d).\n",\ - IPV6_MINMTU)); - error = ENETUNREACH; /* Can you think of a better idea? */ - goto bad; - } -#endif - - /* - * ipv6_fragment returns a chain of outgoing packets. It returns NULL - * if something went wrong. - */ - outgoing = ipv6_fragment(outgoing,outmtu); - if (outgoing == NULL) - error = ENOBUFS; /* Can you think of a better idea? */ - - DPRINTF(IDL_FINISHED,\ - ("ipv6_fragment() returned, attempting to send fragments out.\n")); - - /* - * Walk through chain of fragments, and send them out. - */ - while (outgoing != NULL) - { - struct mbuf *current = outgoing; - - DPRINTF(IDL_FINISHED,("In fragment-sending loop, error == %d.\n",\ - error)); - outgoing = current->m_nextpkt; - current->m_nextpkt = NULL; - - DDO(IDL_FINISHED,printf("Current (0x%lx) 1st mbuf is:\n", (unsigned long)current);\ - dump_mbuf(current)); - - if (error != 0) - m_freem(current); - else - if (ro->ro_rt && (ro->ro_rt->rt_flags & RTF_TUNNEL)) { - DPRINTF(IDL_EVENT,("Sending fragments out tunnel.\n")); - error = ipv6_tunnel_output(current, dst, ro->ro_rt); - } else { - DPRINTF(IDL_EVENT,("After if_output(), error == %d.\n",error)); -#if defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) - if (i6a) { - i6a->i6a_ifa.ifa_opackets++; - i6a->i6a_ifa.ifa_obytes += current->m_pkthdr.len; - } -#endif /* defined(_BSDI_VERSION) && (_BSDI_VERSION >= 199802) */ - error = (*ifp->if_output)(ifp, current,(struct sockaddr *)dst, ro->ro_rt); - } - } - - if (error == 0) - ipv6stat.ips_fragmented++; - -done: - if (ro == &ipv6route && (flags & IPV6_ROUTETOIF) == 0 && ro->ro_rt) - RTFREE(ro->ro_rt); - return (error); - -bad: - if (outgoing != NULL) - m_freem(outgoing); - goto done; -} - -#define INDEX_TO_IFP(index, ifp)\ -{\ - struct in6_ifnet *i6ifp; \ - for (i6ifp = in6_ifnet; i6ifp; i6ifp = i6ifp->i6ifp_next) \ - if (i6ifp->i6ifp_ifp->if_index == index) { \ - (ifp) = i6ifp->i6ifp_ifp; \ - break; \ - }; \ -} \ - -/*---------------------------------------------------------------------- - * Set IPv6 multicast options. - ----------------------------------------------------------------------*/ -int ipv6_setmoptions(int optname, struct inpcb *inp, void *p, size_t len) -{ - register int error = 0; - register int i; - register struct ipv6_mreq *mreq; - register struct ifnet *ifp = NULL; - register struct ipv6_moptions *imo = inp->inp_moptions6; - struct route6 ro; - - if (imo == NULL) - { - imo = (struct ipv6_moptions *)malloc(sizeof(*imo), M_IPMOPTS,M_WAITOK); - if (imo == NULL) - return ENOBUFS; - inp->inp_moptions6 = imo; - inp->inp_flags |= INP_IPV6_MCAST; - imo->i6mo_multicast_ifp = NULL; - imo->i6mo_multicast_ttl = IPV6_DEFAULT_MCAST_HOPS; - imo->i6mo_multicast_loop = IPV6_DEFAULT_MCAST_LOOP; - imo->i6mo_num_memberships = 0; - } - else /* Only if points to v6 moptions can I set them! */ - if (!(inp->inp_flags & INP_IPV6_MCAST)) - return EEXIST; - - switch (optname) - { - case IPV6_MULTICAST_IF: - { - unsigned int index; - if (!p || (len != sizeof(unsigned int))) { - error = EINVAL; - break; - } - - index = *((int *)p); - - if (!index) { - imo->i6mo_multicast_ifp = NULL; - break; - } - - INDEX_TO_IFP(index, ifp); - if (!ifp || !(ifp->if_flags & IFF_MULTICAST)) - error = EADDRNOTAVAIL; - else - imo->i6mo_multicast_ifp = ifp; - }; - break; - - case IPV6_MULTICAST_HOPS: - /* - * Set the IPv6 hop limit for outgoing multicast packets. - */ - if (!p || (len != sizeof(int))) { - error = EINVAL; - break; - } - if (*((int *)p) == -1) - imo->i6mo_multicast_ttl = IPV6_DEFAULT_MCAST_HOPS; - else - if ((*((int *)p) > -1) && (*((int *)p) < 256)) - imo->i6mo_multicast_ttl = *((int *)p); - else - error = EINVAL; - break; - - case IPV6_MULTICAST_LOOP: - /* - * Set the loopback flag for outgoing multicast packets. - * Must be zero or one. - */ - if (!p || (len != sizeof(int))) { - error = EINVAL; - break; - } - switch(*((int *)p)) { - case 0: - case 1: - imo->i6mo_multicast_loop = *((int *)p); - break; - case -1: - imo->i6mo_multicast_loop = IPV6_DEFAULT_MCAST_LOOP; - break; - default: - error = EINVAL; - break; - }; - break; - case IPV6_ADD_MEMBERSHIP: - /* - * Add a multicast group membership. - * Group must be a valid IP multicast address. - */ - if (!p || (len != sizeof(struct ipv6_mreq))) { - error = EINVAL; - break; - } - mreq = (struct ipv6_mreq *)p; - if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { - error = EINVAL; - break; - } - /* - * If no interface address was provided, use the interface of - * the route to the given multicast address. - */ - if (!mreq->ipv6mr_interface) { - ro.ro_rt = NULL; - ro.ro_dst.sin6_family = AF_INET6; - ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6); - ro.ro_dst.sin6_addr = mreq->ipv6mr_multiaddr; - rtalloc((struct route *)&ro); - if (ro.ro_rt == NULL) - { - error = EADDRNOTAVAIL; - break; - } - ifp = ro.ro_rt->rt_ifp; - rtfree(ro.ro_rt); - } - else { - INDEX_TO_IFP(mreq->ipv6mr_interface, ifp); - } - /* - * See if we found an interface, and confirm that it - * supports multicast. - */ - if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { - error = EADDRNOTAVAIL; - break; - } - /* - * See if the membership already exists or if all the - * membership slots are full. - */ - for (i = 0; i < imo->i6mo_num_memberships; ++i) { - if (imo->i6mo_membership[i]->in6m_ifp == ifp && - IN6_ARE_ADDR_EQUAL(&mreq->ipv6mr_multiaddr, - &imo->i6mo_membership[i]->in6m_addr)) - break; - } - if (i < imo->i6mo_num_memberships) { - error = EADDRINUSE; - break; - } - if (i == IN6_MAX_MEMBERSHIPS) { - error = ETOOMANYREFS; - break; - } - /* - * Everything looks good; add a new record to the multicast - * address list for the given interface. - */ - if ((imo->i6mo_membership[i] = in6_addmulti(&mreq->ipv6mr_multiaddr, ifp)) - == NULL) - { - error = ENOBUFS; - break; - } - ++imo->i6mo_num_memberships; - break; - - case IPV6_DROP_MEMBERSHIP: - /* - * Drop a multicast group membership. - * Group must be a valid IP multicast address. - */ - if (!p || (len != sizeof(struct ipv6_mreq))) { - error = EINVAL; - break; - } - mreq = (struct ipv6_mreq *)p; - if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { - error = EINVAL; - break; - } - - /* - * If an interface index was specified, get a pointer to its ifnet - * structure. - */ - if (!mreq->ipv6mr_interface) - ifp = NULL; - else { - INDEX_TO_IFP(mreq->ipv6mr_interface, ifp); - if (ifp == NULL) { - error = EADDRNOTAVAIL; - break; - } - } - - /* - * Find the membership in the membership array. - */ - for (i = 0; i < imo->i6mo_num_memberships; ++i) { - if ((ifp == NULL || - imo->i6mo_membership[i]->in6m_ifp == ifp) && - IN6_ARE_ADDR_EQUAL(&imo->i6mo_membership[i]->in6m_addr, - &mreq->ipv6mr_multiaddr)) - break; - } - - if (i == imo->i6mo_num_memberships) { - error = EADDRNOTAVAIL; - break; - } - /* - * Give up the multicast address record to which the - * membership points. - */ - in6_delmulti(imo->i6mo_membership[i]); - /* - * Remove the gap in the membership array. - */ - for (++i; i < imo->i6mo_num_memberships; ++i) - imo->i6mo_membership[i-1] = imo->i6mo_membership[i]; - - --imo->i6mo_num_memberships; - - break; - default: - error = EOPNOTSUPP; - break; - } - - if (imo->i6mo_multicast_ifp == NULL && - imo->i6mo_multicast_ttl == IPV6_DEFAULT_MCAST_HOPS && - imo->i6mo_multicast_loop == IPV6_DEFAULT_MCAST_LOOP && - imo->i6mo_num_memberships == 0) { - free(inp->inp_moptions6, M_IPMOPTS); - inp->inp_moptions6 = NULL; - inp->inp_flags &= ~INP_IPV6_MCAST; - } - - return (error); -} - -#define IFP_TO_INDEX(ifp, index) \ -{\ - (index) = ifp->if_index; \ -} - -/*---------------------------------------------------------------------- - * Get IPv6 multicast options. - ----------------------------------------------------------------------*/ -/* ... now assumes all returned values are ints... */ -int ipv6_getmoptions(int optname, struct ipv6_moptions *i6mo, int *mp) -{ - switch (optname) - { - case IPV6_MULTICAST_IF: - if (!i6mo == NULL || !i6mo->i6mo_multicast_ifp) - *mp = 0; - else { - IFP_TO_INDEX(i6mo->i6mo_multicast_ifp, *(unsigned int *)mp); - } - return (0); - - case IPV6_MULTICAST_HOPS: - *mp = i6mo ? IPV6_DEFAULT_MCAST_HOPS : i6mo->i6mo_multicast_ttl; - return (0); - - case IPV6_MULTICAST_LOOP: - *mp = i6mo ? IPV6_DEFAULT_MCAST_LOOP : i6mo->i6mo_multicast_loop; - return (0); - - default: - return (EOPNOTSUPP); - } -} - -/*---------------------------------------------------------------------- - * Free IPv6 multicast options. - ----------------------------------------------------------------------*/ -void -ipv6_freemoptions(i6mo) - register struct ipv6_moptions *i6mo; -{ - register int i; - - if (i6mo != NULL) - { - for (i = 0 ; i < i6mo->i6mo_num_memberships ; i++) - in6_delmulti(i6mo->i6mo_membership[i]); - free(i6mo, M_IPMOPTS); - } -} - -/*---------------------------------------------------------------------- - * Handler for IPV6 [gs]etsockopt() calls. One problem arises when an - * AF_INET6 socket actually wants to set IPv4 options. - * - * The decision to call this or to call ip_ctloutput() is best left in - * the hands of TCP/UDP/etc., which have information about which IP is - * in use. - * - ----------------------------------------------------------------------*/ - -#if __FreeBSD__ -int ipv6_ctloutput(struct socket *so, struct sockopt *sopt) -{ - register struct inpcb *inp; - int op; - int level; - int optname; - int optval; - int error = 0; - - DPRINTF(FINISHED, ("ipv6_ctloutput(so=%08x, sopt=%08x)\n", OSDEP_PCAST(so), - OSDEP_PCAST(sopt))); - - inp = sotoinpcb(so); - - switch(sopt->sopt_dir) { - case SOPT_GET: - op = PRCO_GETOPT; - break; - case SOPT_SET: - op = PRCO_SETOPT; - break; - default: - return EINVAL; - }; - - level = sopt->sopt_level; - optname = sopt->sopt_name; - - DS(); -#else /* __FreeBSD__ */ -int -ipv6_ctloutput (op, so, level, optname, mp) - int op; - struct socket *so; - int level; - int optname; - struct mbuf **mp; -{ - register struct inpcb *inp = sotoinpcb(so); - struct mbuf *m = *mp; - int error = 0; - - DPRINTF(IDL_EVENT, ("ipv6_ctloutput(op=%x,so=%08lx,level=%x,optname=%x,mp)\n", op, (unsigned long)so, level, optname)); -#endif /* __FreeBSD__ */ - - if ((level != IPPROTO_IP) && (level != IPPROTO_IPV6) && (level != IPPROTO_ROUTING) && (level != IPPROTO_ICMPV6)) { -#if !__FreeBSD__ - if (op == PRCO_SETOPT && *mp) - m_free(*mp); -#endif /* !__FreeBSD__ */ - return EINVAL; - } - - DS(); - switch (op) { - case PRCO_SETOPT: - switch(optname) { - case IPV6_UNICAST_HOPS: - DPRINTF(IDL_GROSS_EVENT, ("ipv6_ctloutput: Reached IPV6_UNICAST_HOPS\n")); -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) -#endif /* __FreeBSD__ */ - error = EINVAL; - else { - struct tcpcb *tp; -#if __FreeBSD__ - int i; - - if (error = sooptcopyin(sopt, &i, sizeof(int), sizeof(int))) - break; -#else /* __FreeBSD__ */ - int i = *mtod(m, int *); -#endif /* __FreeBSD__ */ - - if (i == -1) - i = ipv6_defhoplmt; - - if ((i < 0) || (i > 255)) { - error = EINVAL; - break; - }; - - inp->inp_ipv6.ipv6_hoplimit = i; - - /* - * Minor optimization for TCP. We change the hoplimit - * in the template here so we don't have to do the extra - * load before the ipv6_output() call in tcp_output() for - * every single packet (as is the case for IPv4). - */ - DS(); -#if !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) - if ((so->so_type == SOCK_STREAM) && (tp = intotcpcb(inp)) && - tp->t_template) -#if __FreeBSD__ - (mtod(tp->t_template, struct ipv6 *))->ipv6_hoplimit = i; -#else /* __FreeBSD__ */ - ((struct ipv6 *)(tp->t_template))->ipv6_hoplimit = i; -#endif /* __FreeBSD__ */ -#else /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */ - if ((so->so_type == SOCK_STREAM) && (tp = intotcpcb(inp))) - ((struct ipv6 *)(tp->t_tcpiphdr))->ipv6_hoplimit = i; -#endif /* !defined(_BSDI_VERSION) || (_BSDI_VERSION < 199802) */ - } - break; - case IPV6_MULTICAST_IF: - case IPV6_MULTICAST_HOPS: - case IPV6_MULTICAST_LOOP: - case IPV6_DROP_MEMBERSHIP: - case IPV6_ADD_MEMBERSHIP: -#if __FreeBSD__ - { - void *val; - - if (!(val = OSDEP_MALLOC(sopt->sopt_valsize))) { - error = ENOMEM; - break; - }; - - if (error = sooptcopyin(sopt, val, sopt->sopt_valsize, - sopt->sopt_valsize)) { - OSDEP_FREE(val); - break; - }; - - error = ipv6_setmoptions(optname, inp, val, sopt->sopt_valsize); - OSDEP_FREE(val); - }; -#else /* __FreeBSD__ */ - error = ipv6_setmoptions(optname, inp, mtod(m, void *), m->m_len); -#endif /* __FreeBSD__ */ - break; - case IPV6_ADDRFORM: - { - int newpf; - int oldpf = sotopf(inp->inp_socket); - union inpaddru new_faddru; - union inpaddru new_laddru; - int new_flags; - struct protosw *new_proto; - int s; - -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) { - DPRINTF(IDL_ERROR, ("addrform: valsize = %d\n", - sopt->sopt_valsize)); -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) { - DPRINTF(IDL_ERROR, ("addrform: m->m_len = %d\n", m->m_len)); -#endif /* __FreeBSD__ */ - error = EINVAL; - break; - }; - -#if __FreeBSD__ - if (error = sooptcopyin(sopt, &newpf, sizeof(int), sizeof(int))) - break; -#else /* __FreeBSD__ */ - newpf = *(mtod(m, int *)); -#endif /* __FreeBSD__ */ - - DPRINTF(IDL_ERROR, ("newpf = %d, oldpf = %d", newpf, oldpf)); - - if (((newpf != AF_INET) && (newpf != AF_INET6)) || - ((oldpf != AF_INET) && (oldpf != AF_INET6))) - return EINVAL; - - DP(ERROR, __LINE__, d); - - if (newpf == oldpf) - return 0; - - DP(ERROR, inp->inp_flags, 08x); - - if (newpf == AF_INET6) - if (!(inp->inp_flags & INP_IPV6_UNDEC) && - !(inp->inp_flags & INP_IPV6_MAPPED)) - return EINVAL; - - DP(ERROR, __LINE__, d); - - if (!(new_proto = pffindproto(newpf, - so->so_proto->pr_protocol, - so->so_proto->pr_type))) - return EINVAL; - - DP(ERROR, new_proto->pr_domain->dom_family, d); - - new_flags = inp->inp_flags; - new_faddru = inp->inp_faddru; - new_laddru = inp->inp_laddru; - - if (newpf == AF_INET) { - if (new_flags & INP_IPV6_UNDEC) { - new_flags &= ~(INP_IPV6 | INP_IPV6_MAPPED | INP_IPV6_UNDEC); - new_laddru.iau_a4u.inaddr.s_addr = INADDR_ANY; - new_faddru.iau_a4u.inaddr.s_addr = INADDR_ANY; - } else { - new_flags &= ~(INP_IPV6 | INP_IPV6_MAPPED); - } - } else { - new_faddru.iau_addr6.s6_addr32[0] = 0; - new_faddru.iau_addr6.s6_addr32[1] = 0; - new_laddru.iau_addr6.s6_addr32[0] = 0; - new_laddru.iau_addr6.s6_addr32[1] = 0; - - if (new_laddru.iau_a4u.inaddr.s_addr == INADDR_ANY) { - new_flags |= (INP_IPV6 | INP_IPV6_MAPPED | INP_IPV6_UNDEC); - new_faddru.iau_addr6.s6_addr32[2] = 0; - new_laddru.iau_addr6.s6_addr32[2] = 0; - } else { - new_flags |= (INP_IPV6 | INP_IPV6_MAPPED); - new_faddru.iau_addr6.s6_addr32[2] = htonl(0xffff); - new_faddru.iau_addr6.s6_addr32[2] = htonl(0xffff); - } - } - - s = splnet(); - - inp->inp_flags = new_flags; - inp->inp_faddru = new_faddru; - inp->inp_laddru = new_laddru; - so->so_proto = new_proto; - - splx(s); - } - break; - case IPV6_PKTINFO: -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) { -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) { -#endif /* __FreeBSD__ */ - error = EINVAL; - break; - }; - -#if __FreeBSD__ - if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int))) - break; - - if (optval) -#else /* __FreeBSD__ */ - if (*(mtod(m, int *))) -#endif /* __FreeBSD__ */ - inp->inp_flags |= INP_RXINFO; - else - inp->inp_flags &= ~INP_RXINFO; - break; - case IPV6_HOPOPTS: -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) { -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) { -#endif /* __FreeBSD__ */ - error = EINVAL; - break; - }; - -#if __FreeBSD__ - if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int))) - break; - - if (optval) -#else /* __FreeBSD__ */ - if (*(mtod(m, int *))) -#endif /* __FreeBSD__ */ - inp->inp_flags |= INP_RXHOPOPTS; - else - inp->inp_flags &= ~INP_RXHOPOPTS; - break; - case IPV6_DSTOPTS: -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) { -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) { -#endif /* __FreeBSD__ */ - error = EINVAL; - break; - }; - -#if __FreeBSD__ - if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int))) - break; - - if (optval) -#else /* __FreeBSD__ */ - if (*(mtod(m, int *))) -#endif /* __FreeBSD__ */ - inp->inp_flags |= INP_RXDSTOPTS; - else - inp->inp_flags &= ~INP_RXDSTOPTS; - break; - case IPV6_RTHDR: -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) { -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) { -#endif /* __FreeBSD__ */ - error = EINVAL; - break; - }; - -#if __FreeBSD__ - if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int))) - break; - - if (optval) -#else /* __FreeBSD__ */ - if (*(mtod(m, int *))) -#endif /* __FreeBSD__ */ - inp->inp_flags |= INP_RXSRCRT; - else - inp->inp_flags &= ~INP_RXSRCRT; - break; - case IPV6_HOPLIMIT: -#if __FreeBSD__ - if (sopt->sopt_valsize != sizeof(int)) { -#else /* __FreeBSD__ */ - if (m->m_len != sizeof(int)) { -#endif /* __FreeBSD__ */ - error = EINVAL; - break; - }; - -#if __FreeBSD__ - if (error = sooptcopyin(sopt, &optval, sizeof(int), sizeof(int))) - break; - - if (optval) -#else /* __FreeBSD__ */ - if (*(mtod(m, int *))) -#endif /* __FreeBSD__ */ - inp->inp_flags |= INP_HOPLIMIT; - else - inp->inp_flags &= ~INP_HOPLIMIT; - break; - default: - error = ENOPROTOOPT; - break; - } -#if !__FreeBSD__ - if (m) - m_free(m); -#endif /* !__FreeBSD__ */ - break; - case PRCO_GETOPT: - switch(optname) - { - case IPV6_ADDRFORM: - { - int pf = sotopf(inp->inp_socket); - DP(ERROR, pf, d); - if ((pf != PF_INET) && (pf != PF_INET6)) - return EINVAL; - DP(ERROR, __LINE__, d); -#if __FreeBSD__ - error = sooptcopyout(sopt, &pf, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - *mp = m; - m->m_len = sizeof(int); - *mtod(m, int *) = pf; -#endif /* __FreeBSD__ */ - } - break; - case IPV6_UNICAST_HOPS: - DPRINTF(IDL_GROSS_EVENT,("ipv6_ctloutput(): Reached IPV6_UNICAST_HOPS:\n")); -#if __FreeBSD__ - error = sooptcopyout(sopt, &inp->inp_ipv6.ipv6_hoplimit, - sizeof(int)); -#else /* __FreeBSD__ */ - if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - *mp = m; - m->m_len = sizeof(int); - *mtod(m, int *) = inp->inp_ipv6.ipv6_hoplimit; -#endif /* __FreeBSD__ */ - break; - case IPV6_MULTICAST_IF: - case IPV6_MULTICAST_HOPS: - case IPV6_MULTICAST_LOOP: - case IPV6_DROP_MEMBERSHIP: - case IPV6_ADD_MEMBERSHIP: -#if __FreeBSD__ - error = ipv6_getmoptions(optname, inp->inp_moptions6, &optval); - if (error) - break; - - error = sooptcopyout(sopt, &optval, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(*mp = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - - error = ipv6_getmoptions(optname, inp->inp_moptions6, - mtod(*mp, int *)); -#endif /* __FreeBSD__ */ - break; - case IPV6_PKTINFO: -#if __FreeBSD__ - optval = (inp->inp_flags & INP_RXINFO) ? 1 : 0; - error = sooptcopyout(sopt, &optval, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - *mp = m; - m->m_len = sizeof(int); - *mtod(m, int *) = (inp->inp_flags & INP_RXINFO) ? 1 : 0; -#endif /* __FreeBSD__ */ - break; - case IPV6_HOPOPTS: -#if __FreeBSD__ - optval = (inp->inp_flags & INP_RXHOPOPTS) ? 1 : 0; - error = sooptcopyout(sopt, &optval, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - m->m_len = sizeof(int); - *mtod(m, int *) = (inp->inp_flags & INP_RXHOPOPTS) ? 1 : 0; -#endif /* __FreeBSD__ */ - break; - case IPV6_DSTOPTS: -#if __FreeBSD__ - optval = (inp->inp_flags & INP_RXDSTOPTS) ? 1 : 0; - error = sooptcopyout(sopt, &optval, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - m->m_len = sizeof(int); - *mtod(m, int *) = (inp->inp_flags & INP_RXDSTOPTS) ? 1 : 0; -#endif /* __FreeBSD__ */ - break; - case IPV6_RTHDR: -#if __FreeBSD__ - optval = (inp->inp_flags & INP_RXSRCRT) ? 1 : 0; - error = sooptcopyout(sopt, &optval, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - m->m_len = sizeof(int); - *mtod(m, int *) = (inp->inp_flags & INP_RXSRCRT) ? 1 : 0; -#endif /* __FreeBSD__ */ - break; - case IPV6_HOPLIMIT: -#if __FreeBSD__ - optval = (inp->inp_flags & INP_HOPLIMIT) ? 1 : 0; - error = sooptcopyout(sopt, &optval, sizeof(int)); -#else /* __FreeBSD__ */ - if (!(*mp = m = m_get(M_NOWAIT, MT_SOOPTS))) { - error = ENOBUFS; - break; - }; - m->m_len = sizeof(int); - *mtod(m, int *) = (inp->inp_flags & INP_HOPLIMIT) ? 1 : 0; -#endif /* __FreeBSD__ */ - break; - default: - error = ENOPROTOOPT; - break; - } - break; - default: - error = ENOPROTOOPT; - break; - } - - return error; -} - -/*---------------------------------------------------------------------- - * Loops back multicast packets to groups of which I'm a member. - ----------------------------------------------------------------------*/ - -void -ipv6_mloopback(ifp, m, dst) - struct ifnet *ifp; - register struct mbuf *m; - register struct sockaddr_in6 *dst; -{ - struct mbuf *copym; - register struct ipv6 *header; - -#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 - if (!loifp) - return; -#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */ - - /* - * Copy mbuf chain in m, and send to loopback interface. - */ - - copym=m_copym(m,0,M_COPYALL,M_DONTWAIT); - if (copym != NULL) - { - header=mtod(copym,struct ipv6 *); - /* Jumbogram? */ - header->ipv6_length = htons(header->ipv6_length); - /* - * Also, there's an issue about address collision. You may want to - * check the ipv6 destination (or the dst address) and set the ifp - * argument to looutput to be the loopback interface itself iff - * it is to a solicited nodes multicast. - * - * Then again, it may be easier for the soliciting code to burn another - * m_flags bit, and look for it on loopback. - */ -#if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 - (*loifp->if_output)(ifp, copym, (struct sockaddr *)dst, NULL); -#else /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */ -#if __FreeBSD__ - loif->if_output(ifp,copym,(struct sockaddr *)dst,NULL); -#else /* __FreeBSD__ */ - looutput(ifp,copym,(struct sockaddr *)dst,NULL); -#endif /* __FreeBSD__ */ -#endif /* defined(_BSDI_VERSION) && _BSDI_VERSION >= 199802 */ - } - else - DPRINTF(IDL_GROSS_EVENT,("m_copym() failed in ipv6_mloopback.\n")); -} - -/*---------------------------------------------------------------------- - * Fragment IPv6 datagram. - * - * Given a large mbuf chain m, with only its ipv6->ipv6_length field in - * host order, fragment into mtu sized chunks, and return a meta-chain - * with m->m_nextpkt being the subsequent fragments. If there's a problem, - * m_freem all fragments, and return NULL. Also, all ipv6->ipv6_length - * fields are in network order, i.e. ready-to-transmit. - * - * Note that there is an unusually large amount of calls to m_pullup, - * m_copym2etc. here. This will cause performance hits. - * - * A redesign of this is in order, but that will have to wait. - ----------------------------------------------------------------------*/ - -struct mbuf * -ipv6_fragment(m,mtu) - struct mbuf *m; - int mtu; -{ - uint8_t nextopt = IPPROTO_FRAGMENT; - uint divpoint = sizeof(struct ipv6), chunksize, sofar = 0, goal; - struct mbuf *retval = NULL, *newfrag = NULL; - struct ipv6_fraghdr *frag = NULL; - - outfragid++; - - DPRINTF(IDL_FINISHED,\ - ("Entering ipv6_fragment, m_pkthdr.len = %d, mtu = %d\n",\ - m->m_pkthdr.len, mtu)); - - /* - * Find the dividing point between pre-fragment and post-fragment options. - */ - divpoint = ipv6_finddivpoint(m, &nextopt); - - /* - * Options being larger than MTU can happen, especially given large routing - * headers and large options bags. - */ - if (divpoint + sizeof(struct ipv6_fraghdr) >= mtu) - { - DPRINTF(IDL_ERROR, - ("ipv6_fragment(): Options are larger than passed-in MTU.\n")); - m_freem(m); - return NULL; - } - -#ifdef DIAGNOSTIC - if (divpoint & 0x7) - panic("divpoint not a multiple of 8!"); -#endif - - /* - * sofar keeps track of how much I've fragmented, chunksize is how - * much per fragment, goal is how much data to actually fragment. - */ - chunksize = mtu - divpoint - sizeof(struct ipv6_fraghdr); - chunksize &= ~7; - goal = m->m_pkthdr.len - divpoint; - - DPRINTF(IDL_FINISHED, \ - ("Found divpoint (%d), nextopt (%d), chunksize(%d) goal(%d)\n",\ - divpoint, nextopt, chunksize,goal)); - - while (sofar < goal) - if ((newfrag = m_copym2(m, 0, divpoint, M_DONTWAIT)) != NULL) - { - struct mbuf *fraghdrmbuf = NULL; - struct ipv6 *ipv6 = NULL; - uint tocopy = (chunksize <= (goal - sofar))?chunksize:(goal - sofar); - - DPRINTF(IDL_FINISHED,("tocopy == %d\n",tocopy)); - - /* - * Create a new IPv6 fragment, using the header that was slightly - * munged by ipv6_finddivpoint(). - * - * The above m_copym2() creates a copy of the first - */ - - newfrag->m_nextpkt = retval; - retval = newfrag; - - /* - * Append IPv6 fragment header to pre-fragment - */ - for(fraghdrmbuf = retval;fraghdrmbuf->m_next != NULL;) - fraghdrmbuf = fraghdrmbuf->m_next; - MGET(fraghdrmbuf->m_next,M_DONTWAIT,MT_DATA); - if (fraghdrmbuf->m_next == NULL) - { - DPRINTF(IDL_ERROR,("couldn't get new mbuf for frag hdr\n")); - ipv6stat.ips_odropped++; - goto bail; - } - - fraghdrmbuf = fraghdrmbuf->m_next; - fraghdrmbuf->m_len = sizeof(struct ipv6_fraghdr); - retval->m_pkthdr.len += sizeof(struct ipv6_fraghdr); - frag = mtod(fraghdrmbuf,struct ipv6_fraghdr *); - frag->frag_nexthdr = nextopt; - frag->frag_reserved = 0; - frag->frag_bitsoffset = htons(sofar | ((sofar + tocopy) < goal)); - frag->frag_id = outfragid; - - /* - * Copy off (rather than just m_split()) the portion of data that - * goes with this fragment. - */ - if ((fraghdrmbuf->m_next = m_copym2(m,divpoint + sofar,tocopy, - M_DONTWAIT)) == NULL) - { - DPRINTF(IDL_ERROR,("couldn't copy segment.\n")); - goto bail; - } - retval->m_pkthdr.len += tocopy; - - /* - * Update fragment's IPv6 header appropriately. - */ - ipv6 = mtod(retval,struct ipv6 *); - ipv6->ipv6_length = htons(retval->m_pkthdr.len - sizeof(struct ipv6)); - ipv6->ipv6_nexthdr = IPPROTO_FRAGMENT; - ipv6stat.ips_ofragments++; - sofar += tocopy; - } - else - { - /* - * Creation of new fragment failed. - */ - DPRINTF(IDL_ERROR,("m_copym2() failed in fragmentation loop.\n")); - ipv6stat.ips_odropped++; - bail: - DPRINTF(IDL_ERROR,("Bailing out of ipv6_fragment()\n")); - while (retval != NULL) - { - newfrag = retval; - retval = retval->m_nextpkt; - m_freem(newfrag); - } - m_freem(m); - return NULL; - } - - m_freem(m); - - /* Dump mbuf chain list constructed for debugging purposes. */ - DDO(IDL_FINISHED,\ - for (newfrag = retval; newfrag != NULL; newfrag = newfrag->m_nextpkt)\ - dump_mbuf(newfrag) ); - - return retval; -} - -/*---------------------------------------------------------------------- - * Find the dividing point between pre-fragment and post-fragment options. - * The argument nexthdr is read/write, on input, it is the next header - * value that should be written into the previous header's "next hdr" field, - * and what is written back is what used to be in the previous field's - * "next hdr" field. For example: - * - * IP (next hdr = routing) becomes --> IP (next hdr = routing) - * Routing (next hdr = TCP) Routing (next hdr = fragment) - * TCP TCP - * argument nexthdr = fragment argument nexthdr = TCP - * - * This function returns the length of the pre-fragment options, ideal for - * calls to m_split. - * - * As in ipv6_fragment, too many calls to m_pullup/m_pullup2 are performed - * here. Another redesign is called for, but not now. - ----------------------------------------------------------------------*/ - -int -ipv6_finddivpoint(m, nexthdr) - struct mbuf *m; - uint8_t *nexthdr; -{ - uint8_t iprevopt, *prevopt = &(iprevopt), new = *nexthdr; - uint8_t *nextopt; - uint divpoint,maybe = 0; - - /* - * IPv4 authentication code calls this function too. It is likely that - * v4 will just return almost immedately, after determining options - * length. (i.e. never go through the while loop.) - */ - if (mtod(m, struct ip *)->ip_v == 4) - { - iprevopt = IPPROTO_IPV4; - nextopt = &(mtod(m, struct ip *)->ip_p); - divpoint = sizeof(struct ip); - } - else - { - iprevopt = IPPROTO_IPV6; - nextopt = &(mtod(m, struct ipv6 *)->ipv6_nexthdr); - divpoint = sizeof(struct ipv6); - } - - /* - * Scan through options finding dividing point. Dividing point - * for authentication and fragmentation is the same place. - * - * Some weirdness here is that there MIGHT be a "Destination options bag" - * which is actually a "per source-route-hop" bag. There is a strong - * argument for giving this particular options bag a separate type, but - * for now, kludge around it. - * - * The "maybe" variable takes into account the length of this options bag. - */ - while (IS_PREFRAG(*nextopt) && *prevopt != IPPROTO_ROUTING) - { - struct ipv6_srcroute0 *i6sr; - struct ipv6_opthdr *oh; - - /* - * ASSUMES: both nextopt and length will be in the first - * 8 bytes of ANY pre-fragment header. - */ - - if ((divpoint + maybe + 8) > MHLEN) - { - /* - * This becomes complicated. Try and collect invariant part into - * first (now cluster) mbuf on chain. m_pullup() doesn't work with - * clusters, so either write m_pullup2() or inline it here. - * - * m_pullup2(), unlike m_pullup() will only collect exactly - * how many bytes the user requested. This is to avoid problems - * with m_copym() and altering data that is merely referenced - * multiple times, rather than actually copied. (We may eliminate - * the Net/2 hack of adding m_copym2().) - */ - if ((m = m_pullup2(m,divpoint + maybe + 8)) == NULL) - { - DPRINTF(IDL_ERROR,\ - ("m_pullup2(%d) failed in ipv6_fragment().\n",\ - divpoint + maybe + 8)); - return 0; - } - } - else - { - if ((m = m_pullup(m,divpoint + maybe + 8)) == NULL) - { - DPRINTF(IDL_ERROR,\ - ("m_pullup() failed in ipv6_fragment().\n")); - return 0; - } - } - - /* - * Find nextopt, and advance accordingly. - */ - switch (*nextopt) - { - case IPPROTO_HOPOPTS: - /* - * Hop-by-hops should be right after IPv6 hdr. If extra is nonzero, - * then there was a destination options bag. If divpoint is not - * only the size of the IPv6 header, then something came before - * hop-by-hop options. This is not good. - */ - if (maybe || divpoint != sizeof(struct ipv6)) - { - DPRINTF(IDL_ERROR, - ("ipv6_input(): Weird ordering in headers.\n")); - m_freem(m); - return 0; - } - oh = (struct ipv6_opthdr *)(m->m_data + divpoint); - prevopt = nextopt; - nextopt = &(oh->oh_nexthdr); - divpoint += 8 + (oh->oh_extlen << 3); - if (oh->oh_extlen) { - if (divpoint > MHLEN) - { - if ((m = m_pullup2(m,divpoint)) == NULL) - { - DPRINTF(IDL_EVENT,\ - ("m_pullup2(%d) failed in IPPROTO_HOPOPTS nextopt.\n",\ - divpoint)); - return 0; - } - } - else - { - if ((m = m_pullup(m,divpoint)) == NULL) - { - DPRINTF(IDL_EVENT,\ - ("m_pullup() failed in IPPROTO_HOPOPTS nextopt.\n")); - return 0; - } - } - } - break; - case IPPROTO_DSTOPTS: - oh = (struct ipv6_opthdr *)(m->m_data + divpoint); - prevopt = nextopt; - nextopt = &(oh->oh_nexthdr); - maybe = 8 + (oh->oh_extlen << 3); - if (oh->oh_extlen) { - if ( divpoint + maybe > MHLEN) - { - if ((m = m_pullup2(m,divpoint + maybe)) == NULL) - { - DPRINTF(IDL_EVENT,\ - ("m_pullup2(%d) failed in IPPROTO_DSTOPTS nextopt.\n",\ - divpoint+maybe)); - return 0; - } - } - else - { - if ((m = m_pullup(m,divpoint + maybe)) == NULL) - { - DPRINTF(IDL_EVENT,\ - ("m_pullup() failed in IPPROTO_DSTOPTS nextopt.\n")); - return 0; - } - } - } - break; - case IPPROTO_ROUTING: - if (maybe) /* i.e. if we had a destination options bag before - this routing header, we should advance the dividing - point. */ - divpoint += maybe; - maybe = 0; - i6sr = (struct ipv6_srcroute0 *)(m->m_data + divpoint); - prevopt = nextopt; - nextopt = &(i6sr->i6sr_nexthdr); - switch (i6sr->i6sr_type) - { - case 0: - divpoint += 8 + (i6sr->i6sr_len * 8); - break; - default: - DPRINTF(IDL_ERROR, - ("ipv6_input(): Unknown outbound routing header.\n")); - break; - } - if (divpoint > MHLEN) - { - if ((m = m_pullup2(m,divpoint)) == NULL) - { - DPRINTF(IDL_EVENT,\ - ("m_pullup2(%d) failed in IPPROTO_ROUTING nextopt.\n",\ - divpoint)); - return 0; - } - } - else - { - if ((m = m_pullup(m,divpoint)) == NULL) - { - DPRINTF(IDL_EVENT,\ - ("m_pullup() failed in IPPROTO_ROUTING nextopt.\n")); - return 0; - } - } - break; - } /* End of switch statement. */ - }; /* End of while loop. */ - *nexthdr = *nextopt; - *nextopt = new; - return divpoint; -} - -int ipv6_controltoheader(struct mbuf **m, struct mbuf *control, struct ifnet **forceifp, int *payload) -{ - struct cmsghdr *cmsghdr; - int error = EINVAL; - struct mbuf *srcrtm = NULL; - - DPRINTF(IDL_EVENT, ("ipv6_controltoheader(m=%08lx, control=%08lx, forceif=%08lx, payload=%08lx)\n", (unsigned long)m, (unsigned long)control, (unsigned long)forceifp, (unsigned long)payload)); - DDO(IDL_EVENT, dump_mchain(control)); - - while((control = m_pullup2(control, sizeof(struct cmsghdr))) && - (cmsghdr = mtod(control, struct cmsghdr *)) && - (control = m_pullup2(control, cmsghdr->cmsg_len))) { - cmsghdr = mtod(control, struct cmsghdr *); - switch(cmsghdr->cmsg_level) { - case IPPROTO_IPV6: - switch(cmsghdr->cmsg_type) { - case IPV6_PKTINFO: - { - struct in6_pktinfo in6_pktinfo; - struct in6_ifnet *i6ifp; - struct ifaddr *ifa; - - if (cmsghdr->cmsg_len != sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)) - goto ret; - - bcopy((caddr_t)cmsghdr + sizeof(struct cmsghdr), &in6_pktinfo, sizeof(struct in6_pktinfo)); - - if (!in6_pktinfo.ipi6_ifindex) { - if (IN6_IS_ADDR_UNSPECIFIED(&in6_pktinfo.ipi6_addr)) { - DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in degenerate IPV6_PKTINFO case\n")); - break; - } else { - struct in6_ifaddr *i6a; - DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in index = unspec, addr = spec case\n")); - for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next) - if (IN6_ARE_ADDR_EQUAL(&I6A_SIN(i6a)->sin6_addr, &in6_pktinfo.ipi6_addr)) - goto l2; - goto ret; - }; - }; - - DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec case\n")); - - for (i6ifp = in6_ifnet; i6ifp && (i6ifp->i6ifp_ifp->if_index != in6_pktinfo.ipi6_ifindex); i6ifp = i6ifp->i6ifp_next); - - if (!i6ifp) - goto ret; - - if (IN6_IS_ADDR_UNSPECIFIED(&in6_pktinfo.ipi6_addr)) { - DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec, addr = unspec case\n")); - goto l1; - }; - - DPRINTF(FINISHED, ("ipv6_controltoheader: in index = spec, addr = spec case\n")); - -#ifdef __FreeBSD__ - for (ifa = i6ifp->i6ifp_ifp->if_addrhead.tqh_first; ifa; ifa = ifa->ifa_link.tqe_next) { -#else /* __FreeBSD__ */ -#if __NetBSD__ || __OpenBSD__ - for (ifa = i6ifp->i6ifp_ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next) { -#else /* __NetBSD__ || __OpenBSD__ */ - for (ifa = i6ifp->i6ifp_ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { -#endif /* __NetBSD__ || __OpenBSD__ */ -#endif /* __FreeBSD__ */ - if ((ifa->ifa_addr->sa_family == AF_INET6) && !bcmp(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, &in6_pktinfo.ipi6_addr, sizeof(struct in6_addr))) - goto l1; - }; - goto ret; - -l1: *forceifp = i6ifp->i6ifp_ifp; -l2: bcopy(&in6_pktinfo.ipi6_addr, &(mtod(*m, struct ipv6 *)->ipv6_src), sizeof(struct in6_addr)); - }; - break; - case IPV6_HOPLIMIT: - { - int i; - - if (cmsghdr->cmsg_len != sizeof(struct cmsghdr) + sizeof(int)) - goto ret; - - i = *((int *)((caddr_t)cmsghdr + sizeof(struct cmsghdr))); - - if (i == -1) { - if (IN6_IS_ADDR_MULTICAST(&mtod(*m, struct ipv6 *)->ipv6_dst)) - i = IPV6_DEFAULT_MCAST_HOPS; - else - i = ipv6_defhoplmt; - } - - if ((i < 0) || (i > 255)) - goto ret; - - mtod(*m, struct ipv6 *)->ipv6_hoplimit = i; - }; - break; - default: - goto ret; - }; - break; - case IPPROTO_ROUTING: - MGET(srcrtm, M_DONTWAIT, MT_DATA); - if (!srcrtm) { - error = ENOBUFS; - goto ret; - }; - - srcrtm->m_len = cmsghdr->cmsg_len - sizeof(struct cmsghdr) + 3; - if (srcrtm->m_len > MLEN) { - DPRINTF(IDL_ERROR, ("ipv6_controltoheader: requested source route that we can't fit in an mbuf (length %d)\n", srcrtm->m_len)); - goto ret; - }; - - if (srcrtm->m_len & 7) { - DPRINTF(IDL_ERROR, ("ipv6_controltoheader: requested source route has an invalid length; %d needs to be a multiple of eight bytes\n", srcrtm->m_len)); - goto ret; - }; - - *(mtod(srcrtm, uint8_t *) + 1) = (srcrtm->m_len >> 3) - 1; - *(mtod(srcrtm, uint8_t *) + 2) = cmsghdr->cmsg_type; - bcopy((caddr_t *)cmsghdr + sizeof(struct cmsghdr), mtod(srcrtm, uint8_t *) + 3, cmsghdr->cmsg_len - sizeof(struct cmsghdr)); - break; - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - /* XXX */ - goto ret; - default: - goto ret; - }; - m_adj(control, cmsghdr->cmsg_len); - if (!control->m_len) - goto finish; - }; - - DPRINTF(IDL_ERROR, ("ipv6_controltoheader: pullups failed\n")); - goto ret; - -finish: - if (srcrtm) { - struct mbuf *m2; - DPRINTF(IDL_EVENT, ("ipv6_controltoheader: in srcrtm case\n")); - if (!(m2 = m_split(*m, sizeof(struct ipv6), M_DONTWAIT))) - goto ret; - (*m)->m_next = srcrtm; - srcrtm->m_next = m2; - *mtod(srcrtm, uint8_t *) = mtod(*m, struct ipv6 *)->ipv6_nexthdr; - mtod(*m, struct ipv6 *)->ipv6_nexthdr = IPPROTO_ROUTING; - - *payload += srcrtm->m_len; - (*m)->m_pkthdr.len += srcrtm->m_len; - }; - m_freem(control); - DPRINTF(IDL_FINISHED, ("ipv6_controltoheader: returning\n")); - return 0; - -ret: - DPRINTF(IDL_ERROR, ("ipv6_controltoheader: returning error %d\n", error)); - if (srcrtm) - m_free(srcrtm); - if (control) - m_freem(control); - return error; -}; |