X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/91447636331957f3d9b5ca5b508f07c526b0074d..813fb2f63a553c957e917ede5f119b021d6ce391:/bsd/netinet6/udp6_output.c diff --git a/bsd/netinet6/udp6_output.c b/bsd/netinet6/udp6_output.c index 226b199c0..c0cd894e4 100644 --- a/bsd/netinet6/udp6_output.c +++ b/bsd/netinet6/udp6_output.c @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2000-2016 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@ + */ + + /* $FreeBSD: src/sys/netinet6/udp6_output.c,v 1.1.2.3 2001/08/31 13:49:58 jlemon Exp $ */ /* $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ */ @@ -78,13 +107,17 @@ #include #include +#include + #include #include #include +#include #include #include #include +#include #include #include #include @@ -97,15 +130,9 @@ #include #include -#ifdef IPSEC -#include -#ifdef INET6 -#include -#endif -extern int ipsec_bypass; -#endif /*IPSEC*/ - -#include "faith.h" +#if NECP +#include +#endif /* NECP */ #include @@ -113,71 +140,84 @@ extern int ipsec_bypass; * UDP protocol inplementation. * Per RFC 768, August, 1980. */ - -#define in6pcb inpcb -#define udp6stat udpstat -#define udp6s_opackets udps_opackets - -static __inline__ u_int16_t -get_socket_id(struct socket * s) -{ - u_int16_t val; - - if (s == NULL) { - return (0); - } - val = (u_int16_t)(((u_int32_t)s) / sizeof(struct socket)); - if (val == 0) { - val = 0xffff; - } - return (val); -} +extern int soreserveheadroom; int -udp6_output(in6p, m, addr6, control, p) - struct in6pcb *in6p; - struct mbuf *m; - struct mbuf *control; - struct sockaddr *addr6; - struct proc *p; +udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, + struct mbuf *control, struct proc *p) { u_int32_t ulen = m->m_pkthdr.len; - u_int32_t plen = sizeof(struct udphdr) + ulen; + u_int32_t plen = sizeof (struct udphdr) + ulen; struct ip6_hdr *ip6; struct udphdr *udp6; struct in6_addr *laddr, *faddr; u_short fport; int error = 0; - struct ip6_pktopts opt, *stickyopt = in6p->in6p_outputopts; - int priv; - int af = AF_INET6, hlen = sizeof(struct ip6_hdr); + struct ip6_pktopts opt, *optp = NULL; + struct ip6_moptions *im6o; + int af = AF_INET6, hlen = sizeof (struct ip6_hdr); int flags; struct sockaddr_in6 tmp; struct in6_addr storage; + int sotc = SO_TC_UNSPEC; + int netsvctype = _NET_SERVICE_TYPE_UNSPEC; + struct ip6_out_args ip6oa = + { IFSCOPE_NONE, { 0 }, IP6OAF_SELECT_SRCIF, 0, 0, 0 }; + struct flowadv *adv = &ip6oa.ip6oa_flowadv; + struct socket *so = in6p->in6p_socket; + struct route_in6 ro; + int flowadv = 0; + + /* Enable flow advisory only when connected */ + flowadv = (so->so_state & SS_ISCONNECTED) ? 1 : 0; + + if (flowadv && INP_WAIT_FOR_IF_FEEDBACK(in6p)) { + error = ENOBUFS; + goto release; + } + + if (in6p->inp_flags & INP_BOUND_IF) { + ip6oa.ip6oa_boundif = in6p->inp_boundifp->if_index; + ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; + } + if (INP_NO_CELLULAR(in6p)) + ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR; + if (INP_NO_EXPENSIVE(in6p)) + ip6oa.ip6oa_flags |= IP6OAF_NO_EXPENSIVE; + if (INP_AWDL_UNRESTRICTED(in6p)) + ip6oa.ip6oa_flags |= IP6OAF_AWDL_UNRESTRICTED; + if (INP_INTCOPROC_ALLOWED(in6p)) + ip6oa.ip6oa_flags |= IP6OAF_INTCOPROC_ALLOWED; - priv = 0; -#ifdef __APPLE__ - if (p && !proc_suser(p)) -#else - if (p && !suser(p)) -#endif - priv = 1; if (control) { - if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) + sotc = so_tc_from_control(control, &netsvctype); + if ((error = ip6_setpktopts(control, &opt, + NULL, IPPROTO_UDP)) != 0) goto release; - in6p->in6p_outputopts = &opt; + optp = &opt; + } else + optp = in6p->in6p_outputopts; + + if (sotc == SO_TC_UNSPEC) { + sotc = so->so_traffic_class; + netsvctype = so->so_netsvctype; } + ip6oa.ip6oa_sotc = sotc; + ip6oa.ip6oa_netsvctype = netsvctype; if (addr6) { /* * IPv4 version of udp_output calls in_pcbconnect in this case, - * which needs splnet and affects performance. + * which has its costs. + * * Since we saw no essential reason for calling in_pcbconnect, * we get rid of such kind of logic, and call in6_selectsrc * and in6_pcbsetport in order to fill in the local address * and the local port. */ - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr6; + struct sockaddr_in6 *sin6 = + (struct sockaddr_in6 *)(void *)addr6; + if (sin6->sin6_port == 0) { error = EADDRNOTAVAIL; goto release; @@ -210,21 +250,22 @@ udp6_output(in6p, m, addr6, control, p) */ error = EINVAL; goto release; - } else + } else { af = AF_INET; + } } /* KAME hack: embed scopeid */ - if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL) != 0) { + if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL, + optp) != 0) { error = EINVAL; goto release; } if (!IN6_IS_ADDR_V4MAPPED(faddr)) { - laddr = in6_selectsrc(sin6, in6p->in6p_outputopts, - in6p->in6p_moptions, - &in6p->in6p_route, - &in6p->in6p_laddr, &storage, &error); + laddr = in6_selectsrc(sin6, optp, + in6p, &in6p->in6p_route, NULL, &storage, + ip6oa.ip6oa_boundif, &error); } else laddr = &in6p->in6p_laddr; /* XXX */ if (laddr == NULL) { @@ -261,14 +302,23 @@ udp6_output(in6p, m, addr6, control, p) fport = in6p->in6p_fport; } + 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); + } + if (af == AF_INET) - hlen = sizeof(struct ip); + hlen = sizeof (struct ip); /* * Calculate data length and get a mbuf * for UDP and IP6 headers. */ - M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT); + M_PREPEND(m, hlen + sizeof (struct udphdr), M_DONTWAIT, 1); if (m == 0) { error = ENOBUFS; goto release; @@ -277,7 +327,7 @@ udp6_output(in6p, m, addr6, control, p) /* * Stuff checksum and output datagram. */ - udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen); + udp6 = (struct udphdr *)(void *)(mtod(m, caddr_t) + hlen); udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */ udp6->uh_dport = fport; if (plen <= 0xffff) @@ -289,36 +339,215 @@ udp6_output(in6p, m, addr6, control, p) switch (af) { case AF_INET6: ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; - ip6->ip6_vfc &= ~IPV6_VERSION_MASK; - ip6->ip6_vfc |= IPV6_VERSION; -#if 0 /* ip6_plen will be filled in ip6_output. */ + ip6->ip6_flow = in6p->inp_flow & IPV6_FLOWINFO_MASK; + ip6->ip6_vfc &= ~IPV6_VERSION_MASK; + ip6->ip6_vfc |= IPV6_VERSION; +#if 0 /* ip6_plen will be filled in ip6_output. */ ip6->ip6_plen = htons((u_short)plen); #endif ip6->ip6_nxt = IPPROTO_UDP; - ip6->ip6_hlim = in6_selecthlim(in6p, - in6p->in6p_route.ro_rt ? - in6p->in6p_route.ro_rt->rt_ifp : NULL); + ip6->ip6_hlim = in6_selecthlim(in6p, in6p->in6p_route.ro_rt ? + in6p->in6p_route.ro_rt->rt_ifp : NULL); ip6->ip6_src = *laddr; ip6->ip6_dst = *faddr; - if ((udp6->uh_sum = in6_cksum(m, IPPROTO_UDP, - sizeof(struct ip6_hdr), plen)) == 0) { - udp6->uh_sum = 0xffff; - } + udp6->uh_sum = in6_pseudo(laddr, faddr, + htonl(plen + IPPROTO_UDP)); + m->m_pkthdr.csum_flags = CSUM_UDPIPV6; + m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + + if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) + ip6oa.ip6oa_flags |= IP6OAF_BOUND_SRCADDR; - flags = 0; + flags = IPV6_OUTARGS; udp6stat.udp6s_opackets++; -#ifdef IPSEC - if (ipsec_bypass == 0 && ipsec_setsocket(m, in6p->in6p_socket) != 0) { + +#if NECP + { + necp_kernel_policy_id policy_id; + u_int32_t route_rule_id; + + /* + * We need a route to perform NECP route rule checks + */ + if (net_qos_policy_restricted != 0 && + ROUTE_UNUSABLE(&in6p->inp_route)) { + struct sockaddr_in6 to; + struct sockaddr_in6 from; + + ROUTE_RELEASE(&in6p->inp_route); + + bzero(&from, sizeof(struct sockaddr_in6)); + from.sin6_family = AF_INET6; + from.sin6_len = sizeof(struct sockaddr_in6); + from.sin6_addr = *laddr; + + bzero(&to, sizeof(struct sockaddr_in6)); + to.sin6_family = AF_INET6; + to.sin6_len = sizeof(struct sockaddr_in6); + to.sin6_addr = *faddr; + + in6p->inp_route.ro_dst.sa_family = AF_INET6; + in6p->inp_route.ro_dst.sa_len = sizeof(struct sockaddr_in6); + ((struct sockaddr_in6 *)(void *)&in6p->inp_route.ro_dst)->sin6_addr = + *faddr; + + rtalloc_scoped(&in6p->inp_route, ip6oa.ip6oa_boundif); + + inp_update_necp_policy(in6p, (struct sockaddr *)&from, + (struct sockaddr *)&to, ip6oa.ip6oa_boundif); + in6p->inp_policyresult.results.qos_marking_gencount = 0; + } + + if (!necp_socket_is_allowed_to_send_recv_v6(in6p, in6p->in6p_lport, fport, laddr, faddr, NULL, &policy_id, &route_rule_id)) { + error = EHOSTUNREACH; + goto release; + } + + necp_mark_packet_from_socket(m, in6p, policy_id, route_rule_id); + + if (net_qos_policy_restricted != 0) { + necp_socket_update_qos_marking(in6p, in6p->in6p_route.ro_rt, + NULL, route_rule_id); + } + } +#endif /* NECP */ + if ((so->so_flags1 & SOF1_QOSMARKING_ALLOWED)) + ip6oa.ip6oa_flags |= IP6OAF_QOSMARKING_ALLOWED; + +#if IPSEC + if (in6p->in6p_sp != NULL && ipsec_setsocket(m, so) != 0) { error = ENOBUFS; goto release; } #endif /*IPSEC*/ - m->m_pkthdr.socket_id = get_socket_id(in6p->in6p_socket); - error = ip6_output(m, in6p->in6p_outputopts, &in6p->in6p_route, - flags, in6p->in6p_moptions, NULL, 0); + + /* In case of IPv4-mapped address used in previous send */ + if (ROUTE_UNUSABLE(&in6p->in6p_route) || + rt_key(in6p->in6p_route.ro_rt)->sa_family != AF_INET6) + ROUTE_RELEASE(&in6p->in6p_route); + + /* Copy the cached route and take an extra reference */ + in6p_route_copyout(in6p, &ro); + + set_packet_service_class(m, so, sotc, PKT_SCF_IPV6); + + m->m_pkthdr.pkt_flowsrc = FLOWSRC_INPCB; + m->m_pkthdr.pkt_flowid = in6p->inp_flowhash; + m->m_pkthdr.pkt_proto = IPPROTO_UDP; + m->m_pkthdr.pkt_flags |= (PKTF_FLOW_ID | PKTF_FLOW_LOCALSRC); + if (flowadv) + m->m_pkthdr.pkt_flags |= PKTF_FLOW_ADV; + + im6o = in6p->in6p_moptions; + if (im6o != NULL) { + IM6O_LOCK(im6o); + IM6O_ADDREF_LOCKED(im6o); + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && + im6o->im6o_multicast_ifp != NULL) { + in6p->in6p_last_outifp = + im6o->im6o_multicast_ifp; + } + IM6O_UNLOCK(im6o); + } + + in6p->inp_sndinprog_cnt++; + + socket_unlock(so, 0); + error = ip6_output(m, optp, &ro, flags, im6o, NULL, &ip6oa); + m = NULL; + socket_lock(so, 0); + + if (im6o != NULL) + IM6O_REMREF(im6o); + + if (error == 0 && nstat_collect) { + boolean_t cell, wifi, wired; + + if (in6p->in6p_route.ro_rt != NULL) { + cell = IFNET_IS_CELLULAR(in6p->in6p_route. + ro_rt->rt_ifp); + wifi = (!cell && IFNET_IS_WIFI(in6p->in6p_route. + ro_rt->rt_ifp)); + wired = (!wifi && IFNET_IS_WIRED(in6p->in6p_route. + ro_rt->rt_ifp)); + } else { + cell = wifi = wired = FALSE; + } + INP_ADD_STAT(in6p, cell, wifi, wired, txpackets, 1); + INP_ADD_STAT(in6p, cell, wifi, wired, txbytes, ulen); + } + + if (flowadv && (adv->code == FADV_FLOW_CONTROLLED || + adv->code == FADV_SUSPENDED)) { + /* + * Return an error to indicate + * that the packet has been dropped. + */ + error = ENOBUFS; + inp_set_fc_state(in6p, adv->code); + } + + VERIFY(in6p->inp_sndinprog_cnt > 0); + if ( --in6p->inp_sndinprog_cnt == 0) + in6p->inp_flags &= ~(INP_FC_FEEDBACK); + + if (ro.ro_rt != NULL) { + struct ifnet *outif = ro.ro_rt->rt_ifp; + + so->so_pktheadroom = P2ROUNDUP( + sizeof(struct udphdr) + + hlen + + ifnet_hdrlen(outif) + + ifnet_packetpreamblelen(outif), + sizeof(u_int32_t)); + } + + /* Synchronize PCB cached route */ + in6p_route_copyin(in6p, &ro); + + if (in6p->in6p_route.ro_rt != NULL) { + struct rtentry *rt = in6p->in6p_route.ro_rt; + struct ifnet *outif; + + if (rt->rt_flags & RTF_MULTICAST) + 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 the destination route is unicast, update outif + * with that of the route interface used by IP. + */ + if (rt != NULL && + (outif = rt->rt_ifp) != in6p->in6p_last_outifp) { + in6p->in6p_last_outifp = outif; + + so->so_pktheadroom = P2ROUNDUP( + sizeof(struct udphdr) + + hlen + + ifnet_hdrlen(outif) + + ifnet_packetpreamblelen(outif), + sizeof(u_int32_t)); + } + } else { + ROUTE_RELEASE(&in6p->in6p_route); + } + + /* + * If output interface was cellular/expensive, and this + * socket is denied access to it, generate an event. + */ + if (error != 0 && (ip6oa.ip6oa_retflags & IP6OARF_IFDENIED) && + (INP_NO_CELLULAR(in6p) || INP_NO_EXPENSIVE(in6p))) + soevent(in6p->inp_socket, (SO_FILT_HINT_LOCKED| + SO_FILT_HINT_IFDENIED)); break; case AF_INET: error = EAFNOSUPPORT; @@ -327,13 +556,14 @@ udp6_output(in6p, m, addr6, control, p) goto releaseopt; release: - m_freem(m); + if (m != NULL) + m_freem(m); releaseopt: - if (control) { - ip6_clearpktopts(in6p->in6p_outputopts, 0, -1); - in6p->in6p_outputopts = stickyopt; + if (control != NULL) { + if (optp == &opt) + ip6_clearpktopts(optp, -1); m_freem(control); } - return(error); + return (error); }