X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..5eebf7385fedb1517b66b53c28e5aa6bb0a2be50:/bsd/netinet6/ip6_output.c?ds=sidebyside diff --git a/bsd/netinet6/ip6_output.c b/bsd/netinet6/ip6_output.c index a6e82dac0..3d5eebd78 100644 --- a/bsd/netinet6/ip6_output.c +++ b/bsd/netinet6/ip6_output.c @@ -1,4 +1,5 @@ -/* $KAME: ip6_output.c,v 1.94 2000/04/04 14:45:44 itojun Exp $ */ +/* $FreeBSD: src/sys/netinet6/ip6_output.c,v 1.43 2002/10/31 19:45:48 ume Exp $ */ +/* $KAME: ip6_output.c,v 1.279 2002/01/26 06:12:30 jinmei Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -64,15 +65,6 @@ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */ -#if __FreeBSD__ -#include "opt_ip6fw.h" -#endif -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__) -#include "opt_inet.h" -#if __NetBSD__ /*XXX*/ -#include "opt_ipsec.h" -#endif -#endif #include #include @@ -82,9 +74,7 @@ #include #include #include -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__) #include -#endif #include #include @@ -92,40 +82,36 @@ #include #include -#if defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802) -#include -#include -#endif +#include +#include #include #include #include -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802) || defined (__APPLE__) #include -#else -#include -#endif #include #if IPSEC #include +#if INET6 +#include +#endif #include -#include +extern int ipsec_bypass; #endif /* IPSEC */ -#ifndef __bsdi__ -#include "loop.h" -#endif +#include #include -#if IPV6FIREWALL -#include -#endif - -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 +#ifndef __APPLE__ static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options"); #endif + +static u_long lo_dl_tag = 0; +extern u_long route_generation; + + struct ip6_exthdrs { struct mbuf *ip6e_ip6; struct mbuf *ip6e_hbh; @@ -134,35 +120,19 @@ struct ip6_exthdrs { struct mbuf *ip6e_dest2; }; -static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **, int)); -static int ip6_getpcbopt __P((struct ip6_pktopts *, int, void **, int *)); -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined(__APPLE__) static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, struct socket *, struct sockopt *sopt)); -#else -static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, - struct socket *)); -#endif -static int ip6_setmoptions __P((int, struct ip6_moptions **, struct mbuf *)); +static int ip6_setmoptions __P((int, struct inpcb *, struct mbuf *)); static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **)); static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int)); static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, struct ip6_frag **)); static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t)); static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *)); -#if defined(__bsdi__) || defined(__OpenBSD__) -extern struct ifnet loif; -#endif - -#if __NetBSD__ -extern struct ifnet **ifindex2ifnet; -extern struct ifnet loif[NLOOP]; -#endif -#if MIP6 -int (*mip6_output_hook)(struct mbuf *m, struct ip6_pktopts **opt); -#endif /* MIP6 */ -static u_long lo_dl_tag = 0; +extern int ip_createmoptions(struct ip_moptions **imop); +extern int ip_addmembership(struct ip_moptions *imo, struct ip_mreq *mreq); +extern int ip_dropmembership(struct ip_moptions *imo, struct ip_mreq *mreq); /* * IP6 output. The packet in mbuf chain m contains a skeletal IP6 @@ -170,6 +140,10 @@ static u_long lo_dl_tag = 0; * This function may modify ver and hlim only. * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. + * + * type of "mtu": rt_rmx.rmx_mtu is u_long, ifnet.ifr_mtu is int, and + * nd_ifinfo.linkmtu is u_int32_t. so we use u_long to hold largest one, + * which is rt_rmx.rmx_mtu. */ int ip6_output(m0, opt, ro, flags, im6o, ifpp) @@ -181,13 +155,13 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) struct ifnet **ifpp; /* XXX: just for statistics */ { struct ip6_hdr *ip6, *mhip6; - struct ifnet *ifp; + struct ifnet *ifp, *origifp; struct mbuf *m = m0; int hlen, tlen, len, off; struct route_in6 ip6route; struct sockaddr_in6 *dst; int error = 0; - struct in6_ifaddr *ia; + struct in6_ifaddr *ia = NULL; u_long mtu; u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; struct ip6_exthdrs exthdrs; @@ -195,21 +169,22 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) struct route_in6 *ro_pmtu = NULL; int hdrsplit = 0; int needipsec = 0; - - #if IPSEC int needipsectun = 0; - struct socket *so; + struct socket *so = NULL; struct secpolicy *sp = NULL; /* for AH processing. stupid to have "socket" variable in IP layer... */ - so = ipsec_getsocket(m); - ipsec_setsocket(m, NULL); + if (ipsec_bypass == 0) + { + so = ipsec_getsocket(m); + (void)ipsec_setsocket(m, NULL); + } ip6 = mtod(m, struct ip6_hdr *); #endif /* IPSEC */ -#define MAKE_EXTHDR(hp,mp) \ - { \ +#define MAKE_EXTHDR(hp, mp) \ + do { \ if (hp) { \ struct ip6_ext *eh = (struct ip6_ext *)(hp); \ error = ip6_copyexthdr((mp), (caddr_t)(hp), \ @@ -217,34 +192,15 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) if (error) \ goto freehdrs; \ } \ - } + } while (0) bzero(&exthdrs, sizeof(exthdrs)); -#if MIP6 - /* - * Mobile IPv6 - * - * Call Mobile IPv6 to check if there are any Destination Header - * options to add. - */ - if (mip6_output_hook) { - error = (*mip6_output_hook)(m, &opt); - if (error) - goto freehdrs; - } -#endif /* MIP6 */ - if (opt) { /* Hop-by-Hop options header */ MAKE_EXTHDR(opt->ip6po_hbh, &exthdrs.ip6e_hbh); - if (opt->ip6po_rthdr) { - /* - * Destination options header(1st part) - * This only makes sence with a routing header. - */ - MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); - } + /* Destination options header(1st part) */ + MAKE_EXTHDR(opt->ip6po_dest1, &exthdrs.ip6e_dest1); /* Routing header */ MAKE_EXTHDR(opt->ip6po_rthdr, &exthdrs.ip6e_rthdr); /* Destination options header(2nd part) */ @@ -252,6 +208,9 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) } #if IPSEC + if (ipsec_bypass != 0) + goto skip_ipsec; + /* get a security policy for this packet */ if (so == NULL) sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); @@ -260,7 +219,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) if (sp == NULL) { ipsec6stat.out_inval++; - goto bad; + goto freehdrs; } error = 0; @@ -272,7 +231,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) * This packet is just discarded. */ ipsec6stat.out_polvio++; - goto bad; + goto freehdrs; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: @@ -284,7 +243,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) if (sp->req == NULL) { /* acquire a policy */ error = key_spdacquire(sp); - goto bad; + goto freehdrs; } needipsec = 1; break; @@ -293,6 +252,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) default: printf("ip6_output: Invalid policy found. %d\n", sp->policy); } + skip_ipsec: #endif /* IPSEC */ /* @@ -361,7 +321,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) /* * we treat dest2 specially. this makes IPsec processing - * much easier. + * much easier. the goal here is to make mprev point the + * mbuf prior to dest2. * * result: IPv6 dest2 payload * m and mprev will point to IPv6 header. @@ -375,8 +336,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) ip6->ip6_nxt = IPPROTO_DSTOPTS; } -#define MAKE_CHAIN(m,mp,p,i)\ - {\ +#define MAKE_CHAIN(m, mp, p, i)\ + do {\ if (m) {\ if (!hdrsplit) \ panic("assumption failed: hdr not split"); \ @@ -387,7 +348,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) (mp)->m_next = (m);\ (mp) = (m);\ }\ - } + } while (0) /* * result: IPv6 hbh dest1 rthdr dest2 payload * m will point to IPv6 header. mprev will point to the @@ -439,7 +400,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp) break; default: printf("ip6_output (ipsec): error code %d\n", error); - /*fall through*/ + /* fall through */ case ENOENT: /* don't show these error codes to the user */ error = 0; @@ -465,19 +426,17 @@ skip_ipsec2:; (struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *)); struct ip6_rthdr0 *rh0; - struct in6_addr *addr; finaldst = ip6->ip6_dst; - switch(rh->ip6r_type) { + switch (rh->ip6r_type) { case IPV6_RTHDR_TYPE_0: rh0 = (struct ip6_rthdr0 *)rh; - addr = (struct in6_addr *)(rh0 + 1); - - ip6->ip6_dst = *addr; - bcopy((caddr_t)(addr + 1), (caddr_t)addr, - sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) + ip6->ip6_dst = rh0->ip6r0_addr[0]; + bcopy((caddr_t)&rh0->ip6r0_addr[1], + (caddr_t)&rh0->ip6r0_addr[0], + sizeof(struct in6_addr)*(rh0->ip6r0_segleft - 1) ); - *(addr + rh0->ip6r0_segleft - 1) = finaldst; + rh0->ip6r0_addr[rh0->ip6r0_segleft - 1] = finaldst; break; default: /* is it possible? */ error = EINVAL; @@ -517,8 +476,10 @@ skip_ipsec2:; * and is still up. If not, free it and try again. */ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || - !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst))) { - RTFREE(ro->ro_rt); + dst->sin6_family != AF_INET6 || + !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_dst) || + ro->ro_rt->generation_id != route_generation)) { + rtfree(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } if (ro->ro_rt == 0) { @@ -526,6 +487,11 @@ skip_ipsec2:; dst->sin6_family = AF_INET6; dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_addr = ip6->ip6_dst; +#if SCOPEDROUTING + /* XXX: sin6_scope_id should already be fixed at this point */ + if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) + dst->sin6_scope_id = ntohs(dst->sin6_addr.s6_addr16[1]); +#endif } #if IPSEC if (needipsec && needipsectun) { @@ -565,7 +531,7 @@ skip_ipsec2:; break; default: printf("ip6_output (ipsec): error code %d\n", error); - /*fall through*/ + /* fall through */ case ENOENT: /* don't show these error codes to the user */ error = 0; @@ -576,7 +542,7 @@ skip_ipsec2:; exthdrs.ip6e_ip6 = m; } -#endif /*IPSEC*/ +#endif /* IPSEC */ if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* Unicast */ @@ -589,18 +555,11 @@ skip_ipsec2:; * ifp must point it. */ if (ro->ro_rt == 0) { -#ifndef __bsdi__ /* * non-bsdi always clone routes, if parent is * PRF_CLONING. */ rtalloc((struct route *)ro); -#else - if (ro == &ip6route) /* xxx kazu */ - rtalloc((struct route *)ro); - else - rtcalloc((struct route *)ro); -#endif } if (ro->ro_rt == 0) { ip6stat.ip6s_noroute++; @@ -678,11 +637,7 @@ skip_ipsec2:; in6_ifstat_inc(ifp, ifs6_out_discard); goto bad; } else { -#ifdef __bsdi__ - ifp = loifp; -#else ifp = &loif[0]; -#endif } } @@ -699,11 +654,7 @@ skip_ipsec2:; if (ifp == NULL) { if (ro->ro_rt == 0) { ro->ro_rt = rtalloc1((struct sockaddr *) - &ro->ro_dst, 0 -#if __FreeBSD__ || defined (__APPLE__) - , 0UL -#endif - ); + &ro->ro_dst, 0, 0UL); } if (ro->ro_rt == 0) { ip6stat.ip6s_noroute++; @@ -779,12 +730,6 @@ skip_ipsec2:; if (ifpp) *ifpp = ifp; - /* - * Upper-layer reachability confirmation - */ - if (opt && (opt->ip6po_flags & IP6PO_REACHCONF)) - nd6_nud_hint(ro->ro_rt, NULL); - /* * Determine path MTU. */ @@ -795,7 +740,7 @@ skip_ipsec2:; if (ro_pmtu->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&sin6_fin->sin6_addr, &finaldst))) { - RTFREE(ro_pmtu->ro_rt); + rtfree(ro_pmtu->ro_rt); ro_pmtu->ro_rt = (struct rtentry *)0; } if (ro_pmtu->ro_rt == 0) { @@ -804,18 +749,14 @@ skip_ipsec2:; sin6_fin->sin6_len = sizeof(struct sockaddr_in6); sin6_fin->sin6_addr = finaldst; -#ifdef __bsdi__ /* bsdi needs rtcalloc to clone a route. */ - rtcalloc((struct route *)ro_pmtu); -#else rtalloc((struct route *)ro_pmtu); -#endif } } if (ro_pmtu->ro_rt != NULL) { u_int32_t ifmtu = nd_ifinfo[ifp->if_index].linkmtu; mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; - if (mtu > ifmtu) { + if (mtu > ifmtu || mtu == 0) { /* * The MTU on the route is larger than the MTU on * the interface! This shouldn't happen, unless the @@ -823,6 +764,9 @@ skip_ipsec2:; * interface was brought up. Change the MTU in the * route to match the interface MTU (as long as the * field isn't locked). + * + * if MTU on the route is 0, we need to fix the MTU. + * this case happens with path MTU discovery timeouts. */ mtu = ifmtu; if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0) @@ -835,29 +779,62 @@ skip_ipsec2:; /* * advanced API (IPV6_USE_MIN_MTU) overrides mtu setting */ - if (mtu > IPV6_MMTU) { - if ((opt && (opt->ip6po_flags & IP6PO_MINMTU)) || - (flags & IPV6_MINMTU)) { - mtu = IPV6_MMTU; - } - } + if ((flags & IPV6_MINMTU) != 0 && mtu > IPV6_MMTU) + mtu = IPV6_MMTU; - /* - * Fake link-local scope-class addresses - */ - if ((ifp->if_flags & IFF_LOOPBACK) == 0) { + /* Fake scoped addresses */ + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + /* + * If source or destination address is a scoped address, and + * the packet is going to be sent to a loopback interface, + * we should keep the original interface. + */ + + /* + * XXX: this is a very experimental and temporary solution. + * We eventually have sockaddr_in6 and use the sin6_scope_id + * field of the structure here. + * We rely on the consistency between two scope zone ids + * of source and destination, which should already be assured. + * Larger scopes than link will be supported in the future. + */ + origifp = NULL; if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) - ip6->ip6_src.s6_addr16[1] = 0; - if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) - ip6->ip6_dst.s6_addr16[1] = 0; + origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; + else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; + /* + * XXX: origifp can be NULL even in those two cases above. + * For example, if we remove the (only) link-local address + * from the loopback interface, and try to send a link-local + * address without link-id information. Then the source + * address is ::1, and the destination address is the + * link-local address with its s6_addr16[1] being zero. + * What is worse, if the packet goes to the loopback interface + * by a default rejected route, the null pointer would be + * passed to looutput, and the kernel would hang. + * The following last resort would prevent such disaster. + */ + if (origifp == NULL) + origifp = ifp; } + else + origifp = ifp; +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); +#endif -#if IPV6FIREWALL /* * Check with the firewall... */ - if (ip6_fw_chk_ptr) { + if (ip6_fw_enable && ip6_fw_chk_ptr) { u_short port = 0; + m->m_pkthdr.rcvif = NULL; /* XXX */ /* If ipfw says divert, we have to just drop packet */ if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) { m_freem(m); @@ -868,7 +845,6 @@ skip_ipsec2:; goto done; } } -#endif /* * If the outgoing packet contains a hop-by-hop options header, @@ -876,11 +852,14 @@ skip_ipsec2:; * (RFC 2460, section 4.) */ if (exthdrs.ip6e_hbh) { - struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, - struct ip6_hbh *); + struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); u_int32_t dummy1; /* XXX unused */ u_int32_t dummy2; /* XXX unused */ +#if DIAGNOSTIC + if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) + panic("ip6e_hbh is not continuous"); +#endif /* * XXX: if we have to send an ICMPv6 error to the sender, * we need the M_LOOP flag since icmp6_error() expects @@ -923,23 +902,19 @@ skip_ipsec2:; #endif ) { -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); - if (ia6) { - ia->ia_ifa.ifa_data.ifad_outbytes += - m->m_pkthdr.len; - } - } + /* Record statistics for this interface address. */ + if (ia && !(flags & IPV6_FORWARDING)) { +#ifndef __APPLE__ + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; #endif -#if OLDIP6OUTPUT - error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, - ro->ro_rt); -#else - error = nd6_output(ifp, m, dst, ro->ro_rt); + } +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); #endif + + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); goto done; } else if (mtu < IPV6_MMTU) { /* @@ -967,6 +942,7 @@ skip_ipsec2:; hlen = unfragpartlen; if (mtu > IPV6_MAXPACKET) mtu = IPV6_MAXPACKET; + len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; if (len < 8) { error = EMSGSIZE; @@ -996,7 +972,8 @@ skip_ipsec2:; /* * Loop through length of segment after first fragment, - * make new header and copy data of each part and link onto chain. + * make new header and copy data of each part and link onto + * chain. */ m0 = m; for (off = hlen; off < tlen; off += len) { @@ -1006,6 +983,7 @@ skip_ipsec2:; ip6stat.ip6s_odropped++; goto sendorfree; } + m->m_pkthdr.rcvif = NULL; m->m_flags = m0->m_flags & M_COPYFLAGS; *mnext = m; mnext = &m->m_nextpkt; @@ -1055,24 +1033,18 @@ sendorfree: m0 = m->m_nextpkt; m->m_nextpkt = 0; if (error == 0) { -#if defined(__NetBSD__) && defined(IFA_STATS) - if (IFA_STATS) { - struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); - ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); - if (ia6) { - ia->ia_ifa.ifa_data.ifad_outbytes += - m->m_pkthdr.len; - } - } + /* Record statistics for this interface address. */ + if (ia) { +#ifndef __APPLE__ + ia->ia_ifa.if_opackets++; + ia->ia_ifa.if_obytes += m->m_pkthdr.len; #endif -#if OLDIP6OUTPUT - error = (*ifp->if_output)(ifp, m, - (struct sockaddr *)dst, - ro->ro_rt); -#else - error = nd6_output(ifp, m, dst, ro->ro_rt); + } +#if IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); #endif + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); } else m_freem(m); } @@ -1081,10 +1053,10 @@ sendorfree: ip6stat.ip6s_fragmented++; done: - if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */ - RTFREE(ro->ro_rt); + if (ro == &ip6route && ro->ro_rt) { /* brace necessary for rtfree */ + rtfree(ro->ro_rt); } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) { - RTFREE(ro_pmtu->ro_rt); + rtfree(ro_pmtu->ro_rt); } #if IPSEC @@ -1145,6 +1117,7 @@ ip6_insert_jumboopt(exthdrs, plen) { struct mbuf *mopt; u_char *optbuf; + u_int32_t v; #define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ @@ -1167,18 +1140,42 @@ ip6_insert_jumboopt(exthdrs, plen) mopt = exthdrs->ip6e_hbh; if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { - caddr_t oldoptp = mtod(mopt, caddr_t); + /* + * XXX assumption: + * - exthdrs->ip6e_hbh is not referenced from places + * other than exthdrs. + * - exthdrs->ip6e_hbh is not an mbuf chain. + */ int oldoptlen = mopt->m_len; + struct mbuf *n; - if (mopt->m_flags & M_EXT) - return(ENOBUFS); /* XXX */ - MCLGET(mopt, M_DONTWAIT); - if ((mopt->m_flags & M_EXT) == 0) + /* + * XXX: give up if the whole (new) hbh header does + * not fit even in an mbuf cluster. + */ + if (oldoptlen + JUMBOOPTLEN > MCLBYTES) return(ENOBUFS); - bcopy(oldoptp, mtod(mopt, caddr_t), oldoptlen); - optbuf = mtod(mopt, caddr_t) + oldoptlen; - mopt->m_len = oldoptlen + JUMBOOPTLEN; + /* + * As a consequence, we must always prepare a cluster + * at this point. + */ + MGET(n, M_DONTWAIT, MT_DATA); + if (n) { + MCLGET(n, M_DONTWAIT); + if ((n->m_flags & M_EXT) == 0) { + m_freem(n); + n = NULL; + } + } + if (!n) + return(ENOBUFS); + n->m_len = oldoptlen + JUMBOOPTLEN; + bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t), + oldoptlen); + optbuf = mtod(n, caddr_t) + oldoptlen; + m_freem(mopt); + mopt = exthdrs->ip6e_hbh = n; } else { optbuf = mtod(mopt, u_char *) + mopt->m_len; mopt->m_len += JUMBOOPTLEN; @@ -1197,7 +1194,8 @@ ip6_insert_jumboopt(exthdrs, plen) /* fill in the option. */ optbuf[2] = IP6OPT_JUMBO; optbuf[3] = 4; - *(u_int32_t *)&optbuf[4] = htonl(plen + JUMBOOPTLEN); + v = (u_int32_t)htonl(plen + JUMBOOPTLEN); + bcopy(&v, &optbuf[4], sizeof(u_int32_t)); /* finally, adjust the packet header length */ exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; @@ -1231,7 +1229,7 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp) ; if ((mlast->m_flags & M_EXT) == 0 && - M_TRAILINGSPACE(mlast) < sizeof(struct ip6_frag)) { + M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) { /* use the trailing space of the last mbuf for the fragment hdr */ *frghdrp = (struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len); @@ -1252,109 +1250,57 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp) return(0); } +extern int load_ipfw(); + /* * IP6 socket option processing. */ -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) int ip6_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt; -#else -int -ip6_ctloutput(op, so, level, optname, mp) - int op; - struct socket *so; - int level, optname; - struct mbuf **mp; -#endif { - int privileged, optdatalen; - void *optdata; - struct ip6_recvpktopts *rcvopts; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - register struct inpcb *in6p = sotoinpcb(so); + int privileged; + struct inpcb *in6p = sotoinpcb(so); int error, optval; int level, op, optname; int optlen; struct proc *p; - if (sopt) { + if (sopt == NULL) + panic("ip6_ctloutput: arg soopt is NULL"); + else { level = sopt->sopt_level; op = sopt->sopt_dir; optname = sopt->sopt_name; optlen = sopt->sopt_valsize; p = sopt->sopt_p; - } else { - panic("ip6_ctloutput: arg soopt is NULL"); } -#else -#if HAVE_NRL_INPCB - register struct inpcb *inp = sotoinpcb(so); -#else - register struct in6pcb *in6p = sotoin6pcb(so); -#endif - register struct mbuf *m = *mp; - int error, optval; - int optlen; -#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) && !defined (__APPLE__) - struct proc *p = curproc; /* XXX */ -#endif - - optlen = m ? m->m_len : 0; -#endif error = optval = 0; -#if !defined(__bsdi__) && !(defined(__FreeBSD__) && __FreeBSD__ < 3) && !defined (__APPLE__) privileged = (p == 0 || suser(p->p_ucred, &p->p_acflag)) ? 0 : 1; -#else -#if HAVE_NRL_INPCB - privileged = (inp->inp_socket->so_state & SS_PRIV); -#else - privileged = (in6p->in6p_socket->so_state & SS_PRIV); -#endif -#endif - -#if defined(HAVE_NRL_INPCB) - rcvopts = inp->inp_inputopts6; -#else - rcvopts = in6p->in6p_inputopts; -#endif if (level == IPPROTO_IPV6) { switch (op) { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) case SOPT_SET: -#else - case PRCO_SETOPT: -#endif switch (optname) { case IPV6_PKTOPTIONS: - { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) + { struct mbuf *m; - error = sooptgetm(sopt, &m); /* XXX */ + error = soopt_getm(sopt, &m); /* XXX */ if (error != NULL) break; - error = sooptmcopyin(sopt, m); /* XXX */ + error = soopt_mcopyin(sopt, m); /* XXX */ if (error != NULL) break; error = ip6_pcbopts(&in6p->in6p_outputopts, m, so, sopt); m_freem(m); /* XXX */ -#else -#if HAVE_NRL_INPCB - error = ip6_pcbopts(&inp->inp_outputopts6, - m, so); -#else - error = ip6_pcbopts(&in6p->in6p_outputopts, - m, so); -#endif /* HAVE_NRL_INPCB */ -#endif /* FreeBSD >= 3 */ break; } + /* * Use of some Hop-by-Hop options or some * Destination options, might require special @@ -1368,244 +1314,109 @@ ip6_ctloutput(op, so, level, optname, mp) * receiving ANY hbh/dst options in order to avoid * overhead of parsing options in the kernel. */ - case IPV6_RECVHOPOPTS: - case IPV6_RECVDSTOPTS: - case IPV6_RECVRTHDRDSTOPTS: - if (!privileged) { - error = EPERM; - break; - } - /* fall through */ case IPV6_UNICAST_HOPS: - case IPV6_HOPLIMIT: case IPV6_CHECKSUM: case IPV6_FAITH: - case IPV6_RECVPKTINFO: - case IPV6_RECVHOPLIMIT: - case IPV6_RECVRTHDR: - case IPV6_USE_MIN_MTU: -#ifdef notyet /* To be implemented */ - case IPV6_RECVPATHMTU: -#endif -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - case IPV6_BINDV6ONLY: -#endif - if (optlen != sizeof(int)) + case IPV6_V6ONLY: + if (optlen != sizeof(int)) { error = EINVAL; - else { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - error = sooptcopyin(sopt, &optval, - sizeof optval, sizeof optval); - if (error) - break; -#else - optval = *mtod(m, int *); -#endif - switch (optname) { - - case IPV6_UNICAST_HOPS: - if (optval < -1 || optval >= 256) - error = EINVAL; - else { - /* -1 = kernel default */ -#if HAVE_NRL_INPCB - inp->inp_hops = optval; -#else - in6p->in6p_hops = optval; + break; + } + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - if ((in6p->in6p_vflag & - INP_IPV4) != 0) - in6p->inp_ip_ttl = optval; -#endif -#endif - } - break; -#if HAVE_NRL_INPCB -#define OPTSET(bit) \ - if (optval) \ - inp->inp_flags |= (bit); \ - else \ - inp->inp_flags &= ~(bit); -#else + case IPV6_UNICAST_HOPS: + if (optval < -1 || optval >= 256) + error = EINVAL; + else { + /* -1 = kernel default */ + in6p->in6p_hops = optval; + + if ((in6p->in6p_vflag & + INP_IPV4) != 0) + in6p->inp_ip_ttl = optval; + } + break; #define OPTSET(bit) \ +do { \ if (optval) \ in6p->in6p_flags |= (bit); \ else \ - in6p->in6p_flags &= ~(bit); -#endif -#if HAVE_NRL_INPCB -#define OPTBIT(bit) (inp->inp_flags & (bit) ? 1 : 0) -#else + in6p->in6p_flags &= ~(bit); \ +} while (0) #define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0) -#endif - - case IPV6_RECVPKTINFO: - OPTSET(IN6P_PKTINFO); - if (OPTBIT(IN6P_PKTINFO) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVPKTINFO); - break; - - case IPV6_HOPLIMIT: - { -#if COMPAT_RFC2292 - OPTSET(IN6P_HOPLIMIT); - if (OPTBIT(IN6P_HOPLIMIT) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPLIMIT); - break; -#else /* new advanced API (2292bis) */ - struct ip6_pktopts **optp; -#if HAVE_NRL_INPCB - optp = &inp->inp_outputopts6; -#else - optp = &in6p->in6p_outputopts; -#endif - - error = ip6_pcbopt(IPV6_HOPLIMIT, - (u_char *)&optval, - sizeof(optval), - optp, - privileged); - break; -#endif - } - - case IPV6_RECVHOPLIMIT: - OPTSET(IN6P_HOPLIMIT); - if (OPTBIT(IN6P_HOPLIMIT) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPLIMIT); - break; - - case IPV6_RECVHOPOPTS: - OPTSET(IN6P_HOPOPTS); - if (OPTBIT(IN6P_HOPOPTS) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPOPTS); - break; - - case IPV6_RECVDSTOPTS: - OPTSET(IN6P_DSTOPTS); - if (OPTBIT(IN6P_DSTOPTS) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVDSTOPTS); - break; - - case IPV6_RECVRTHDRDSTOPTS: - OPTSET(IN6P_RTHDRDSTOPTS); - if (OPTBIT(IN6P_RTHDRDSTOPTS) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDRDSTOPTS); - break; - case IPV6_RECVRTHDR: - OPTSET(IN6P_RTHDR); - if (OPTBIT(IN6P_RTHDR) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDR); - break; - - case IPV6_CHECKSUM: -#if HAVE_NRL_INPCB - inp->inp_csumoffset = optval; -#else - in6p->in6p_cksum = optval; -#endif - break; - - case IPV6_FAITH: - OPTSET(IN6P_FAITH); - break; + case IPV6_CHECKSUM: + in6p->in6p_cksum = optval; + break; - case IPV6_USE_MIN_MTU: - OPTSET(IN6P_MINMTU); - break; + case IPV6_FAITH: + OPTSET(IN6P_FAITH); + break; -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(INET6_BINDV6ONLY)) || defined (__APPLE__) - case IPV6_BINDV6ONLY: - OPTSET(IN6P_BINDV6ONLY); + case IPV6_V6ONLY: + /* + * make setsockopt(IPV6_V6ONLY) + * available only prior to bind(2). + * see ipng mailing list, Jun 22 2001. + */ + if (in6p->in6p_lport || + !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) + { + error = EINVAL; break; -#endif } + OPTSET(IN6P_IPV6_V6ONLY); + if (optval) + in6p->in6p_vflag &= ~INP_IPV4; + else + in6p->in6p_vflag |= INP_IPV4; + break; } break; + case IPV6_PKTINFO: + case IPV6_HOPLIMIT: case IPV6_HOPOPTS: - case IPV6_RTHDR: case IPV6_DSTOPTS: - case IPV6_RTHDRDSTOPTS: - if (optlen == sizeof(int)) { - /* RFC 2292 */ -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - error = sooptcopyin(sopt, &optval, - sizeof optval, sizeof optval); - if (error == 0) - break; -#else - optval = *mtod(m, int *); -#endif - switch(optname) { - case IPV6_PKTINFO: - OPTSET(IN6P_PKTINFO); - if (OPTBIT(IN6P_PKTINFO) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVPKTINFO); - break; - case IPV6_HOPOPTS: - /* - * Check super-user privilege. - * See comments for - * IPV6_RECVHOPOPTS. - */ - if (!privileged) - return(EPERM); - OPTSET(IN6P_HOPOPTS); - if (OPTBIT(IN6P_HOPOPTS) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPOPTS); - break; - case IPV6_DSTOPTS: - if (!privileged) - return(EPERM); - OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ - if (OPTBIT(IN6P_DSTOPTS) == 0) { - ip6_reset_rcvopt(rcvopts, IPV6_RECVDSTOPTS); - ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDRDSTOPTS); - } - break; - case IPV6_RTHDR: - OPTSET(IN6P_RTHDR); - if (OPTBIT(IN6P_RTHDR) == 0) - ip6_reset_rcvopt(rcvopts, IPV6_RECVRTHDR); - break; - } + case IPV6_RTHDR: + /* RFC 2292 */ + if (optlen != sizeof(int)) { + error = EINVAL; + break; + } + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; + switch (optname) { + case IPV6_PKTINFO: + OPTSET(IN6P_PKTINFO); + break; + case IPV6_HOPLIMIT: + OPTSET(IN6P_HOPLIMIT); + break; + case IPV6_HOPOPTS: + /* + * Check super-user privilege. + * See comments for IPV6_RECVHOPOPTS. + */ + if (!privileged) + return(EPERM); + OPTSET(IN6P_HOPOPTS); + break; + case IPV6_DSTOPTS: + if (!privileged) + return(EPERM); + OPTSET(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ + break; + case IPV6_RTHDR: + OPTSET(IN6P_RTHDR); break; - } else { - /* new advanced API (2292bis) */ - u_char *optbuf; - int optlen; - struct ip6_pktopts **optp; - -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - optbuf = sopt->sopt_val; - optlen = sopt->sopt_valsize; -#else /* !fbsd3 */ - if (m && m->m_next) { - error = EINVAL; /* XXX */ - break; - } - if (m) { - optbuf = mtod(m, u_char *); - optlen = m->m_len; - } else { - optbuf = NULL; - optlen = 0; - } -#endif - -#if HAVE_NRL_INPCB - optp = &inp->inp_outputopts6; -#else - optp = &in6p->in6p_outputopts; -#endif - - error = ip6_pcbopt(optname, - optbuf, optlen, - optp, privileged); } break; #undef OPTSET @@ -1615,7 +1426,6 @@ ip6_ctloutput(op, so, level, optname, mp) case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) { struct mbuf *m; if (sopt->sopt_valsize > MLEN) { @@ -1631,270 +1441,122 @@ ip6_ctloutput(op, so, level, optname, mp) m->m_len = sopt->sopt_valsize; error = sooptcopyin(sopt, mtod(m, char *), m->m_len, m->m_len); - error = ip6_setmoptions(sopt->sopt_name, - &in6p->in6p_moptions, - m); + error = ip6_setmoptions(sopt->sopt_name, in6p, m); (void)m_free(m); } -#else -#if HAVE_NRL_INPCB - error = ip6_setmoptions(optname, - &inp->inp_moptions6, m); - /* - * XXX: setting the flag would be redundant - * except at the first time. Also, we - * actually don't have to reset the flag, - * since ip6_freemoptions() would simply - * return when the inp_moptions6 is NULL. - */ - if (inp->inp_moptions6) - inp->inp_flags |= INP_IPV6_MCAST; - else - inp->inp_flags &= ~INP_IPV6_MCAST; -#else - error = ip6_setmoptions(optname, - &in6p->in6p_moptions, m); -#endif -#endif break; -#ifndef __bsdi__ - case IPV6_PORTRANGE: -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - error = sooptcopyin(sopt, &optval, sizeof optval, - sizeof optval); - if (error) - break; -#else - optval = *mtod(m, int *); -#endif + case IPV6_PORTRANGE: + error = sooptcopyin(sopt, &optval, + sizeof optval, sizeof optval); + if (error) + break; -#if HAVE_NRL_INPCB -# define in6p inp -# define in6p_flags inp_flags -#endif - switch (optval) { - case IPV6_PORTRANGE_DEFAULT: - in6p->in6p_flags &= ~(IN6P_LOWPORT); - in6p->in6p_flags &= ~(IN6P_HIGHPORT); - break; + switch (optval) { + case IPV6_PORTRANGE_DEFAULT: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + break; - case IPV6_PORTRANGE_HIGH: - in6p->in6p_flags &= ~(IN6P_LOWPORT); - in6p->in6p_flags |= IN6P_HIGHPORT; - break; + case IPV6_PORTRANGE_HIGH: + in6p->in6p_flags &= ~(IN6P_LOWPORT); + in6p->in6p_flags |= IN6P_HIGHPORT; + break; - case IPV6_PORTRANGE_LOW: - in6p->in6p_flags &= ~(IN6P_HIGHPORT); - in6p->in6p_flags |= IN6P_LOWPORT; - break; + case IPV6_PORTRANGE_LOW: + in6p->in6p_flags &= ~(IN6P_HIGHPORT); + in6p->in6p_flags |= IN6P_LOWPORT; + break; - default: - error = EINVAL; + default: + error = EINVAL; + break; + } break; - } -#if HAVE_NRL_INPCB -# undef in6p -# undef in6p_flags -#endif - break; -#endif #if IPSEC case IPV6_IPSEC_POLICY: { caddr_t req = NULL; size_t len = 0; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) struct mbuf *m; -#endif -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - if (error = sooptgetm(sopt, &m)) /* XXX */ + if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; - if (error = sooptmcopyin(sopt, m)) /* XXX */ + if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */ break; -#endif if (m) { req = mtod(m, caddr_t); len = m->m_len; } -#if HAVE_NRL_INPCB - error = ipsec6_set_policy(inp, optname, req, - len, privileged); -#else error = ipsec6_set_policy(in6p, optname, req, len, privileged); -#endif -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) m_freem(m); -#endif } break; -#endif /* IPSEC */ +#endif /* KAME IPSEC */ -#if IPV6FIREWALL case IPV6_FW_ADD: case IPV6_FW_DEL: case IPV6_FW_FLUSH: case IPV6_FW_ZERO: - { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - struct mbuf *m; - struct mbuf **mp = &m; -#endif - -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - if (ip6_fw_ctl_ptr == NULL) - return EINVAL; - if (error = sooptgetm(sopt, &m)) /* XXX */ - break; - if (error = sooptmcopyin(sopt, m)) /* XXX */ - break; -#else - if (ip6_fw_ctl_ptr == NULL) { - if (m) (void)m_free(m); + { + if (ip6_fw_ctl_ptr == NULL && load_ipfw() != 0) return EINVAL; + + error = (*ip6_fw_ctl_ptr)(sopt); } -#endif - error = (*ip6_fw_ctl_ptr)(optname, mp); - m = *mp; - } break; -#endif default: error = ENOPROTOOPT; break; } -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__) - if (m) - (void)m_free(m); -#endif break; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) case SOPT_GET: -#else - case PRCO_GETOPT: -#endif switch (optname) { case IPV6_PKTOPTIONS: -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - if (in6p->in6p_inputopts && - in6p->in6p_inputopts->head) { - error = sooptmcopyout(sopt, - in6p->in6p_inputopts->head); + if (in6p->in6p_options) { + struct mbuf *m; + m = m_copym(in6p->in6p_options, + 0, M_COPYALL, M_WAIT); + error = soopt_mcopyout(sopt, m); + if (error == 0) + m_freem(m); } else sopt->sopt_valsize = 0; -#elif defined(HAVE_NRL_INPCB) - if (inp->inp_options) { - *mp = m_copym(inp->inp_options, 0, - M_COPYALL, M_WAIT); - } else { - *mp = m_get(M_WAIT, MT_SOOPTS); - (*mp)->m_len = 0; - } -#else - if (in6p->in6p_inputopts && - in6p->in6p_inputopts->head) { - *mp = m_copym(in6p->in6p_inputopts->head, - 0, M_COPYALL, M_WAIT); - } else { - *mp = m_get(M_WAIT, MT_SOOPTS); - (*mp)->m_len = 0; - } -#endif break; - case IPV6_RECVHOPOPTS: - case IPV6_RECVDSTOPTS: - case IPV6_RECVRTHDRDSTOPTS: - if (!privileged) { - error = EPERM; - break; - } - /* fall through */ case IPV6_UNICAST_HOPS: case IPV6_CHECKSUM: - case IPV6_RECVPKTINFO: - case IPV6_RECVHOPLIMIT: - case IPV6_RECVRTHDR: - case IPV6_USE_MIN_MTU: -#ifdef notyet /* To be implemented */ - case IPV6_RECVPATHMTU: -#endif - case IPV6_FAITH: -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(INET6_BINDV6ONLY)) || defined(__APPLE__) - case IPV6_BINDV6ONLY: -#endif -#ifndef __bsdi__ + case IPV6_V6ONLY: case IPV6_PORTRANGE: -#endif switch (optname) { case IPV6_UNICAST_HOPS: -#if HAVE_NRL_INPCB - optval = inp->inp_hops; -#else optval = in6p->in6p_hops; -#endif - break; - - case IPV6_RECVPKTINFO: - optval = OPTBIT(IN6P_PKTINFO); - break; - - case IPV6_RECVHOPLIMIT: - optval = OPTBIT(IN6P_HOPLIMIT); - break; - - case IPV6_RECVHOPOPTS: - optval = OPTBIT(IN6P_HOPOPTS); - break; - - case IPV6_RECVDSTOPTS: - optval = OPTBIT(IN6P_DSTOPTS); - break; - - case IPV6_RECVRTHDRDSTOPTS: - optval = OPTBIT(IN6P_RTHDRDSTOPTS); break; case IPV6_CHECKSUM: -#if HAVE_NRL_INPCB - optval = inp->inp_csumoffset; -#else optval = in6p->in6p_cksum; -#endif - break; - - case IPV6_USE_MIN_MTU: - optval = OPTBIT(IN6P_MINMTU); break; case IPV6_FAITH: optval = OPTBIT(IN6P_FAITH); break; -#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || (defined(__NetBSD__) && !defined(INET6_BINDV6ONLY)) || defined (__APPLE__) - case IPV6_BINDV6ONLY: - optval = OPTBIT(IN6P_BINDV6ONLY); + case IPV6_V6ONLY: + optval = OPTBIT(IN6P_IPV6_V6ONLY); break; -#endif -#ifndef __bsdi__ case IPV6_PORTRANGE: { int flags; -#if HAVE_NRL_INPCB - flags = inp->inp_flags; -#else flags = in6p->in6p_flags; -#endif if (flags & IN6P_HIGHPORT) optval = IPV6_PORTRANGE_HIGH; else if (flags & IN6P_LOWPORT) @@ -1903,84 +1565,43 @@ ip6_ctloutput(op, so, level, optname, mp) optval = 0; break; } -#endif } -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) error = sooptcopyout(sopt, &optval, sizeof optval); -#else - *mp = m = m_get(M_WAIT, MT_SOOPTS); - m->m_len = sizeof(int); - *mtod(m, int *) = optval; -#endif break; case IPV6_PKTINFO: + case IPV6_HOPLIMIT: case IPV6_HOPOPTS: case IPV6_RTHDR: case IPV6_DSTOPTS: - case IPV6_RTHDRDSTOPTS: -#if COMPAT_RFC2292 if (optname == IPV6_HOPOPTS || optname == IPV6_DSTOPTS || !privileged) return(EPERM); - switch(optname) { + switch (optname) { case IPV6_PKTINFO: - optbit = OPTBIT(IN6P_PKTINFO); + optval = OPTBIT(IN6P_PKTINFO); break; case IPV6_HOPLIMIT: optval = OPTBIT(IN6P_HOPLIMIT); break; case IPV6_HOPOPTS: - optbit = OPTBIT(IN6P_HOPOPTS); + if (!privileged) + return(EPERM); + optval = OPTBIT(IN6P_HOPOPTS); break; case IPV6_RTHDR: - optbit = OPTBIT(IN6P_RTHDR); + optval = OPTBIT(IN6P_RTHDR); break; case IPV6_DSTOPTS: - optbit = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); + if (!privileged) + return(EPERM); + optval = OPTBIT(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); break; - case IPV6_RTHDRDSTOPTS: /* in 2292bis only */ - return(EOPNOTSUPP); } -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) error = sooptcopyout(sopt, &optval, sizeof optval); -#else - *mp = m = m_get(M_WAIT, MT_SOOPTS); - m->m_len = sizeof(int); - *mtod(m, int *) = optval; -#endif /* FreeBSD3 */ -#else /* new advanced API */ -#if HAVE_NRL_INPCB -#define in6p inp -#define in6p_outputopts inp_outputopts6 -#endif - error = ip6_getpcbopt(in6p->in6p_outputopts, - optname, &optdata, - &optdatalen); - if (error == 0) { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - /* note that optdatalen maybe 0 */ - error = sooptcopyout(sopt, optdata, - optdatalen); -#else /* !FreeBSD3 */ - if (optdatalen > MCLBYTES) - return(EMSGSIZE); /* XXX */ - *mp = m = m_get(M_WAIT, MT_SOOPTS); - if (optdatalen > MLEN) - MCLGET(m, M_WAIT); - m->m_len = optdatalen; - bcopy(optdata, mtod(m, void *), - optdatalen); -#endif /* FreeBSD3 */ - } -#if HAVE_NRL_INPCB -#undef in6p -#undef in6p_outputopts -#endif -#endif /* COMPAT_RFC2292 */ break; case IPV6_MULTICAST_IF: @@ -1988,7 +1609,6 @@ ip6_ctloutput(op, so, level, optname, mp) case IPV6_MULTICAST_LOOP: case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) { struct mbuf *m; error = ip6_getmoptions(sopt->sopt_name, @@ -1998,11 +1618,6 @@ ip6_ctloutput(op, so, level, optname, mp) mtod(m, char *), m->m_len); m_freem(m); } -#elif defined(HAVE_NRL_INPCB) - error = ip6_getmoptions(optname, inp->inp_moptions6, mp); -#else - error = ip6_getmoptions(optname, in6p->in6p_moptions, mp); -#endif break; #if IPSEC @@ -2010,61 +1625,36 @@ ip6_ctloutput(op, so, level, optname, mp) { caddr_t req = NULL; size_t len = 0; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) struct mbuf *m = NULL; struct mbuf **mp = &m; - error = sooptgetm(sopt, &m); /* XXX */ + error = soopt_getm(sopt, &m); /* XXX */ if (error != NULL) break; - error = sooptmcopyin(sopt, m); /* XXX */ + error = soopt_mcopyin(sopt, m); /* XXX */ if (error != NULL) break; -#endif if (m) { req = mtod(m, caddr_t); len = m->m_len; } -#if HAVE_NRL_INPCB - error = ipsec6_get_policy(inp, req, len, mp); -#else error = ipsec6_get_policy(in6p, req, len, mp); -#endif -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) if (error == 0) - error = sooptmcopyout(sopt, m); /*XXX*/ - m_freem(m); -#endif + error = soopt_mcopyout(sopt, m); /*XXX*/ + if (error == 0 && m) + m_freem(m); break; } -#endif /* IPSEC */ +#endif /* KAME IPSEC */ -#if IPV6FIREWALL case IPV6_FW_GET: - { -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - struct mbuf *m; - struct mbuf **mp = &m; -#endif - - if (ip6_fw_ctl_ptr == NULL) - { -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined (__APPLE__) - if (m) - (void)m_free(m); -#endif + { + if (ip6_fw_ctl_ptr == NULL && load_ipfw() != 0) return EINVAL; + + error = (*ip6_fw_ctl_ptr)(sopt); } - error = (*ip6_fw_ctl_ptr)(optname, mp); -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) - if (error == 0) - error = sooptmcopyout(sopt, m); /* XXX */ - if (m) - m_freem(m); -#endif - } break; -#endif default: error = ENOPROTOOPT; @@ -2074,10 +1664,6 @@ ip6_ctloutput(op, so, level, optname, mp) } } else { error = EINVAL; -#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__APPLE__) - if (op == PRCO_SETOPT && *mp) - (void)m_free(*mp); -#endif } return(error); } @@ -2087,57 +1673,46 @@ ip6_ctloutput(op, so, level, optname, mp) * specifying behavior of outgoing packets. */ static int -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) ip6_pcbopts(pktopt, m, so, sopt) -#else -ip6_pcbopts(pktopt, m, so) -#endif struct ip6_pktopts **pktopt; - register struct mbuf *m; + struct mbuf *m; struct socket *so; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) struct sockopt *sopt; -#endif { - register struct ip6_pktopts *opt = *pktopt; + struct ip6_pktopts *opt = *pktopt; int error = 0; -#if defined(__FreeBSD__) && __FreeBSD__ >= 3 || defined (__APPLE__) struct proc *p = sopt->sopt_p; -#else - struct proc *p = curproc; /* XXX */ -#endif int priv = 0; /* turn off any old options. */ if (opt) { #if DIAGNOSTIC - if (opt->ip6po_pktinfo || opt->ip6po_nexthop || - opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || - opt->ip6po_rhinfo.ip6po_rhi_rthdr) - printf("ip6_pcbopts: all specified options are cleared.\n"); + if (opt->ip6po_pktinfo || opt->ip6po_nexthop || + opt->ip6po_hbh || opt->ip6po_dest1 || opt->ip6po_dest2 || + opt->ip6po_rhinfo.ip6po_rhi_rthdr) + printf("ip6_pcbopts: all specified options are cleared.\n"); #endif ip6_clearpktopts(opt, 1, -1); - } - else + } else opt = _MALLOC(sizeof(*opt), M_IP6OPT, M_WAITOK); *pktopt = NULL; if (!m || m->m_len == 0) { /* - * Only turning off any previous options. + * Only turning off any previous options, regardless of + * whether the opt is just created or given. */ if (opt) - _FREE(opt, M_IP6OPT); + FREE(opt, M_IP6OPT); return(0); } /* set options specified by user. */ -#if 0 if (p && !suser(p->p_ucred, &p->p_acflag)) priv = 1; -#endif if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) { ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */ + FREE(opt, M_IP6OPT); return(error); } *pktopt = opt; @@ -2145,288 +1720,16 @@ ip6_pcbopts(pktopt, m, so) } /* - * Set up an IP6 option in pcb for insertion in output packets or - * specifying behavior of outgoing packets. - * XXX: The logic of this function is very similar to ip6_setpktoptions(). + * initialize ip6_pktopts. beware that there are non-zero default values in + * the struct. */ -static int -ip6_pcbopt(optname, buf, len, pktopt, priv) - int optname, len, priv; - u_char *buf; - struct ip6_pktopts **pktopt; -{ +void +init_ip6pktopts(opt) struct ip6_pktopts *opt; - struct in6_pktinfo *pktinfo; - - if (*pktopt == NULL) { - *pktopt = _MALLOC(sizeof(struct ip6_pktopts), M_IP6OPT, - M_WAITOK); - bzero(*pktopt, sizeof(struct ip6_pktopts)); - (*pktopt)->ip6po_hlim = -1; - } - opt = *pktopt; - - switch(optname) { - case IPV6_PKTINFO: - if (len == 0) { /* just remove the option */ - ip6_clearpktopts(opt, 1, IPV6_PKTINFO); - break; - } - - if (len != sizeof(struct in6_pktinfo)) - return EINVAL; - pktinfo = (struct in6_pktinfo *)buf; - - /* - * An application can clear any sticky IPV6_PKTINFO option by - * doing a "regular" setsockopt with ipi6_addr being - * in6addr_any and ipi6_ifindex being zero. - * [rfc2292bis-01, Section 6] - * XXX: Is this a good feature?? (jinmei@kame.net) - */ - if (pktinfo->ipi6_ifindex == 0 && - IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { - ip6_clearpktopts(opt, 1, IPV6_PKTINFO); - break; - } - - /* XXX: this overrides the original data space */ - if (pktinfo->ipi6_ifindex && - IN6_IS_ADDR_LINKLOCAL(&pktinfo->ipi6_addr)) - pktinfo->ipi6_addr.s6_addr16[1] = - htons(pktinfo->ipi6_ifindex); - - if (pktinfo->ipi6_ifindex > if_index || - pktinfo->ipi6_ifindex < 0) - return(ENXIO); - - /* - * Check if the requested source address is indeed a unicast - * address assigned to the node. - */ - if (!IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) { - struct ifaddr *ia; - struct sockaddr_in6 sin6; - - bzero(&sin6, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = pktinfo->ipi6_addr; - ia = ifa_ifwithaddr(sin6tosa(&sin6)); - if (ia == NULL) - return(EADDRNOTAVAIL); - } - - if (opt->ip6po_pktinfo == NULL) - opt->ip6po_pktinfo = _MALLOC(sizeof(struct in6_pktinfo), - M_IP6OPT, M_WAITOK); - bcopy(pktinfo, opt->ip6po_pktinfo, sizeof(*pktinfo)); - - break; - case IPV6_HOPLIMIT: - { - int *hlimp; - - if (len != sizeof(int)) - return(EINVAL); - hlimp = (int *)buf; - if (*hlimp < -1 || *hlimp > 255) - return(EINVAL); - - opt->ip6po_hlim = *hlimp; - break; - } - case IPV6_NEXTHOP: - if (!priv) - return(EPERM); - - if (len == 0) { /* just remove the option */ - ip6_clearpktopts(opt, 1, IPV6_NEXTHOP); - break; - } - - /* check if cmsg_len is large enough for sa_len */ - if (len < sizeof(u_char) || - len < *buf) - return(EINVAL); - - /* turn off the previous option */ - ip6_clearpktopts(opt, 1, IPV6_NEXTHOP); - - opt->ip6po_nexthop = _MALLOC(*buf, M_IP6OPT, M_WAITOK); - bcopy(buf, opt->ip6po_nexthop, *buf); - break; - case IPV6_HOPOPTS: - { - struct ip6_hbh *hbh; - int hbhlen; - - /* - * XXX: We don't allow a non-privileged user to set ANY HbH - * options, since per-option restriction has too much - * overhead. - */ - if (!priv) - return(EPERM); - - if (len == 0) { - ip6_clearpktopts(opt, 1, IPV6_HOPOPTS); - break; /* just remove the option */ - } - - if (len < sizeof(struct ip6_hbh)) - return(EINVAL); - hbh = (struct ip6_hbh *)buf; - hbhlen = (hbh->ip6h_len + 1) << 3; - if (len != hbhlen) - return(EINVAL); - - /* turn off the previous option */ - ip6_clearpktopts(opt, 1, IPV6_HOPOPTS); - - opt->ip6po_hbh = _MALLOC(hbhlen, M_IP6OPT, M_WAITOK); - bcopy(buf, opt->ip6po_hbh, hbhlen); - - break; - } - case IPV6_DSTOPTS: - case IPV6_RTHDRDSTOPTS: - { - struct ip6_dest *dest, *newdest; - int destlen; - - if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */ - return(EPERM); - - if (len == 0) { - ip6_clearpktopts(opt, 1, optname); - break; /* just remove the option */ - } - - if (len < sizeof(struct ip6_dest)) - return(EINVAL); - dest = (struct ip6_dest *)buf; - destlen = (dest->ip6d_len + 1) << 3; - if (len != destlen) - return(EINVAL); - - /* turn off the previous option */ - ip6_clearpktopts(opt, 1, optname); - - newdest = _MALLOC(destlen, M_IP6OPT, M_WAITOK); - bcopy(buf, newdest, destlen); - - if (optname == IPV6_DSTOPTS) - opt->ip6po_dest2 = newdest; - else - opt->ip6po_dest1 = newdest; - - break; - } - case IPV6_RTHDR: - { - struct ip6_rthdr *rth; - int rthlen; - - if (len == 0) { - ip6_clearpktopts(opt, 1, IPV6_RTHDR); - break; /* just remove the option */ - } - - if (len < sizeof(struct ip6_rthdr)) - return(EINVAL); - rth = (struct ip6_rthdr *)buf; - rthlen = (rth->ip6r_len + 1) << 3; - if (len != rthlen) - return(EINVAL); - - switch(rth->ip6r_type) { - case IPV6_RTHDR_TYPE_0: - if (rth->ip6r_len == 0) /* must contain one addr */ - return(EINVAL); - if (rth->ip6r_len % 2) /* length must be even */ - return(EINVAL); - if (rth->ip6r_len / 2 != rth->ip6r_segleft) - return(EINVAL); - break; - default: - return(EINVAL); /* not supported */ - } - - /* turn off the previous option */ - ip6_clearpktopts(opt, 1, IPV6_RTHDR); - - opt->ip6po_rthdr = _MALLOC(rthlen, M_IP6OPT, M_WAITOK); - bcopy(buf, opt->ip6po_rthdr, rthlen); - - break; - } - default: - return(ENOPROTOOPT); - } /* end of switch */ - - return(0); -} - -static int -ip6_getpcbopt(pktopt, optname, datap, datalenp) - struct ip6_pktopts *pktopt; - int optname, *datalenp; - void **datap; { - void *optdata = NULL; - struct ip6_ext *ip6e; - int optdatalen = 0; - if (pktopt == NULL) - goto end; - - switch(optname) { - case IPV6_PKTINFO: - if (pktopt->ip6po_pktinfo) { - optdata = (void *)pktopt->ip6po_pktinfo; - optdatalen = sizeof(struct in6_pktinfo); - } - break; - case IPV6_HOPLIMIT: - optdata = (void *)&pktopt->ip6po_hlim; - optdatalen = sizeof(int); - break; - case IPV6_HOPOPTS: - if (pktopt->ip6po_hbh) { - optdata = (void *)pktopt->ip6po_hbh; - ip6e = (struct ip6_ext *)pktopt->ip6po_hbh; - optdatalen = (ip6e->ip6e_len + 1) << 3; - } - break; - case IPV6_RTHDR: - if (pktopt->ip6po_rthdr) { - optdata = (void *)pktopt->ip6po_rthdr; - ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr; - optdatalen = (ip6e->ip6e_len + 1) << 3; - } - break; - case IPV6_RTHDRDSTOPTS: - if (pktopt->ip6po_dest1) { - optdata = (void *)pktopt->ip6po_dest1; - ip6e = (struct ip6_ext *)pktopt->ip6po_dest1; - optdatalen = (ip6e->ip6e_len + 1) << 3; - } - break; - case IPV6_DSTOPTS: - if (pktopt->ip6po_dest2) { - optdata = (void *)pktopt->ip6po_dest2; - ip6e = (struct ip6_ext *)pktopt->ip6po_dest2; - optdatalen = (ip6e->ip6e_len + 1) << 3; - } - break; - } - - end: - *datap = optdata; - *datalenp = optdatalen; - - return(0); + bzero(opt, sizeof(*opt)); + opt->ip6po_hlim = -1; /* -1 means default hop limit */ } void @@ -2437,52 +1740,55 @@ ip6_clearpktopts(pktopt, needfree, optname) if (pktopt == NULL) return; - if (optname == -1 || optname == IPV6_PKTINFO) { + if (optname == -1) { if (needfree && pktopt->ip6po_pktinfo) - _FREE(pktopt->ip6po_pktinfo, M_IP6OPT); + FREE(pktopt->ip6po_pktinfo, M_IP6OPT); pktopt->ip6po_pktinfo = NULL; } - if (optname == -1 || optname == IPV6_HOPLIMIT) + if (optname == -1) pktopt->ip6po_hlim = -1; - if (optname == -1 || optname == IPV6_NEXTHOP) { + if (optname == -1) { if (needfree && pktopt->ip6po_nexthop) - _FREE(pktopt->ip6po_nexthop, M_IP6OPT); + FREE(pktopt->ip6po_nexthop, M_IP6OPT); pktopt->ip6po_nexthop = NULL; } - if (optname == -1 || optname == IPV6_HOPOPTS) { + if (optname == -1) { if (needfree && pktopt->ip6po_hbh) - _FREE(pktopt->ip6po_hbh, M_IP6OPT); + FREE(pktopt->ip6po_hbh, M_IP6OPT); pktopt->ip6po_hbh = NULL; } - if (optname == -1 || optname == IPV6_RTHDRDSTOPTS) { + if (optname == -1) { if (needfree && pktopt->ip6po_dest1) - _FREE(pktopt->ip6po_dest1, M_IP6OPT); + FREE(pktopt->ip6po_dest1, M_IP6OPT); pktopt->ip6po_dest1 = NULL; } - if (optname == -1 || optname == IPV6_RTHDR) { + if (optname == -1) { if (needfree && pktopt->ip6po_rhinfo.ip6po_rhi_rthdr) - _FREE(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); + FREE(pktopt->ip6po_rhinfo.ip6po_rhi_rthdr, M_IP6OPT); pktopt->ip6po_rhinfo.ip6po_rhi_rthdr = NULL; if (pktopt->ip6po_route.ro_rt) { - RTFREE(pktopt->ip6po_route.ro_rt); + rtfree(pktopt->ip6po_route.ro_rt); pktopt->ip6po_route.ro_rt = NULL; } } - if (optname == -1 || optname == IPV6_DSTOPTS) { + if (optname == -1) { if (needfree && pktopt->ip6po_dest2) - _FREE(pktopt->ip6po_dest2, M_IP6OPT); + FREE(pktopt->ip6po_dest2, M_IP6OPT); pktopt->ip6po_dest2 = NULL; } } -#define PKTOPT_EXTHDRCPY(type) if (src->type) {\ +#define PKTOPT_EXTHDRCPY(type) \ +do {\ + if (src->type) {\ int hlen =\ (((struct ip6_ext *)src->type)->ip6e_len + 1) << 3;\ dst->type = _MALLOC(hlen, M_IP6OPT, canwait);\ if (dst->type == NULL && canwait == M_NOWAIT)\ goto bad;\ bcopy(src->type, dst->type, hlen);\ - } + }\ +} while (0) struct ip6_pktopts * ip6_copypktopts(src, canwait) @@ -2498,11 +1804,10 @@ ip6_copypktopts(src, canwait) dst = _MALLOC(sizeof(*dst), M_IP6OPT, canwait); if (dst == NULL && canwait == M_NOWAIT) - goto bad; + return (NULL); bzero(dst, sizeof(*dst)); dst->ip6po_hlim = src->ip6po_hlim; - dst->ip6po_flags = src->ip6po_flags; if (src->ip6po_pktinfo) { dst->ip6po_pktinfo = _MALLOC(sizeof(*dst->ip6po_pktinfo), M_IP6OPT, canwait); @@ -2525,13 +1830,13 @@ ip6_copypktopts(src, canwait) return(dst); bad: - printf("ip6_copypktopts: copy failed"); - if (dst->ip6po_pktinfo) _FREE(dst->ip6po_pktinfo, M_IP6OPT); - if (dst->ip6po_nexthop) _FREE(dst->ip6po_nexthop, M_IP6OPT); - if (dst->ip6po_hbh) _FREE(dst->ip6po_hbh, M_IP6OPT); - if (dst->ip6po_dest1) _FREE(dst->ip6po_dest1, M_IP6OPT); - if (dst->ip6po_dest2) _FREE(dst->ip6po_dest2, M_IP6OPT); - if (dst->ip6po_rthdr) _FREE(dst->ip6po_rthdr, M_IP6OPT); + if (dst->ip6po_pktinfo) FREE(dst->ip6po_pktinfo, M_IP6OPT); + if (dst->ip6po_nexthop) FREE(dst->ip6po_nexthop, M_IP6OPT); + if (dst->ip6po_hbh) FREE(dst->ip6po_hbh, M_IP6OPT); + if (dst->ip6po_dest1) FREE(dst->ip6po_dest1, M_IP6OPT); + if (dst->ip6po_dest2) FREE(dst->ip6po_dest2, M_IP6OPT); + if (dst->ip6po_rthdr) FREE(dst->ip6po_rthdr, M_IP6OPT); + FREE(dst, M_IP6OPT); return(NULL); } #undef PKTOPT_EXTHDRCPY @@ -2545,28 +1850,29 @@ ip6_freepcbopts(pktopt) ip6_clearpktopts(pktopt, 1, -1); - _FREE(pktopt, M_IP6OPT); + FREE(pktopt, M_IP6OPT); } /* * Set the IP6 multicast options in response to user setsockopt(). */ static int -ip6_setmoptions(optname, im6op, m) +ip6_setmoptions(optname, in6p, m) int optname; - struct ip6_moptions **im6op; + struct inpcb* in6p; struct mbuf *m; { int error = 0; u_int loop, ifindex; struct ipv6_mreq *mreq; struct ifnet *ifp; + struct ip6_moptions **im6op = &in6p->in6p_moptions; struct ip6_moptions *im6o = *im6op; + struct ip_moptions *imo; struct route_in6 ro; struct sockaddr_in6 *dst; struct in6_multi_mship *imm; - - struct proc *p = current_proc(); /* ### */ + struct proc *p = current_proc(); /* XXX */ if (im6o == NULL) { /* @@ -2584,6 +1890,18 @@ ip6_setmoptions(optname, im6op, m) im6o->im6o_multicast_loop = IPV6_DEFAULT_MULTICAST_LOOP; LIST_INIT(&im6o->im6o_memberships); } + + if (in6p->inp_moptions == NULL) { + /* + * No IPv4 multicast option buffer attached to the pcb; + * call ip_createmoptions to allocate one and initialize + * to default values. + */ + error = ip_createmoptions(&in6p->inp_moptions); + if (error != 0) + return error; + } + imo = in6p->inp_moptions; switch (optname) { @@ -2595,7 +1913,7 @@ ip6_setmoptions(optname, im6op, m) error = EINVAL; break; } - ifindex = *(mtod(m, u_int *)); + bcopy(mtod(m, u_int *), &ifindex, sizeof(ifindex)); if (ifindex < 0 || if_index < ifindex) { error = ENXIO; /* XXX EINVAL? */ break; @@ -2606,6 +1924,7 @@ ip6_setmoptions(optname, im6op, m) break; } im6o->im6o_multicast_ifp = ifp; + imo->imo_multicast_ifp = ifp; break; case IPV6_MULTICAST_HOPS: @@ -2618,13 +1937,16 @@ ip6_setmoptions(optname, im6op, m) error = EINVAL; break; } - optval = *(mtod(m, u_int *)); + bcopy(mtod(m, u_int *), &optval, sizeof(optval)); if (optval < -1 || optval >= 256) error = EINVAL; - else if (optval == -1) + else if (optval == -1) { im6o->im6o_multicast_hlim = ip6_defmcasthlim; - else + imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; + } else { im6o->im6o_multicast_hlim = optval; + imo->imo_multicast_ttl = optval; + } break; } @@ -2633,12 +1955,17 @@ ip6_setmoptions(optname, im6op, m) * Set the loopback flag for outgoing multicast packets. * Must be zero or one. */ - if (m == NULL || m->m_len != sizeof(u_int) || - (loop = *(mtod(m, u_int *))) > 1) { + if (m == NULL || m->m_len != sizeof(u_int)) { + error = EINVAL; + break; + } + bcopy(mtod(m, u_int *), &loop, sizeof(loop)); + if (loop > 1) { error = EINVAL; break; } im6o->im6o_multicast_loop = loop; + imo->imo_multicast_loop = loop; break; case IPV6_JOIN_GROUP: @@ -2651,31 +1978,58 @@ ip6_setmoptions(optname, im6op, m) break; } mreq = mtod(m, struct ipv6_mreq *); + /* + * If the interface is specified, validate it. + */ + if (mreq->ipv6mr_interface < 0 + || if_index < mreq->ipv6mr_interface) { + error = ENXIO; /* XXX EINVAL? */ + break; + } + if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { /* * We use the unspecified address to specify to accept * all multicast addresses. Only super user is allowed * to do this. */ -#if ISFB31 - if (suser(p->p_ucred, &p->p_acflag)) { + if (suser(p->p_ucred, &p->p_acflag)) + { error = EACCES; break; } -#endif + } else if (IN6_IS_ADDR_V4MAPPED(&mreq->ipv6mr_multiaddr)) { + struct ip_mreq v4req; + + v4req.imr_multiaddr.s_addr = mreq->ipv6mr_multiaddr.s6_addr32[3]; + v4req.imr_interface.s_addr = INADDR_ANY; + + /* Find an IPv4 address on the specified interface. */ + if (mreq->ipv6mr_interface != 0) { + struct in_ifaddr *ifa; + + ifp = ifindex2ifnet[mreq->ipv6mr_interface]; + + TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) { + if (ifa->ia_ifp == ifp) { + v4req.imr_interface = IA_SIN(ifa)->sin_addr; + break; + } + } + + if (v4req.imr_multiaddr.s_addr == 0) { + /* Interface has no IPv4 address. */ + error = EINVAL; + break; + } + } + + error = ip_addmembership(imo, &v4req); + break; } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { error = EINVAL; break; } - - /* - * If the interface is specified, validate it. - */ - if (mreq->ipv6mr_interface < 0 - || if_index < mreq->ipv6mr_interface) { - error = ENXIO; /* XXX EINVAL? */ - break; - } /* * If no interface was explicitly specified, choose an * appropriate one according to the given multicast address. @@ -2689,11 +2043,7 @@ ip6_setmoptions(optname, im6op, m) * XXX: is it a good approach? */ if (IN6_IS_ADDR_MC_NODELOCAL(&mreq->ipv6mr_multiaddr)) { -#ifdef __bsdi__ - ifp = loifp; -#else ifp = &loif[0]; -#endif } else { ro.ro_rt = NULL; dst = (struct sockaddr_in6 *)&ro.ro_dst; @@ -2752,7 +2102,7 @@ ip6_setmoptions(optname, im6op, m) } if ((imm->i6mm_maddr = in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) { - _FREE(imm, M_IPMADDR); + FREE(imm, M_IPMADDR); break; } LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); @@ -2768,15 +2118,6 @@ ip6_setmoptions(optname, im6op, m) break; } mreq = mtod(m, struct ipv6_mreq *); - if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { - if (suser(p->p_ucred, &p->p_acflag)) { - error = EACCES; - break; - } - } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { - error = EINVAL; - break; - } /* * If an interface address was specified, get a pointer * to its ifnet structure. @@ -2787,6 +2128,35 @@ ip6_setmoptions(optname, im6op, m) break; } ifp = ifindex2ifnet[mreq->ipv6mr_interface]; + + if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) { + if (suser(p->p_ucred, &p->p_acflag)) { + error = EACCES; + break; + } + } else if (IN6_IS_ADDR_V4MAPPED(&mreq->ipv6mr_multiaddr)) { + struct ip_mreq v4req; + + v4req.imr_multiaddr.s_addr = mreq->ipv6mr_multiaddr.s6_addr32[3]; + v4req.imr_interface.s_addr = INADDR_ANY; + + if (ifp != NULL) { + struct in_ifaddr *ifa; + + TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) { + if (ifa->ia_ifp == ifp) { + v4req.imr_interface = IA_SIN(ifa)->sin_addr; + break; + } + } + } + + error = ip_dropmembership(imo, &v4req); + break; + } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { + error = EINVAL; + break; + } /* * Put interface index into the multicast address, * if the address has link-local scope. @@ -2817,7 +2187,7 @@ ip6_setmoptions(optname, im6op, m) */ LIST_REMOVE(imm, i6mm_chain); in6_delmulti(imm->i6mm_maddr); - _FREE(imm, M_IPMADDR); + FREE(imm, M_IPMADDR); break; default: @@ -2832,9 +2202,17 @@ ip6_setmoptions(optname, im6op, m) im6o->im6o_multicast_hlim == ip6_defmcasthlim && im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP && im6o->im6o_memberships.lh_first == NULL) { - _FREE(*im6op, M_IPMOPTS); + FREE(*im6op, M_IPMOPTS); *im6op = NULL; } + if (imo->imo_multicast_ifp == NULL && + imo->imo_multicast_vif == -1 && + imo->imo_multicast_ttl == IP_DEFAULT_MULTICAST_TTL && + imo->imo_multicast_loop == IP_DEFAULT_MULTICAST_LOOP && + imo->imo_num_memberships == 0) { + ip_freemoptions(imo); + in6p->inp_moptions = 0; + } return(error); } @@ -2845,16 +2223,12 @@ ip6_setmoptions(optname, im6op, m) static int ip6_getmoptions(optname, im6o, mp) int optname; - register struct ip6_moptions *im6o; - register struct mbuf **mp; + struct ip6_moptions *im6o; + struct mbuf **mp; { u_int *hlim, *loop, *ifindex; -#if __FreeBSD__ || defined (__APPLE__) *mp = m_get(M_WAIT, MT_HEADER); /*XXX*/ -#else - *mp = m_get(M_WAIT, MT_SOOPTS); -#endif switch (optname) { @@ -2895,7 +2269,7 @@ ip6_getmoptions(optname, im6o, mp) */ void ip6_freemoptions(im6o) - register struct ip6_moptions *im6o; + struct ip6_moptions *im6o; { struct in6_multi_mship *imm; @@ -2906,9 +2280,9 @@ ip6_freemoptions(im6o) LIST_REMOVE(imm, i6mm_chain); if (imm->i6mm_maddr) in6_delmulti(imm->i6mm_maddr); - _FREE(imm, M_IPMADDR); + FREE(imm, M_IPMADDR); } - _FREE(im6o, M_IPMOPTS); + FREE(im6o, M_IPMOPTS); } /* @@ -2920,13 +2294,12 @@ ip6_setpktoptions(control, opt, priv, needcopy) struct ip6_pktopts *opt; int priv, needcopy; { - register struct cmsghdr *cm = 0; + struct cmsghdr *cm = 0; if (control == 0 || opt == 0) return(EINVAL); - bzero(opt, sizeof(*opt)); - opt->ip6po_hlim = -1; /* -1 means to use default hop limit */ + init_ip6pktopts(opt); /* * XXX: Currently, we assume all the optional information is stored @@ -2943,7 +2316,10 @@ ip6_setpktoptions(control, opt, priv, needcopy) if (cm->cmsg_level != IPPROTO_IPV6) continue; - switch(cm->cmsg_type) { + /* + * XXX should check if RFC2292 API is mixed with 2292bis API + */ + switch (cm->cmsg_type) { case IPV6_PKTINFO: if (cm->cmsg_len != CMSG_LEN(sizeof(struct in6_pktinfo))) return(EINVAL); @@ -2952,8 +2328,8 @@ ip6_setpktoptions(control, opt, priv, needcopy) opt->ip6po_pktinfo = _MALLOC(sizeof(struct in6_pktinfo), M_IP6OPT, M_WAITOK); - *opt->ip6po_pktinfo = - *(struct in6_pktinfo *)CMSG_DATA(cm); + bcopy(CMSG_DATA(cm), opt->ip6po_pktinfo, + sizeof(struct in6_pktinfo)); } else opt->ip6po_pktinfo = (struct in6_pktinfo *)CMSG_DATA(cm); @@ -2969,10 +2345,11 @@ ip6_setpktoptions(control, opt, priv, needcopy) /* * Check if the requested source address is indeed a - * unicast address assigned to the node. + * unicast address assigned to the node, and can be + * used as the packet's source address. */ if (!IN6_IS_ADDR_UNSPECIFIED(&opt->ip6po_pktinfo->ipi6_addr)) { - struct ifaddr *ia; + struct in6_ifaddr *ia6; struct sockaddr_in6 sin6; bzero(&sin6, sizeof(sin6)); @@ -2980,8 +2357,10 @@ ip6_setpktoptions(control, opt, priv, needcopy) sin6.sin6_family = AF_INET6; sin6.sin6_addr = opt->ip6po_pktinfo->ipi6_addr; - ia = ifa_ifwithaddr(sin6tosa(&sin6)); - if (ia == NULL) + ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6)); + if (ia6 == NULL || + (ia6->ia6_flags & (IN6_IFF_ANYCAST | + IN6_IFF_NOTREADY)) != 0) return(EADDRNOTAVAIL); } break; @@ -2998,7 +2377,7 @@ ip6_setpktoptions(control, opt, priv, needcopy) case IPV6_NEXTHOP: if (!priv) return(EPERM); - + if (cm->cmsg_len < sizeof(u_char) || /* check if cmsg_len is large enough for sa_len */ cm->cmsg_len < CMSG_LEN(*CMSG_DATA(cm))) @@ -3039,7 +2418,7 @@ ip6_setpktoptions(control, opt, priv, needcopy) case IPV6_DSTOPTS: { - struct ip6_dest *dest; + struct ip6_dest *dest, **newdest; int destlen; if (cm->cmsg_len < CMSG_LEN(sizeof(struct ip6_dest))) @@ -3049,29 +2428,30 @@ ip6_setpktoptions(control, opt, priv, needcopy) if (cm->cmsg_len != CMSG_LEN(destlen)) return(EINVAL); - /* - * If there is no routing header yet, the destination - * options header should be put on the 1st part. - * Otherwise, the header should be on the 2nd part. - * (See RFC 2460, section 4.1) + /* + * The old advacned API is ambiguous on this + * point. Our approach is to determine the + * position based according to the existence + * of a routing header. Note, however, that + * this depends on the order of the extension + * headers in the ancillary data; the 1st part + * of the destination options header must + * appear before the routing header in the + * ancillary data, too. + * RFC2292bis solved the ambiguity by + * introducing separate cmsg types. */ - if (opt->ip6po_rthdr == NULL) { - if (needcopy) { - opt->ip6po_dest1 = - _MALLOC(destlen, M_IP6OPT, - M_WAITOK); - bcopy(dest, opt->ip6po_dest1, destlen); - } else - opt->ip6po_dest1 = dest; - } else { - if (needcopy) { - opt->ip6po_dest2 = - _MALLOC(destlen, M_IP6OPT, - M_WAITOK); - bcopy(dest, opt->ip6po_dest2, destlen); - } else - opt->ip6po_dest2 = dest; - } + if (opt->ip6po_rthdr == NULL) + newdest = &opt->ip6po_dest1; + else + newdest = &opt->ip6po_dest2; + + if (needcopy) { + *newdest = _MALLOC(destlen, M_IP6OPT, M_WAITOK); + bcopy(dest, *newdest, destlen); + } else + *newdest = dest; + break; } @@ -3087,7 +2467,7 @@ ip6_setpktoptions(control, opt, priv, needcopy) if (cm->cmsg_len != CMSG_LEN(rthlen)) return(EINVAL); - switch(rth->ip6r_type) { + switch (rth->ip6r_type) { case IPV6_RTHDR_TYPE_0: /* must contain one addr */ if (rth->ip6r_len == 0) @@ -3112,28 +2492,6 @@ ip6_setpktoptions(control, opt, priv, needcopy) break; } - case IPV6_REACHCONF: -#if 1 - /* - * it looks dangerous to allow IPV6_REACHCONF to - * normal user. it affects the ND state (system state) - * and can affect communication by others - jinmei - */ - if (!priv) - return(EPERM); -#endif - - if (cm->cmsg_len != CMSG_LEN(0)) - return(EINVAL); - opt->ip6po_flags |= IP6PO_REACHCONF; - break; - - case IPV6_USE_MIN_MTU: - if (cm->cmsg_len != CMSG_LEN(0)) - return(EINVAL); - opt->ip6po_flags |= IP6PO_MINMTU; - break; - default: return(ENOPROTOOPT); } @@ -3151,34 +2509,64 @@ ip6_setpktoptions(control, opt, priv, needcopy) void ip6_mloopback(ifp, m, dst) struct ifnet *ifp; - register struct mbuf *m; - register struct sockaddr_in6 *dst; + struct mbuf *m; + struct sockaddr_in6 *dst; { - struct mbuf *copym; + struct mbuf *copym; + struct ip6_hdr *ip6; copym = m_copy(m, 0, M_COPYALL); - if (copym != NULL) { + if (copym == NULL) + return; + + /* + * Make sure to deep-copy IPv6 header portion in case the data + * is in an mbuf cluster, so that we can safely override the IPv6 + * header portion later. + */ + if ((copym->m_flags & M_EXT) != 0 || + copym->m_len < sizeof(struct ip6_hdr)) { + copym = m_pullup(copym, sizeof(struct ip6_hdr)); + if (copym == NULL) + return; + } + +#if DIAGNOSTIC + if (copym->m_len < sizeof(*ip6)) { + m_freem(copym); + return; + } +#endif + + ip6 = mtod(copym, struct ip6_hdr *); +#ifndef SCOPEDROUTING + /* + * clear embedded scope identifiers if necessary. + * in6_clearscope will touch the addresses only when necessary. + */ + in6_clearscope(&ip6->ip6_src); + in6_clearscope(&ip6->ip6_dst); +#endif + #ifdef __APPLE__ - /* - * TedW: - * We need to send all loopback traffic down to dlil in case - * a filter has tapped-in. - */ - - if (lo_dl_tag == 0) - dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET6, &lo_dl_tag); - - if (lo_dl_tag) - dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *) dst, 0); - else { - printf("Warning: ip6_mloopback call to dlil_find_dltag failed!\n"); - m_freem(copym); - } + + /* Makes sure the HW checksum flags are cleaned before sending the packet */ + + copym->m_pkthdr.rcvif = (struct ifnet *)0; + copym->m_pkthdr.csum_data = 0; + copym->m_pkthdr.csum_flags = 0; + + if (lo_dl_tag == 0) + dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET, &lo_dl_tag); + + if (lo_dl_tag) { + copym->m_pkthdr.rcvif = ifp; + dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *)dst, 0); + } else + m_free(copym); #else - (void)if_simloop(ifp, copym, (struct sockaddr *)dst, NULL); - (void)looutput(ifp, copym, (struct sockaddr *)dst, NULL); + (void)if_simloop(ifp, copym, dst->sin6_family, NULL); #endif - } } /* @@ -3216,10 +2604,6 @@ ip6_splithdr(m, exthdrs) /* * Compute IPv6 extension header length. */ -#if HAVE_NRL_INPCB -# define in6pcb inpcb -# define in6p_outputopts inp_outputopts6 -#endif int ip6_optlen(in6p) struct in6pcb *in6p; @@ -3242,7 +2626,3 @@ ip6_optlen(in6p) return len; #undef elen } -#if HAVE_NRL_INPCB -# undef in6pcb -# undef in6p_outputopts -#endif