X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/39236c6e673c41db228275375ab7fdb0f837b292..813fb2f63a553c957e917ede5f119b021d6ce391:/bsd/netinet6/udp6_output.c diff --git a/bsd/netinet6/udp6_output.c b/bsd/netinet6/udp6_output.c index 71d7cdffd..c0cd894e4 100644 --- a/bsd/netinet6/udp6_output.c +++ b/bsd/netinet6/udp6_output.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2013 Apple Inc. All rights reserved. + * 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 @@ -11,10 +11,10 @@ * 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, @@ -22,7 +22,7 @@ * 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@ */ @@ -117,6 +117,7 @@ #include #include #include +#include #include #include #include @@ -129,11 +130,9 @@ #include #include -#if IPSEC -#include -#include -extern int ipsec_bypass; -#endif /* IPSEC */ +#if NECP +#include +#endif /* NECP */ #include @@ -141,6 +140,7 @@ extern int ipsec_bypass; * UDP protocol inplementation. * Per RFC 768, August, 1980. */ +extern int soreserveheadroom; int udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, @@ -159,9 +159,10 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, int flags; struct sockaddr_in6 tmp; struct in6_addr storage; - mbuf_svc_class_t msc = MBUF_SC_UNSPEC; + int sotc = SO_TC_UNSPEC; + int netsvctype = _NET_SERVICE_TYPE_UNSPEC; struct ip6_out_args ip6oa = - { IFSCOPE_NONE, { 0 }, IP6OAF_SELECT_SRCIF, 0 }; + { 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; @@ -179,11 +180,17 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, ip6oa.ip6oa_boundif = in6p->inp_boundifp->if_index; ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; } - if (in6p->inp_flags & INP_NO_IFT_CELLULAR) + 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; if (control) { - msc = mbuf_service_class_from_control(control); + sotc = so_tc_from_control(control, &netsvctype); if ((error = ip6_setpktopts(control, &opt, NULL, IPPROTO_UDP)) != 0) goto release; @@ -191,6 +198,13 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, } 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, @@ -304,7 +318,7 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, * 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; @@ -348,12 +362,66 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, flags = IPV6_OUTARGS; udp6stat.udp6s_opackets++; + +#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 (ipsec_bypass == 0 && ipsec_setsocket(m, so) != 0) { + if (in6p->in6p_sp != NULL && ipsec_setsocket(m, so) != 0) { error = ENOBUFS; goto release; } -#endif /* IPSEC */ +#endif /*IPSEC*/ /* In case of IPv4-mapped address used in previous send */ if (ROUTE_UNUSABLE(&in6p->in6p_route) || @@ -363,7 +431,7 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, /* Copy the cached route and take an extra reference */ in6p_route_copyout(in6p, &ro); - set_packet_service_class(m, so, msc, PKT_SCF_IPV6); + 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; @@ -395,18 +463,20 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, IM6O_REMREF(im6o); if (error == 0 && nstat_collect) { - boolean_t cell, wifi; + 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 = FALSE; + cell = wifi = wired = FALSE; } - INP_ADD_STAT(in6p, cell, wifi, txpackets, 1); - INP_ADD_STAT(in6p, cell, wifi, txbytes, ulen); + 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 || @@ -423,6 +493,17 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, 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); @@ -445,18 +526,26 @@ udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, * with that of the route interface used by IP. */ if (rt != NULL && - (outif = rt->rt_ifp) != in6p->in6p_last_outifp) + (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, and this socket is - * denied access to it, generate an event. + * 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) && - (in6p->inp_flags & INP_NO_IFT_CELLULAR)) + (INP_NO_CELLULAR(in6p) || INP_NO_EXPENSIVE(in6p))) soevent(in6p->inp_socket, (SO_FILT_HINT_LOCKED| SO_FILT_HINT_IFDENIED)); break;