1 | /* $NetBSD: raw_ip6.c,v 1.153 2016/11/18 06:50:04 knakahara Exp $ */ |
2 | /* $KAME: raw_ip6.c,v 1.82 2001/07/23 18:57:56 jinmei Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. Neither the name of the project nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 | * SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * Copyright (c) 1982, 1986, 1988, 1993 |
35 | * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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 | * @(#)raw_ip.c 8.2 (Berkeley) 1/4/94 |
62 | */ |
63 | |
64 | #include <sys/cdefs.h> |
65 | __KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.153 2016/11/18 06:50:04 knakahara Exp $" ); |
66 | |
67 | #ifdef _KERNEL_OPT |
68 | #include "opt_ipsec.h" |
69 | #include "opt_net_mpsafe.h" |
70 | #endif |
71 | |
72 | #include <sys/param.h> |
73 | #include <sys/sysctl.h> |
74 | #include <sys/mbuf.h> |
75 | #include <sys/socket.h> |
76 | #include <sys/protosw.h> |
77 | #include <sys/socketvar.h> |
78 | #include <sys/systm.h> |
79 | #include <sys/proc.h> |
80 | #include <sys/kauth.h> |
81 | #include <sys/kmem.h> |
82 | |
83 | #include <net/if.h> |
84 | #include <net/if_types.h> |
85 | #include <net/net_stats.h> |
86 | |
87 | #include <netinet/in.h> |
88 | #include <netinet/in_var.h> |
89 | #include <netinet/ip6.h> |
90 | #include <netinet6/ip6_var.h> |
91 | #include <netinet6/ip6_private.h> |
92 | #include <netinet6/ip6_mroute.h> |
93 | #include <netinet/icmp6.h> |
94 | #include <netinet6/icmp6_private.h> |
95 | #include <netinet6/in6_pcb.h> |
96 | #include <netinet6/nd6.h> |
97 | #include <netinet6/ip6protosw.h> |
98 | #include <netinet6/scope6_var.h> |
99 | #include <netinet6/raw_ip6.h> |
100 | |
101 | #ifdef IPSEC |
102 | #include <netipsec/ipsec.h> |
103 | #include <netipsec/ipsec_var.h> |
104 | #include <netipsec/ipsec_private.h> |
105 | #include <netipsec/ipsec6.h> |
106 | #endif |
107 | |
108 | #include "faith.h" |
109 | #if defined(NFAITH) && 0 < NFAITH |
110 | #include <net/if_faith.h> |
111 | #endif |
112 | |
113 | extern struct inpcbtable rawcbtable; |
114 | struct inpcbtable raw6cbtable; |
115 | #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) |
116 | |
117 | /* |
118 | * Raw interface to IP6 protocol. |
119 | */ |
120 | |
121 | static percpu_t *rip6stat_percpu; |
122 | |
123 | #define RIP6_STATINC(x) _NET_STATINC(rip6stat_percpu, x) |
124 | |
125 | static void sysctl_net_inet6_raw6_setup(struct sysctllog **); |
126 | |
127 | /* |
128 | * Initialize raw connection block queue. |
129 | */ |
130 | void |
131 | rip6_init(void) |
132 | { |
133 | |
134 | sysctl_net_inet6_raw6_setup(NULL); |
135 | in6_pcbinit(&raw6cbtable, 1, 1); |
136 | |
137 | rip6stat_percpu = percpu_alloc(sizeof(uint64_t) * RIP6_NSTATS); |
138 | } |
139 | |
140 | /* |
141 | * Setup generic address and protocol structures |
142 | * for raw_input routine, then pass them along with |
143 | * mbuf chain. |
144 | */ |
145 | int |
146 | rip6_input(struct mbuf **mp, int *offp, int proto) |
147 | { |
148 | struct mbuf *m = *mp; |
149 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
150 | struct inpcb_hdr *inph; |
151 | struct in6pcb *in6p; |
152 | struct in6pcb *last = NULL; |
153 | struct sockaddr_in6 rip6src; |
154 | struct mbuf *opts = NULL; |
155 | |
156 | RIP6_STATINC(RIP6_STAT_IPACKETS); |
157 | |
158 | #if defined(NFAITH) && 0 < NFAITH |
159 | if (faithprefix(&ip6->ip6_dst)) { |
160 | /* send icmp6 host unreach? */ |
161 | m_freem(m); |
162 | return IPPROTO_DONE; |
163 | } |
164 | #endif |
165 | |
166 | /* Be proactive about malicious use of IPv4 mapped address */ |
167 | if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || |
168 | IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { |
169 | /* XXX stat */ |
170 | m_freem(m); |
171 | return IPPROTO_DONE; |
172 | } |
173 | |
174 | sockaddr_in6_init(&rip6src, &ip6->ip6_src, 0, 0, 0); |
175 | if (sa6_recoverscope(&rip6src) != 0) { |
176 | /* XXX: should be impossible. */ |
177 | m_freem(m); |
178 | return IPPROTO_DONE; |
179 | } |
180 | |
181 | TAILQ_FOREACH(inph, &raw6cbtable.inpt_queue, inph_queue) { |
182 | in6p = (struct in6pcb *)inph; |
183 | if (in6p->in6p_af != AF_INET6) |
184 | continue; |
185 | if (in6p->in6p_ip6.ip6_nxt && |
186 | in6p->in6p_ip6.ip6_nxt != proto) |
187 | continue; |
188 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && |
189 | !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) |
190 | continue; |
191 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && |
192 | !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) |
193 | continue; |
194 | if (in6p->in6p_cksum != -1) { |
195 | RIP6_STATINC(RIP6_STAT_ISUM); |
196 | if (in6_cksum(m, proto, *offp, |
197 | m->m_pkthdr.len - *offp)) { |
198 | RIP6_STATINC(RIP6_STAT_BADSUM); |
199 | continue; |
200 | } |
201 | } |
202 | if (last) { |
203 | struct mbuf *n; |
204 | |
205 | #ifdef IPSEC |
206 | /* |
207 | * Check AH/ESP integrity |
208 | */ |
209 | if (ipsec_used && !ipsec6_in_reject(m, last)) |
210 | #endif /* IPSEC */ |
211 | if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { |
212 | if (last->in6p_flags & IN6P_CONTROLOPTS) |
213 | ip6_savecontrol(last, &opts, ip6, n); |
214 | /* strip intermediate headers */ |
215 | m_adj(n, *offp); |
216 | if (sbappendaddr(&last->in6p_socket->so_rcv, |
217 | sin6tosa(&rip6src), n, opts) == 0) { |
218 | /* should notify about lost packet */ |
219 | m_freem(n); |
220 | if (opts) |
221 | m_freem(opts); |
222 | RIP6_STATINC(RIP6_STAT_FULLSOCK); |
223 | } else |
224 | sorwakeup(last->in6p_socket); |
225 | opts = NULL; |
226 | } |
227 | } |
228 | last = in6p; |
229 | } |
230 | #ifdef IPSEC |
231 | if (ipsec_used && last && ipsec6_in_reject(m, last)) { |
232 | m_freem(m); |
233 | /* |
234 | * XXX ipsec6_in_reject update stat if there is an error |
235 | * so we just need to update stats by hand in the case of last is |
236 | * NULL |
237 | */ |
238 | if (!last) |
239 | IPSEC6_STATINC(IPSEC_STAT_IN_POLVIO); |
240 | IP6_STATDEC(IP6_STAT_DELIVERED); |
241 | /* do not inject data into pcb */ |
242 | } else |
243 | #endif /* IPSEC */ |
244 | if (last) { |
245 | if (last->in6p_flags & IN6P_CONTROLOPTS) |
246 | ip6_savecontrol(last, &opts, ip6, m); |
247 | /* strip intermediate headers */ |
248 | m_adj(m, *offp); |
249 | if (sbappendaddr(&last->in6p_socket->so_rcv, |
250 | sin6tosa(&rip6src), m, opts) == 0) { |
251 | m_freem(m); |
252 | if (opts) |
253 | m_freem(opts); |
254 | RIP6_STATINC(RIP6_STAT_FULLSOCK); |
255 | } else |
256 | sorwakeup(last->in6p_socket); |
257 | } else { |
258 | RIP6_STATINC(RIP6_STAT_NOSOCK); |
259 | if (m->m_flags & M_MCAST) |
260 | RIP6_STATINC(RIP6_STAT_NOSOCKMCAST); |
261 | if (proto == IPPROTO_NONE) |
262 | m_freem(m); |
263 | else { |
264 | int s; |
265 | struct ifnet *rcvif = m_get_rcvif(m, &s); |
266 | u_int8_t *prvnxtp = ip6_get_prevhdr(m, *offp); /* XXX */ |
267 | in6_ifstat_inc(rcvif, ifs6_in_protounknown); |
268 | m_put_rcvif(rcvif, &s); |
269 | icmp6_error(m, ICMP6_PARAM_PROB, |
270 | ICMP6_PARAMPROB_NEXTHEADER, |
271 | prvnxtp - mtod(m, u_int8_t *)); |
272 | } |
273 | IP6_STATDEC(IP6_STAT_DELIVERED); |
274 | } |
275 | return IPPROTO_DONE; |
276 | } |
277 | |
278 | void * |
279 | rip6_ctlinput(int cmd, const struct sockaddr *sa, void *d) |
280 | { |
281 | struct ip6_hdr *ip6; |
282 | struct ip6ctlparam *ip6cp = NULL; |
283 | const struct sockaddr_in6 *sa6_src = NULL; |
284 | void *cmdarg; |
285 | void (*notify)(struct in6pcb *, int) = in6_rtchange; |
286 | int nxt; |
287 | |
288 | if (sa->sa_family != AF_INET6 || |
289 | sa->sa_len != sizeof(struct sockaddr_in6)) |
290 | return NULL; |
291 | |
292 | if ((unsigned)cmd >= PRC_NCMDS) |
293 | return NULL; |
294 | if (PRC_IS_REDIRECT(cmd)) |
295 | notify = in6_rtchange, d = NULL; |
296 | else if (cmd == PRC_HOSTDEAD) |
297 | d = NULL; |
298 | else if (cmd == PRC_MSGSIZE) |
299 | ; /* special code is present, see below */ |
300 | else if (inet6ctlerrmap[cmd] == 0) |
301 | return NULL; |
302 | |
303 | /* if the parameter is from icmp6, decode it. */ |
304 | if (d != NULL) { |
305 | ip6cp = (struct ip6ctlparam *)d; |
306 | ip6 = ip6cp->ip6c_ip6; |
307 | cmdarg = ip6cp->ip6c_cmdarg; |
308 | sa6_src = ip6cp->ip6c_src; |
309 | nxt = ip6cp->ip6c_nxt; |
310 | } else { |
311 | ip6 = NULL; |
312 | cmdarg = NULL; |
313 | sa6_src = &sa6_any; |
314 | nxt = -1; |
315 | } |
316 | |
317 | if (ip6 && cmd == PRC_MSGSIZE) { |
318 | const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *)sa; |
319 | int valid = 0; |
320 | struct in6pcb *in6p; |
321 | |
322 | /* |
323 | * Check to see if we have a valid raw IPv6 socket |
324 | * corresponding to the address in the ICMPv6 message |
325 | * payload, and the protocol (ip6_nxt) meets the socket. |
326 | * XXX chase extension headers, or pass final nxt value |
327 | * from icmp6_notify_error() |
328 | */ |
329 | in6p = NULL; |
330 | in6p = in6_pcblookup_connect(&raw6cbtable, &sa6->sin6_addr, 0, |
331 | (const struct in6_addr *)&sa6_src->sin6_addr, 0, 0, 0); |
332 | #if 0 |
333 | if (!in6p) { |
334 | /* |
335 | * As the use of sendto(2) is fairly popular, |
336 | * we may want to allow non-connected pcb too. |
337 | * But it could be too weak against attacks... |
338 | * We should at least check if the local |
339 | * address (= s) is really ours. |
340 | */ |
341 | in6p = in6_pcblookup_bind(&raw6cbtable, |
342 | &sa6->sin6_addr, 0, 0); |
343 | } |
344 | #endif |
345 | |
346 | if (in6p && in6p->in6p_ip6.ip6_nxt && |
347 | in6p->in6p_ip6.ip6_nxt == nxt) |
348 | valid++; |
349 | |
350 | /* |
351 | * Depending on the value of "valid" and routing table |
352 | * size (mtudisc_{hi,lo}wat), we will: |
353 | * - recalculate the new MTU and create the |
354 | * corresponding routing entry, or |
355 | * - ignore the MTU change notification. |
356 | */ |
357 | icmp6_mtudisc_update((struct ip6ctlparam *)d, valid); |
358 | |
359 | /* |
360 | * regardless of if we called icmp6_mtudisc_update(), |
361 | * we need to call in6_pcbnotify(), to notify path MTU |
362 | * change to the userland (RFC3542), because some |
363 | * unconnected sockets may share the same destination |
364 | * and want to know the path MTU. |
365 | */ |
366 | } |
367 | |
368 | (void) in6_pcbnotify(&raw6cbtable, sa, 0, |
369 | sin6tocsa(sa6_src), 0, cmd, cmdarg, notify); |
370 | return NULL; |
371 | } |
372 | |
373 | /* |
374 | * Generate IPv6 header and pass packet to ip6_output. |
375 | * Tack on options user may have setup with control call. |
376 | */ |
377 | int |
378 | rip6_output(struct mbuf *m, struct socket * const so, |
379 | struct sockaddr_in6 * const dstsock, struct mbuf * const control) |
380 | { |
381 | struct in6_addr *dst; |
382 | struct ip6_hdr *ip6; |
383 | struct in6pcb *in6p; |
384 | u_int plen = m->m_pkthdr.len; |
385 | int error = 0; |
386 | struct ip6_pktopts opt, *optp = NULL; |
387 | struct ifnet *oifp = NULL; |
388 | int type, code; /* for ICMPv6 output statistics only */ |
389 | int scope_ambiguous = 0; |
390 | int bound = curlwp_bind(); |
391 | struct psref psref; |
392 | |
393 | in6p = sotoin6pcb(so); |
394 | |
395 | dst = &dstsock->sin6_addr; |
396 | if (control) { |
397 | if ((error = ip6_setpktopts(control, &opt, |
398 | in6p->in6p_outputopts, |
399 | kauth_cred_get(), so->so_proto->pr_protocol)) != 0) { |
400 | goto bad; |
401 | } |
402 | optp = &opt; |
403 | } else |
404 | optp = in6p->in6p_outputopts; |
405 | |
406 | /* |
407 | * Check and convert scope zone ID into internal form. |
408 | * XXX: we may still need to determine the zone later. |
409 | */ |
410 | if (!(so->so_state & SS_ISCONNECTED)) { |
411 | if (dstsock->sin6_scope_id == 0 && !ip6_use_defzone) |
412 | scope_ambiguous = 1; |
413 | if ((error = sa6_embedscope(dstsock, ip6_use_defzone)) != 0) |
414 | goto bad; |
415 | } |
416 | |
417 | /* |
418 | * For an ICMPv6 packet, we should know its type and code |
419 | * to update statistics. |
420 | */ |
421 | if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { |
422 | struct icmp6_hdr *icmp6; |
423 | if (m->m_len < sizeof(struct icmp6_hdr) && |
424 | (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { |
425 | error = ENOBUFS; |
426 | goto bad; |
427 | } |
428 | icmp6 = mtod(m, struct icmp6_hdr *); |
429 | type = icmp6->icmp6_type; |
430 | code = icmp6->icmp6_code; |
431 | } else { |
432 | type = 0; |
433 | code = 0; |
434 | } |
435 | |
436 | M_PREPEND(m, sizeof(*ip6), M_DONTWAIT); |
437 | if (!m) { |
438 | error = ENOBUFS; |
439 | goto bad; |
440 | } |
441 | ip6 = mtod(m, struct ip6_hdr *); |
442 | |
443 | /* |
444 | * Next header might not be ICMP6 but use its pseudo header anyway. |
445 | */ |
446 | ip6->ip6_dst = *dst; |
447 | |
448 | /* |
449 | * Source address selection. |
450 | */ |
451 | error = in6_selectsrc(dstsock, optp, in6p->in6p_moptions, |
452 | &in6p->in6p_route, &in6p->in6p_laddr, &oifp, &psref, &ip6->ip6_src); |
453 | if (error != 0) |
454 | goto bad; |
455 | |
456 | if (oifp && scope_ambiguous) { |
457 | /* |
458 | * Application should provide a proper zone ID or the use of |
459 | * default zone IDs should be enabled. Unfortunately, some |
460 | * applications do not behave as it should, so we need a |
461 | * workaround. Even if an appropriate ID is not determined |
462 | * (when it's required), if we can determine the outgoing |
463 | * interface. determine the zone ID based on the interface. |
464 | */ |
465 | error = in6_setscope(&dstsock->sin6_addr, oifp, NULL); |
466 | if (error != 0) |
467 | goto bad; |
468 | } |
469 | ip6->ip6_dst = dstsock->sin6_addr; |
470 | |
471 | /* fill in the rest of the IPv6 header fields */ |
472 | ip6->ip6_flow = in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK; |
473 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; |
474 | ip6->ip6_vfc |= IPV6_VERSION; |
475 | /* ip6_plen will be filled in ip6_output, so not fill it here. */ |
476 | ip6->ip6_nxt = in6p->in6p_ip6.ip6_nxt; |
477 | ip6->ip6_hlim = in6_selecthlim(in6p, oifp); |
478 | |
479 | if_put(oifp, &psref); |
480 | oifp = NULL; |
481 | |
482 | if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || |
483 | in6p->in6p_cksum != -1) { |
484 | int off; |
485 | u_int16_t sum; |
486 | |
487 | /* compute checksum */ |
488 | if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) |
489 | off = offsetof(struct icmp6_hdr, icmp6_cksum); |
490 | else |
491 | off = in6p->in6p_cksum; |
492 | if (plen < off + 1) { |
493 | error = EINVAL; |
494 | goto bad; |
495 | } |
496 | off += sizeof(struct ip6_hdr); |
497 | |
498 | sum = 0; |
499 | m = m_copyback_cow(m, off, sizeof(sum), (void *)&sum, |
500 | M_DONTWAIT); |
501 | if (m == NULL) { |
502 | error = ENOBUFS; |
503 | goto bad; |
504 | } |
505 | sum = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); |
506 | m = m_copyback_cow(m, off, sizeof(sum), (void *)&sum, |
507 | M_DONTWAIT); |
508 | if (m == NULL) { |
509 | error = ENOBUFS; |
510 | goto bad; |
511 | } |
512 | } |
513 | |
514 | { |
515 | struct ifnet *ret_oifp = NULL; |
516 | |
517 | error = ip6_output(m, optp, &in6p->in6p_route, 0, |
518 | in6p->in6p_moptions, so, &ret_oifp); |
519 | if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { |
520 | if (ret_oifp) |
521 | icmp6_ifoutstat_inc(ret_oifp, type, code); |
522 | ICMP6_STATINC(ICMP6_STAT_OUTHIST + type); |
523 | } else |
524 | RIP6_STATINC(RIP6_STAT_OPACKETS); |
525 | } |
526 | |
527 | goto freectl; |
528 | |
529 | bad: |
530 | if (m) |
531 | m_freem(m); |
532 | |
533 | freectl: |
534 | if (control) { |
535 | ip6_clearpktopts(&opt, -1); |
536 | m_freem(control); |
537 | } |
538 | if_put(oifp, &psref); |
539 | curlwp_bindx(bound); |
540 | return error; |
541 | } |
542 | |
543 | /* |
544 | * Raw IPv6 socket option processing. |
545 | */ |
546 | int |
547 | rip6_ctloutput(int op, struct socket *so, struct sockopt *sopt) |
548 | { |
549 | int error = 0; |
550 | |
551 | if (sopt->sopt_level == SOL_SOCKET && sopt->sopt_name == SO_NOHEADER) { |
552 | int optval; |
553 | |
554 | /* need to fiddle w/ opt(IPPROTO_IPV6, IPV6_CHECKSUM)? */ |
555 | if (op == PRCO_GETOPT) { |
556 | optval = 1; |
557 | error = sockopt_set(sopt, &optval, sizeof(optval)); |
558 | } else if (op == PRCO_SETOPT) { |
559 | error = sockopt_getint(sopt, &optval); |
560 | if (error) |
561 | goto out; |
562 | if (optval == 0) |
563 | error = EINVAL; |
564 | } |
565 | |
566 | goto out; |
567 | } else if (sopt->sopt_level != IPPROTO_IPV6) |
568 | return ip6_ctloutput(op, so, sopt); |
569 | |
570 | switch (sopt->sopt_name) { |
571 | case MRT6_INIT: |
572 | case MRT6_DONE: |
573 | case MRT6_ADD_MIF: |
574 | case MRT6_DEL_MIF: |
575 | case MRT6_ADD_MFC: |
576 | case MRT6_DEL_MFC: |
577 | case MRT6_PIM: |
578 | if (op == PRCO_SETOPT) |
579 | error = ip6_mrouter_set(so, sopt); |
580 | else if (op == PRCO_GETOPT) |
581 | error = ip6_mrouter_get(so, sopt); |
582 | else |
583 | error = EINVAL; |
584 | break; |
585 | case IPV6_CHECKSUM: |
586 | return ip6_raw_ctloutput(op, so, sopt); |
587 | default: |
588 | return ip6_ctloutput(op, so, sopt); |
589 | } |
590 | out: |
591 | return error; |
592 | } |
593 | |
594 | extern u_long rip6_sendspace; |
595 | extern u_long rip6_recvspace; |
596 | |
597 | int |
598 | rip6_attach(struct socket *so, int proto) |
599 | { |
600 | struct in6pcb *in6p; |
601 | int s, error; |
602 | |
603 | KASSERT(sotoin6pcb(so) == NULL); |
604 | sosetlock(so); |
605 | |
606 | error = kauth_authorize_network(curlwp->l_cred, |
607 | KAUTH_NETWORK_SOCKET, KAUTH_REQ_NETWORK_SOCKET_RAWSOCK, |
608 | KAUTH_ARG(AF_INET6), |
609 | KAUTH_ARG(SOCK_RAW), |
610 | KAUTH_ARG(so->so_proto->pr_protocol)); |
611 | if (error) { |
612 | return error; |
613 | } |
614 | s = splsoftnet(); |
615 | error = soreserve(so, rip6_sendspace, rip6_recvspace); |
616 | if (error) { |
617 | splx(s); |
618 | return error; |
619 | } |
620 | if ((error = in6_pcballoc(so, &raw6cbtable)) != 0) { |
621 | splx(s); |
622 | return error; |
623 | } |
624 | splx(s); |
625 | in6p = sotoin6pcb(so); |
626 | in6p->in6p_ip6.ip6_nxt = proto; |
627 | in6p->in6p_cksum = -1; |
628 | |
629 | in6p->in6p_icmp6filt = kmem_alloc(sizeof(struct icmp6_filter), KM_SLEEP); |
630 | if (in6p->in6p_icmp6filt == NULL) { |
631 | in6_pcbdetach(in6p); |
632 | return ENOMEM; |
633 | } |
634 | ICMP6_FILTER_SETPASSALL(in6p->in6p_icmp6filt); |
635 | KASSERT(solocked(so)); |
636 | return error; |
637 | } |
638 | |
639 | static void |
640 | rip6_detach(struct socket *so) |
641 | { |
642 | struct in6pcb *in6p = sotoin6pcb(so); |
643 | |
644 | KASSERT(solocked(so)); |
645 | KASSERT(in6p != NULL); |
646 | |
647 | if (so == ip6_mrouter) { |
648 | ip6_mrouter_done(); |
649 | } |
650 | /* xxx: RSVP */ |
651 | if (in6p->in6p_icmp6filt != NULL) { |
652 | kmem_free(in6p->in6p_icmp6filt, sizeof(struct icmp6_filter)); |
653 | in6p->in6p_icmp6filt = NULL; |
654 | } |
655 | in6_pcbdetach(in6p); |
656 | } |
657 | |
658 | static int |
659 | rip6_accept(struct socket *so, struct sockaddr *nam) |
660 | { |
661 | KASSERT(solocked(so)); |
662 | |
663 | return EOPNOTSUPP; |
664 | } |
665 | |
666 | static int |
667 | rip6_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) |
668 | { |
669 | struct in6pcb *in6p = sotoin6pcb(so); |
670 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; |
671 | struct ifaddr *ifa = NULL; |
672 | int error = 0; |
673 | int s; |
674 | |
675 | KASSERT(solocked(so)); |
676 | KASSERT(in6p != NULL); |
677 | KASSERT(nam != NULL); |
678 | |
679 | if (addr->sin6_len != sizeof(*addr)) |
680 | return EINVAL; |
681 | if (IFNET_READER_EMPTY() || addr->sin6_family != AF_INET6) |
682 | return EADDRNOTAVAIL; |
683 | |
684 | if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) |
685 | return error; |
686 | |
687 | /* |
688 | * we don't support mapped address here, it would confuse |
689 | * users so reject it |
690 | */ |
691 | if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) |
692 | return EADDRNOTAVAIL; |
693 | s = pserialize_read_enter(); |
694 | if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && |
695 | (ifa = ifa_ifwithaddr(sin6tosa(addr))) == NULL) { |
696 | error = EADDRNOTAVAIL; |
697 | goto out; |
698 | } |
699 | if (ifa && (ifatoia6(ifa))->ia6_flags & |
700 | (IN6_IFF_ANYCAST | IN6_IFF_DUPLICATED)) { |
701 | error = EADDRNOTAVAIL; |
702 | goto out; |
703 | } |
704 | |
705 | in6p->in6p_laddr = addr->sin6_addr; |
706 | error = 0; |
707 | out: |
708 | pserialize_read_exit(s); |
709 | return error; |
710 | } |
711 | |
712 | static int |
713 | rip6_listen(struct socket *so, struct lwp *l) |
714 | { |
715 | KASSERT(solocked(so)); |
716 | |
717 | return EOPNOTSUPP; |
718 | } |
719 | |
720 | static int |
721 | rip6_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) |
722 | { |
723 | struct in6pcb *in6p = sotoin6pcb(so); |
724 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; |
725 | struct in6_addr in6a; |
726 | struct ifnet *ifp = NULL; |
727 | int scope_ambiguous = 0; |
728 | int error = 0; |
729 | struct psref psref; |
730 | int bound; |
731 | |
732 | KASSERT(solocked(so)); |
733 | KASSERT(in6p != NULL); |
734 | KASSERT(nam != NULL); |
735 | |
736 | if (IFNET_READER_EMPTY()) |
737 | return EADDRNOTAVAIL; |
738 | if (addr->sin6_family != AF_INET6) |
739 | return EAFNOSUPPORT; |
740 | |
741 | /* |
742 | * Application should provide a proper zone ID or the use of |
743 | * default zone IDs should be enabled. Unfortunately, some |
744 | * applications do not behave as it should, so we need a |
745 | * workaround. Even if an appropriate ID is not determined, |
746 | * we'll see if we can determine the outgoing interface. If we |
747 | * can, determine the zone ID based on the interface below. |
748 | */ |
749 | if (addr->sin6_scope_id == 0 && !ip6_use_defzone) |
750 | scope_ambiguous = 1; |
751 | if ((error = sa6_embedscope(addr, ip6_use_defzone)) != 0) |
752 | return error; |
753 | |
754 | bound = curlwp_bind(); |
755 | /* Source address selection. XXX: need pcblookup? */ |
756 | error = in6_selectsrc(addr, in6p->in6p_outputopts, |
757 | in6p->in6p_moptions, &in6p->in6p_route, |
758 | &in6p->in6p_laddr, &ifp, &psref, &in6a); |
759 | if (error != 0) |
760 | goto out; |
761 | /* XXX: see above */ |
762 | if (ifp && scope_ambiguous && |
763 | (error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) { |
764 | goto out; |
765 | } |
766 | in6p->in6p_laddr = in6a; |
767 | in6p->in6p_faddr = addr->sin6_addr; |
768 | soisconnected(so); |
769 | out: |
770 | if_put(ifp, &psref); |
771 | curlwp_bindx(bound); |
772 | return error; |
773 | } |
774 | |
775 | static int |
776 | rip6_connect2(struct socket *so, struct socket *so2) |
777 | { |
778 | KASSERT(solocked(so)); |
779 | |
780 | return EOPNOTSUPP; |
781 | } |
782 | |
783 | static int |
784 | rip6_disconnect(struct socket *so) |
785 | { |
786 | struct in6pcb *in6p = sotoin6pcb(so); |
787 | |
788 | KASSERT(solocked(so)); |
789 | KASSERT(in6p != NULL); |
790 | |
791 | if ((so->so_state & SS_ISCONNECTED) == 0) |
792 | return ENOTCONN; |
793 | |
794 | in6p->in6p_faddr = in6addr_any; |
795 | so->so_state &= ~SS_ISCONNECTED; /* XXX */ |
796 | return 0; |
797 | } |
798 | |
799 | static int |
800 | rip6_shutdown(struct socket *so) |
801 | { |
802 | KASSERT(solocked(so)); |
803 | |
804 | /* |
805 | * Mark the connection as being incapable of futther input. |
806 | */ |
807 | socantsendmore(so); |
808 | return 0; |
809 | } |
810 | |
811 | static int |
812 | rip6_abort(struct socket *so) |
813 | { |
814 | KASSERT(solocked(so)); |
815 | |
816 | soisdisconnected(so); |
817 | rip6_detach(so); |
818 | return 0; |
819 | } |
820 | |
821 | static int |
822 | rip6_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp) |
823 | { |
824 | return in6_control(so, cmd, nam, ifp); |
825 | } |
826 | |
827 | static int |
828 | rip6_stat(struct socket *so, struct stat *ub) |
829 | { |
830 | KASSERT(solocked(so)); |
831 | |
832 | /* stat: don't bother with a blocksize */ |
833 | return 0; |
834 | } |
835 | |
836 | static int |
837 | rip6_peeraddr(struct socket *so, struct sockaddr *nam) |
838 | { |
839 | KASSERT(solocked(so)); |
840 | KASSERT(sotoin6pcb(so) != NULL); |
841 | KASSERT(nam != NULL); |
842 | |
843 | in6_setpeeraddr(sotoin6pcb(so), (struct sockaddr_in6 *)nam); |
844 | return 0; |
845 | } |
846 | |
847 | static int |
848 | rip6_sockaddr(struct socket *so, struct sockaddr *nam) |
849 | { |
850 | KASSERT(solocked(so)); |
851 | KASSERT(sotoin6pcb(so) != NULL); |
852 | KASSERT(nam != NULL); |
853 | |
854 | in6_setsockaddr(sotoin6pcb(so), (struct sockaddr_in6 *)nam); |
855 | return 0; |
856 | } |
857 | |
858 | static int |
859 | rip6_rcvd(struct socket *so, int flags, struct lwp *l) |
860 | { |
861 | KASSERT(solocked(so)); |
862 | |
863 | return EOPNOTSUPP; |
864 | } |
865 | |
866 | static int |
867 | rip6_recvoob(struct socket *so, struct mbuf *m, int flags) |
868 | { |
869 | KASSERT(solocked(so)); |
870 | |
871 | return EOPNOTSUPP; |
872 | } |
873 | |
874 | static int |
875 | rip6_send(struct socket *so, struct mbuf *m, struct sockaddr *nam, |
876 | struct mbuf *control, struct lwp *l) |
877 | { |
878 | struct in6pcb *in6p = sotoin6pcb(so); |
879 | struct sockaddr_in6 tmp; |
880 | struct sockaddr_in6 *dst; |
881 | int error = 0; |
882 | |
883 | KASSERT(solocked(so)); |
884 | KASSERT(in6p != NULL); |
885 | KASSERT(m != NULL); |
886 | |
887 | /* |
888 | * Ship a packet out. The appropriate raw output |
889 | * routine handles any messaging necessary. |
890 | */ |
891 | |
892 | /* always copy sockaddr to avoid overwrites */ |
893 | if (so->so_state & SS_ISCONNECTED) { |
894 | if (nam) { |
895 | error = EISCONN; |
896 | goto release; |
897 | } |
898 | /* XXX */ |
899 | sockaddr_in6_init(&tmp, &in6p->in6p_faddr, 0, 0, 0); |
900 | dst = &tmp; |
901 | } else { |
902 | if (nam == NULL) { |
903 | error = ENOTCONN; |
904 | goto release; |
905 | } |
906 | tmp = *(struct sockaddr_in6 *)nam; |
907 | dst = &tmp; |
908 | |
909 | if (dst->sin6_family != AF_INET6) { |
910 | error = EAFNOSUPPORT; |
911 | goto release; |
912 | } |
913 | } |
914 | error = rip6_output(m, so, dst, control); |
915 | m = NULL; |
916 | |
917 | release: |
918 | if (m) |
919 | m_freem(m); |
920 | |
921 | return error; |
922 | } |
923 | |
924 | static int |
925 | rip6_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control) |
926 | { |
927 | KASSERT(solocked(so)); |
928 | |
929 | if (m) |
930 | m_freem(m); |
931 | |
932 | return EOPNOTSUPP; |
933 | } |
934 | |
935 | static int |
936 | rip6_purgeif(struct socket *so, struct ifnet *ifp) |
937 | { |
938 | |
939 | #ifndef NET_MPSAFE |
940 | mutex_enter(softnet_lock); |
941 | #endif |
942 | in6_pcbpurgeif0(&raw6cbtable, ifp); |
943 | in6_purgeif(ifp); |
944 | in6_pcbpurgeif(&raw6cbtable, ifp); |
945 | #ifndef NET_MPSAFE |
946 | mutex_exit(softnet_lock); |
947 | #endif |
948 | |
949 | return 0; |
950 | } |
951 | |
952 | static int |
953 | sysctl_net_inet6_raw6_stats(SYSCTLFN_ARGS) |
954 | { |
955 | |
956 | return (NETSTAT_SYSCTL(rip6stat_percpu, RIP6_NSTATS)); |
957 | } |
958 | |
959 | static void |
960 | sysctl_net_inet6_raw6_setup(struct sysctllog **clog) |
961 | { |
962 | |
963 | sysctl_createv(clog, 0, NULL, NULL, |
964 | CTLFLAG_PERMANENT, |
965 | CTLTYPE_NODE, "inet6" , NULL, |
966 | NULL, 0, NULL, 0, |
967 | CTL_NET, PF_INET6, CTL_EOL); |
968 | sysctl_createv(clog, 0, NULL, NULL, |
969 | CTLFLAG_PERMANENT, |
970 | CTLTYPE_NODE, "raw6" , |
971 | SYSCTL_DESCR("Raw IPv6 settings" ), |
972 | NULL, 0, NULL, 0, |
973 | CTL_NET, PF_INET6, IPPROTO_RAW, CTL_EOL); |
974 | |
975 | sysctl_createv(clog, 0, NULL, NULL, |
976 | CTLFLAG_PERMANENT, |
977 | CTLTYPE_STRUCT, "pcblist" , |
978 | SYSCTL_DESCR("Raw IPv6 control block list" ), |
979 | sysctl_inpcblist, 0, &raw6cbtable, 0, |
980 | CTL_NET, PF_INET6, IPPROTO_RAW, |
981 | CTL_CREATE, CTL_EOL); |
982 | sysctl_createv(clog, 0, NULL, NULL, |
983 | CTLFLAG_PERMANENT, |
984 | CTLTYPE_STRUCT, "stats" , |
985 | SYSCTL_DESCR("Raw IPv6 statistics" ), |
986 | sysctl_net_inet6_raw6_stats, 0, NULL, 0, |
987 | CTL_NET, PF_INET6, IPPROTO_RAW, RAW6CTL_STATS, |
988 | CTL_EOL); |
989 | } |
990 | |
991 | PR_WRAP_USRREQS(rip6) |
992 | #define rip6_attach rip6_attach_wrapper |
993 | #define rip6_detach rip6_detach_wrapper |
994 | #define rip6_accept rip6_accept_wrapper |
995 | #define rip6_bind rip6_bind_wrapper |
996 | #define rip6_listen rip6_listen_wrapper |
997 | #define rip6_connect rip6_connect_wrapper |
998 | #define rip6_connect2 rip6_connect2_wrapper |
999 | #define rip6_disconnect rip6_disconnect_wrapper |
1000 | #define rip6_shutdown rip6_shutdown_wrapper |
1001 | #define rip6_abort rip6_abort_wrapper |
1002 | #define rip6_ioctl rip6_ioctl_wrapper |
1003 | #define rip6_stat rip6_stat_wrapper |
1004 | #define rip6_peeraddr rip6_peeraddr_wrapper |
1005 | #define rip6_sockaddr rip6_sockaddr_wrapper |
1006 | #define rip6_rcvd rip6_rcvd_wrapper |
1007 | #define rip6_recvoob rip6_recvoob_wrapper |
1008 | #define rip6_send rip6_send_wrapper |
1009 | #define rip6_sendoob rip6_sendoob_wrapper |
1010 | #define rip6_purgeif rip6_purgeif_wrapper |
1011 | |
1012 | const struct pr_usrreqs rip6_usrreqs = { |
1013 | .pr_attach = rip6_attach, |
1014 | .pr_detach = rip6_detach, |
1015 | .pr_accept = rip6_accept, |
1016 | .pr_bind = rip6_bind, |
1017 | .pr_listen = rip6_listen, |
1018 | .pr_connect = rip6_connect, |
1019 | .pr_connect2 = rip6_connect2, |
1020 | .pr_disconnect = rip6_disconnect, |
1021 | .pr_shutdown = rip6_shutdown, |
1022 | .pr_abort = rip6_abort, |
1023 | .pr_ioctl = rip6_ioctl, |
1024 | .pr_stat = rip6_stat, |
1025 | .pr_peeraddr = rip6_peeraddr, |
1026 | .pr_sockaddr = rip6_sockaddr, |
1027 | .pr_rcvd = rip6_rcvd, |
1028 | .pr_recvoob = rip6_recvoob, |
1029 | .pr_send = rip6_send, |
1030 | .pr_sendoob = rip6_sendoob, |
1031 | .pr_purgeif = rip6_purgeif, |
1032 | }; |
1033 | |