1 | /* |
2 | * Copyright (c) 2000-2021 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | |
30 | /* $FreeBSD: src/sys/netinet6/udp6_output.c,v 1.1.2.3 2001/08/31 13:49:58 jlemon Exp $ */ |
31 | /* $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ */ |
32 | |
33 | /* |
34 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
35 | * All rights reserved. |
36 | * |
37 | * Redistribution and use in source and binary forms, with or without |
38 | * modification, are permitted provided that the following conditions |
39 | * are met: |
40 | * 1. Redistributions of source code must retain the above copyright |
41 | * notice, this list of conditions and the following disclaimer. |
42 | * 2. Redistributions in binary form must reproduce the above copyright |
43 | * notice, this list of conditions and the following disclaimer in the |
44 | * documentation and/or other materials provided with the distribution. |
45 | * 3. Neither the name of the project nor the names of its contributors |
46 | * may be used to endorse or promote products derived from this software |
47 | * without specific prior written permission. |
48 | * |
49 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
50 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
51 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
52 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
53 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
54 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
55 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
56 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
57 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
58 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
59 | * SUCH DAMAGE. |
60 | */ |
61 | |
62 | /* |
63 | * Copyright (c) 1982, 1986, 1989, 1993 |
64 | * The Regents of the University of California. All rights reserved. |
65 | * |
66 | * Redistribution and use in source and binary forms, with or without |
67 | * modification, are permitted provided that the following conditions |
68 | * are met: |
69 | * 1. Redistributions of source code must retain the above copyright |
70 | * notice, this list of conditions and the following disclaimer. |
71 | * 2. Redistributions in binary form must reproduce the above copyright |
72 | * notice, this list of conditions and the following disclaimer in the |
73 | * documentation and/or other materials provided with the distribution. |
74 | * 3. All advertising materials mentioning features or use of this software |
75 | * must display the following acknowledgement: |
76 | * This product includes software developed by the University of |
77 | * California, Berkeley and its contributors. |
78 | * 4. Neither the name of the University nor the names of its contributors |
79 | * may be used to endorse or promote products derived from this software |
80 | * without specific prior written permission. |
81 | * |
82 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
83 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
84 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
85 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
86 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
87 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
88 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
89 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
90 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
91 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
92 | * SUCH DAMAGE. |
93 | * |
94 | * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 |
95 | */ |
96 | |
97 | #include <sys/param.h> |
98 | #include <sys/malloc.h> |
99 | #include <sys/mbuf.h> |
100 | #include <sys/protosw.h> |
101 | #include <sys/socket.h> |
102 | #include <sys/socketvar.h> |
103 | #include <sys/sysctl.h> |
104 | #include <sys/errno.h> |
105 | #include <sys/stat.h> |
106 | #include <sys/systm.h> |
107 | #include <sys/proc.h> |
108 | #include <sys/syslog.h> |
109 | |
110 | #include <machine/endian.h> |
111 | |
112 | #include <net/if.h> |
113 | #include <net/route.h> |
114 | #include <net/if_types.h> |
115 | #include <net/ntstat.h> |
116 | |
117 | #include <netinet/in.h> |
118 | #include <netinet/in_var.h> |
119 | #include <netinet/in_systm.h> |
120 | #include <netinet/in_tclass.h> |
121 | #include <netinet/ip.h> |
122 | #include <netinet/ip_var.h> |
123 | #include <netinet/in_pcb.h> |
124 | #include <netinet/udp.h> |
125 | #include <netinet/udp_var.h> |
126 | #include <netinet/ip6.h> |
127 | #include <netinet6/ip6_var.h> |
128 | #include <netinet6/in6_pcb.h> |
129 | #include <netinet6/udp6_var.h> |
130 | #include <netinet/icmp6.h> |
131 | #include <netinet6/ip6protosw.h> |
132 | |
133 | #if NECP |
134 | #include <net/necp.h> |
135 | #endif /* NECP */ |
136 | |
137 | #include <net/net_osdep.h> |
138 | |
139 | #if CONTENT_FILTER |
140 | #include <net/content_filter.h> |
141 | #endif /* CONTENT_FILTER */ |
142 | |
143 | #include <net/sockaddr_utils.h> |
144 | |
145 | /* |
146 | * UDP protocol inplementation. |
147 | * Per RFC 768, August, 1980. |
148 | */ |
149 | int |
150 | udp6_output(struct in6pcb *in6p, struct mbuf *m, struct sockaddr *addr6, |
151 | struct mbuf *control, struct proc *p) |
152 | { |
153 | u_int32_t ulen = m->m_pkthdr.len; |
154 | u_int32_t plen = sizeof(struct udphdr) + ulen; |
155 | struct ip6_hdr *ip6; |
156 | struct udphdr *udp6; |
157 | struct in6_addr *laddr, *faddr; |
158 | u_short fport; |
159 | int error = 0; |
160 | struct ip6_pktopts opt, *optp = NULL; |
161 | struct ip6_moptions *im6o; |
162 | int af = AF_INET6, hlen = sizeof(struct ip6_hdr); |
163 | int flags; |
164 | struct sockaddr_in6 tmp; |
165 | struct in6_addr storage; |
166 | int sotc = SO_TC_UNSPEC; |
167 | int netsvctype = _NET_SERVICE_TYPE_UNSPEC; |
168 | struct ip6_out_args ip6oa; |
169 | struct flowadv *adv = &ip6oa.ip6oa_flowadv; |
170 | struct socket *so = in6p->in6p_socket; |
171 | struct route_in6 ro; |
172 | int flowadv = 0; |
173 | bool sndinprog_cnt_used = false; |
174 | #if CONTENT_FILTER |
175 | struct m_tag *cfil_tag = NULL; |
176 | bool cfil_faddr_use = false; |
177 | uint32_t cfil_so_state_change_cnt = 0; |
178 | struct sockaddr *cfil_faddr = NULL; |
179 | struct sockaddr_in6 *cfil_sin6 = NULL; |
180 | #endif |
181 | bool check_qos_marking_again = (so->so_flags1 & SOF1_QOSMARKING_POLICY_OVERRIDE) ? FALSE : TRUE; |
182 | uint32_t lifscope = IFSCOPE_NONE, fifscope = IFSCOPE_NONE; |
183 | |
184 | bzero(s: &ip6oa, n: sizeof(ip6oa)); |
185 | ip6oa.ip6oa_boundif = IFSCOPE_NONE; |
186 | ip6oa.ip6oa_flags = IP6OAF_SELECT_SRCIF; |
187 | |
188 | /* Enable flow advisory only when connected */ |
189 | flowadv = (so->so_state & SS_ISCONNECTED) ? 1 : 0; |
190 | |
191 | if (flowadv && INP_WAIT_FOR_IF_FEEDBACK(in6p)) { |
192 | error = ENOBUFS; |
193 | goto release; |
194 | } |
195 | |
196 | if (in6p->inp_flags & INP_BOUND_IF) { |
197 | ip6oa.ip6oa_boundif = in6p->inp_boundifp->if_index; |
198 | ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; |
199 | } else if (!in6_embedded_scope && IN6_IS_SCOPE_EMBED(&in6p->in6p_faddr)) { |
200 | ip6oa.ip6oa_boundif = in6p->inp_fifscope; |
201 | ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; |
202 | } |
203 | if (INP_NO_CELLULAR(in6p)) { |
204 | ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR; |
205 | } |
206 | if (INP_NO_EXPENSIVE(in6p)) { |
207 | ip6oa.ip6oa_flags |= IP6OAF_NO_EXPENSIVE; |
208 | } |
209 | if (INP_NO_CONSTRAINED(in6p)) { |
210 | ip6oa.ip6oa_flags |= IP6OAF_NO_CONSTRAINED; |
211 | } |
212 | if (INP_AWDL_UNRESTRICTED(in6p)) { |
213 | ip6oa.ip6oa_flags |= IP6OAF_AWDL_UNRESTRICTED; |
214 | } |
215 | if (INP_INTCOPROC_ALLOWED(in6p)) { |
216 | ip6oa.ip6oa_flags |= IP6OAF_INTCOPROC_ALLOWED; |
217 | } |
218 | if (INP_MANAGEMENT_ALLOWED(in6p)) { |
219 | ip6oa.ip6oa_flags |= IP6OAF_MANAGEMENT_ALLOWED; |
220 | } |
221 | |
222 | #if CONTENT_FILTER |
223 | /* |
224 | * If socket is subject to UDP Content Filter and no addr is passed in, |
225 | * retrieve CFIL saved state from mbuf and use it if necessary. |
226 | */ |
227 | if (CFIL_DGRAM_FILTERED(so) && !addr6) { |
228 | cfil_tag = cfil_dgram_get_socket_state(m, state_change_cnt: &cfil_so_state_change_cnt, NULL, faddr: &cfil_faddr, NULL); |
229 | if (cfil_tag) { |
230 | cfil_sin6 = SIN6(cfil_faddr); |
231 | if ((so->so_state_change_cnt != cfil_so_state_change_cnt) && |
232 | (in6p->in6p_fport != cfil_sin6->sin6_port || |
233 | !in6_are_addr_equal_scoped(&in6p->in6p_faddr, &cfil_sin6->sin6_addr, in6p->inp_fifscope, cfil_sin6->sin6_scope_id))) { |
234 | /* |
235 | * Socket is connected but socket state and dest addr/port changed. |
236 | * We need to use the saved faddr info. |
237 | */ |
238 | cfil_faddr_use = true; |
239 | } |
240 | } |
241 | } |
242 | #endif |
243 | |
244 | if (control) { |
245 | sotc = so_tc_from_control(control, &netsvctype); |
246 | if ((error = ip6_setpktopts(control, opt: &opt, |
247 | NULL, IPPROTO_UDP)) != 0) { |
248 | goto release; |
249 | } |
250 | optp = &opt; |
251 | } else { |
252 | optp = in6p->in6p_outputopts; |
253 | } |
254 | |
255 | if (sotc == SO_TC_UNSPEC) { |
256 | sotc = so->so_traffic_class; |
257 | netsvctype = so->so_netsvctype; |
258 | } |
259 | ip6oa.ip6oa_sotc = sotc; |
260 | ip6oa.ip6oa_netsvctype = netsvctype; |
261 | |
262 | in6p->inp_sndinprog_cnt++; |
263 | sndinprog_cnt_used = true; |
264 | |
265 | if (addr6) { |
266 | /* |
267 | * IPv4 version of udp_output calls in_pcbconnect in this case, |
268 | * which has its costs. |
269 | * |
270 | * Since we saw no essential reason for calling in_pcbconnect, |
271 | * we get rid of such kind of logic, and call in6_selectsrc |
272 | * and in6_pcbsetport in order to fill in the local address |
273 | * and the local port. |
274 | */ |
275 | struct sockaddr_in6 *sin6 = SIN6(addr6); |
276 | |
277 | if (sin6->sin6_port == 0) { |
278 | error = EADDRNOTAVAIL; |
279 | goto release; |
280 | } |
281 | |
282 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { |
283 | /* how about ::ffff:0.0.0.0 case? */ |
284 | error = EISCONN; |
285 | goto release; |
286 | } |
287 | |
288 | /* protect *sin6 from overwrites */ |
289 | tmp = *sin6; |
290 | sin6 = &tmp; |
291 | |
292 | faddr = &sin6->sin6_addr; |
293 | fport = sin6->sin6_port; /* allow 0 port */ |
294 | |
295 | if (IN6_IS_ADDR_V4MAPPED(faddr)) { |
296 | if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) { |
297 | /* |
298 | * I believe we should explicitly discard the |
299 | * packet when mapped addresses are disabled, |
300 | * rather than send the packet as an IPv6 one. |
301 | * If we chose the latter approach, the packet |
302 | * might be sent out on the wire based on the |
303 | * default route, the situation which we'd |
304 | * probably want to avoid. |
305 | * (20010421 jinmei@kame.net) |
306 | */ |
307 | error = EINVAL; |
308 | goto release; |
309 | } else { |
310 | af = AF_INET; |
311 | } |
312 | } |
313 | |
314 | /* KAME hack: embed scopeid */ |
315 | if (in6_embedscope(&sin6->sin6_addr, sin6, in6p, NULL, |
316 | optp, IN6_NULL_IF_EMBEDDED_SCOPE(&sin6->sin6_scope_id)) != 0) { |
317 | error = EINVAL; |
318 | goto release; |
319 | } |
320 | fifscope = sin6->sin6_scope_id; |
321 | |
322 | if (!IN6_IS_ADDR_V4MAPPED(faddr)) { |
323 | struct ifnet *src_ifp = NULL; |
324 | laddr = in6_selectsrc(sin6, optp, |
325 | in6p, &in6p->in6p_route, &src_ifp, &storage, |
326 | ip6oa.ip6oa_boundif, &error); |
327 | if (src_ifp != NULL) { |
328 | lifscope = src_ifp->if_index; |
329 | ifnet_release(interface: src_ifp); |
330 | } |
331 | } else { |
332 | laddr = &in6p->in6p_laddr; /* XXX */ |
333 | lifscope = in6p->inp_lifscope; |
334 | } |
335 | if (laddr == NULL) { |
336 | if (error == 0) { |
337 | error = EADDRNOTAVAIL; |
338 | } |
339 | goto release; |
340 | } |
341 | if (in6p->in6p_lport == 0 && |
342 | (error = in6_pcbsetport(laddr, in6p, p, 0)) != 0) { |
343 | goto release; |
344 | } |
345 | } else { |
346 | if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { |
347 | error = ENOTCONN; |
348 | goto release; |
349 | } |
350 | laddr = &in6p->in6p_laddr; |
351 | faddr = &in6p->in6p_faddr; |
352 | fport = in6p->in6p_fport; |
353 | fifscope = in6p->inp_fifscope; |
354 | lifscope = in6p->inp_lifscope; |
355 | #if CONTENT_FILTER |
356 | if (cfil_faddr_use) { |
357 | faddr = &SIN6(cfil_faddr)->sin6_addr; |
358 | fport = SIN6(cfil_faddr)->sin6_port; |
359 | fifscope = SIN6(cfil_faddr)->sin6_scope_id; |
360 | |
361 | /* Do not use cached route */ |
362 | ROUTE_RELEASE(&in6p->in6p_route); |
363 | } |
364 | #endif |
365 | if (IN6_IS_ADDR_V4MAPPED(faddr)) { |
366 | if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY)) { |
367 | /* |
368 | * XXX: this case would happen when the |
369 | * application sets the V6ONLY flag after |
370 | * connecting the foreign address. |
371 | * Such applications should be fixed, |
372 | * so we bark here. |
373 | */ |
374 | log(LOG_INFO, "udp6_output: IPV6_V6ONLY " |
375 | "option was set for a connected socket\n" ); |
376 | error = EINVAL; |
377 | goto release; |
378 | } else { |
379 | af = AF_INET; |
380 | } |
381 | } |
382 | } |
383 | |
384 | if (in6p->inp_flowhash == 0) { |
385 | inp_calc_flowhash(in6p); |
386 | ASSERT(in6p->inp_flowhash != 0); |
387 | } |
388 | /* update flowinfo - RFC 6437 */ |
389 | if (in6p->inp_flow == 0 && in6p->in6p_flags & IN6P_AUTOFLOWLABEL) { |
390 | in6p->inp_flow &= ~IPV6_FLOWLABEL_MASK; |
391 | in6p->inp_flow |= |
392 | (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); |
393 | } |
394 | |
395 | if (af == AF_INET) { |
396 | hlen = sizeof(struct ip); |
397 | } |
398 | |
399 | if (fport == htons(53) && !(so->so_flags1 & SOF1_DNS_COUNTED)) { |
400 | so->so_flags1 |= SOF1_DNS_COUNTED; |
401 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_inet_dgram_dns); |
402 | } |
403 | |
404 | /* |
405 | * Calculate data length and get a mbuf |
406 | * for UDP and IP6 headers. |
407 | */ |
408 | M_PREPEND(m, hlen + sizeof(struct udphdr), M_DONTWAIT, 1); |
409 | if (m == 0) { |
410 | error = ENOBUFS; |
411 | goto release; |
412 | } |
413 | |
414 | /* |
415 | * Stuff checksum and output datagram. |
416 | */ |
417 | udp6 = (struct udphdr *)(void *)(mtod(m, caddr_t) + hlen); |
418 | udp6->uh_sport = in6p->in6p_lport; /* lport is always set in the PCB */ |
419 | udp6->uh_dport = fport; |
420 | if (plen <= 0xffff) { |
421 | udp6->uh_ulen = htons((u_short)plen); |
422 | } else { |
423 | udp6->uh_ulen = 0; |
424 | } |
425 | udp6->uh_sum = 0; |
426 | |
427 | switch (af) { |
428 | case AF_INET6: |
429 | ip6 = mtod(m, struct ip6_hdr *); |
430 | ip6->ip6_flow = in6p->inp_flow & IPV6_FLOWINFO_MASK; |
431 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; |
432 | ip6->ip6_vfc |= IPV6_VERSION; |
433 | #if 0 /* ip6_plen will be filled in ip6_output. */ |
434 | ip6->ip6_plen = htons((u_short)plen); |
435 | #endif |
436 | ip6->ip6_nxt = IPPROTO_UDP; |
437 | ip6->ip6_hlim = in6_selecthlim(in6p, in6p->in6p_route.ro_rt ? |
438 | in6p->in6p_route.ro_rt->rt_ifp : NULL); |
439 | ip6->ip6_src = *laddr; |
440 | ip6->ip6_dst = *faddr; |
441 | |
442 | udp6->uh_sum = in6_pseudo(laddr, faddr, |
443 | htonl(plen + IPPROTO_UDP)); |
444 | m->m_pkthdr.csum_flags = (CSUM_UDPIPV6 | CSUM_ZERO_INVERT); |
445 | m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); |
446 | |
447 | if (!IN6_IS_ADDR_UNSPECIFIED(laddr)) { |
448 | ip6oa.ip6oa_flags |= IP6OAF_BOUND_SRCADDR; |
449 | } |
450 | |
451 | flags = IPV6_OUTARGS; |
452 | |
453 | udp6stat.udp6s_opackets++; |
454 | |
455 | #if NECP |
456 | { |
457 | necp_kernel_policy_id policy_id; |
458 | necp_kernel_policy_id skip_policy_id; |
459 | u_int32_t route_rule_id; |
460 | u_int32_t pass_flags; |
461 | |
462 | /* |
463 | * We need a route to perform NECP route rule checks |
464 | */ |
465 | if (net_qos_policy_restricted != 0 && |
466 | ROUTE_UNUSABLE(&in6p->inp_route)) { |
467 | struct sockaddr_in6 to; |
468 | struct sockaddr_in6 from; |
469 | |
470 | ROUTE_RELEASE(&in6p->inp_route); |
471 | |
472 | SOCKADDR_ZERO(&from, sizeof(struct sockaddr_in6)); |
473 | from.sin6_family = AF_INET6; |
474 | from.sin6_len = sizeof(struct sockaddr_in6); |
475 | from.sin6_addr = *laddr; |
476 | |
477 | SOCKADDR_ZERO(&to, sizeof(struct sockaddr_in6)); |
478 | to.sin6_family = AF_INET6; |
479 | to.sin6_len = sizeof(struct sockaddr_in6); |
480 | to.sin6_addr = *faddr; |
481 | |
482 | in6p->inp_route.ro_dst.sa_family = AF_INET6; |
483 | in6p->inp_route.ro_dst.sa_len = sizeof(struct sockaddr_in6); |
484 | SIN6(&in6p->inp_route.ro_dst)->sin6_addr = *faddr; |
485 | |
486 | if (!in6_embedded_scope) { |
487 | SIN6(&in6p->inp_route.ro_dst)->sin6_scope_id = |
488 | IN6_IS_SCOPE_EMBED(faddr) ? fifscope : IFSCOPE_NONE; |
489 | } |
490 | rtalloc_scoped(&in6p->inp_route, ip6oa.ip6oa_boundif); |
491 | |
492 | inp_update_necp_policy(in6p, SA(&from), |
493 | SA(&to), ip6oa.ip6oa_boundif); |
494 | in6p->inp_policyresult.results.qos_marking_gencount = 0; |
495 | } |
496 | |
497 | if (!necp_socket_is_allowed_to_send_recv_v6(inp: in6p, local_port: in6p->in6p_lport, remote_port: fport, local_addr: laddr, remote_addr: faddr, NULL, pf_tag: 0, return_policy_id: &policy_id, return_route_rule_id: &route_rule_id, return_skip_policy_id: &skip_policy_id, return_pass_flags: &pass_flags)) { |
498 | error = EHOSTUNREACH; |
499 | goto release; |
500 | } |
501 | |
502 | necp_mark_packet_from_socket(packet: m, inp: in6p, policy_id, route_rule_id, skip_policy_id, pass_flags); |
503 | |
504 | if (net_qos_policy_restricted != 0) { |
505 | necp_socket_update_qos_marking(inp: in6p, route: in6p->in6p_route.ro_rt, route_rule_id); |
506 | } |
507 | } |
508 | #endif /* NECP */ |
509 | if ((so->so_flags1 & SOF1_QOSMARKING_ALLOWED)) { |
510 | ip6oa.ip6oa_flags |= IP6OAF_QOSMARKING_ALLOWED; |
511 | } |
512 | if (check_qos_marking_again) { |
513 | ip6oa.ip6oa_flags |= IP6OAF_REDO_QOSMARKING_POLICY; |
514 | } |
515 | ip6oa.qos_marking_gencount = in6p->inp_policyresult.results.qos_marking_gencount; |
516 | |
517 | #if IPSEC |
518 | if (in6p->in6p_sp != NULL && ipsec_setsocket(m, so) != 0) { |
519 | error = ENOBUFS; |
520 | goto release; |
521 | } |
522 | #endif /*IPSEC*/ |
523 | |
524 | /* In case of IPv4-mapped address used in previous send */ |
525 | if (ROUTE_UNUSABLE(&in6p->in6p_route) || |
526 | rt_key(in6p->in6p_route.ro_rt)->sa_family != AF_INET6) { |
527 | ROUTE_RELEASE(&in6p->in6p_route); |
528 | } |
529 | |
530 | /* Copy the cached route and take an extra reference */ |
531 | in6p_route_copyout(in6p, &ro); |
532 | |
533 | set_packet_service_class(m, so, sotc, PKT_SCF_IPV6); |
534 | |
535 | m->m_pkthdr.pkt_flowsrc = FLOWSRC_INPCB; |
536 | m->m_pkthdr.pkt_flowid = in6p->inp_flowhash; |
537 | m->m_pkthdr.pkt_proto = IPPROTO_UDP; |
538 | m->m_pkthdr.pkt_flags |= (PKTF_FLOW_ID | PKTF_FLOW_LOCALSRC); |
539 | if (flowadv) { |
540 | m->m_pkthdr.pkt_flags |= PKTF_FLOW_ADV; |
541 | } |
542 | m->m_pkthdr.tx_udp_pid = so->last_pid; |
543 | if (so->so_flags & SOF_DELEGATED) { |
544 | m->m_pkthdr.tx_udp_e_pid = so->e_pid; |
545 | } else { |
546 | m->m_pkthdr.tx_udp_e_pid = 0; |
547 | } |
548 | #if (DEBUG || DEVELOPMENT) |
549 | if (so->so_flags & SOF_MARK_WAKE_PKT) { |
550 | so->so_flags &= ~SOF_MARK_WAKE_PKT; |
551 | m->m_pkthdr.pkt_flags |= PKTF_WAKE_PKT; |
552 | } |
553 | #endif /* (DEBUG || DEVELOPMENT) */ |
554 | |
555 | im6o = in6p->in6p_moptions; |
556 | if (im6o != NULL) { |
557 | IM6O_LOCK(im6o); |
558 | IM6O_ADDREF_LOCKED(im6o); |
559 | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && |
560 | im6o->im6o_multicast_ifp != NULL) { |
561 | in6p->in6p_last_outifp = |
562 | im6o->im6o_multicast_ifp; |
563 | #if SKYWALK |
564 | if (NETNS_TOKEN_VALID(&in6p->inp_netns_token)) { |
565 | netns_set_ifnet(token: &in6p->inp_netns_token, |
566 | ifp: in6p->in6p_last_outifp); |
567 | } |
568 | #endif /* SKYWALK */ |
569 | } |
570 | IM6O_UNLOCK(im6o); |
571 | } |
572 | |
573 | ip6_output_setdstifscope(m, fifscope, NULL); |
574 | ip6_output_setsrcifscope(m, lifscope, NULL); |
575 | |
576 | socket_unlock(so, refcount: 0); |
577 | error = ip6_output(m, optp, &ro, flags, im6o, NULL, &ip6oa); |
578 | m = NULL; |
579 | socket_lock(so, refcount: 0); |
580 | |
581 | if (im6o != NULL) { |
582 | IM6O_REMREF(im6o); |
583 | } |
584 | |
585 | if (check_qos_marking_again) { |
586 | in6p->inp_policyresult.results.qos_marking_gencount = ip6oa.qos_marking_gencount; |
587 | if (ip6oa.ip6oa_flags & IP6OAF_QOSMARKING_ALLOWED) { |
588 | in6p->inp_socket->so_flags1 |= SOF1_QOSMARKING_ALLOWED; |
589 | } else { |
590 | in6p->inp_socket->so_flags1 &= ~SOF1_QOSMARKING_ALLOWED; |
591 | } |
592 | } |
593 | |
594 | if (error == 0 && nstat_collect) { |
595 | boolean_t cell, wifi, wired; |
596 | |
597 | if (in6p->in6p_route.ro_rt != NULL) { |
598 | cell = IFNET_IS_CELLULAR(in6p->in6p_route. |
599 | ro_rt->rt_ifp); |
600 | wifi = (!cell && IFNET_IS_WIFI(in6p->in6p_route. |
601 | ro_rt->rt_ifp)); |
602 | wired = (!wifi && IFNET_IS_WIRED(in6p->in6p_route. |
603 | ro_rt->rt_ifp)); |
604 | } else { |
605 | cell = wifi = wired = FALSE; |
606 | } |
607 | INP_ADD_STAT(in6p, cell, wifi, wired, txpackets, 1); |
608 | INP_ADD_STAT(in6p, cell, wifi, wired, txbytes, ulen); |
609 | inp_set_activity_bitmap(inp: in6p); |
610 | } |
611 | |
612 | if (flowadv && (adv->code == FADV_FLOW_CONTROLLED || |
613 | adv->code == FADV_SUSPENDED)) { |
614 | /* |
615 | * Return an error to indicate |
616 | * that the packet has been dropped. |
617 | */ |
618 | error = ENOBUFS; |
619 | inp_set_fc_state(in6p, advcode: adv->code); |
620 | } |
621 | |
622 | if (ro.ro_rt != NULL) { |
623 | struct ifnet *outif = ro.ro_rt->rt_ifp; |
624 | |
625 | so->so_pktheadroom = (uint16_t)P2ROUNDUP( |
626 | sizeof(struct udphdr) + |
627 | hlen + |
628 | ifnet_hdrlen(outif) + |
629 | ifnet_mbuf_packetpreamblelen(outif), |
630 | sizeof(u_int32_t)); |
631 | } |
632 | |
633 | /* Synchronize PCB cached route */ |
634 | in6p_route_copyin(in6p, &ro); |
635 | |
636 | if (in6p->in6p_route.ro_rt != NULL) { |
637 | struct rtentry *rt = in6p->in6p_route.ro_rt; |
638 | struct ifnet *outif; |
639 | |
640 | if (IS_LOCALNET_ROUTE(rt)) { |
641 | in6p->in6p_flags2 |= INP2_LAST_ROUTE_LOCAL; |
642 | } else { |
643 | in6p->in6p_flags2 &= ~INP2_LAST_ROUTE_LOCAL; |
644 | } |
645 | |
646 | if (rt->rt_flags & RTF_MULTICAST) { |
647 | rt = NULL; /* unusable */ |
648 | } |
649 | #if CONTENT_FILTER |
650 | /* |
651 | * Discard temporary route for cfil case |
652 | */ |
653 | if (cfil_faddr_use) { |
654 | rt = NULL; /* unusable */ |
655 | } |
656 | #endif |
657 | |
658 | /* |
659 | * Always discard the cached route for unconnected |
660 | * socket or if it is a multicast route. |
661 | */ |
662 | if (rt == NULL) { |
663 | ROUTE_RELEASE(&in6p->in6p_route); |
664 | } |
665 | |
666 | /* |
667 | * If the destination route is unicast, update outif |
668 | * with that of the route interface used by IP. |
669 | */ |
670 | if (rt != NULL) { |
671 | /* |
672 | * When an NECP IP tunnel policy forces the outbound interface, |
673 | * ip6_output_list() informs the transport layer what is the actual |
674 | * outgoing interface |
675 | */ |
676 | if (ip6oa.ip6oa_flags & IP6OAF_BOUND_IF) { |
677 | outif = ifindex2ifnet[ip6oa.ip6oa_boundif]; |
678 | } else { |
679 | outif = rt->rt_ifp; |
680 | } |
681 | if (outif != NULL && outif != in6p->in6p_last_outifp) { |
682 | in6p->in6p_last_outifp = outif; |
683 | #if SKYWALK |
684 | if (NETNS_TOKEN_VALID(&in6p->inp_netns_token)) { |
685 | netns_set_ifnet(token: &in6p->inp_netns_token, |
686 | ifp: in6p->in6p_last_outifp); |
687 | } |
688 | #endif /* SKYWALK */ |
689 | |
690 | so->so_pktheadroom = (uint16_t)P2ROUNDUP( |
691 | sizeof(struct udphdr) + |
692 | hlen + |
693 | ifnet_hdrlen(outif) + |
694 | ifnet_mbuf_packetpreamblelen(outif), |
695 | sizeof(u_int32_t)); |
696 | } |
697 | } |
698 | } else { |
699 | ROUTE_RELEASE(&in6p->in6p_route); |
700 | } |
701 | |
702 | /* |
703 | * If output interface was cellular/expensive, and this |
704 | * socket is denied access to it, generate an event. |
705 | */ |
706 | if (error != 0 && (ip6oa.ip6oa_flags & IP6OAF_R_IFDENIED) && |
707 | (INP_NO_CELLULAR(in6p) || INP_NO_EXPENSIVE(in6p) || INP_NO_CONSTRAINED(in6p))) { |
708 | soevent(so: in6p->inp_socket, hint: (SO_FILT_HINT_LOCKED | |
709 | SO_FILT_HINT_IFDENIED)); |
710 | } |
711 | break; |
712 | case AF_INET: |
713 | error = EAFNOSUPPORT; |
714 | goto release; |
715 | } |
716 | goto releaseopt; |
717 | |
718 | release: |
719 | |
720 | if (m != NULL) { |
721 | m_freem(m); |
722 | } |
723 | |
724 | releaseopt: |
725 | if (control != NULL) { |
726 | if (optp == &opt) { |
727 | ip6_clearpktopts(optp, -1); |
728 | } |
729 | m_freem(control); |
730 | } |
731 | #if CONTENT_FILTER |
732 | if (cfil_tag) { |
733 | m_tag_free(cfil_tag); |
734 | } |
735 | #endif |
736 | if (sndinprog_cnt_used) { |
737 | VERIFY(in6p->inp_sndinprog_cnt > 0); |
738 | if (--in6p->inp_sndinprog_cnt == 0) { |
739 | in6p->inp_flags &= ~(INP_FC_FEEDBACK); |
740 | if (in6p->inp_sndingprog_waiters > 0) { |
741 | wakeup(chan: &in6p->inp_sndinprog_cnt); |
742 | } |
743 | } |
744 | sndinprog_cnt_used = false; |
745 | } |
746 | |
747 | return error; |
748 | } |
749 | |