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 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
30 | * All rights reserved. |
31 | * |
32 | * Redistribution and use in source and binary forms, with or without |
33 | * modification, are permitted provided that the following conditions |
34 | * are met: |
35 | * 1. Redistributions of source code must retain the above copyright |
36 | * notice, this list of conditions and the following disclaimer. |
37 | * 2. Redistributions in binary form must reproduce the above copyright |
38 | * notice, this list of conditions and the following disclaimer in the |
39 | * documentation and/or other materials provided with the distribution. |
40 | * 3. Neither the name of the project nor the names of its contributors |
41 | * may be used to endorse or promote products derived from this software |
42 | * without specific prior written permission. |
43 | * |
44 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
45 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
46 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
47 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
48 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
49 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
50 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
51 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
52 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
53 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
54 | * SUCH DAMAGE. |
55 | * |
56 | * $FreeBSD: src/sys/netinet6/raw_ip6.c,v 1.7.2.4 2001/07/29 19:32:40 ume Exp $ |
57 | */ |
58 | |
59 | /* |
60 | * Copyright (c) 1982, 1986, 1988, 1993 |
61 | * The Regents of the University of California. All rights reserved. |
62 | * |
63 | * Redistribution and use in source and binary forms, with or without |
64 | * modification, are permitted provided that the following conditions |
65 | * are met: |
66 | * 1. Redistributions of source code must retain the above copyright |
67 | * notice, this list of conditions and the following disclaimer. |
68 | * 2. Redistributions in binary form must reproduce the above copyright |
69 | * notice, this list of conditions and the following disclaimer in the |
70 | * documentation and/or other materials provided with the distribution. |
71 | * 3. All advertising materials mentioning features or use of this software |
72 | * must display the following acknowledgement: |
73 | * This product includes software developed by the University of |
74 | * California, Berkeley and its contributors. |
75 | * 4. Neither the name of the University nor the names of its contributors |
76 | * may be used to endorse or promote products derived from this software |
77 | * without specific prior written permission. |
78 | * |
79 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
80 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
81 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
82 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
83 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
84 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
85 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
86 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
87 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
88 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
89 | * SUCH DAMAGE. |
90 | * |
91 | * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 |
92 | */ |
93 | #include <sys/param.h> |
94 | #include <sys/malloc.h> |
95 | #include <sys/proc.h> |
96 | #include <sys/mcache.h> |
97 | #include <sys/mbuf.h> |
98 | #include <sys/socket.h> |
99 | #include <sys/protosw.h> |
100 | #include <sys/socketvar.h> |
101 | #include <sys/errno.h> |
102 | #include <sys/systm.h> |
103 | |
104 | #include <net/if.h> |
105 | #include <net/net_api_stats.h> |
106 | #include <net/route.h> |
107 | #include <net/if_types.h> |
108 | #include <net/content_filter.h> |
109 | |
110 | #include <netinet/in.h> |
111 | #include <netinet/in_var.h> |
112 | #include <netinet/in_systm.h> |
113 | #include <netinet/in_tclass.h> |
114 | #include <netinet/ip6.h> |
115 | #include <netinet6/ip6_var.h> |
116 | #include <netinet/icmp6.h> |
117 | #include <netinet/in_pcb.h> |
118 | #include <netinet6/in6_pcb.h> |
119 | #include <netinet6/nd6.h> |
120 | #include <netinet6/ip6protosw.h> |
121 | #include <netinet6/scope6_var.h> |
122 | #include <netinet6/raw_ip6.h> |
123 | |
124 | #if IPSEC |
125 | #include <netinet6/ipsec.h> |
126 | #include <netinet6/ipsec6.h> |
127 | #endif /*IPSEC*/ |
128 | |
129 | #if NECP |
130 | #include <net/necp.h> |
131 | #endif |
132 | |
133 | /* |
134 | * Raw interface to IP6 protocol. |
135 | */ |
136 | |
137 | extern struct inpcbhead ripcb; |
138 | extern struct inpcbinfo ripcbinfo; |
139 | extern u_int32_t rip_sendspace; |
140 | extern u_int32_t rip_recvspace; |
141 | |
142 | struct rip6stat rip6stat; |
143 | |
144 | /* |
145 | * Setup generic address and protocol structures |
146 | * for raw_input routine, then pass them along with |
147 | * mbuf chain. |
148 | */ |
149 | int |
150 | rip6_input( |
151 | struct mbuf **mp, |
152 | int *offp, |
153 | int proto) |
154 | { |
155 | struct mbuf *m = *mp; |
156 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
157 | struct inpcb *in6p; |
158 | struct inpcb *last = 0; |
159 | struct mbuf *opts = NULL; |
160 | struct sockaddr_in6 rip6src; |
161 | int ret; |
162 | struct ifnet *ifp = m->m_pkthdr.rcvif; |
163 | boolean_t is_wake_pkt = false; |
164 | |
165 | /* Expect 32-bit aligned data pointer on strict-align platforms */ |
166 | MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); |
167 | |
168 | rip6stat.rip6s_ipackets++; |
169 | |
170 | init_sin6(sin6: &rip6src, m); /* general init */ |
171 | |
172 | if ((m->m_flags & M_PKTHDR) && (m->m_pkthdr.pkt_flags & PKTF_WAKE_PKT)) { |
173 | is_wake_pkt = true; |
174 | } |
175 | |
176 | lck_rw_lock_shared(lck: &ripcbinfo.ipi_lock); |
177 | LIST_FOREACH(in6p, &ripcb, inp_list) { |
178 | if ((in6p->in6p_vflag & INP_IPV6) == 0) { |
179 | continue; |
180 | } |
181 | if (in6p->in6p_ip6_nxt && |
182 | in6p->in6p_ip6_nxt != proto) { |
183 | continue; |
184 | } |
185 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && |
186 | !in6_are_addr_equal_scoped(&in6p->in6p_laddr, &ip6->ip6_dst, in6p->inp_lifscope, ifp->if_index)) { |
187 | continue; |
188 | } |
189 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && |
190 | !in6_are_addr_equal_scoped(&in6p->in6p_faddr, &ip6->ip6_src, in6p->inp_fifscope, ifp->if_index)) { |
191 | continue; |
192 | } |
193 | |
194 | if (inp_restricted_recv(in6p, ifp)) { |
195 | continue; |
196 | } |
197 | |
198 | if (proto == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { |
199 | rip6stat.rip6s_isum++; |
200 | if (in6_cksum(m, ip6->ip6_nxt, *offp, |
201 | m->m_pkthdr.len - *offp)) { |
202 | rip6stat.rip6s_badsum++; |
203 | continue; |
204 | } |
205 | } |
206 | if (last) { |
207 | struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); |
208 | |
209 | #if NECP |
210 | if (n && !necp_socket_is_allowed_to_send_recv_v6(inp: in6p, local_port: 0, remote_port: 0, |
211 | local_addr: &ip6->ip6_dst, remote_addr: &ip6->ip6_src, input_interface: ifp, pf_tag: 0, NULL, NULL, NULL, NULL)) { |
212 | m_freem(n); |
213 | /* do not inject data into pcb */ |
214 | } else |
215 | #endif /* NECP */ |
216 | if (n) { |
217 | if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || |
218 | SOFLOW_ENABLED(last->in6p_socket) || |
219 | SO_RECV_CONTROL_OPTS(last->in6p_socket)) { |
220 | ret = ip6_savecontrol(last, n, &opts); |
221 | if (ret != 0) { |
222 | m_freem(n); |
223 | m_freem(opts); |
224 | opts = NULL; |
225 | last = in6p; |
226 | continue; |
227 | } |
228 | } |
229 | /* strip intermediate headers */ |
230 | m_adj(n, *offp); |
231 | so_recv_data_stat(last->in6p_socket, m, 0); |
232 | if (sbappendaddr(sb: &last->in6p_socket->so_rcv, |
233 | asa: (struct sockaddr *)&rip6src, |
234 | m0: n, control: opts, NULL) == 0) { |
235 | rip6stat.rip6s_fullsock++; |
236 | } else { |
237 | sorwakeup(so: last->in6p_socket); |
238 | } |
239 | if (is_wake_pkt) { |
240 | soevent(so: in6p->in6p_socket, |
241 | SO_FILT_HINT_LOCKED | SO_FILT_HINT_WAKE_PKT); |
242 | } |
243 | opts = NULL; |
244 | } |
245 | } |
246 | last = in6p; |
247 | } |
248 | |
249 | #if NECP |
250 | if (last && !necp_socket_is_allowed_to_send_recv_v6(inp: in6p, local_port: 0, remote_port: 0, |
251 | local_addr: &ip6->ip6_dst, remote_addr: &ip6->ip6_src, input_interface: ifp, pf_tag: 0, NULL, NULL, NULL, NULL)) { |
252 | m_freem(m); |
253 | ip6stat.ip6s_delivered--; |
254 | /* do not inject data into pcb */ |
255 | } else |
256 | #endif /* NECP */ |
257 | if (last) { |
258 | if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || |
259 | SOFLOW_ENABLED(last->in6p_socket) || |
260 | SO_RECV_CONTROL_OPTS(last->in6p_socket)) { |
261 | ret = ip6_savecontrol(last, m, &opts); |
262 | if (ret != 0) { |
263 | m_freem(m); |
264 | m_freem(opts); |
265 | ip6stat.ip6s_delivered--; |
266 | goto unlock; |
267 | } |
268 | } |
269 | /* strip intermediate headers */ |
270 | m_adj(m, *offp); |
271 | so_recv_data_stat(last->in6p_socket, m, 0); |
272 | if (sbappendaddr(sb: &last->in6p_socket->so_rcv, |
273 | asa: (struct sockaddr *)&rip6src, m0: m, control: opts, NULL) == 0) { |
274 | rip6stat.rip6s_fullsock++; |
275 | } else { |
276 | sorwakeup(so: last->in6p_socket); |
277 | } |
278 | if (is_wake_pkt) { |
279 | soevent(so: last->in6p_socket, |
280 | SO_FILT_HINT_LOCKED | SO_FILT_HINT_WAKE_PKT); |
281 | } |
282 | } else { |
283 | rip6stat.rip6s_nosock++; |
284 | if (m->m_flags & M_MCAST) { |
285 | rip6stat.rip6s_nosockmcast++; |
286 | } |
287 | if (proto == IPPROTO_NONE) { |
288 | m_freem(m); |
289 | } else { |
290 | char *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ |
291 | icmp6_error(m, ICMP6_PARAM_PROB, |
292 | ICMP6_PARAMPROB_NEXTHEADER, |
293 | (int)(prvnxtp - mtod(m, char *))); |
294 | } |
295 | ip6stat.ip6s_delivered--; |
296 | } |
297 | |
298 | unlock: |
299 | lck_rw_done(lck: &ripcbinfo.ipi_lock); |
300 | |
301 | return IPPROTO_DONE; |
302 | } |
303 | |
304 | void |
305 | rip6_ctlinput( |
306 | int cmd, |
307 | struct sockaddr *sa, |
308 | void *d, |
309 | __unused struct ifnet *ifp) |
310 | { |
311 | struct ip6_hdr *ip6 = NULL; |
312 | struct mbuf *m = NULL; |
313 | void *cmdarg = NULL; |
314 | int off = 0; |
315 | struct ip6ctlparam *ip6cp = NULL; |
316 | const struct sockaddr_in6 *sa6_src = NULL; |
317 | void (*notify)(struct inpcb *, int) = in6_rtchange; |
318 | |
319 | if (sa->sa_family != AF_INET6 || |
320 | sa->sa_len != sizeof(struct sockaddr_in6)) { |
321 | return; |
322 | } |
323 | |
324 | if ((unsigned)cmd >= PRC_NCMDS) { |
325 | return; |
326 | } |
327 | if (PRC_IS_REDIRECT(cmd)) { |
328 | notify = in6_rtchange; |
329 | d = NULL; |
330 | } else if (cmd == PRC_HOSTDEAD) { |
331 | d = NULL; |
332 | } else if (inet6ctlerrmap[cmd] == 0) { |
333 | return; |
334 | } |
335 | |
336 | /* if the parameter is from icmp6, decode it. */ |
337 | if (d != NULL) { |
338 | ip6cp = (struct ip6ctlparam *)d; |
339 | m = ip6cp->ip6c_m; |
340 | ip6 = ip6cp->ip6c_ip6; |
341 | off = ip6cp->ip6c_off; |
342 | cmdarg = ip6cp->ip6c_cmdarg; |
343 | sa6_src = ip6cp->ip6c_src; |
344 | } else { |
345 | m = NULL; |
346 | ip6 = NULL; |
347 | cmdarg = NULL; |
348 | sa6_src = &sa6_any; |
349 | } |
350 | |
351 | (void) in6_pcbnotify(&ripcbinfo, sa, 0, (const struct sockaddr *)sa6_src, |
352 | 0, cmd, cmdarg, notify); |
353 | } |
354 | |
355 | /* |
356 | * Generate IPv6 header and pass packet to ip6_output. |
357 | * Tack on options user may have setup with control call. |
358 | */ |
359 | int |
360 | rip6_output( |
361 | struct mbuf *m, |
362 | struct socket *so, |
363 | struct sockaddr_in6 *dstsock, |
364 | struct mbuf *control, |
365 | int israw) |
366 | { |
367 | struct in6_addr *dst; |
368 | struct ip6_hdr *ip6; |
369 | struct inpcb *in6p; |
370 | u_int plen = m->m_pkthdr.len; |
371 | int error = 0; |
372 | struct ip6_pktopts opt, *optp = NULL; |
373 | struct ip6_moptions *im6o = NULL; |
374 | struct ifnet *oifp = NULL; |
375 | int type = 0, code = 0; /* for ICMPv6 output statistics only */ |
376 | int sotc = SO_TC_UNSPEC; |
377 | int netsvctype = _NET_SERVICE_TYPE_UNSPEC; |
378 | struct ip6_out_args ip6oa; |
379 | int flags = IPV6_OUTARGS; |
380 | struct sockaddr_in6 tmp; |
381 | #if CONTENT_FILTER |
382 | struct m_tag *cfil_tag = NULL; |
383 | bool cfil_faddr_use = false; |
384 | uint32_t cfil_so_state_change_cnt = 0; |
385 | uint32_t cfil_so_options = 0; |
386 | uint32_t sifscope = IFSCOPE_NONE, difscope = IFSCOPE_NONE; |
387 | struct sockaddr *cfil_faddr = NULL; |
388 | struct sockaddr_in6 *cfil_sin6 = NULL; |
389 | #endif |
390 | |
391 | in6p = sotoin6pcb(so); |
392 | if (in6p == NULL) { |
393 | error = EINVAL; |
394 | goto bad; |
395 | } |
396 | |
397 | #if CONTENT_FILTER |
398 | /* |
399 | * If socket is subject to Content Filter and no addr is passed in, |
400 | * retrieve CFIL saved state from mbuf and use it if necessary. |
401 | */ |
402 | if (CFIL_DGRAM_FILTERED(so) && !dstsock) { |
403 | cfil_tag = cfil_dgram_get_socket_state(m, state_change_cnt: &cfil_so_state_change_cnt, options: &cfil_so_options, faddr: &cfil_faddr, NULL); |
404 | if (cfil_tag) { |
405 | cfil_sin6 = SIN6(cfil_faddr); |
406 | if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr)) { |
407 | /* |
408 | * Socket is unconnected, simply use the saved faddr as 'addr' to go through |
409 | * the connect/disconnect logic. |
410 | */ |
411 | dstsock = cfil_sin6; |
412 | } else if ((so->so_state_change_cnt != cfil_so_state_change_cnt) && |
413 | (in6p->in6p_fport != cfil_sin6->sin6_port || |
414 | !in6_are_addr_equal_scoped(&in6p->in6p_faddr, &cfil_sin6->sin6_addr, in6p->inp_fifscope, cfil_sin6->sin6_scope_id))) { |
415 | /* |
416 | * Socket is connected but socket state and dest addr/port changed. |
417 | * We need to use the saved faddr and socket options. |
418 | */ |
419 | cfil_faddr_use = true; |
420 | } |
421 | } |
422 | } |
423 | #endif |
424 | |
425 | /* always copy sockaddr to avoid overwrites */ |
426 | if (so->so_state & SS_ISCONNECTED) { |
427 | if (dstsock != NULL) { |
428 | error = EISCONN; |
429 | goto bad; |
430 | } |
431 | /* XXX */ |
432 | bzero(s: &tmp, n: sizeof(tmp)); |
433 | tmp.sin6_family = AF_INET6; |
434 | tmp.sin6_len = sizeof(struct sockaddr_in6); |
435 | bcopy( |
436 | #if CONTENT_FILTER |
437 | src: cfil_faddr_use ? &cfil_sin6->sin6_addr : |
438 | #endif |
439 | &in6p->in6p_faddr, dst: &tmp.sin6_addr, n: sizeof(struct in6_addr)); |
440 | dstsock = &tmp; |
441 | } else { |
442 | if (dstsock == NULL) { |
443 | error = ENOTCONN; |
444 | goto bad; |
445 | } |
446 | tmp = *dstsock; |
447 | dstsock = &tmp; |
448 | } |
449 | |
450 | #if ENABLE_DEFAULT_SCOPE |
451 | if (dstsock->sin6_scope_id == 0) { /* not change if specified */ |
452 | dstsock->sin6_scope_id = scope6_addr2default(&dstsock->sin6_addr); |
453 | } |
454 | #endif |
455 | |
456 | bzero(s: &ip6oa, n: sizeof(ip6oa)); |
457 | ip6oa.ip6oa_boundif = IFSCOPE_NONE; |
458 | ip6oa.ip6oa_flags = IP6OAF_SELECT_SRCIF; |
459 | |
460 | if (in6p == NULL |
461 | #if NECP |
462 | || (necp_socket_should_use_flow_divert(inp: in6p)) |
463 | #endif /* NECP */ |
464 | ) { |
465 | if (in6p == NULL) { |
466 | error = EINVAL; |
467 | } else { |
468 | error = EPROTOTYPE; |
469 | } |
470 | goto bad; |
471 | } |
472 | if (dstsock != NULL && IN6_IS_ADDR_V4MAPPED(&dstsock->sin6_addr)) { |
473 | error = EINVAL; |
474 | goto bad; |
475 | } |
476 | |
477 | if (in6p->inp_flags & INP_BOUND_IF) { |
478 | ip6oa.ip6oa_boundif = in6p->inp_boundifp->if_index; |
479 | ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; |
480 | } else if (!in6_embedded_scope && IN6_IS_SCOPE_EMBED(&in6p->in6p_faddr)) { |
481 | ip6oa.ip6oa_boundif = dstsock->sin6_scope_id; |
482 | ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; |
483 | } |
484 | if (INP_NO_CELLULAR(in6p)) { |
485 | ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR; |
486 | } |
487 | if (INP_NO_EXPENSIVE(in6p)) { |
488 | ip6oa.ip6oa_flags |= IP6OAF_NO_EXPENSIVE; |
489 | } |
490 | if (INP_NO_CONSTRAINED(in6p)) { |
491 | ip6oa.ip6oa_flags |= IP6OAF_NO_CONSTRAINED; |
492 | } |
493 | if (INP_AWDL_UNRESTRICTED(in6p)) { |
494 | ip6oa.ip6oa_flags |= IP6OAF_AWDL_UNRESTRICTED; |
495 | } |
496 | if (INP_INTCOPROC_ALLOWED(in6p)) { |
497 | ip6oa.ip6oa_flags |= IP6OAF_INTCOPROC_ALLOWED; |
498 | } |
499 | if (INP_MANAGEMENT_ALLOWED(in6p)) { |
500 | ip6oa.ip6oa_flags |= IP6OAF_MANAGEMENT_ALLOWED; |
501 | } |
502 | |
503 | dst = &dstsock->sin6_addr; |
504 | if (control) { |
505 | sotc = so_tc_from_control(control, &netsvctype); |
506 | |
507 | if ((error = ip6_setpktopts(control, opt: &opt, NULL, |
508 | SOCK_PROTO(so))) != 0) { |
509 | goto bad; |
510 | } |
511 | optp = &opt; |
512 | } else { |
513 | optp = in6p->in6p_outputopts; |
514 | } |
515 | if (sotc == SO_TC_UNSPEC) { |
516 | sotc = so->so_traffic_class; |
517 | netsvctype = so->so_netsvctype; |
518 | } |
519 | ip6oa.ip6oa_sotc = sotc; |
520 | ip6oa.ip6oa_netsvctype = netsvctype; |
521 | |
522 | /* |
523 | * For an ICMPv6 packet, we should know its type and code |
524 | * to update statistics. |
525 | */ |
526 | if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { |
527 | struct icmp6_hdr *icmp6; |
528 | if (m->m_len < sizeof(struct icmp6_hdr) && |
529 | (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { |
530 | error = ENOBUFS; |
531 | goto bad; |
532 | } |
533 | icmp6 = mtod(m, struct icmp6_hdr *); |
534 | type = icmp6->icmp6_type; |
535 | code = icmp6->icmp6_code; |
536 | } |
537 | |
538 | if (in6p->inp_flowhash == 0) { |
539 | inp_calc_flowhash(in6p); |
540 | ASSERT(in6p->inp_flowhash != 0); |
541 | } |
542 | /* update flowinfo - RFC 6437 */ |
543 | if (in6p->inp_flow == 0 && in6p->in6p_flags & IN6P_AUTOFLOWLABEL) { |
544 | in6p->inp_flow &= ~IPV6_FLOWLABEL_MASK; |
545 | in6p->inp_flow |= |
546 | (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); |
547 | } |
548 | |
549 | M_PREPEND(m, sizeof(*ip6), M_WAIT, 1); |
550 | if (m == NULL) { |
551 | error = ENOBUFS; |
552 | goto bad; |
553 | } |
554 | ip6 = mtod(m, struct ip6_hdr *); |
555 | |
556 | /* |
557 | * Next header might not be ICMP6 but use its pseudo header anyway. |
558 | */ |
559 | ip6->ip6_dst = *dst; |
560 | |
561 | im6o = in6p->in6p_moptions; |
562 | |
563 | /* |
564 | * If the scope of the destination is link-local, embed the interface |
565 | * index in the address. |
566 | * |
567 | * XXX advanced-api value overrides sin6_scope_id |
568 | */ |
569 | if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { |
570 | struct in6_pktinfo *pi; |
571 | struct ifnet *im6o_multicast_ifp = NULL; |
572 | |
573 | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && im6o != NULL) { |
574 | IM6O_LOCK(im6o); |
575 | im6o_multicast_ifp = im6o->im6o_multicast_ifp; |
576 | IM6O_UNLOCK(im6o); |
577 | } |
578 | /* |
579 | * XXX Boundary check is assumed to be already done in |
580 | * ip6_setpktoptions(). |
581 | */ |
582 | ifnet_head_lock_shared(); |
583 | if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) { |
584 | if (in6_embedded_scope) { |
585 | ip6->ip6_dst.s6_addr16[1] = htons((uint16_t)pi->ipi6_ifindex); |
586 | } |
587 | oifp = ifindex2ifnet[pi->ipi6_ifindex]; |
588 | difscope = pi->ipi6_ifindex; |
589 | if (oifp != NULL) { |
590 | ifnet_reference(interface: oifp); |
591 | } |
592 | } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && |
593 | im6o != NULL && im6o_multicast_ifp != NULL) { |
594 | oifp = im6o_multicast_ifp; |
595 | ifnet_reference(interface: oifp); |
596 | if (in6_embedded_scope) { |
597 | ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index); |
598 | } |
599 | difscope = oifp->if_index; |
600 | } else if (dstsock->sin6_scope_id) { |
601 | /* |
602 | * boundary check |
603 | * |
604 | * Sinced stsock->sin6_scope_id is unsigned, we don't |
605 | * need to check if it's < 0 |
606 | */ |
607 | if (!IF_INDEX_IN_RANGE(dstsock->sin6_scope_id)) { |
608 | error = ENXIO; /* XXX EINVAL? */ |
609 | ifnet_head_done(); |
610 | goto bad; |
611 | } |
612 | if (in6_embedded_scope) { |
613 | ip6->ip6_dst.s6_addr16[1] |
614 | = htons(dstsock->sin6_scope_id & 0xffff); /*XXX*/ |
615 | } |
616 | difscope = dstsock->sin6_scope_id; |
617 | } |
618 | ifnet_head_done(); |
619 | |
620 | ip6_output_setdstifscope(m, difscope, NULL); |
621 | } |
622 | |
623 | /* |
624 | * Source address selection. |
625 | */ |
626 | { |
627 | struct in6_addr *in6a; |
628 | struct in6_addr storage; |
629 | u_short index = 0; |
630 | |
631 | if (israw != 0 && optp && optp->ip6po_pktinfo && !IN6_IS_ADDR_UNSPECIFIED(&optp->ip6po_pktinfo->ipi6_addr)) { |
632 | in6a = &optp->ip6po_pktinfo->ipi6_addr; |
633 | flags |= IPV6_FLAG_NOSRCIFSEL; |
634 | sifscope = optp->ip6po_pktinfo->ipi6_ifindex; |
635 | } else { |
636 | struct ifnet *src_ifp = NULL; |
637 | in6a = in6_selectsrc(dstsock, optp, in6p, |
638 | &in6p->in6p_route, &src_ifp, &storage, ip6oa.ip6oa_boundif, |
639 | &error); |
640 | if (src_ifp != NULL) { |
641 | in6p->inp_lifscope = src_ifp->if_index; |
642 | ifnet_release(interface: src_ifp); |
643 | } else { |
644 | in6p->inp_lifscope = ip6oa.ip6oa_boundif; |
645 | } |
646 | if (in6a != 0) { |
647 | ip6oa.ip6oa_flags |= IP6OAF_BOUND_SRCADDR; |
648 | } else { |
649 | if (error == 0) { |
650 | error = EADDRNOTAVAIL; |
651 | } |
652 | goto bad; |
653 | } |
654 | } |
655 | |
656 | ip6->ip6_src = *in6a; |
657 | if (IN6_IS_SCOPE_EMBED(in6a) && sifscope == IFSCOPE_NONE) { |
658 | sifscope = difscope; |
659 | } |
660 | ip6_output_setsrcifscope(m, sifscope, NULL); |
661 | |
662 | if (in6p->in6p_route.ro_rt != NULL) { |
663 | RT_LOCK(in6p->in6p_route.ro_rt); |
664 | if (in6p->in6p_route.ro_rt->rt_ifp != NULL) { |
665 | index = in6p->in6p_route.ro_rt->rt_ifp->if_index; |
666 | } |
667 | RT_UNLOCK(in6p->in6p_route.ro_rt); |
668 | if (oifp != NULL) { |
669 | ifnet_release(interface: oifp); |
670 | } |
671 | ifnet_head_lock_shared(); |
672 | if (!IF_INDEX_IN_RANGE(index)) { |
673 | panic("bad if_index on interface from route" ); |
674 | } |
675 | oifp = ifindex2ifnet[index]; |
676 | if (oifp != NULL) { |
677 | ifnet_reference(interface: oifp); |
678 | } |
679 | ifnet_head_done(); |
680 | } |
681 | } |
682 | ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | |
683 | (in6p->inp_flow & IPV6_FLOWINFO_MASK); |
684 | ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | |
685 | (IPV6_VERSION & IPV6_VERSION_MASK); |
686 | /* ip6_plen will be filled in ip6_output, so not fill it here. */ |
687 | ip6->ip6_nxt = in6p->in6p_ip6_nxt; |
688 | ip6->ip6_hlim = in6_selecthlim(in6p, oifp); |
689 | |
690 | if (SOCK_PROTO(so) == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { |
691 | struct mbuf *n; |
692 | int off; |
693 | u_int16_t *p; |
694 | |
695 | /* compute checksum */ |
696 | if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { |
697 | off = offsetof(struct icmp6_hdr, icmp6_cksum); |
698 | } else { |
699 | off = in6p->in6p_cksum; |
700 | } |
701 | if (plen < (unsigned int)(off + 1)) { |
702 | error = EINVAL; |
703 | goto bad; |
704 | } |
705 | off += sizeof(struct ip6_hdr); |
706 | |
707 | n = m; |
708 | while (n && n->m_len <= off) { |
709 | off -= n->m_len; |
710 | n = n->m_next; |
711 | } |
712 | if (!n) { |
713 | goto bad; |
714 | } |
715 | p = (u_int16_t *)(void *)(mtod(n, caddr_t) + off); |
716 | *p = 0; |
717 | *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); |
718 | } |
719 | |
720 | #if NECP |
721 | { |
722 | necp_kernel_policy_id policy_id; |
723 | necp_kernel_policy_id skip_policy_id; |
724 | u_int32_t route_rule_id; |
725 | u_int32_t pass_flags; |
726 | |
727 | /* |
728 | * We need a route to perform NECP route rule checks |
729 | */ |
730 | if ((net_qos_policy_restricted != 0 && |
731 | ROUTE_UNUSABLE(&in6p->in6p_route)) |
732 | #if CONTENT_FILTER |
733 | || cfil_faddr_use |
734 | #endif |
735 | ) { |
736 | struct sockaddr_in6 to; |
737 | struct sockaddr_in6 from; |
738 | |
739 | ROUTE_RELEASE(&in6p->in6p_route); |
740 | |
741 | bzero(s: &from, n: sizeof(struct sockaddr_in6)); |
742 | from.sin6_family = AF_INET6; |
743 | from.sin6_len = sizeof(struct sockaddr_in6); |
744 | from.sin6_addr = ip6->ip6_src; |
745 | |
746 | bzero(s: &to, n: sizeof(struct sockaddr_in6)); |
747 | to.sin6_family = AF_INET6; |
748 | to.sin6_len = sizeof(struct sockaddr_in6); |
749 | to.sin6_addr = ip6->ip6_dst; |
750 | |
751 | in6p->in6p_route.ro_dst.sin6_family = AF_INET6; |
752 | in6p->in6p_route.ro_dst.sin6_len = sizeof(struct sockaddr_in6); |
753 | ((struct sockaddr_in6 *)(void *)&in6p->in6p_route.ro_dst)->sin6_addr = |
754 | ip6->ip6_dst; |
755 | |
756 | rtalloc_scoped((struct route *)&in6p->in6p_route, ip6oa.ip6oa_boundif); |
757 | |
758 | inp_update_necp_policy(in6p, (struct sockaddr *)&from, |
759 | (struct sockaddr *)&to, ip6oa.ip6oa_boundif); |
760 | in6p->inp_policyresult.results.qos_marking_gencount = 0; |
761 | } |
762 | |
763 | if (!necp_socket_is_allowed_to_send_recv_v6(inp: in6p, local_port: 0, remote_port: 0, |
764 | local_addr: &ip6->ip6_src, remote_addr: &ip6->ip6_dst, 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)) { |
765 | error = EHOSTUNREACH; |
766 | goto bad; |
767 | } |
768 | |
769 | necp_mark_packet_from_socket(packet: m, inp: in6p, policy_id, route_rule_id, skip_policy_id, pass_flags); |
770 | |
771 | if (net_qos_policy_restricted != 0) { |
772 | necp_socket_update_qos_marking(inp: in6p, route: in6p->in6p_route.ro_rt, route_rule_id); |
773 | } |
774 | } |
775 | #endif /* NECP */ |
776 | if ((so->so_flags1 & SOF1_QOSMARKING_ALLOWED)) { |
777 | ip6oa.ip6oa_flags |= IP6OAF_QOSMARKING_ALLOWED; |
778 | } |
779 | |
780 | #if IPSEC |
781 | if (in6p->in6p_sp != NULL && ipsec_setsocket(m, so) != 0) { |
782 | error = ENOBUFS; |
783 | goto bad; |
784 | } |
785 | #endif /*IPSEC*/ |
786 | |
787 | if (ROUTE_UNUSABLE(&in6p->in6p_route)) { |
788 | ROUTE_RELEASE(&in6p->in6p_route); |
789 | } |
790 | |
791 | if (oifp != NULL) { |
792 | ifnet_release(interface: oifp); |
793 | oifp = NULL; |
794 | } |
795 | |
796 | set_packet_service_class(m, so, sotc, PKT_SCF_IPV6); |
797 | m->m_pkthdr.pkt_flowsrc = FLOWSRC_INPCB; |
798 | m->m_pkthdr.pkt_flowid = in6p->inp_flowhash; |
799 | m->m_pkthdr.pkt_flags |= (PKTF_FLOW_ID | PKTF_FLOW_LOCALSRC | |
800 | PKTF_FLOW_RAWSOCK); |
801 | m->m_pkthdr.pkt_proto = in6p->in6p_ip6_nxt; |
802 | m->m_pkthdr.tx_rawip_pid = so->last_pid; |
803 | if (so->so_flags & SOF_DELEGATED) { |
804 | m->m_pkthdr.tx_rawip_e_pid = so->e_pid; |
805 | } else { |
806 | m->m_pkthdr.tx_rawip_e_pid = 0; |
807 | } |
808 | #if (DEBUG || DEVELOPMENT) |
809 | if (so->so_flags & SOF_MARK_WAKE_PKT) { |
810 | so->so_flags &= ~SOF_MARK_WAKE_PKT; |
811 | m->m_pkthdr.pkt_flags |= PKTF_WAKE_PKT; |
812 | } |
813 | #endif /* (DEBUG || DEVELOPMENT) */ |
814 | |
815 | if (im6o != NULL) { |
816 | IM6O_ADDREF(im6o); |
817 | } |
818 | |
819 | error = ip6_output(m, optp, &in6p->in6p_route, flags, im6o, |
820 | &oifp, &ip6oa); |
821 | |
822 | if (im6o != NULL) { |
823 | IM6O_REMREF(im6o); |
824 | } |
825 | |
826 | if (in6p->in6p_route.ro_rt != NULL) { |
827 | struct rtentry *rt = in6p->in6p_route.ro_rt; |
828 | struct ifnet *outif; |
829 | |
830 | if ((rt->rt_flags & RTF_MULTICAST) || |
831 | in6p->in6p_socket == NULL || |
832 | #if CONTENT_FILTER |
833 | /* Discard temporary route for cfil case */ |
834 | cfil_faddr_use || |
835 | #endif |
836 | !(in6p->in6p_socket->so_state & SS_ISCONNECTED)) { |
837 | rt = NULL; /* unusable */ |
838 | } |
839 | /* |
840 | * Always discard the cached route for unconnected |
841 | * socket or if it is a multicast route. |
842 | */ |
843 | if (rt == NULL) { |
844 | ROUTE_RELEASE(&in6p->in6p_route); |
845 | } |
846 | |
847 | /* |
848 | * If this is a connected socket and the destination |
849 | * route is not multicast, update outif with that of |
850 | * the route interface index used by IP. |
851 | */ |
852 | if (rt != NULL) { |
853 | /* |
854 | * When an NECP IP tunnel policy forces the outbound interface, |
855 | * ip6_output_list() informs the transport layer what is the actual |
856 | * outgoing interface |
857 | */ |
858 | if (ip6oa.ip6oa_flags & IP6OAF_BOUND_IF) { |
859 | outif = ifindex2ifnet[ip6oa.ip6oa_boundif]; |
860 | } else { |
861 | outif = rt->rt_ifp; |
862 | } |
863 | if (outif != NULL) { |
864 | in6p->in6p_last_outifp = outif; |
865 | } |
866 | } |
867 | } else { |
868 | ROUTE_RELEASE(&in6p->in6p_route); |
869 | } |
870 | |
871 | /* |
872 | * If output interface was cellular/expensive, and this socket is |
873 | * denied access to it, generate an event. |
874 | */ |
875 | if (error != 0 && (ip6oa.ip6oa_flags & IP6OAF_R_IFDENIED) && |
876 | (INP_NO_CELLULAR(in6p) || INP_NO_EXPENSIVE(in6p) || INP_NO_CONSTRAINED(in6p))) { |
877 | soevent(so: in6p->inp_socket, hint: (SO_FILT_HINT_LOCKED | |
878 | SO_FILT_HINT_IFDENIED)); |
879 | } |
880 | |
881 | if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { |
882 | if (oifp) { |
883 | icmp6_ifoutstat_inc(oifp, type, code); |
884 | } |
885 | icmp6stat.icp6s_outhist[type]++; |
886 | } else { |
887 | rip6stat.rip6s_opackets++; |
888 | } |
889 | |
890 | goto freectl; |
891 | |
892 | bad: |
893 | if (m != NULL) { |
894 | m_freem(m); |
895 | } |
896 | |
897 | freectl: |
898 | if (optp == &opt && optp->ip6po_rthdr) { |
899 | ROUTE_RELEASE(&optp->ip6po_route); |
900 | } |
901 | |
902 | if (control != NULL) { |
903 | if (optp == &opt) { |
904 | ip6_clearpktopts(optp, -1); |
905 | } |
906 | m_freem(control); |
907 | } |
908 | if (oifp != NULL) { |
909 | ifnet_release(interface: oifp); |
910 | } |
911 | #if CONTENT_FILTER |
912 | if (cfil_tag) { |
913 | m_tag_free(cfil_tag); |
914 | } |
915 | #endif |
916 | |
917 | return error; |
918 | } |
919 | |
920 | /* |
921 | * Raw IPv6 socket option processing. |
922 | */ |
923 | int |
924 | rip6_ctloutput( |
925 | struct socket *so, |
926 | struct sockopt *sopt) |
927 | { |
928 | int error, optval; |
929 | |
930 | /* Allow <SOL_SOCKET,SO_FLUSH> at this level */ |
931 | if (sopt->sopt_level == IPPROTO_ICMPV6) { |
932 | /* |
933 | * XXX: is it better to call icmp6_ctloutput() directly |
934 | * from protosw? |
935 | */ |
936 | return icmp6_ctloutput(so, sopt); |
937 | } else if (sopt->sopt_level != IPPROTO_IPV6 && |
938 | !(sopt->sopt_level == SOL_SOCKET && sopt->sopt_name == SO_FLUSH)) { |
939 | return EINVAL; |
940 | } |
941 | |
942 | error = 0; |
943 | |
944 | switch (sopt->sopt_dir) { |
945 | case SOPT_GET: |
946 | switch (sopt->sopt_name) { |
947 | case IPV6_CHECKSUM: |
948 | error = ip6_raw_ctloutput(so, sopt); |
949 | break; |
950 | default: |
951 | error = ip6_ctloutput(so, sopt); |
952 | break; |
953 | } |
954 | break; |
955 | |
956 | case SOPT_SET: |
957 | switch (sopt->sopt_name) { |
958 | case IPV6_CHECKSUM: |
959 | error = ip6_raw_ctloutput(so, sopt); |
960 | break; |
961 | |
962 | case SO_FLUSH: |
963 | if ((error = sooptcopyin(sopt, &optval, len: sizeof(optval), |
964 | minlen: sizeof(optval))) != 0) { |
965 | break; |
966 | } |
967 | |
968 | error = inp_flush(sotoinpcb(so), optval); |
969 | break; |
970 | |
971 | default: |
972 | error = ip6_ctloutput(so, sopt); |
973 | break; |
974 | } |
975 | break; |
976 | } |
977 | |
978 | return error; |
979 | } |
980 | |
981 | static int |
982 | rip6_attach(struct socket *so, int proto, struct proc *p) |
983 | { |
984 | struct inpcb *inp; |
985 | int error; |
986 | |
987 | inp = sotoinpcb(so); |
988 | if (inp) { |
989 | panic("rip6_attach" ); |
990 | } |
991 | if ((error = proc_suser(p)) != 0) { |
992 | return error; |
993 | } |
994 | |
995 | error = soreserve(so, sndcc: rip_sendspace, rcvcc: rip_recvspace); |
996 | if (error) { |
997 | return error; |
998 | } |
999 | error = in_pcballoc(so, &ripcbinfo, p); |
1000 | if (error) { |
1001 | return error; |
1002 | } |
1003 | inp = (struct inpcb *)so->so_pcb; |
1004 | inp->inp_vflag |= INP_IPV6; |
1005 | inp->in6p_ip6_nxt = (char)proto; |
1006 | inp->in6p_hops = -1; /* use kernel default */ |
1007 | inp->in6p_cksum = -1; |
1008 | inp->in6p_icmp6filt = kalloc_type(struct icmp6_filter, |
1009 | Z_WAITOK | Z_NOFAIL); |
1010 | ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); |
1011 | return 0; |
1012 | } |
1013 | |
1014 | static int |
1015 | rip6_detach(struct socket *so) |
1016 | { |
1017 | struct inpcb *inp; |
1018 | |
1019 | inp = sotoinpcb(so); |
1020 | if (inp == 0) { |
1021 | panic("rip6_detach" ); |
1022 | } |
1023 | /* xxx: RSVP */ |
1024 | if (inp->in6p_icmp6filt) { |
1025 | kfree_type(struct icmp6_filter, inp->in6p_icmp6filt); |
1026 | inp->in6p_icmp6filt = NULL; |
1027 | } |
1028 | in6_pcbdetach(inp); |
1029 | return 0; |
1030 | } |
1031 | |
1032 | static int |
1033 | rip6_abort(struct socket *so) |
1034 | { |
1035 | soisdisconnected(so); |
1036 | return rip6_detach(so); |
1037 | } |
1038 | |
1039 | static int |
1040 | rip6_disconnect(struct socket *so) |
1041 | { |
1042 | struct inpcb *inp = sotoinpcb(so); |
1043 | |
1044 | if ((so->so_state & SS_ISCONNECTED) == 0) { |
1045 | return ENOTCONN; |
1046 | } |
1047 | inp->in6p_faddr = in6addr_any; |
1048 | inp->inp_fifscope = IFSCOPE_NONE; |
1049 | return rip6_abort(so); |
1050 | } |
1051 | |
1052 | static int |
1053 | rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) |
1054 | { |
1055 | #pragma unused(p) |
1056 | struct inpcb *inp = sotoinpcb(so); |
1057 | struct sockaddr_in6 sin6; |
1058 | struct ifaddr *ifa = NULL; |
1059 | struct ifnet *outif = NULL; |
1060 | uint32_t ifscope = IFSCOPE_NONE; |
1061 | int error; |
1062 | |
1063 | if (inp == NULL |
1064 | #if NECP |
1065 | || (necp_socket_should_use_flow_divert(inp)) |
1066 | #endif /* NECP */ |
1067 | ) { |
1068 | return inp == NULL ? EINVAL : EPROTOTYPE; |
1069 | } |
1070 | |
1071 | if (nam->sa_len != sizeof(struct sockaddr_in6)) { |
1072 | return EINVAL; |
1073 | } |
1074 | |
1075 | if (TAILQ_EMPTY(&ifnet_head) || SIN6(nam)->sin6_family != AF_INET6) { |
1076 | return EADDRNOTAVAIL; |
1077 | } |
1078 | |
1079 | bzero(s: &sin6, n: sizeof(sin6)); |
1080 | *(&sin6) = *SIN6(nam); |
1081 | |
1082 | if ((error = sa6_embedscope(&sin6, ip6_use_defzone, &ifscope)) != 0) { |
1083 | return error; |
1084 | } |
1085 | |
1086 | /* Sanitize local copy for address searches */ |
1087 | sin6.sin6_flowinfo = 0; |
1088 | sin6.sin6_port = 0; |
1089 | if (in6_embedded_scope) { |
1090 | sin6.sin6_scope_id = 0; |
1091 | } |
1092 | |
1093 | if (!IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr) && |
1094 | (ifa = ifa_ifwithaddr(SA(&sin6))) == 0) { |
1095 | return EADDRNOTAVAIL; |
1096 | } |
1097 | if (ifa != NULL) { |
1098 | IFA_LOCK(ifa); |
1099 | if (((struct in6_ifaddr *)ifa)->ia6_flags & |
1100 | (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY | IN6_IFF_CLAT46 | |
1101 | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED)) { |
1102 | IFA_UNLOCK(ifa); |
1103 | ifa_remref(ifa); |
1104 | return EADDRNOTAVAIL; |
1105 | } |
1106 | outif = ifa->ifa_ifp; |
1107 | IFA_UNLOCK(ifa); |
1108 | ifa_remref(ifa); |
1109 | } |
1110 | inp->in6p_laddr = sin6.sin6_addr; |
1111 | inp->in6p_last_outifp = outif; |
1112 | inp->inp_lifscope = ifscope; |
1113 | in6_verify_ifscope(&inp->in6p_laddr, inp->inp_lifscope); |
1114 | return 0; |
1115 | } |
1116 | |
1117 | static int |
1118 | rip6_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) |
1119 | { |
1120 | struct inpcb *inp = sotoinpcb(so); |
1121 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)(void *)nam; |
1122 | struct in6_addr *in6a = NULL; |
1123 | struct in6_addr storage; |
1124 | int error = 0; |
1125 | #if ENABLE_DEFAULT_SCOPE |
1126 | struct sockaddr_in6 tmp; |
1127 | #endif |
1128 | unsigned int ifscope; |
1129 | struct ifnet *outif = NULL; |
1130 | |
1131 | if (inp == NULL |
1132 | #if NECP |
1133 | || (necp_socket_should_use_flow_divert(inp)) |
1134 | #endif /* NECP */ |
1135 | ) { |
1136 | return inp == NULL ? EINVAL : EPROTOTYPE; |
1137 | } |
1138 | if (nam->sa_len != sizeof(*addr)) { |
1139 | return EINVAL; |
1140 | } |
1141 | if (TAILQ_EMPTY(&ifnet_head)) { |
1142 | return EADDRNOTAVAIL; |
1143 | } |
1144 | if (addr->sin6_family != AF_INET6) { |
1145 | return EAFNOSUPPORT; |
1146 | } |
1147 | |
1148 | if (!(so->so_flags1 & SOF1_CONNECT_COUNTED)) { |
1149 | so->so_flags1 |= SOF1_CONNECT_COUNTED; |
1150 | INC_ATOMIC_INT64_LIM(net_api_stats.nas_socket_inet6_dgram_connected); |
1151 | } |
1152 | |
1153 | #if ENABLE_DEFAULT_SCOPE |
1154 | if (addr->sin6_scope_id == 0) { /* not change if specified */ |
1155 | /* avoid overwrites */ |
1156 | tmp = *addr; |
1157 | addr = &tmp; |
1158 | addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); |
1159 | } |
1160 | #endif |
1161 | |
1162 | /* KAME hack: embed scopeid */ |
1163 | if (in6_embedscope(&SIN6(nam)->sin6_addr, SIN6(nam), inp, NULL, NULL, IN6_NULL_IF_EMBEDDED_SCOPE(&SIN6(nam)->sin6_scope_id)) != 0) { |
1164 | return EINVAL; |
1165 | } |
1166 | |
1167 | ifscope = (inp->inp_flags & INP_BOUND_IF) ? |
1168 | inp->inp_boundifp->if_index : IFSCOPE_NONE; |
1169 | |
1170 | /* Source address selection. XXX: need pcblookup? */ |
1171 | struct ifnet *src_ifp = NULL; |
1172 | in6a = in6_selectsrc(addr, inp->in6p_outputopts, inp, &inp->in6p_route, |
1173 | &src_ifp, &storage, ifscope, &error); |
1174 | if (src_ifp != NULL) { |
1175 | if (in6a != NULL) { |
1176 | inp->inp_lifscope = in6_addr2scopeid(src_ifp, in6a); |
1177 | } |
1178 | ifnet_release(interface: src_ifp); |
1179 | src_ifp = NULL; |
1180 | } |
1181 | if (IN6_IS_SCOPE_EMBED(&addr->sin6_addr) && inp->inp_lifscope == IFSCOPE_NONE) { |
1182 | inp->inp_lifscope = addr->sin6_scope_id; |
1183 | } |
1184 | |
1185 | if (in6a == NULL) { |
1186 | return error ? error : EADDRNOTAVAIL; |
1187 | } |
1188 | inp->in6p_laddr = *in6a; |
1189 | inp->in6p_faddr = addr->sin6_addr; |
1190 | if (inp->in6p_route.ro_rt != NULL) { |
1191 | outif = inp->in6p_route.ro_rt->rt_ifp; |
1192 | } |
1193 | inp->in6p_last_outifp = outif; |
1194 | in6_verify_ifscope(&inp->in6p_laddr, inp->inp_lifscope); |
1195 | inp->inp_fifscope = addr->sin6_scope_id; |
1196 | in6_verify_ifscope(&inp->in6p_faddr, inp->inp_fifscope); |
1197 | |
1198 | soisconnected(so); |
1199 | return 0; |
1200 | } |
1201 | |
1202 | static int |
1203 | rip6_shutdown(struct socket *so) |
1204 | { |
1205 | socantsendmore(so); |
1206 | return 0; |
1207 | } |
1208 | |
1209 | static int |
1210 | rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, |
1211 | struct mbuf *control, struct proc *p) |
1212 | { |
1213 | #pragma unused(flags, p) |
1214 | struct inpcb *inp = sotoinpcb(so); |
1215 | int error = 0; |
1216 | |
1217 | if (inp == NULL |
1218 | #if NECP |
1219 | || (necp_socket_should_use_flow_divert(inp)) |
1220 | #endif /* NECP */ |
1221 | ) { |
1222 | if (inp == NULL) { |
1223 | error = EINVAL; |
1224 | } else { |
1225 | error = EPROTOTYPE; |
1226 | } |
1227 | goto bad; |
1228 | } |
1229 | |
1230 | return rip6_output(m, so, SIN6(nam), control, israw: 1); |
1231 | |
1232 | bad: |
1233 | VERIFY(error != 0); |
1234 | |
1235 | if (m != NULL) { |
1236 | m_freem(m); |
1237 | } |
1238 | if (control != NULL) { |
1239 | m_freem(control); |
1240 | } |
1241 | |
1242 | return error; |
1243 | } |
1244 | |
1245 | struct pr_usrreqs rip6_usrreqs = { |
1246 | .pru_abort = rip6_abort, |
1247 | .pru_attach = rip6_attach, |
1248 | .pru_bind = rip6_bind, |
1249 | .pru_connect = rip6_connect, |
1250 | .pru_control = in6_control, |
1251 | .pru_detach = rip6_detach, |
1252 | .pru_disconnect = rip6_disconnect, |
1253 | .pru_peeraddr = in6_getpeeraddr, |
1254 | .pru_send = rip6_send, |
1255 | .pru_shutdown = rip6_shutdown, |
1256 | .pru_sockaddr = in6_getsockaddr, |
1257 | .pru_sosend = sosend, |
1258 | .pru_soreceive = soreceive, |
1259 | }; |
1260 | |
1261 | __private_extern__ struct pr_usrreqs icmp6_dgram_usrreqs = { |
1262 | .pru_abort = rip6_abort, |
1263 | .pru_attach = icmp6_dgram_attach, |
1264 | .pru_bind = rip6_bind, |
1265 | .pru_connect = rip6_connect, |
1266 | .pru_control = in6_control, |
1267 | .pru_detach = rip6_detach, |
1268 | .pru_disconnect = rip6_disconnect, |
1269 | .pru_peeraddr = in6_getpeeraddr, |
1270 | .pru_send = icmp6_dgram_send, |
1271 | .pru_shutdown = rip6_shutdown, |
1272 | .pru_sockaddr = in6_getsockaddr, |
1273 | .pru_sosend = sosend, |
1274 | .pru_soreceive = soreceive, |
1275 | }; |
1276 | |