X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/91447636331957f3d9b5ca5b508f07c526b0074d..143464d58d2bd6378e74eec636961ceb0d32fb91:/bsd/netinet6/raw_ip6.c diff --git a/bsd/netinet6/raw_ip6.c b/bsd/netinet6/raw_ip6.c index 7e3094d30..09728f630 100644 --- a/bsd/netinet6/raw_ip6.c +++ b/bsd/netinet6/raw_ip6.c @@ -1,3 +1,30 @@ +/* + * Copyright (c) 2000-2013 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -66,6 +93,7 @@ #include #include #include +#include #include #include #include @@ -88,9 +116,7 @@ #include #include #include -#if ENABLE_DEFAULT_SCOPE #include -#endif #include #include @@ -98,27 +124,16 @@ #include #include extern int ipsec_bypass; -extern lck_mtx_t *sadb_mutex; #endif /*IPSEC*/ - -#include "faith.h" -#if defined(NFAITH) && 0 < NFAITH -#include -#endif - -#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) -#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) - /* * Raw interface to IP6 protocol. */ extern struct inpcbhead ripcb; extern struct inpcbinfo ripcbinfo; -extern u_long rip_sendspace; -extern u_long rip_recvspace; -extern u_long route_generation; +extern u_int32_t rip_sendspace; +extern u_int32_t rip_recvspace; struct rip6stat rip6stat; @@ -130,29 +145,26 @@ struct rip6stat rip6stat; int rip6_input( struct mbuf **mp, - int *offp) + int *offp, + int proto) { struct mbuf *m = *mp; - register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register struct inpcb *in6p; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct inpcb *in6p; struct inpcb *last = 0; struct mbuf *opts = NULL; struct sockaddr_in6 rip6src; - int proto = ip6->ip6_nxt; + int ret; + struct ifnet *ifp = m->m_pkthdr.rcvif; - rip6stat.rip6s_ipackets++; + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); -#if defined(NFAITH) && 0 < NFAITH - if (faithprefix(&ip6->ip6_dst)) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } -#endif + rip6stat.rip6s_ipackets++; init_sin6(&rip6src, m); /* general init */ - lck_rw_lock_shared(ripcbinfo.mtx); + lck_rw_lock_shared(ripcbinfo.ipi_lock); LIST_FOREACH(in6p, &ripcb, inp_list) { if ((in6p->in6p_vflag & INP_IPV6) == 0) continue; @@ -165,7 +177,15 @@ rip6_input( if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; - if (in6p->in6p_cksum != -1) { + + if (inp_restricted(in6p, ifp)) + continue; + + if (ifp != NULL && IFNET_IS_CELLULAR(ifp) && + (in6p->in6p_flags & INP_NO_IFT_CELLULAR)) + continue; + + if (proto == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { rip6stat.rip6s_isum++; if (in6_cksum(m, ip6->ip6_nxt, *offp, m->m_pkthdr.len - *offp)) { @@ -180,22 +200,27 @@ rip6_input( /* * Check AH/ESP integrity. */ - if (ipsec_bypass == 0 && n) { - lck_mtx_lock(sadb_mutex); - if (ipsec6_in_reject_so(n, last->inp_socket)) { + if (ipsec_bypass == 0 && n && ipsec6_in_reject_so(n, last->inp_socket)) { m_freem(n); - ipsec6stat.in_polvio++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); /* do not inject data into pcb */ - } - lck_mtx_unlock(sadb_mutex); } else #endif /*IPSEC*/ if (n) { - if (last->in6p_flags & IN6P_CONTROLOPTS || - last->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(last, &opts, ip6, n); + if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { + ret = ip6_savecontrol(last, n, &opts); + if (ret != 0) { + m_freem(n); + m_freem(opts); + last = in6p; + continue; + } + } /* strip intermediate headers */ m_adj(n, *offp); + so_recv_data_stat(last->in6p_socket, m, 0); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, n, opts, NULL) == 0) { @@ -207,28 +232,34 @@ rip6_input( } last = in6p; } - lck_rw_done(ripcbinfo.mtx); + #if IPSEC /* * Check AH/ESP integrity. */ - if (ipsec_bypass == 0 && last) { - lck_mtx_lock(sadb_mutex); - if (ipsec6_in_reject_so(m, last->inp_socket)) { + if (ipsec_bypass == 0 && last && ipsec6_in_reject_so(m, last->inp_socket)) { m_freem(m); - ipsec6stat.in_polvio++; + IPSEC_STAT_INCREMENT(ipsec6stat.in_polvio); ip6stat.ip6s_delivered--; /* do not inject data into pcb */ - } - lck_mtx_unlock(sadb_mutex); } else #endif /*IPSEC*/ if (last) { - if (last->in6p_flags & IN6P_CONTROLOPTS || - last->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(last, &opts, ip6, m); + if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { + ret = ip6_savecontrol(last, m, &opts); + if (ret != 0) { + m_freem(m); + m_freem(opts); + ip6stat.ip6s_delivered--; + goto unlock; + } + + } /* strip intermediate headers */ m_adj(m, *offp); + so_recv_data_stat(last->in6p_socket, m, 0); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, m, opts, NULL) == 0) { rip6stat.rip6s_fullsock++; @@ -248,6 +279,10 @@ rip6_input( } ip6stat.ip6s_delivered--; } + +unlock: + lck_rw_done(ripcbinfo.ipi_lock); + return IPPROTO_DONE; } @@ -259,6 +294,7 @@ rip6_ctlinput( { struct ip6_hdr *ip6; struct mbuf *m; + void *cmdarg = NULL; int off = 0; struct ip6ctlparam *ip6cp = NULL; const struct sockaddr_in6 *sa6_src = NULL; @@ -283,6 +319,7 @@ rip6_ctlinput( m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + cmdarg = ip6cp->ip6c_cmdarg; sa6_src = ip6cp->ip6c_src; } else { m = NULL; @@ -290,8 +327,8 @@ rip6_ctlinput( sa6_src = &sa6_any; } - (void) in6_pcbnotify(&ripcbinfo, sa, 0, (struct sockaddr *)sa6_src, - 0, cmd, notify); + (void) in6_pcbnotify(&ripcbinfo, sa, 0, (const struct sockaddr *)sa6_src, + 0, cmd, cmdarg, notify); } /* @@ -300,29 +337,50 @@ rip6_ctlinput( */ int rip6_output( - register struct mbuf *m, + struct mbuf *m, struct socket *so, struct sockaddr_in6 *dstsock, - struct mbuf *control) + struct mbuf *control, + int israw) { struct in6_addr *dst; struct ip6_hdr *ip6; struct inpcb *in6p; u_int plen = m->m_pkthdr.len; int error = 0; - struct ip6_pktopts opt, *optp = 0; + struct ip6_pktopts opt, *optp = NULL; + struct ip6_moptions *im6o = NULL; struct ifnet *oifp = NULL; int type = 0, code = 0; /* for ICMPv6 output statistics only */ - int priv = 0; + mbuf_svc_class_t msc = MBUF_SC_UNSPEC; + struct ip6_out_args ip6oa = + { IFSCOPE_NONE, { 0 }, IP6OAF_SELECT_SRCIF, 0 }; + int flags = IPV6_OUTARGS; in6p = sotoin6pcb(so); - priv = 0; - if (so->so_uid == 0) - priv = 1; + if (in6p == NULL || (in6p->inp_flags2 & INP2_WANT_FLOW_DIVERT)) { + error = (in6p == NULL ? EINVAL : EPROTOTYPE); + goto bad; + } + if (dstsock != NULL && IN6_IS_ADDR_V4MAPPED(&dstsock->sin6_addr)) { + error = EINVAL; + goto bad; + } + + if (in6p->inp_flags & INP_BOUND_IF) { + ip6oa.ip6oa_boundif = in6p->inp_boundifp->if_index; + ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; + } + if (in6p->inp_flags & INP_NO_IFT_CELLULAR) + ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR; + dst = &dstsock->sin6_addr; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) + msc = mbuf_service_class_from_control(control); + + if ((error = ip6_setpktopts(control, &opt, NULL, + SOCK_PROTO(so))) != 0) goto bad; optp = &opt; } else @@ -332,7 +390,7 @@ rip6_output( * For an ICMPv6 packet, we should know its type and code * to update statistics. */ - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { struct icmp6_hdr *icmp6; if (m->m_len < sizeof(struct icmp6_hdr) && (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { @@ -344,7 +402,20 @@ rip6_output( code = icmp6->icmp6_code; } + if (in6p->inp_flowhash == 0) + in6p->inp_flowhash = inp_calc_flowhash(in6p); + /* update flowinfo - RFC 6437 */ + if (in6p->inp_flow == 0 && in6p->in6p_flags & IN6P_AUTOFLOWLABEL) { + in6p->inp_flow &= ~IPV6_FLOWLABEL_MASK; + in6p->inp_flow |= + (htonl(in6p->inp_flowhash) & IPV6_FLOWLABEL_MASK); + } + M_PREPEND(m, sizeof(*ip6), M_WAIT); + if (m == NULL) { + error = ENOBUFS; + goto bad; + } ip6 = mtod(m, struct ip6_hdr *); /* @@ -352,6 +423,8 @@ rip6_output( */ ip6->ip6_dst = *dst; + im6o = in6p->in6p_moptions; + /* * If the scope of the destination is link-local, embed the interface * index in the address. @@ -360,29 +433,44 @@ rip6_output( */ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { struct in6_pktinfo *pi; + struct ifnet *im6o_multicast_ifp = NULL; + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && im6o != NULL) { + IM6O_LOCK(im6o); + im6o_multicast_ifp = im6o->im6o_multicast_ifp; + IM6O_UNLOCK(im6o); + } /* * XXX Boundary check is assumed to be already done in * ip6_setpktoptions(). */ + ifnet_head_lock_shared(); if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) { ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex); oifp = ifindex2ifnet[pi->ipi6_ifindex]; + if (oifp != NULL) + ifnet_reference(oifp); } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && - in6p->in6p_moptions && - in6p->in6p_moptions->im6o_multicast_ifp) { - oifp = in6p->in6p_moptions->im6o_multicast_ifp; + im6o != NULL && im6o_multicast_ifp != NULL) { + oifp = im6o_multicast_ifp; + ifnet_reference(oifp); ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index); } else if (dstsock->sin6_scope_id) { - /* boundary check */ - if (dstsock->sin6_scope_id < 0 - || if_index < dstsock->sin6_scope_id) { + /* + * boundary check + * + * Sinced stsock->sin6_scope_id is unsigned, we don't + * need to check if it's < 0 + */ + if (if_index < dstsock->sin6_scope_id) { error = ENXIO; /* XXX EINVAL? */ + ifnet_head_done(); goto bad; } ip6->ip6_dst.s6_addr16[1] = htons(dstsock->sin6_scope_id & 0xffff);/*XXX*/ } + ifnet_head_done(); } /* @@ -391,40 +479,57 @@ rip6_output( { struct in6_addr *in6a; struct in6_addr storage; - - if ((in6a = in6_selectsrc(dstsock, optp, - in6p->in6p_moptions, - &in6p->in6p_route, - &in6p->in6p_laddr, - &storage, &error)) == 0) { + u_short index = 0; + + if (israw != 0 && optp && optp->ip6po_pktinfo && !IN6_IS_ADDR_UNSPECIFIED(&optp->ip6po_pktinfo->ipi6_addr)) { + in6a = &optp->ip6po_pktinfo->ipi6_addr; + flags |= IPV6_FLAG_NOSRCIFSEL; + } else if ((in6a = in6_selectsrc(dstsock, optp, in6p, + &in6p->in6p_route, NULL, &storage, ip6oa.ip6oa_boundif, + &error)) == 0) { if (error == 0) error = EADDRNOTAVAIL; goto bad; + } else { + ip6oa.ip6oa_flags |= IP6OAF_BOUND_SRCADDR; } ip6->ip6_src = *in6a; - if (in6p->in6p_route.ro_rt) - oifp = ifindex2ifnet[in6p->in6p_route.ro_rt->rt_ifp->if_index]; + if (in6p->in6p_route.ro_rt != NULL) { + RT_LOCK(in6p->in6p_route.ro_rt); + if (in6p->in6p_route.ro_rt->rt_ifp != NULL) + index = in6p->in6p_route.ro_rt->rt_ifp->if_index; + RT_UNLOCK(in6p->in6p_route.ro_rt); + if (oifp != NULL) + ifnet_release(oifp); + ifnet_head_lock_shared(); + if (index == 0 || if_index < index) { + panic("bad if_index on interface from route"); + } + oifp = ifindex2ifnet[index]; + if (oifp != NULL) + ifnet_reference(oifp); + ifnet_head_done(); + } } ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | - (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); + (in6p->inp_flow & IPV6_FLOWINFO_MASK); ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | (IPV6_VERSION & IPV6_VERSION_MASK); /* ip6_plen will be filled in ip6_output, so not fill it here. */ ip6->ip6_nxt = in6p->in6p_ip6_nxt; ip6->ip6_hlim = in6_selecthlim(in6p, oifp); - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || - in6p->in6p_cksum != -1) { + if (SOCK_PROTO(so) == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { struct mbuf *n; int off; u_int16_t *p; /* compute checksum */ - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) + if (SOCK_PROTO(so) == IPPROTO_ICMPV6) off = offsetof(struct icmp6_hdr, icmp6_cksum); else off = in6p->in6p_cksum; - if (plen < off + 1) { + if (plen < (unsigned int)(off + 1)) { error = EINVAL; goto bad; } @@ -437,7 +542,7 @@ rip6_output( } if (!n) goto bad; - p = (u_int16_t *)(mtod(n, caddr_t) + off); + p = (u_int16_t *)(void *)(mtod(n, caddr_t) + off); *p = 0; *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); } @@ -449,14 +554,68 @@ rip6_output( } #endif /*IPSEC*/ - if (in6p->in6p_route.ro_rt && in6p->in6p_route.ro_rt->generation_id != route_generation) { - rtfree(in6p->in6p_route.ro_rt); - in6p->in6p_route.ro_rt = (struct rtentry *)0; + if (ROUTE_UNUSABLE(&in6p->in6p_route)) + ROUTE_RELEASE(&in6p->in6p_route); + + if (oifp != NULL) { + ifnet_release(oifp); + oifp = NULL; } - error = ip6_output(m, optp, &in6p->in6p_route, 0, - in6p->in6p_moptions, &oifp, 0); - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + set_packet_service_class(m, so, msc, PKT_SCF_IPV6); + m->m_pkthdr.pkt_flowsrc = FLOWSRC_INPCB; + m->m_pkthdr.pkt_flowid = in6p->inp_flowhash; + m->m_pkthdr.pkt_flags |= (PKTF_FLOW_ID | PKTF_FLOW_LOCALSRC | + PKTF_FLOW_RAWSOCK); + m->m_pkthdr.pkt_proto = in6p->in6p_ip6_nxt; + + if (im6o != NULL) + IM6O_ADDREF(im6o); + + error = ip6_output(m, optp, &in6p->in6p_route, flags, im6o, + &oifp, &ip6oa); + + if (im6o != NULL) + IM6O_REMREF(im6o); + + if (in6p->in6p_route.ro_rt != NULL) { + struct rtentry *rt = in6p->in6p_route.ro_rt; + struct ifnet *outif; + + if ((rt->rt_flags & RTF_MULTICAST) || + in6p->in6p_socket == NULL || + !(in6p->in6p_socket->so_state & SS_ISCONNECTED)) { + rt = NULL; /* unusable */ + } + /* + * Always discard the cached route for unconnected + * socket or if it is a multicast route. + */ + if (rt == NULL) + ROUTE_RELEASE(&in6p->in6p_route); + + /* + * If this is a connected socket and the destination + * route is not multicast, update outif with that of + * the route interface index used by IP. + */ + if (rt != NULL && + (outif = rt->rt_ifp) != in6p->in6p_last_outifp) + in6p->in6p_last_outifp = outif; + } else { + ROUTE_RELEASE(&in6p->in6p_route); + } + + /* + * If output interface was cellular, and this socket is denied + * access to it, generate an event. + */ + if (error != 0 && (ip6oa.ip6oa_retflags & IP6OARF_IFDENIED) && + (in6p->inp_flags & INP_NO_IFT_CELLULAR)) + soevent(in6p->inp_socket, (SO_FILT_HINT_LOCKED| + SO_FILT_HINT_IFDENIED)); + + if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { if (oifp) icmp6_ifoutstat_inc(oifp, type, code); icmp6stat.icp6s_outhist[type]++; @@ -465,26 +624,31 @@ rip6_output( goto freectl; - bad: - if (m) +bad: + if (m != NULL) m_freem(m); - freectl: - if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) - rtfree(optp->ip6po_route.ro_rt); - if (control) { +freectl: + if (optp == &opt && optp->ip6po_rthdr) + ROUTE_RELEASE(&optp->ip6po_route); + + if (control != NULL) { if (optp == &opt) - ip6_clearpktopts(optp, 0, -1); + ip6_clearpktopts(optp, -1); m_freem(control); } + if (oifp != NULL) + ifnet_release(oifp); return(error); } -static void -load_ip6fw() +#if IPFW2 +__private_extern__ void +load_ip6fw(void) { ip6_fw_init(); } +#endif /* * Raw IPv6 socket option processing. @@ -494,15 +658,17 @@ rip6_ctloutput( struct socket *so, struct sockopt *sopt) { - int error; + int error, optval; + /* Allow at this level */ if (sopt->sopt_level == IPPROTO_ICMPV6) /* * XXX: is it better to call icmp6_ctloutput() directly * from protosw? */ return(icmp6_ctloutput(so, sopt)); - else if (sopt->sopt_level != IPPROTO_IPV6) + else if (sopt->sopt_level != IPPROTO_IPV6 && + !(sopt->sopt_level == SOL_SOCKET && sopt->sopt_name == SO_FLUSH)) return (EINVAL); error = 0; @@ -510,6 +676,7 @@ rip6_ctloutput( switch (sopt->sopt_dir) { case SOPT_GET: switch (sopt->sopt_name) { +#if IPFW2 case IPV6_FW_ADD: case IPV6_FW_GET: if (ip6_fw_ctl_ptr == 0) @@ -519,6 +686,7 @@ rip6_ctloutput( else error = ENOPROTOOPT; break; +#endif case MRT6_INIT: case MRT6_DONE: @@ -527,7 +695,14 @@ rip6_ctloutput( case MRT6_ADD_MFC: case MRT6_DEL_MFC: case MRT6_PIM: +#if MROUTING error = ip6_mrouter_get(so, sopt); +#else + error = ENOPROTOOPT; +#endif /* MROUTING */ + break; + case IPV6_CHECKSUM: + error = ip6_raw_ctloutput(so, sopt); break; default: error = ip6_ctloutput(so, sopt); @@ -537,6 +712,7 @@ rip6_ctloutput( case SOPT_SET: switch (sopt->sopt_name) { +#if IPFW2 case IPV6_FW_ADD: case IPV6_FW_DEL: case IPV6_FW_FLUSH: @@ -548,6 +724,7 @@ rip6_ctloutput( else error = ENOPROTOOPT; break; +#endif case MRT6_INIT: case MRT6_DONE: @@ -556,8 +733,24 @@ rip6_ctloutput( case MRT6_ADD_MFC: case MRT6_DEL_MFC: case MRT6_PIM: +#if MROUTING error = ip6_mrouter_set(so, sopt); +#else + error = ENOPROTOOPT; +#endif + break; + case IPV6_CHECKSUM: + error = ip6_raw_ctloutput(so, sopt); break; + + case SO_FLUSH: + if ((error = sooptcopyin(sopt, &optval, sizeof (optval), + sizeof (optval))) != 0) + break; + + error = inp_flush(sotoinpcb(so), optval); + break; + default: error = ip6_ctloutput(so, sopt); break; @@ -572,25 +765,23 @@ static int rip6_attach(struct socket *so, int proto, struct proc *p) { struct inpcb *inp; - int error, s; + int error; inp = sotoinpcb(so); if (inp) panic("rip6_attach"); - if (p && (error = proc_suser(p)) != 0) + if ((error = proc_suser(p)) != 0) return error; error = soreserve(so, rip_sendspace, rip_recvspace); if (error) return error; - s = splnet(); error = in_pcballoc(so, &ripcbinfo, p); - splx(s); if (error) return error; inp = (struct inpcb *)so->so_pcb; inp->inp_vflag |= INP_IPV6; - inp->in6p_ip6_nxt = (long)proto; + inp->in6p_ip6_nxt = (char)proto; inp->in6p_hops = -1; /* use kernel default */ inp->in6p_cksum = -1; MALLOC(inp->in6p_icmp6filt, struct icmp6_filter *, @@ -610,8 +801,10 @@ rip6_detach(struct socket *so) if (inp == 0) panic("rip6_detach"); /* xxx: RSVP */ +#if MROUTING if (so == ip6_mrouter) ip6_mrouter_done(); +#endif if (inp->in6p_icmp6filt) { FREE(inp->in6p_icmp6filt, M_PCB); inp->in6p_icmp6filt = NULL; @@ -641,47 +834,70 @@ rip6_disconnect(struct socket *so) static int rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { +#pragma unused(p) struct inpcb *inp = sotoinpcb(so); - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; - struct ifaddr *ia = NULL; + struct sockaddr_in6 sin6; + struct ifaddr *ifa = NULL; + struct ifnet *outif = NULL; + int error; - if (nam->sa_len != sizeof(*addr)) - return EINVAL; + if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) + return (inp == NULL ? EINVAL : EPROTOTYPE); - if (TAILQ_EMPTY(&ifnet_head) || addr->sin6_family != AF_INET6) - return EADDRNOTAVAIL; -#if ENABLE_DEFAULT_SCOPE - if (addr->sin6_scope_id == 0) { /* not change if specified */ - addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); - } -#endif - if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && - (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) - return EADDRNOTAVAIL; - if (ia && - ((struct in6_ifaddr *)ia)->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| - IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { - if (ia) ifafree(ia); - return(EADDRNOTAVAIL); + if (nam->sa_len != sizeof (struct sockaddr_in6)) + return (EINVAL); + + if (TAILQ_EMPTY(&ifnet_head) || SIN6(nam)->sin6_family != AF_INET6) + return (EADDRNOTAVAIL); + + bzero(&sin6, sizeof (sin6)); + *(&sin6) = *SIN6(nam); + + if ((error = sa6_embedscope(&sin6, ip6_use_defzone)) != 0) + return (error); + + /* Sanitize local copy for address searches */ + sin6.sin6_flowinfo = 0; + sin6.sin6_scope_id = 0; + sin6.sin6_port = 0; + + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr) && + (ifa = ifa_ifwithaddr(SA(&sin6))) == 0) + return (EADDRNOTAVAIL); + if (ifa != NULL) { + IFA_LOCK(ifa); + if (((struct in6_ifaddr *)ifa)->ia6_flags & + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| + IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + IFA_UNLOCK(ifa); + IFA_REMREF(ifa); + return (EADDRNOTAVAIL); + } + outif = ifa->ifa_ifp; + IFA_UNLOCK(ifa); + IFA_REMREF(ifa); } - ifafree(ia); - inp->in6p_laddr = addr->sin6_addr; - return 0; + inp->in6p_laddr = sin6.sin6_addr; + inp->in6p_last_outifp = outif; + return (0); } static int -rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +rip6_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) { struct inpcb *inp = sotoinpcb(so); - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)(void *)nam; struct in6_addr *in6a = NULL; struct in6_addr storage; int error = 0; #if ENABLE_DEFAULT_SCOPE struct sockaddr_in6 tmp; #endif + unsigned int ifscope; + struct ifnet *outif = NULL; + if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) + return (inp == NULL ? EINVAL : EPROTOTYPE); if (nam->sa_len != sizeof(*addr)) return EINVAL; if (TAILQ_EMPTY(&ifnet_head)) @@ -696,14 +912,20 @@ rip6_connect(struct socket *so, struct sockaddr *nam, struct proc *p) addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); } #endif + + ifscope = (inp->inp_flags & INP_BOUND_IF) ? + inp->inp_boundifp->if_index : IFSCOPE_NONE; + /* Source address selection. XXX: need pcblookup? */ - in6a = in6_selectsrc(addr, inp->in6p_outputopts, - inp->in6p_moptions, &inp->in6p_route, - &inp->in6p_laddr, &storage, &error); + in6a = in6_selectsrc(addr, inp->in6p_outputopts, inp, &inp->in6p_route, + NULL, &storage, ifscope, &error); if (in6a == NULL) return (error ? error : EADDRNOTAVAIL); inp->in6p_laddr = *in6a; inp->in6p_faddr = addr->sin6_addr; + if (inp->in6p_route.ro_rt != NULL) + outif = inp->in6p_route.ro_rt->rt_ifp; + inp->in6p_last_outifp = outif; soisconnected(so); return 0; } @@ -717,17 +939,24 @@ rip6_shutdown(struct socket *so) static int rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, - struct mbuf *control, struct proc *p) + struct mbuf *control, struct proc *p) { +#pragma unused(flags, p) struct inpcb *inp = sotoinpcb(so); struct sockaddr_in6 tmp; - struct sockaddr_in6 *dst; + struct sockaddr_in6 *dst = (struct sockaddr_in6 *)(void *)nam; + int error = 0; + + if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) { + error = (inp == NULL ? EINVAL : EPROTOTYPE); + goto bad; + } /* always copy sockaddr to avoid overwrites */ if (so->so_state & SS_ISCONNECTED) { - if (nam) { - m_freem(m); - return EISCONN; + if (nam != NULL) { + error = EISCONN; + goto bad; } /* XXX */ bzero(&tmp, sizeof(tmp)); @@ -738,10 +967,10 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, dst = &tmp; } else { if (nam == NULL) { - m_freem(m); - return ENOTCONN; + error = ENOTCONN; + goto bad; } - tmp = *(struct sockaddr_in6 *)nam; + tmp = *(struct sockaddr_in6 *)(void *)nam; dst = &tmp; } #if ENABLE_DEFAULT_SCOPE @@ -749,13 +978,47 @@ rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); } #endif - return rip6_output(m, so, dst, control); + return (rip6_output(m, so, dst, control, 1)); + +bad: + VERIFY(error != 0); + + if (m != NULL) + m_freem(m); + if (control != NULL) + m_freem(control); + + return (error); } struct pr_usrreqs rip6_usrreqs = { - rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect, - pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect, - pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown, - in6_setsockaddr, sosend, soreceive, pru_sopoll_notsupp + .pru_abort = rip6_abort, + .pru_attach = rip6_attach, + .pru_bind = rip6_bind, + .pru_connect = rip6_connect, + .pru_control = in6_control, + .pru_detach = rip6_detach, + .pru_disconnect = rip6_disconnect, + .pru_peeraddr = in6_getpeeraddr, + .pru_send = rip6_send, + .pru_shutdown = rip6_shutdown, + .pru_sockaddr = in6_getsockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, +}; + +__private_extern__ struct pr_usrreqs icmp6_dgram_usrreqs = { + .pru_abort = rip6_abort, + .pru_attach = icmp6_dgram_attach, + .pru_bind = rip6_bind, + .pru_connect = rip6_connect, + .pru_control = in6_control, + .pru_detach = rip6_detach, + .pru_disconnect = rip6_disconnect, + .pru_peeraddr = in6_getpeeraddr, + .pru_send = icmp6_dgram_send, + .pru_shutdown = rip6_shutdown, + .pru_sockaddr = in6_getsockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, };