1 | /* $NetBSD: icmp6.c,v 1.201 2016/11/15 20:50:28 mlelstv Exp $ */ |
2 | /* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 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 | * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 |
62 | */ |
63 | |
64 | #include <sys/cdefs.h> |
65 | __KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.201 2016/11/15 20:50:28 mlelstv Exp $" ); |
66 | |
67 | #ifdef _KERNEL_OPT |
68 | #include "opt_inet.h" |
69 | #include "opt_ipsec.h" |
70 | #endif |
71 | |
72 | #include <sys/param.h> |
73 | #include <sys/systm.h> |
74 | #include <sys/kmem.h> |
75 | #include <sys/mbuf.h> |
76 | #include <sys/protosw.h> |
77 | #include <sys/socket.h> |
78 | #include <sys/socketvar.h> |
79 | #include <sys/time.h> |
80 | #include <sys/kernel.h> |
81 | #include <sys/syslog.h> |
82 | #include <sys/domain.h> |
83 | #include <sys/sysctl.h> |
84 | |
85 | #include <net/if.h> |
86 | #include <net/route.h> |
87 | #include <net/if_dl.h> |
88 | #include <net/if_types.h> |
89 | |
90 | #include <netinet/in.h> |
91 | #include <netinet/in_var.h> |
92 | #include <netinet/ip6.h> |
93 | #include <netinet6/ip6_var.h> |
94 | #include <netinet6/ip6_private.h> |
95 | #include <netinet/icmp6.h> |
96 | #include <netinet6/icmp6_private.h> |
97 | #include <netinet6/mld6_var.h> |
98 | #include <netinet6/in6_pcb.h> |
99 | #include <netinet6/nd6.h> |
100 | #include <netinet6/in6_ifattach.h> |
101 | #include <netinet6/ip6protosw.h> |
102 | #include <netinet6/scope6_var.h> |
103 | |
104 | #ifdef IPSEC |
105 | #include <netipsec/ipsec.h> |
106 | #include <netipsec/key.h> |
107 | #endif |
108 | |
109 | |
110 | #include "faith.h" |
111 | #if defined(NFAITH) && 0 < NFAITH |
112 | #include <net/if_faith.h> |
113 | #endif |
114 | |
115 | #include <net/net_osdep.h> |
116 | |
117 | extern struct domain inet6domain; |
118 | |
119 | percpu_t *icmp6stat_percpu; |
120 | |
121 | extern struct inpcbtable raw6cbtable; |
122 | extern int icmp6errppslim; |
123 | static int icmp6errpps_count = 0; |
124 | static struct timeval icmp6errppslim_last; |
125 | extern int icmp6_nodeinfo; |
126 | |
127 | /* |
128 | * List of callbacks to notify when Path MTU changes are made. |
129 | */ |
130 | struct icmp6_mtudisc_callback { |
131 | LIST_ENTRY(icmp6_mtudisc_callback) mc_list; |
132 | void (*mc_func)(struct in6_addr *); |
133 | }; |
134 | |
135 | LIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks = |
136 | LIST_HEAD_INITIALIZER(&icmp6_mtudisc_callbacks); |
137 | |
138 | static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL; |
139 | extern int pmtu_expire; |
140 | |
141 | /* XXX do these values make any sense? */ |
142 | static int icmp6_mtudisc_hiwat = 1280; |
143 | static int icmp6_mtudisc_lowat = 256; |
144 | |
145 | /* |
146 | * keep track of # of redirect routes. |
147 | */ |
148 | static struct rttimer_queue *icmp6_redirect_timeout_q = NULL; |
149 | |
150 | /* XXX experimental, turned off */ |
151 | static int icmp6_redirect_hiwat = -1; |
152 | static int icmp6_redirect_lowat = -1; |
153 | |
154 | static void icmp6_errcount(u_int, int, int); |
155 | static int icmp6_rip6_input(struct mbuf **, int); |
156 | static int icmp6_ratelimit(const struct in6_addr *, const int, const int); |
157 | static const char *icmp6_redirect_diag(struct in6_addr *, |
158 | struct in6_addr *, struct in6_addr *); |
159 | static struct mbuf *ni6_input(struct mbuf *, int); |
160 | static struct mbuf *ni6_nametodns(const char *, int, int); |
161 | static int ni6_dnsmatch(const char *, int, const char *, int); |
162 | static int ni6_addrs(struct icmp6_nodeinfo *, struct mbuf *, |
163 | struct ifnet **, char *, struct psref *); |
164 | static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, |
165 | struct ifnet *, int); |
166 | static int icmp6_notify_error(struct mbuf *, int, int, int); |
167 | static struct rtentry *icmp6_mtudisc_clone(struct sockaddr *); |
168 | static void icmp6_mtudisc_timeout(struct rtentry *, struct rttimer *); |
169 | static void icmp6_redirect_timeout(struct rtentry *, struct rttimer *); |
170 | static void sysctl_net_inet6_icmp6_setup(struct sysctllog **); |
171 | |
172 | |
173 | void |
174 | icmp6_init(void) |
175 | { |
176 | |
177 | sysctl_net_inet6_icmp6_setup(NULL); |
178 | mld_init(); |
179 | icmp6_mtudisc_timeout_q = rt_timer_queue_create(pmtu_expire); |
180 | icmp6_redirect_timeout_q = rt_timer_queue_create(icmp6_redirtimeout); |
181 | |
182 | icmp6stat_percpu = percpu_alloc(sizeof(uint64_t) * ICMP6_NSTATS); |
183 | } |
184 | |
185 | static void |
186 | icmp6_errcount(u_int base, int type, int code) |
187 | { |
188 | switch (type) { |
189 | case ICMP6_DST_UNREACH: |
190 | switch (code) { |
191 | case ICMP6_DST_UNREACH_NOROUTE: |
192 | ICMP6_STATINC(base + ICMP6_ERRSTAT_DST_UNREACH_NOROUTE); |
193 | return; |
194 | case ICMP6_DST_UNREACH_ADMIN: |
195 | ICMP6_STATINC(base + ICMP6_ERRSTAT_DST_UNREACH_ADMIN); |
196 | return; |
197 | case ICMP6_DST_UNREACH_BEYONDSCOPE: |
198 | ICMP6_STATINC(base + |
199 | ICMP6_ERRSTAT_DST_UNREACH_BEYONDSCOPE); |
200 | return; |
201 | case ICMP6_DST_UNREACH_ADDR: |
202 | ICMP6_STATINC(base + ICMP6_ERRSTAT_DST_UNREACH_ADDR); |
203 | return; |
204 | case ICMP6_DST_UNREACH_NOPORT: |
205 | ICMP6_STATINC(base + ICMP6_ERRSTAT_DST_UNREACH_NOPORT); |
206 | return; |
207 | } |
208 | break; |
209 | case ICMP6_PACKET_TOO_BIG: |
210 | ICMP6_STATINC(base + ICMP6_ERRSTAT_PACKET_TOO_BIG); |
211 | return; |
212 | case ICMP6_TIME_EXCEEDED: |
213 | switch (code) { |
214 | case ICMP6_TIME_EXCEED_TRANSIT: |
215 | ICMP6_STATINC(base + ICMP6_ERRSTAT_TIME_EXCEED_TRANSIT); |
216 | return; |
217 | case ICMP6_TIME_EXCEED_REASSEMBLY: |
218 | ICMP6_STATINC(base + |
219 | ICMP6_ERRSTAT_TIME_EXCEED_REASSEMBLY); |
220 | return; |
221 | } |
222 | break; |
223 | case ICMP6_PARAM_PROB: |
224 | switch (code) { |
225 | case ICMP6_PARAMPROB_HEADER: |
226 | ICMP6_STATINC(base + ICMP6_ERRSTAT_PARAMPROB_HEADER); |
227 | return; |
228 | case ICMP6_PARAMPROB_NEXTHEADER: |
229 | ICMP6_STATINC(base + |
230 | ICMP6_ERRSTAT_PARAMPROB_NEXTHEADER); |
231 | return; |
232 | case ICMP6_PARAMPROB_OPTION: |
233 | ICMP6_STATINC(base + ICMP6_ERRSTAT_PARAMPROB_OPTION); |
234 | return; |
235 | } |
236 | break; |
237 | case ND_REDIRECT: |
238 | ICMP6_STATINC(base + ICMP6_ERRSTAT_REDIRECT); |
239 | return; |
240 | } |
241 | ICMP6_STATINC(base + ICMP6_ERRSTAT_UNKNOWN); |
242 | } |
243 | |
244 | /* |
245 | * Register a Path MTU Discovery callback. |
246 | */ |
247 | void |
248 | icmp6_mtudisc_callback_register(void (*func)(struct in6_addr *)) |
249 | { |
250 | struct icmp6_mtudisc_callback *mc; |
251 | |
252 | for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL; |
253 | mc = LIST_NEXT(mc, mc_list)) { |
254 | if (mc->mc_func == func) |
255 | return; |
256 | } |
257 | |
258 | mc = kmem_alloc(sizeof(*mc), KM_SLEEP); |
259 | mc->mc_func = func; |
260 | LIST_INSERT_HEAD(&icmp6_mtudisc_callbacks, mc, mc_list); |
261 | } |
262 | |
263 | /* |
264 | * A wrapper function for icmp6_error() necessary when the erroneous packet |
265 | * may not contain enough scope zone information. |
266 | */ |
267 | void |
268 | icmp6_error2(struct mbuf *m, int type, int code, int param, |
269 | struct ifnet *ifp) |
270 | { |
271 | struct ip6_hdr *ip6; |
272 | |
273 | if (ifp == NULL) |
274 | return; |
275 | |
276 | if (m->m_len < sizeof(struct ip6_hdr)) { |
277 | m = m_pullup(m, sizeof(struct ip6_hdr)); |
278 | if (m == NULL) |
279 | return; |
280 | } |
281 | |
282 | ip6 = mtod(m, struct ip6_hdr *); |
283 | |
284 | if (in6_setscope(&ip6->ip6_src, ifp, NULL) != 0) |
285 | return; |
286 | if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) |
287 | return; |
288 | |
289 | icmp6_error(m, type, code, param); |
290 | } |
291 | |
292 | /* |
293 | * Generate an error packet of type error in response to bad IP6 packet. |
294 | */ |
295 | void |
296 | icmp6_error(struct mbuf *m, int type, int code, int param) |
297 | { |
298 | struct ip6_hdr *oip6, *nip6; |
299 | struct icmp6_hdr *icmp6; |
300 | u_int preplen; |
301 | int off; |
302 | int nxt; |
303 | |
304 | ICMP6_STATINC(ICMP6_STAT_ERROR); |
305 | |
306 | /* count per-type-code statistics */ |
307 | icmp6_errcount(ICMP6_STAT_OUTERRHIST, type, code); |
308 | |
309 | if (m->m_flags & M_DECRYPTED) { |
310 | ICMP6_STATINC(ICMP6_STAT_CANTERROR); |
311 | goto freeit; |
312 | } |
313 | |
314 | if (M_UNWRITABLE(m, sizeof(struct ip6_hdr)) && |
315 | (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) |
316 | return; |
317 | oip6 = mtod(m, struct ip6_hdr *); |
318 | |
319 | /* |
320 | * If the destination address of the erroneous packet is a multicast |
321 | * address, or the packet was sent using link-layer multicast, |
322 | * we should basically suppress sending an error (RFC 2463, Section |
323 | * 2.4). |
324 | * We have two exceptions (the item e.2 in that section): |
325 | * - the Pakcet Too Big message can be sent for path MTU discovery. |
326 | * - the Parameter Problem Message that can be allowed an icmp6 error |
327 | * in the option type field. This check has been done in |
328 | * ip6_unknown_opt(), so we can just check the type and code. |
329 | */ |
330 | if ((m->m_flags & (M_BCAST|M_MCAST) || |
331 | IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && |
332 | (type != ICMP6_PACKET_TOO_BIG && |
333 | (type != ICMP6_PARAM_PROB || |
334 | code != ICMP6_PARAMPROB_OPTION))) |
335 | goto freeit; |
336 | |
337 | /* |
338 | * RFC 2463, 2.4 (e.5): source address check. |
339 | * XXX: the case of anycast source? |
340 | */ |
341 | if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || |
342 | IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) |
343 | goto freeit; |
344 | |
345 | /* |
346 | * If we are about to send ICMPv6 against ICMPv6 error/redirect, |
347 | * don't do it. |
348 | */ |
349 | nxt = -1; |
350 | off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); |
351 | if (off >= 0 && nxt == IPPROTO_ICMPV6) { |
352 | struct icmp6_hdr *icp; |
353 | |
354 | IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off, |
355 | sizeof(*icp)); |
356 | if (icp == NULL) { |
357 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
358 | return; |
359 | } |
360 | if (icp->icmp6_type < ICMP6_ECHO_REQUEST || |
361 | icp->icmp6_type == ND_REDIRECT) { |
362 | /* |
363 | * ICMPv6 error |
364 | * Special case: for redirect (which is |
365 | * informational) we must not send icmp6 error. |
366 | */ |
367 | ICMP6_STATINC(ICMP6_STAT_CANTERROR); |
368 | goto freeit; |
369 | } else { |
370 | /* ICMPv6 informational - send the error */ |
371 | } |
372 | } |
373 | #if 0 /* controversial */ |
374 | else if (off >= 0 && nxt == IPPROTO_ESP) { |
375 | /* |
376 | * It could be ICMPv6 error inside ESP. Take a safer side, |
377 | * don't respond. |
378 | */ |
379 | ICMP6_STATINC(ICMP6_STAT_CANTERROR); |
380 | goto freeit; |
381 | } |
382 | #endif |
383 | else { |
384 | /* non-ICMPv6 - send the error */ |
385 | } |
386 | |
387 | oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ |
388 | |
389 | /* Finally, do rate limitation check. */ |
390 | if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { |
391 | ICMP6_STATINC(ICMP6_STAT_TOOFREQ); |
392 | goto freeit; |
393 | } |
394 | |
395 | /* |
396 | * OK, ICMP6 can be generated. |
397 | */ |
398 | |
399 | if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) |
400 | m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); |
401 | |
402 | preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); |
403 | M_PREPEND(m, preplen, M_DONTWAIT); |
404 | if (m && M_UNWRITABLE(m, preplen)) |
405 | m = m_pullup(m, preplen); |
406 | if (m == NULL) { |
407 | nd6log(LOG_DEBUG, "ENOBUFS in icmp6_error %d\n" , __LINE__); |
408 | return; |
409 | } |
410 | |
411 | nip6 = mtod(m, struct ip6_hdr *); |
412 | nip6->ip6_src = oip6->ip6_src; |
413 | nip6->ip6_dst = oip6->ip6_dst; |
414 | |
415 | in6_clearscope(&oip6->ip6_src); |
416 | in6_clearscope(&oip6->ip6_dst); |
417 | |
418 | icmp6 = (struct icmp6_hdr *)(nip6 + 1); |
419 | icmp6->icmp6_type = type; |
420 | icmp6->icmp6_code = code; |
421 | icmp6->icmp6_pptr = htonl((u_int32_t)param); |
422 | |
423 | /* |
424 | * icmp6_reflect() is designed to be in the input path. |
425 | * icmp6_error() can be called from both input and output path, |
426 | * and if we are in output path rcvif could contain bogus value. |
427 | * clear m->m_pkthdr.rcvif for safety, we should have enough scope |
428 | * information in ip header (nip6). |
429 | */ |
430 | m_reset_rcvif(m); |
431 | |
432 | ICMP6_STATINC(ICMP6_STAT_OUTHIST + type); |
433 | icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */ |
434 | |
435 | return; |
436 | |
437 | freeit: |
438 | /* |
439 | * If we can't tell whether or not we can generate ICMP6, free it. |
440 | */ |
441 | m_freem(m); |
442 | } |
443 | |
444 | /* |
445 | * Process a received ICMP6 message. |
446 | */ |
447 | int |
448 | icmp6_input(struct mbuf **mp, int *offp, int proto) |
449 | { |
450 | struct mbuf *m = *mp, *n; |
451 | struct ip6_hdr *ip6, *nip6; |
452 | struct icmp6_hdr *icmp6, *nicmp6; |
453 | int off = *offp; |
454 | int icmp6len = m->m_pkthdr.len - *offp; |
455 | int code, sum, noff; |
456 | struct ifnet *rcvif; |
457 | struct psref psref; |
458 | |
459 | rcvif = m_get_rcvif_psref(m, &psref); |
460 | if (__predict_false(rcvif == NULL)) |
461 | goto freeit; |
462 | |
463 | #define ICMP6_MAXLEN (sizeof(*nip6) + sizeof(*nicmp6) + 4) |
464 | KASSERT(ICMP6_MAXLEN < MCLBYTES); |
465 | icmp6_ifstat_inc(rcvif, ifs6_in_msg); |
466 | |
467 | /* |
468 | * Locate icmp6 structure in mbuf, and check |
469 | * that not corrupted and of at least minimum length |
470 | */ |
471 | |
472 | if (icmp6len < sizeof(struct icmp6_hdr)) { |
473 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
474 | icmp6_ifstat_inc(rcvif, ifs6_in_error); |
475 | goto freeit; |
476 | } |
477 | |
478 | ip6 = mtod(m, struct ip6_hdr *); |
479 | IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); |
480 | if (icmp6 == NULL) { |
481 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
482 | icmp6_ifstat_inc(rcvif, ifs6_in_error); |
483 | goto freeit; |
484 | } |
485 | /* |
486 | * Enforce alignment requirements that are violated in |
487 | * some cases, see kern/50766 for details. |
488 | */ |
489 | if (IP6_HDR_ALIGNED_P(icmp6) == 0) { |
490 | m = m_copyup(m, off + sizeof(struct icmp6_hdr), 0); |
491 | if (m == NULL) { |
492 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
493 | icmp6_ifstat_inc(rcvif, ifs6_in_error); |
494 | goto freeit; |
495 | } |
496 | ip6 = mtod(m, struct ip6_hdr *); |
497 | icmp6 = (struct icmp6_hdr *)(ip6 + 1); |
498 | } |
499 | KASSERT(IP6_HDR_ALIGNED_P(icmp6)); |
500 | |
501 | /* |
502 | * calculate the checksum |
503 | */ |
504 | if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { |
505 | nd6log(LOG_ERR, "ICMP6 checksum error(%d|%x) %s\n" , |
506 | icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)); |
507 | ICMP6_STATINC(ICMP6_STAT_CHECKSUM); |
508 | icmp6_ifstat_inc(rcvif, ifs6_in_error); |
509 | goto freeit; |
510 | } |
511 | |
512 | #if defined(NFAITH) && 0 < NFAITH |
513 | if (faithprefix(&ip6->ip6_dst)) { |
514 | /* |
515 | * Deliver very specific ICMP6 type only. |
516 | * This is important to deliver TOOBIG. Otherwise PMTUD |
517 | * will not work. |
518 | */ |
519 | switch (icmp6->icmp6_type) { |
520 | case ICMP6_DST_UNREACH: |
521 | case ICMP6_PACKET_TOO_BIG: |
522 | case ICMP6_TIME_EXCEEDED: |
523 | break; |
524 | default: |
525 | goto freeit; |
526 | } |
527 | } |
528 | #endif |
529 | |
530 | code = icmp6->icmp6_code; |
531 | ICMP6_STATINC(ICMP6_STAT_INHIST + icmp6->icmp6_type); |
532 | |
533 | switch (icmp6->icmp6_type) { |
534 | case ICMP6_DST_UNREACH: |
535 | icmp6_ifstat_inc(rcvif, ifs6_in_dstunreach); |
536 | switch (code) { |
537 | case ICMP6_DST_UNREACH_NOROUTE: |
538 | code = PRC_UNREACH_NET; |
539 | break; |
540 | case ICMP6_DST_UNREACH_ADMIN: |
541 | icmp6_ifstat_inc(rcvif, ifs6_in_adminprohib); |
542 | code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ |
543 | break; |
544 | case ICMP6_DST_UNREACH_ADDR: |
545 | code = PRC_HOSTDEAD; |
546 | break; |
547 | #ifdef COMPAT_RFC1885 |
548 | case ICMP6_DST_UNREACH_NOTNEIGHBOR: |
549 | code = PRC_UNREACH_SRCFAIL; |
550 | break; |
551 | #else |
552 | case ICMP6_DST_UNREACH_BEYONDSCOPE: |
553 | /* I mean "source address was incorrect." */ |
554 | code = PRC_UNREACH_NET; |
555 | break; |
556 | #endif |
557 | case ICMP6_DST_UNREACH_NOPORT: |
558 | code = PRC_UNREACH_PORT; |
559 | break; |
560 | default: |
561 | goto badcode; |
562 | } |
563 | goto deliver; |
564 | |
565 | case ICMP6_PACKET_TOO_BIG: |
566 | icmp6_ifstat_inc(rcvif, ifs6_in_pkttoobig); |
567 | |
568 | /* |
569 | * MTU is checked in icmp6_mtudisc. |
570 | */ |
571 | code = PRC_MSGSIZE; |
572 | |
573 | /* |
574 | * Updating the path MTU will be done after examining |
575 | * intermediate extension headers. |
576 | */ |
577 | goto deliver; |
578 | |
579 | case ICMP6_TIME_EXCEEDED: |
580 | icmp6_ifstat_inc(rcvif, ifs6_in_timeexceed); |
581 | switch (code) { |
582 | case ICMP6_TIME_EXCEED_TRANSIT: |
583 | code = PRC_TIMXCEED_INTRANS; |
584 | break; |
585 | case ICMP6_TIME_EXCEED_REASSEMBLY: |
586 | code = PRC_TIMXCEED_REASS; |
587 | break; |
588 | default: |
589 | goto badcode; |
590 | } |
591 | goto deliver; |
592 | |
593 | case ICMP6_PARAM_PROB: |
594 | icmp6_ifstat_inc(rcvif, ifs6_in_paramprob); |
595 | switch (code) { |
596 | case ICMP6_PARAMPROB_NEXTHEADER: |
597 | code = PRC_UNREACH_PROTOCOL; |
598 | break; |
599 | case ICMP6_PARAMPROB_HEADER: |
600 | case ICMP6_PARAMPROB_OPTION: |
601 | code = PRC_PARAMPROB; |
602 | break; |
603 | default: |
604 | goto badcode; |
605 | } |
606 | goto deliver; |
607 | |
608 | case ICMP6_ECHO_REQUEST: |
609 | icmp6_ifstat_inc(rcvif, ifs6_in_echo); |
610 | if (code != 0) |
611 | goto badcode; |
612 | /* |
613 | * Copy mbuf to send to two data paths: userland socket(s), |
614 | * and to the querier (echo reply). |
615 | * m: a copy for socket, n: a copy for querier |
616 | * |
617 | * If the first mbuf is shared, or the first mbuf is too short, |
618 | * copy the first part of the data into a fresh mbuf. |
619 | * Otherwise, we will wrongly overwrite both copies. |
620 | */ |
621 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
622 | /* Give up local */ |
623 | n = m; |
624 | m = NULL; |
625 | } else if (M_READONLY(n) || |
626 | n->m_len < off + sizeof(struct icmp6_hdr)) { |
627 | struct mbuf *n0 = n; |
628 | |
629 | /* |
630 | * Prepare an internal mbuf. m_pullup() doesn't |
631 | * always copy the length we specified. |
632 | */ |
633 | if ((n = m_dup(n0, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
634 | /* Give up local */ |
635 | n = m; |
636 | m = NULL; |
637 | } |
638 | m_freem(n0); |
639 | } |
640 | IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off, |
641 | sizeof(*nicmp6)); |
642 | if (nicmp6 == NULL) |
643 | goto freeit; |
644 | nicmp6->icmp6_type = ICMP6_ECHO_REPLY; |
645 | nicmp6->icmp6_code = 0; |
646 | if (n) { |
647 | uint64_t *icmp6s = ICMP6_STAT_GETREF(); |
648 | icmp6s[ICMP6_STAT_REFLECT]++; |
649 | icmp6s[ICMP6_STAT_OUTHIST + ICMP6_ECHO_REPLY]++; |
650 | ICMP6_STAT_PUTREF(); |
651 | icmp6_reflect(n, off); |
652 | } |
653 | if (!m) |
654 | goto freeit; |
655 | break; |
656 | |
657 | case ICMP6_ECHO_REPLY: |
658 | icmp6_ifstat_inc(rcvif, ifs6_in_echoreply); |
659 | if (code != 0) |
660 | goto badcode; |
661 | break; |
662 | |
663 | case MLD_LISTENER_QUERY: |
664 | case MLD_LISTENER_REPORT: |
665 | if (icmp6len < sizeof(struct mld_hdr)) |
666 | goto badlen; |
667 | if (icmp6->icmp6_type == MLD_LISTENER_QUERY) /* XXX: ugly... */ |
668 | icmp6_ifstat_inc(rcvif, ifs6_in_mldquery); |
669 | else |
670 | icmp6_ifstat_inc(rcvif, ifs6_in_mldreport); |
671 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
672 | /* give up local */ |
673 | mld_input(m, off); |
674 | m = NULL; |
675 | goto freeit; |
676 | } |
677 | mld_input(n, off); |
678 | /* m stays. */ |
679 | break; |
680 | |
681 | case MLD_LISTENER_DONE: |
682 | icmp6_ifstat_inc(rcvif, ifs6_in_mlddone); |
683 | if (icmp6len < sizeof(struct mld_hdr)) /* necessary? */ |
684 | goto badlen; |
685 | break; /* nothing to be done in kernel */ |
686 | |
687 | case MLD_MTRACE_RESP: |
688 | case MLD_MTRACE: |
689 | /* XXX: these two are experimental. not officially defined. */ |
690 | /* XXX: per-interface statistics? */ |
691 | break; /* just pass it to applications */ |
692 | |
693 | case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ |
694 | { |
695 | enum { WRU, FQDN } mode; |
696 | |
697 | if (!icmp6_nodeinfo) |
698 | break; |
699 | |
700 | if (icmp6len == sizeof(struct icmp6_hdr) + 4) |
701 | mode = WRU; |
702 | else if (icmp6len >= sizeof(struct icmp6_nodeinfo)) |
703 | mode = FQDN; |
704 | else |
705 | goto badlen; |
706 | |
707 | if (mode == FQDN) { |
708 | n = m_copym(m, 0, M_COPYALL, M_DONTWAIT); |
709 | if (n) |
710 | n = ni6_input(n, off); |
711 | /* XXX meaningless if n == NULL */ |
712 | noff = sizeof(struct ip6_hdr); |
713 | } else { |
714 | u_char *p; |
715 | int maxhlen; |
716 | |
717 | if ((icmp6_nodeinfo & 5) != 5) |
718 | break; |
719 | |
720 | if (code != 0) |
721 | goto badcode; |
722 | MGETHDR(n, M_DONTWAIT, m->m_type); |
723 | if (n && ICMP6_MAXLEN > MHLEN) { |
724 | MCLGET(n, M_DONTWAIT); |
725 | if ((n->m_flags & M_EXT) == 0) { |
726 | m_free(n); |
727 | n = NULL; |
728 | } |
729 | } |
730 | if (n == NULL) { |
731 | /* Give up remote */ |
732 | break; |
733 | } |
734 | m_reset_rcvif(n); |
735 | n->m_len = 0; |
736 | maxhlen = M_TRAILINGSPACE(n) - ICMP6_MAXLEN; |
737 | if (maxhlen < 0) |
738 | break; |
739 | if (maxhlen > hostnamelen) |
740 | maxhlen = hostnamelen; |
741 | /* |
742 | * Copy IPv6 and ICMPv6 only. |
743 | */ |
744 | nip6 = mtod(n, struct ip6_hdr *); |
745 | bcopy(ip6, nip6, sizeof(struct ip6_hdr)); |
746 | nicmp6 = (struct icmp6_hdr *)(nip6 + 1); |
747 | bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); |
748 | p = (u_char *)(nicmp6 + 1); |
749 | memset(p, 0, 4); |
750 | bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */ |
751 | noff = sizeof(struct ip6_hdr); |
752 | M_COPY_PKTHDR(n, m); /* just for rcvif */ |
753 | n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + |
754 | sizeof(struct icmp6_hdr) + 4 + maxhlen; |
755 | nicmp6->icmp6_type = ICMP6_WRUREPLY; |
756 | nicmp6->icmp6_code = 0; |
757 | } |
758 | #undef hostnamelen |
759 | if (n) { |
760 | uint64_t *icmp6s = ICMP6_STAT_GETREF(); |
761 | icmp6s[ICMP6_STAT_REFLECT]++; |
762 | icmp6s[ICMP6_STAT_OUTHIST + ICMP6_WRUREPLY]++; |
763 | ICMP6_STAT_PUTREF(); |
764 | icmp6_reflect(n, noff); |
765 | } |
766 | break; |
767 | } |
768 | |
769 | case ICMP6_WRUREPLY: |
770 | if (code != 0) |
771 | goto badcode; |
772 | break; |
773 | |
774 | case ND_ROUTER_SOLICIT: |
775 | icmp6_ifstat_inc(rcvif, ifs6_in_routersolicit); |
776 | if (code != 0) |
777 | goto badcode; |
778 | if (icmp6len < sizeof(struct nd_router_solicit)) |
779 | goto badlen; |
780 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
781 | /* give up local */ |
782 | nd6_rs_input(m, off, icmp6len); |
783 | m = NULL; |
784 | goto freeit; |
785 | } |
786 | nd6_rs_input(n, off, icmp6len); |
787 | /* m stays. */ |
788 | break; |
789 | |
790 | case ND_ROUTER_ADVERT: |
791 | icmp6_ifstat_inc(rcvif, ifs6_in_routeradvert); |
792 | if (code != 0) |
793 | goto badcode; |
794 | if (icmp6len < sizeof(struct nd_router_advert)) |
795 | goto badlen; |
796 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
797 | /* give up local */ |
798 | nd6_ra_input(m, off, icmp6len); |
799 | m = NULL; |
800 | goto freeit; |
801 | } |
802 | nd6_ra_input(n, off, icmp6len); |
803 | /* m stays. */ |
804 | break; |
805 | |
806 | case ND_NEIGHBOR_SOLICIT: |
807 | icmp6_ifstat_inc(rcvif, ifs6_in_neighborsolicit); |
808 | if (code != 0) |
809 | goto badcode; |
810 | if (icmp6len < sizeof(struct nd_neighbor_solicit)) |
811 | goto badlen; |
812 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
813 | /* give up local */ |
814 | nd6_ns_input(m, off, icmp6len); |
815 | m = NULL; |
816 | goto freeit; |
817 | } |
818 | nd6_ns_input(n, off, icmp6len); |
819 | /* m stays. */ |
820 | break; |
821 | |
822 | case ND_NEIGHBOR_ADVERT: |
823 | icmp6_ifstat_inc(rcvif, ifs6_in_neighboradvert); |
824 | if (code != 0) |
825 | goto badcode; |
826 | if (icmp6len < sizeof(struct nd_neighbor_advert)) |
827 | goto badlen; |
828 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
829 | /* give up local */ |
830 | nd6_na_input(m, off, icmp6len); |
831 | m = NULL; |
832 | goto freeit; |
833 | } |
834 | nd6_na_input(n, off, icmp6len); |
835 | /* m stays. */ |
836 | break; |
837 | |
838 | case ND_REDIRECT: |
839 | icmp6_ifstat_inc(rcvif, ifs6_in_redirect); |
840 | if (code != 0) |
841 | goto badcode; |
842 | if (icmp6len < sizeof(struct nd_redirect)) |
843 | goto badlen; |
844 | if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { |
845 | /* give up local */ |
846 | icmp6_redirect_input(m, off); |
847 | m = NULL; |
848 | goto freeit; |
849 | } |
850 | icmp6_redirect_input(n, off); |
851 | /* m stays. */ |
852 | break; |
853 | |
854 | case ICMP6_ROUTER_RENUMBERING: |
855 | if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && |
856 | code != ICMP6_ROUTER_RENUMBERING_RESULT) |
857 | goto badcode; |
858 | if (icmp6len < sizeof(struct icmp6_router_renum)) |
859 | goto badlen; |
860 | break; |
861 | |
862 | default: |
863 | nd6log(LOG_DEBUG, "unknown type %d(src=%s, dst=%s, ifid=%d)\n" , |
864 | icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), |
865 | ip6_sprintf(&ip6->ip6_dst), |
866 | rcvif ? rcvif->if_index : 0); |
867 | if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { |
868 | /* ICMPv6 error: MUST deliver it by spec... */ |
869 | code = PRC_NCMDS; |
870 | /* deliver */ |
871 | } else { |
872 | /* ICMPv6 informational: MUST not deliver */ |
873 | break; |
874 | } |
875 | deliver: |
876 | if (icmp6_notify_error(m, off, icmp6len, code)) { |
877 | /* In this case, m should've been freed. */ |
878 | m_put_rcvif_psref(rcvif, &psref); |
879 | return (IPPROTO_DONE); |
880 | } |
881 | break; |
882 | |
883 | badcode: |
884 | ICMP6_STATINC(ICMP6_STAT_BADCODE); |
885 | break; |
886 | |
887 | badlen: |
888 | ICMP6_STATINC(ICMP6_STAT_BADLEN); |
889 | break; |
890 | } |
891 | m_put_rcvif_psref(rcvif, &psref); |
892 | |
893 | /* deliver the packet to appropriate sockets */ |
894 | icmp6_rip6_input(&m, *offp); |
895 | |
896 | return IPPROTO_DONE; |
897 | |
898 | freeit: |
899 | m_put_rcvif_psref(rcvif, &psref); |
900 | m_freem(m); |
901 | return IPPROTO_DONE; |
902 | } |
903 | |
904 | static int |
905 | icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code) |
906 | { |
907 | struct icmp6_hdr *icmp6; |
908 | struct ip6_hdr *eip6; |
909 | u_int32_t notifymtu; |
910 | struct sockaddr_in6 icmp6src, icmp6dst; |
911 | |
912 | if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { |
913 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
914 | goto freeit; |
915 | } |
916 | IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, |
917 | sizeof(*icmp6) + sizeof(struct ip6_hdr)); |
918 | if (icmp6 == NULL) { |
919 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
920 | return (-1); |
921 | } |
922 | eip6 = (struct ip6_hdr *)(icmp6 + 1); |
923 | |
924 | /* Detect the upper level protocol */ |
925 | { |
926 | void (*ctlfunc)(int, struct sockaddr *, void *); |
927 | u_int8_t nxt = eip6->ip6_nxt; |
928 | int eoff = off + sizeof(struct icmp6_hdr) + |
929 | sizeof(struct ip6_hdr); |
930 | struct ip6ctlparam ip6cp; |
931 | struct in6_addr *finaldst = NULL; |
932 | int icmp6type = icmp6->icmp6_type; |
933 | struct ip6_frag *fh; |
934 | struct ip6_rthdr *rth; |
935 | struct ip6_rthdr0 *rth0; |
936 | int rthlen; |
937 | struct ifnet *rcvif; |
938 | int s; |
939 | |
940 | while (1) { /* XXX: should avoid infinite loop explicitly? */ |
941 | struct ip6_ext *eh; |
942 | |
943 | switch (nxt) { |
944 | case IPPROTO_HOPOPTS: |
945 | case IPPROTO_DSTOPTS: |
946 | case IPPROTO_AH: |
947 | IP6_EXTHDR_GET(eh, struct ip6_ext *, m, |
948 | eoff, sizeof(*eh)); |
949 | if (eh == NULL) { |
950 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
951 | return (-1); |
952 | } |
953 | |
954 | if (nxt == IPPROTO_AH) |
955 | eoff += (eh->ip6e_len + 2) << 2; |
956 | else |
957 | eoff += (eh->ip6e_len + 1) << 3; |
958 | nxt = eh->ip6e_nxt; |
959 | break; |
960 | case IPPROTO_ROUTING: |
961 | /* |
962 | * When the erroneous packet contains a |
963 | * routing header, we should examine the |
964 | * header to determine the final destination. |
965 | * Otherwise, we can't properly update |
966 | * information that depends on the final |
967 | * destination (e.g. path MTU). |
968 | */ |
969 | IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, |
970 | eoff, sizeof(*rth)); |
971 | if (rth == NULL) { |
972 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
973 | return (-1); |
974 | } |
975 | rthlen = (rth->ip6r_len + 1) << 3; |
976 | /* |
977 | * XXX: currently there is no |
978 | * officially defined type other |
979 | * than type-0. |
980 | * Note that if the segment left field |
981 | * is 0, all intermediate hops must |
982 | * have been passed. |
983 | */ |
984 | if (rth->ip6r_segleft && |
985 | rth->ip6r_type == IPV6_RTHDR_TYPE_0) { |
986 | int hops; |
987 | |
988 | IP6_EXTHDR_GET(rth0, |
989 | struct ip6_rthdr0 *, m, |
990 | eoff, rthlen); |
991 | if (rth0 == NULL) { |
992 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
993 | return (-1); |
994 | } |
995 | /* just ignore a bogus header */ |
996 | if ((rth0->ip6r0_len % 2) == 0 && |
997 | (hops = rth0->ip6r0_len/2)) |
998 | finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); |
999 | } |
1000 | eoff += rthlen; |
1001 | nxt = rth->ip6r_nxt; |
1002 | break; |
1003 | case IPPROTO_FRAGMENT: |
1004 | IP6_EXTHDR_GET(fh, struct ip6_frag *, m, |
1005 | eoff, sizeof(*fh)); |
1006 | if (fh == NULL) { |
1007 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
1008 | return (-1); |
1009 | } |
1010 | /* |
1011 | * Data after a fragment header is meaningless |
1012 | * unless it is the first fragment, but |
1013 | * we'll go to the notify label for path MTU |
1014 | * discovery. |
1015 | */ |
1016 | if (fh->ip6f_offlg & IP6F_OFF_MASK) |
1017 | goto notify; |
1018 | |
1019 | eoff += sizeof(struct ip6_frag); |
1020 | nxt = fh->ip6f_nxt; |
1021 | break; |
1022 | default: |
1023 | /* |
1024 | * This case includes ESP and the No Next |
1025 | * Header. In such cases going to the notify |
1026 | * label does not have any meaning |
1027 | * (i.e. ctlfunc will be NULL), but we go |
1028 | * anyway since we might have to update |
1029 | * path MTU information. |
1030 | */ |
1031 | goto notify; |
1032 | } |
1033 | } |
1034 | notify: |
1035 | IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, |
1036 | sizeof(*icmp6) + sizeof(struct ip6_hdr)); |
1037 | if (icmp6 == NULL) { |
1038 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
1039 | return (-1); |
1040 | } |
1041 | |
1042 | /* |
1043 | * retrieve parameters from the inner IPv6 header, and convert |
1044 | * them into sockaddr structures. |
1045 | * XXX: there is no guarantee that the source or destination |
1046 | * addresses of the inner packet are in the same scope zone as |
1047 | * the addresses of the icmp packet. But there is no other |
1048 | * way to determine the zone. |
1049 | */ |
1050 | eip6 = (struct ip6_hdr *)(icmp6 + 1); |
1051 | |
1052 | rcvif = m_get_rcvif(m, &s); |
1053 | sockaddr_in6_init(&icmp6dst, |
1054 | (finaldst == NULL) ? &eip6->ip6_dst : finaldst, 0, 0, 0); |
1055 | if (in6_setscope(&icmp6dst.sin6_addr, rcvif, NULL)) { |
1056 | m_put_rcvif(rcvif, &s); |
1057 | goto freeit; |
1058 | } |
1059 | sockaddr_in6_init(&icmp6src, &eip6->ip6_src, 0, 0, 0); |
1060 | if (in6_setscope(&icmp6src.sin6_addr, rcvif, NULL)) { |
1061 | m_put_rcvif(rcvif, &s); |
1062 | goto freeit; |
1063 | } |
1064 | m_put_rcvif(rcvif, &s); |
1065 | |
1066 | icmp6src.sin6_flowinfo = |
1067 | (eip6->ip6_flow & IPV6_FLOWLABEL_MASK); |
1068 | |
1069 | if (finaldst == NULL) |
1070 | finaldst = &eip6->ip6_dst; |
1071 | ip6cp.ip6c_m = m; |
1072 | ip6cp.ip6c_icmp6 = icmp6; |
1073 | ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); |
1074 | ip6cp.ip6c_off = eoff; |
1075 | ip6cp.ip6c_finaldst = finaldst; |
1076 | ip6cp.ip6c_src = &icmp6src; |
1077 | ip6cp.ip6c_nxt = nxt; |
1078 | |
1079 | if (icmp6type == ICMP6_PACKET_TOO_BIG) { |
1080 | notifymtu = ntohl(icmp6->icmp6_mtu); |
1081 | ip6cp.ip6c_cmdarg = (void *)¬ifymtu; |
1082 | } |
1083 | |
1084 | ctlfunc = (void (*)(int, struct sockaddr *, void *)) |
1085 | (inet6sw[ip6_protox[nxt]].pr_ctlinput); |
1086 | if (ctlfunc) { |
1087 | (void) (*ctlfunc)(code, sin6tosa(&icmp6dst), |
1088 | &ip6cp); |
1089 | } |
1090 | } |
1091 | return (0); |
1092 | |
1093 | freeit: |
1094 | m_freem(m); |
1095 | return (-1); |
1096 | } |
1097 | |
1098 | void |
1099 | icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated) |
1100 | { |
1101 | unsigned long rtcount; |
1102 | struct icmp6_mtudisc_callback *mc; |
1103 | struct in6_addr *dst = ip6cp->ip6c_finaldst; |
1104 | struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; |
1105 | struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */ |
1106 | u_int mtu = ntohl(icmp6->icmp6_mtu); |
1107 | struct rtentry *rt = NULL; |
1108 | struct sockaddr_in6 sin6; |
1109 | struct ifnet *rcvif; |
1110 | int s; |
1111 | |
1112 | /* |
1113 | * The MTU should not be less than the minimal IPv6 MTU except for the |
1114 | * hack in ip6_output/ip6_setpmtu where we always include a frag header. |
1115 | * In that one case, the MTU might be less than 1280. |
1116 | */ |
1117 | if (__predict_false(mtu < IPV6_MMTU - sizeof(struct ip6_frag))) { |
1118 | /* is the mtu even sane? */ |
1119 | if (mtu < sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + 8) |
1120 | return; |
1121 | if (!validated) |
1122 | return; |
1123 | mtu = IPV6_MMTU - sizeof(struct ip6_frag); |
1124 | } |
1125 | |
1126 | /* |
1127 | * allow non-validated cases if memory is plenty, to make traffic |
1128 | * from non-connected pcb happy. |
1129 | */ |
1130 | rtcount = rt_timer_count(icmp6_mtudisc_timeout_q); |
1131 | if (validated) { |
1132 | if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat) |
1133 | return; |
1134 | else if (0 <= icmp6_mtudisc_lowat && |
1135 | rtcount > icmp6_mtudisc_lowat) { |
1136 | /* |
1137 | * XXX nuke a victim, install the new one. |
1138 | */ |
1139 | } |
1140 | } else { |
1141 | if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat) |
1142 | return; |
1143 | } |
1144 | |
1145 | memset(&sin6, 0, sizeof(sin6)); |
1146 | sin6.sin6_family = PF_INET6; |
1147 | sin6.sin6_len = sizeof(struct sockaddr_in6); |
1148 | sin6.sin6_addr = *dst; |
1149 | rcvif = m_get_rcvif(m, &s); |
1150 | if (in6_setscope(&sin6.sin6_addr, rcvif, NULL)) { |
1151 | m_put_rcvif(rcvif, &s); |
1152 | return; |
1153 | } |
1154 | m_put_rcvif(rcvif, &s); |
1155 | |
1156 | rt = icmp6_mtudisc_clone(sin6tosa(&sin6)); |
1157 | |
1158 | if (rt && (rt->rt_flags & RTF_HOST) && |
1159 | !(rt->rt_rmx.rmx_locks & RTV_MTU) && |
1160 | (rt->rt_rmx.rmx_mtu > mtu || rt->rt_rmx.rmx_mtu == 0)) { |
1161 | if (mtu < IN6_LINKMTU(rt->rt_ifp)) { |
1162 | ICMP6_STATINC(ICMP6_STAT_PMTUCHG); |
1163 | rt->rt_rmx.rmx_mtu = mtu; |
1164 | } |
1165 | } |
1166 | if (rt) { |
1167 | rtfree(rt); |
1168 | } |
1169 | |
1170 | /* |
1171 | * Notify protocols that the MTU for this destination |
1172 | * has changed. |
1173 | */ |
1174 | for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL; |
1175 | mc = LIST_NEXT(mc, mc_list)) |
1176 | (*mc->mc_func)(&sin6.sin6_addr); |
1177 | } |
1178 | |
1179 | /* |
1180 | * Process a Node Information Query packet, based on |
1181 | * draft-ietf-ipngwg-icmp-name-lookups-07. |
1182 | * |
1183 | * Spec incompatibilities: |
1184 | * - IPv6 Subject address handling |
1185 | * - IPv4 Subject address handling support missing |
1186 | * - Proxy reply (answer even if it's not for me) |
1187 | * - joins NI group address at in6_ifattach() time only, does not cope |
1188 | * with hostname changes by sethostname(3) |
1189 | */ |
1190 | static struct mbuf * |
1191 | ni6_input(struct mbuf *m, int off) |
1192 | { |
1193 | struct icmp6_nodeinfo *ni6, *nni6; |
1194 | struct mbuf *n = NULL; |
1195 | u_int16_t qtype; |
1196 | int subjlen; |
1197 | int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); |
1198 | struct ni_reply_fqdn *fqdn; |
1199 | int addrs; /* for NI_QTYPE_NODEADDR */ |
1200 | struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ |
1201 | struct sockaddr_in6 sin6; /* ip6_dst */ |
1202 | struct in6_addr in6_subj; /* subject address */ |
1203 | struct ip6_hdr *ip6; |
1204 | int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ |
1205 | char *subj = NULL; |
1206 | struct ifnet *rcvif; |
1207 | int s, ss; |
1208 | struct ifaddr *ifa; |
1209 | struct psref psref; |
1210 | |
1211 | ip6 = mtod(m, struct ip6_hdr *); |
1212 | IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6)); |
1213 | if (ni6 == NULL) { |
1214 | /* m is already reclaimed */ |
1215 | return NULL; |
1216 | } |
1217 | |
1218 | /* |
1219 | * Validate IPv6 destination address. |
1220 | * |
1221 | * The Responder must discard the Query without further processing |
1222 | * unless it is one of the Responder's unicast or anycast addresses, or |
1223 | * a link-local scope multicast address which the Responder has joined. |
1224 | * [icmp-name-lookups-07, Section 4.] |
1225 | */ |
1226 | sockaddr_in6_init(&sin6, &ip6->ip6_dst, 0, 0, 0); |
1227 | /* XXX scopeid */ |
1228 | ss = pserialize_read_enter(); |
1229 | ifa = ifa_ifwithaddr(sin6tosa(&sin6)); |
1230 | if (ifa != NULL) |
1231 | ; /* unicast/anycast, fine */ |
1232 | else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) |
1233 | ; /* link-local multicast, fine */ |
1234 | else { |
1235 | pserialize_read_exit(ss); |
1236 | goto bad; |
1237 | } |
1238 | pserialize_read_exit(ss); |
1239 | |
1240 | /* validate query Subject field. */ |
1241 | qtype = ntohs(ni6->ni_qtype); |
1242 | subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo); |
1243 | switch (qtype) { |
1244 | case NI_QTYPE_NOOP: |
1245 | case NI_QTYPE_SUPTYPES: |
1246 | /* 07 draft */ |
1247 | if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0) |
1248 | break; |
1249 | /* FALLTHROUGH */ |
1250 | case NI_QTYPE_FQDN: |
1251 | case NI_QTYPE_NODEADDR: |
1252 | case NI_QTYPE_IPV4ADDR: |
1253 | switch (ni6->ni_code) { |
1254 | case ICMP6_NI_SUBJ_IPV6: |
1255 | #if ICMP6_NI_SUBJ_IPV6 != 0 |
1256 | case 0: |
1257 | #endif |
1258 | /* |
1259 | * backward compatibility - try to accept 03 draft |
1260 | * format, where no Subject is present. |
1261 | */ |
1262 | if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 && |
1263 | subjlen == 0) { |
1264 | oldfqdn++; |
1265 | break; |
1266 | } |
1267 | #if ICMP6_NI_SUBJ_IPV6 != 0 |
1268 | if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6) |
1269 | goto bad; |
1270 | #endif |
1271 | |
1272 | if (subjlen != sizeof(sin6.sin6_addr)) |
1273 | goto bad; |
1274 | |
1275 | /* |
1276 | * Validate Subject address. |
1277 | * |
1278 | * Not sure what exactly "address belongs to the node" |
1279 | * means in the spec, is it just unicast, or what? |
1280 | * |
1281 | * At this moment we consider Subject address as |
1282 | * "belong to the node" if the Subject address equals |
1283 | * to the IPv6 destination address; validation for |
1284 | * IPv6 destination address should have done enough |
1285 | * check for us. |
1286 | * |
1287 | * We do not do proxy at this moment. |
1288 | */ |
1289 | /* m_pulldown instead of copy? */ |
1290 | m_copydata(m, off + sizeof(struct icmp6_nodeinfo), |
1291 | subjlen, (void *)&in6_subj); |
1292 | rcvif = m_get_rcvif(m, &s); |
1293 | if (in6_setscope(&in6_subj, rcvif, NULL)) { |
1294 | m_put_rcvif(rcvif, &s); |
1295 | goto bad; |
1296 | } |
1297 | m_put_rcvif(rcvif, &s); |
1298 | |
1299 | subj = (char *)&in6_subj; |
1300 | if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &in6_subj)) |
1301 | break; |
1302 | |
1303 | /* |
1304 | * XXX if we are to allow other cases, we should really |
1305 | * be careful about scope here. |
1306 | * basically, we should disallow queries toward IPv6 |
1307 | * destination X with subject Y, if scope(X) > scope(Y). |
1308 | * if we allow scope(X) > scope(Y), it will result in |
1309 | * information leakage across scope boundary. |
1310 | */ |
1311 | goto bad; |
1312 | |
1313 | case ICMP6_NI_SUBJ_FQDN: |
1314 | /* |
1315 | * Validate Subject name with gethostname(3). |
1316 | * |
1317 | * The behavior may need some debate, since: |
1318 | * - we are not sure if the node has FQDN as |
1319 | * hostname (returned by gethostname(3)). |
1320 | * - the code does wildcard match for truncated names. |
1321 | * however, we are not sure if we want to perform |
1322 | * wildcard match, if gethostname(3) side has |
1323 | * truncated hostname. |
1324 | */ |
1325 | n = ni6_nametodns(hostname, hostnamelen, 0); |
1326 | if (!n || n->m_next || n->m_len == 0) |
1327 | goto bad; |
1328 | IP6_EXTHDR_GET(subj, char *, m, |
1329 | off + sizeof(struct icmp6_nodeinfo), subjlen); |
1330 | if (subj == NULL) |
1331 | goto bad; |
1332 | if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *), |
1333 | n->m_len)) { |
1334 | goto bad; |
1335 | } |
1336 | m_freem(n); |
1337 | n = NULL; |
1338 | break; |
1339 | |
1340 | case ICMP6_NI_SUBJ_IPV4: /* XXX: to be implemented? */ |
1341 | default: |
1342 | goto bad; |
1343 | } |
1344 | break; |
1345 | } |
1346 | |
1347 | /* refuse based on configuration. XXX ICMP6_NI_REFUSED? */ |
1348 | switch (qtype) { |
1349 | case NI_QTYPE_FQDN: |
1350 | if ((icmp6_nodeinfo & 1) == 0) |
1351 | goto bad; |
1352 | break; |
1353 | case NI_QTYPE_NODEADDR: |
1354 | case NI_QTYPE_IPV4ADDR: |
1355 | if ((icmp6_nodeinfo & 2) == 0) |
1356 | goto bad; |
1357 | break; |
1358 | } |
1359 | |
1360 | /* guess reply length */ |
1361 | switch (qtype) { |
1362 | case NI_QTYPE_NOOP: |
1363 | break; /* no reply data */ |
1364 | case NI_QTYPE_SUPTYPES: |
1365 | replylen += sizeof(u_int32_t); |
1366 | break; |
1367 | case NI_QTYPE_FQDN: |
1368 | /* XXX will append an mbuf */ |
1369 | replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); |
1370 | break; |
1371 | case NI_QTYPE_NODEADDR: |
1372 | addrs = ni6_addrs(ni6, m, &ifp, subj, &psref); |
1373 | if ((replylen += addrs * (sizeof(struct in6_addr) + |
1374 | sizeof(u_int32_t))) > MCLBYTES) |
1375 | replylen = MCLBYTES; /* XXX: will truncate pkt later */ |
1376 | break; |
1377 | case NI_QTYPE_IPV4ADDR: |
1378 | /* unsupported - should respond with unknown Qtype? */ |
1379 | goto bad; |
1380 | default: |
1381 | /* |
1382 | * XXX: We must return a reply with the ICMP6 code |
1383 | * `unknown Qtype' in this case. However we regard the case |
1384 | * as an FQDN query for backward compatibility. |
1385 | * Older versions set a random value to this field, |
1386 | * so it rarely varies in the defined qtypes. |
1387 | * But the mechanism is not reliable... |
1388 | * maybe we should obsolete older versions. |
1389 | */ |
1390 | qtype = NI_QTYPE_FQDN; |
1391 | /* XXX will append an mbuf */ |
1392 | replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); |
1393 | oldfqdn++; |
1394 | break; |
1395 | } |
1396 | |
1397 | /* allocate an mbuf to reply. */ |
1398 | MGETHDR(n, M_DONTWAIT, m->m_type); |
1399 | if (n == NULL) { |
1400 | if_put(ifp, &psref); |
1401 | m_freem(m); |
1402 | return (NULL); |
1403 | } |
1404 | M_MOVE_PKTHDR(n, m); /* just for rcvif */ |
1405 | if (replylen > MHLEN) { |
1406 | if (replylen > MCLBYTES) { |
1407 | /* |
1408 | * XXX: should we try to allocate more? But MCLBYTES |
1409 | * is probably much larger than IPV6_MMTU... |
1410 | */ |
1411 | goto bad; |
1412 | } |
1413 | MCLGET(n, M_DONTWAIT); |
1414 | if ((n->m_flags & M_EXT) == 0) { |
1415 | goto bad; |
1416 | } |
1417 | } |
1418 | n->m_pkthdr.len = n->m_len = replylen; |
1419 | |
1420 | /* copy mbuf header and IPv6 + Node Information base headers */ |
1421 | bcopy(mtod(m, void *), mtod(n, void *), sizeof(struct ip6_hdr)); |
1422 | nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); |
1423 | bcopy((void *)ni6, (void *)nni6, sizeof(struct icmp6_nodeinfo)); |
1424 | |
1425 | /* qtype dependent procedure */ |
1426 | switch (qtype) { |
1427 | case NI_QTYPE_NOOP: |
1428 | nni6->ni_code = ICMP6_NI_SUCCESS; |
1429 | nni6->ni_flags = 0; |
1430 | break; |
1431 | case NI_QTYPE_SUPTYPES: |
1432 | { |
1433 | u_int32_t v; |
1434 | nni6->ni_code = ICMP6_NI_SUCCESS; |
1435 | nni6->ni_flags = htons(0x0000); /* raw bitmap */ |
1436 | /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */ |
1437 | v = (u_int32_t)htonl(0x0000000f); |
1438 | bcopy(&v, nni6 + 1, sizeof(u_int32_t)); |
1439 | break; |
1440 | } |
1441 | case NI_QTYPE_FQDN: |
1442 | nni6->ni_code = ICMP6_NI_SUCCESS; |
1443 | fqdn = (struct ni_reply_fqdn *)(mtod(n, char *) + |
1444 | sizeof(struct ip6_hdr) + |
1445 | sizeof(struct icmp6_nodeinfo)); |
1446 | nni6->ni_flags = 0; /* XXX: meaningless TTL */ |
1447 | fqdn->ni_fqdn_ttl = 0; /* ditto. */ |
1448 | /* |
1449 | * XXX do we really have FQDN in variable "hostname"? |
1450 | */ |
1451 | n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn); |
1452 | if (n->m_next == NULL) |
1453 | goto bad; |
1454 | /* XXX we assume that n->m_next is not a chain */ |
1455 | if (n->m_next->m_next != NULL) |
1456 | goto bad; |
1457 | n->m_pkthdr.len += n->m_next->m_len; |
1458 | break; |
1459 | case NI_QTYPE_NODEADDR: |
1460 | { |
1461 | int lenlim, copied; |
1462 | |
1463 | nni6->ni_code = ICMP6_NI_SUCCESS; |
1464 | n->m_pkthdr.len = n->m_len = |
1465 | sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); |
1466 | lenlim = M_TRAILINGSPACE(n); |
1467 | copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); |
1468 | if_put(ifp, &psref); |
1469 | ifp = NULL; |
1470 | /* XXX: reset mbuf length */ |
1471 | n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + |
1472 | sizeof(struct icmp6_nodeinfo) + copied; |
1473 | break; |
1474 | } |
1475 | default: |
1476 | break; /* XXX impossible! */ |
1477 | } |
1478 | |
1479 | nni6->ni_type = ICMP6_NI_REPLY; |
1480 | m_freem(m); |
1481 | return (n); |
1482 | |
1483 | bad: |
1484 | if_put(ifp, &psref); |
1485 | m_freem(m); |
1486 | if (n) |
1487 | m_freem(n); |
1488 | return (NULL); |
1489 | } |
1490 | #undef hostnamelen |
1491 | |
1492 | #define isupper(x) ('A' <= (x) && (x) <= 'Z') |
1493 | #define isalpha(x) (('A' <= (x) && (x) <= 'Z') || ('a' <= (x) && (x) <= 'z')) |
1494 | #define isalnum(x) (isalpha(x) || ('0' <= (x) && (x) <= '9')) |
1495 | #define tolower(x) (isupper(x) ? (x) + 'a' - 'A' : (x)) |
1496 | |
1497 | /* |
1498 | * make a mbuf with DNS-encoded string. no compression support. |
1499 | * |
1500 | * XXX names with less than 2 dots (like "foo" or "foo.section") will be |
1501 | * treated as truncated name (two \0 at the end). this is a wild guess. |
1502 | * |
1503 | * old - return pascal string if non-zero |
1504 | */ |
1505 | static struct mbuf * |
1506 | ni6_nametodns(const char *name, int namelen, int old) |
1507 | { |
1508 | struct mbuf *m; |
1509 | char *cp, *ep; |
1510 | const char *p, *q; |
1511 | int i, len, nterm; |
1512 | |
1513 | if (old) |
1514 | len = namelen + 1; |
1515 | else |
1516 | len = MCLBYTES; |
1517 | |
1518 | /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */ |
1519 | MGET(m, M_DONTWAIT, MT_DATA); |
1520 | if (m && len > MLEN) { |
1521 | MCLGET(m, M_DONTWAIT); |
1522 | if ((m->m_flags & M_EXT) == 0) |
1523 | goto fail; |
1524 | } |
1525 | if (!m) |
1526 | goto fail; |
1527 | m->m_next = NULL; |
1528 | |
1529 | if (old) { |
1530 | m->m_len = len; |
1531 | *mtod(m, char *) = namelen; |
1532 | bcopy(name, mtod(m, char *) + 1, namelen); |
1533 | return m; |
1534 | } else { |
1535 | m->m_len = 0; |
1536 | cp = mtod(m, char *); |
1537 | ep = mtod(m, char *) + M_TRAILINGSPACE(m); |
1538 | |
1539 | /* if not certain about my name, return empty buffer */ |
1540 | if (namelen == 0) |
1541 | return m; |
1542 | |
1543 | /* |
1544 | * guess if it looks like shortened hostname, or FQDN. |
1545 | * shortened hostname needs two trailing "\0". |
1546 | */ |
1547 | i = 0; |
1548 | for (p = name; p < name + namelen; p++) { |
1549 | if (*p == '.') |
1550 | i++; |
1551 | } |
1552 | if (i < 2) |
1553 | nterm = 2; |
1554 | else |
1555 | nterm = 1; |
1556 | |
1557 | p = name; |
1558 | while (cp < ep && p < name + namelen) { |
1559 | i = 0; |
1560 | for (q = p; q < name + namelen && *q && *q != '.'; q++) |
1561 | i++; |
1562 | /* result does not fit into mbuf */ |
1563 | if (cp + i + 1 >= ep) |
1564 | goto fail; |
1565 | /* |
1566 | * DNS label length restriction, RFC1035 page 8. |
1567 | * "i == 0" case is included here to avoid returning |
1568 | * 0-length label on "foo..bar". |
1569 | */ |
1570 | if (i <= 0 || i >= 64) |
1571 | goto fail; |
1572 | *cp++ = i; |
1573 | if (!isalpha(p[0]) || !isalnum(p[i - 1])) |
1574 | goto fail; |
1575 | while (i > 0) { |
1576 | if (!isalnum(*p) && *p != '-') |
1577 | goto fail; |
1578 | if (isupper(*p)) { |
1579 | *cp++ = tolower(*p); |
1580 | p++; |
1581 | } else |
1582 | *cp++ = *p++; |
1583 | i--; |
1584 | } |
1585 | p = q; |
1586 | if (p < name + namelen && *p == '.') |
1587 | p++; |
1588 | } |
1589 | /* termination */ |
1590 | if (cp + nterm >= ep) |
1591 | goto fail; |
1592 | while (nterm-- > 0) |
1593 | *cp++ = '\0'; |
1594 | m->m_len = cp - mtod(m, char *); |
1595 | return m; |
1596 | } |
1597 | |
1598 | panic("should not reach here" ); |
1599 | /* NOTREACHED */ |
1600 | |
1601 | fail: |
1602 | if (m) |
1603 | m_freem(m); |
1604 | return NULL; |
1605 | } |
1606 | |
1607 | /* |
1608 | * check if two DNS-encoded string matches. takes care of truncated |
1609 | * form (with \0\0 at the end). no compression support. |
1610 | * XXX upper/lowercase match (see RFC2065) |
1611 | */ |
1612 | static int |
1613 | ni6_dnsmatch(const char *a, int alen, const char *b, int blen) |
1614 | { |
1615 | const char *a0, *b0; |
1616 | int l; |
1617 | |
1618 | /* simplest case - need validation? */ |
1619 | if (alen == blen && memcmp(a, b, alen) == 0) |
1620 | return 1; |
1621 | |
1622 | a0 = a; |
1623 | b0 = b; |
1624 | |
1625 | /* termination is mandatory */ |
1626 | if (alen < 2 || blen < 2) |
1627 | return 0; |
1628 | if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0') |
1629 | return 0; |
1630 | alen--; |
1631 | blen--; |
1632 | |
1633 | while (a - a0 < alen && b - b0 < blen) { |
1634 | if (a - a0 + 1 > alen || b - b0 + 1 > blen) |
1635 | return 0; |
1636 | |
1637 | if ((signed char)a[0] < 0 || (signed char)b[0] < 0) |
1638 | return 0; |
1639 | /* we don't support compression yet */ |
1640 | if (a[0] >= 64 || b[0] >= 64) |
1641 | return 0; |
1642 | |
1643 | /* truncated case */ |
1644 | if (a[0] == 0 && a - a0 == alen - 1) |
1645 | return 1; |
1646 | if (b[0] == 0 && b - b0 == blen - 1) |
1647 | return 1; |
1648 | if (a[0] == 0 || b[0] == 0) |
1649 | return 0; |
1650 | |
1651 | if (a[0] != b[0]) |
1652 | return 0; |
1653 | l = a[0]; |
1654 | if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen) |
1655 | return 0; |
1656 | if (memcmp(a + 1, b + 1, l) != 0) |
1657 | return 0; |
1658 | |
1659 | a += 1 + l; |
1660 | b += 1 + l; |
1661 | } |
1662 | |
1663 | if (a - a0 == alen && b - b0 == blen) |
1664 | return 1; |
1665 | else |
1666 | return 0; |
1667 | } |
1668 | |
1669 | /* |
1670 | * calculate the number of addresses to be returned in the node info reply. |
1671 | */ |
1672 | static int |
1673 | ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, |
1674 | struct ifnet **ifpp, char *subj, struct psref *psref) |
1675 | { |
1676 | struct ifnet *ifp; |
1677 | struct in6_ifaddr *ia6; |
1678 | struct ifaddr *ifa; |
1679 | struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */ |
1680 | int addrs = 0, addrsofif, iffound = 0; |
1681 | int niflags = ni6->ni_flags; |
1682 | int s; |
1683 | |
1684 | if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { |
1685 | switch (ni6->ni_code) { |
1686 | case ICMP6_NI_SUBJ_IPV6: |
1687 | if (subj == NULL) /* must be impossible... */ |
1688 | return (0); |
1689 | subj_ip6 = (struct sockaddr_in6 *)subj; |
1690 | break; |
1691 | default: |
1692 | /* |
1693 | * XXX: we only support IPv6 subject address for |
1694 | * this Qtype. |
1695 | */ |
1696 | return (0); |
1697 | } |
1698 | } |
1699 | |
1700 | s = pserialize_read_enter(); |
1701 | IFNET_READER_FOREACH(ifp) { |
1702 | addrsofif = 0; |
1703 | IFADDR_READER_FOREACH(ifa, ifp) { |
1704 | if (ifa->ifa_addr->sa_family != AF_INET6) |
1705 | continue; |
1706 | ia6 = (struct in6_ifaddr *)ifa; |
1707 | |
1708 | if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 && |
1709 | IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr, |
1710 | &ia6->ia_addr.sin6_addr)) |
1711 | iffound = 1; |
1712 | |
1713 | /* |
1714 | * IPv4-mapped addresses can only be returned by a |
1715 | * Node Information proxy, since they represent |
1716 | * addresses of IPv4-only nodes, which perforce do |
1717 | * not implement this protocol. |
1718 | * [icmp-name-lookups-07, Section 5.4] |
1719 | * So we don't support NI_NODEADDR_FLAG_COMPAT in |
1720 | * this function at this moment. |
1721 | */ |
1722 | |
1723 | /* What do we have to do about ::1? */ |
1724 | switch (in6_addrscope(&ia6->ia_addr.sin6_addr)) { |
1725 | case IPV6_ADDR_SCOPE_LINKLOCAL: |
1726 | if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) |
1727 | continue; |
1728 | break; |
1729 | case IPV6_ADDR_SCOPE_SITELOCAL: |
1730 | if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) |
1731 | continue; |
1732 | break; |
1733 | case IPV6_ADDR_SCOPE_GLOBAL: |
1734 | if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) |
1735 | continue; |
1736 | break; |
1737 | default: |
1738 | continue; |
1739 | } |
1740 | |
1741 | /* |
1742 | * check if anycast is okay. |
1743 | * XXX: just experimental. not in the spec. |
1744 | */ |
1745 | if ((ia6->ia6_flags & IN6_IFF_ANYCAST) != 0 && |
1746 | (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) |
1747 | continue; /* we need only unicast addresses */ |
1748 | |
1749 | addrsofif++; /* count the address */ |
1750 | } |
1751 | if (iffound) { |
1752 | if_acquire_NOMPSAFE(ifp, psref); |
1753 | pserialize_read_exit(s); |
1754 | *ifpp = ifp; |
1755 | return (addrsofif); |
1756 | } |
1757 | |
1758 | addrs += addrsofif; |
1759 | } |
1760 | pserialize_read_exit(s); |
1761 | |
1762 | return (addrs); |
1763 | } |
1764 | |
1765 | static int |
1766 | ni6_store_addrs(struct icmp6_nodeinfo *ni6, |
1767 | struct icmp6_nodeinfo *nni6, struct ifnet *ifp0, |
1768 | int resid) |
1769 | { |
1770 | struct ifnet *ifp; |
1771 | struct in6_ifaddr *ia6; |
1772 | struct ifaddr *ifa; |
1773 | struct ifnet *ifp_dep = NULL; |
1774 | int copied = 0, allow_deprecated = 0; |
1775 | u_char *cp = (u_char *)(nni6 + 1); |
1776 | int niflags = ni6->ni_flags; |
1777 | u_int32_t ltime; |
1778 | int s; |
1779 | |
1780 | if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL)) |
1781 | return (0); /* needless to copy */ |
1782 | |
1783 | s = pserialize_read_enter(); |
1784 | ifp = ifp0 ? ifp0 : IFNET_READER_FIRST(); |
1785 | again: |
1786 | |
1787 | for (; ifp; ifp = IFNET_READER_NEXT(ifp)) |
1788 | { |
1789 | IFADDR_READER_FOREACH(ifa, ifp) { |
1790 | if (ifa->ifa_addr->sa_family != AF_INET6) |
1791 | continue; |
1792 | ia6 = (struct in6_ifaddr *)ifa; |
1793 | |
1794 | if ((ia6->ia6_flags & IN6_IFF_DEPRECATED) != 0 && |
1795 | allow_deprecated == 0) { |
1796 | /* |
1797 | * prefererred address should be put before |
1798 | * deprecated addresses. |
1799 | */ |
1800 | |
1801 | /* record the interface for later search */ |
1802 | if (ifp_dep == NULL) |
1803 | ifp_dep = ifp; |
1804 | |
1805 | continue; |
1806 | } |
1807 | else if ((ia6->ia6_flags & IN6_IFF_DEPRECATED) == 0 && |
1808 | allow_deprecated != 0) |
1809 | continue; /* we now collect deprecated addrs */ |
1810 | |
1811 | /* What do we have to do about ::1? */ |
1812 | switch (in6_addrscope(&ia6->ia_addr.sin6_addr)) { |
1813 | case IPV6_ADDR_SCOPE_LINKLOCAL: |
1814 | if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0) |
1815 | continue; |
1816 | break; |
1817 | case IPV6_ADDR_SCOPE_SITELOCAL: |
1818 | if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0) |
1819 | continue; |
1820 | break; |
1821 | case IPV6_ADDR_SCOPE_GLOBAL: |
1822 | if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0) |
1823 | continue; |
1824 | break; |
1825 | default: |
1826 | continue; |
1827 | } |
1828 | |
1829 | /* |
1830 | * check if anycast is okay. |
1831 | * XXX: just experimental. not in the spec. |
1832 | */ |
1833 | if ((ia6->ia6_flags & IN6_IFF_ANYCAST) != 0 && |
1834 | (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0) |
1835 | continue; |
1836 | |
1837 | /* now we can copy the address */ |
1838 | if (resid < sizeof(struct in6_addr) + |
1839 | sizeof(u_int32_t)) { |
1840 | /* |
1841 | * We give up much more copy. |
1842 | * Set the truncate flag and return. |
1843 | */ |
1844 | nni6->ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; |
1845 | goto out; |
1846 | } |
1847 | |
1848 | /* |
1849 | * Set the TTL of the address. |
1850 | * The TTL value should be one of the following |
1851 | * according to the specification: |
1852 | * |
1853 | * 1. The remaining lifetime of a DHCP lease on the |
1854 | * address, or |
1855 | * 2. The remaining Valid Lifetime of a prefix from |
1856 | * which the address was derived through Stateless |
1857 | * Autoconfiguration. |
1858 | * |
1859 | * Note that we currently do not support stateful |
1860 | * address configuration by DHCPv6, so the former |
1861 | * case can't happen. |
1862 | * |
1863 | * TTL must be 2^31 > TTL >= 0. |
1864 | */ |
1865 | if (ia6->ia6_lifetime.ia6t_expire == 0) |
1866 | ltime = ND6_INFINITE_LIFETIME; |
1867 | else { |
1868 | if (ia6->ia6_lifetime.ia6t_expire > |
1869 | time_uptime) |
1870 | ltime = ia6->ia6_lifetime.ia6t_expire - |
1871 | time_uptime; |
1872 | else |
1873 | ltime = 0; |
1874 | } |
1875 | if (ltime > 0x7fffffff) |
1876 | ltime = 0x7fffffff; |
1877 | ltime = htonl(ltime); |
1878 | |
1879 | bcopy(<ime, cp, sizeof(u_int32_t)); |
1880 | cp += sizeof(u_int32_t); |
1881 | |
1882 | /* copy the address itself */ |
1883 | bcopy(&ia6->ia_addr.sin6_addr, cp, |
1884 | sizeof(struct in6_addr)); |
1885 | in6_clearscope((struct in6_addr *)cp); /* XXX */ |
1886 | cp += sizeof(struct in6_addr); |
1887 | |
1888 | resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t)); |
1889 | copied += (sizeof(struct in6_addr) + sizeof(u_int32_t)); |
1890 | } |
1891 | if (ifp0) /* we need search only on the specified IF */ |
1892 | break; |
1893 | } |
1894 | |
1895 | if (allow_deprecated == 0 && ifp_dep != NULL) { |
1896 | ifp = ifp_dep; |
1897 | allow_deprecated = 1; |
1898 | |
1899 | goto again; |
1900 | } |
1901 | out: |
1902 | pserialize_read_exit(s); |
1903 | return (copied); |
1904 | } |
1905 | |
1906 | /* |
1907 | * XXX almost dup'ed code with rip6_input. |
1908 | */ |
1909 | static int |
1910 | icmp6_rip6_input(struct mbuf **mp, int off) |
1911 | { |
1912 | struct mbuf *m = *mp; |
1913 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
1914 | struct inpcb_hdr *inph; |
1915 | struct in6pcb *in6p; |
1916 | struct in6pcb *last = NULL; |
1917 | struct sockaddr_in6 rip6src; |
1918 | struct icmp6_hdr *icmp6; |
1919 | struct mbuf *opts = NULL; |
1920 | |
1921 | IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); |
1922 | if (icmp6 == NULL) { |
1923 | /* m is already reclaimed */ |
1924 | return IPPROTO_DONE; |
1925 | } |
1926 | |
1927 | /* |
1928 | * XXX: the address may have embedded scope zone ID, which should be |
1929 | * hidden from applications. |
1930 | */ |
1931 | sockaddr_in6_init(&rip6src, &ip6->ip6_src, 0, 0, 0); |
1932 | if (sa6_recoverscope(&rip6src)) { |
1933 | m_freem(m); |
1934 | return (IPPROTO_DONE); |
1935 | } |
1936 | |
1937 | TAILQ_FOREACH(inph, &raw6cbtable.inpt_queue, inph_queue) { |
1938 | in6p = (struct in6pcb *)inph; |
1939 | if (in6p->in6p_af != AF_INET6) |
1940 | continue; |
1941 | if (in6p->in6p_ip6.ip6_nxt != IPPROTO_ICMPV6) |
1942 | continue; |
1943 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && |
1944 | !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) |
1945 | continue; |
1946 | if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && |
1947 | !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) |
1948 | continue; |
1949 | if (in6p->in6p_icmp6filt |
1950 | && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, |
1951 | in6p->in6p_icmp6filt)) |
1952 | continue; |
1953 | if (last) { |
1954 | struct mbuf *n; |
1955 | if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { |
1956 | if (last->in6p_flags & IN6P_CONTROLOPTS) |
1957 | ip6_savecontrol(last, &opts, ip6, n); |
1958 | /* strip intermediate headers */ |
1959 | m_adj(n, off); |
1960 | if (sbappendaddr(&last->in6p_socket->so_rcv, |
1961 | sin6tosa(&rip6src), n, opts) == 0) { |
1962 | /* should notify about lost packet */ |
1963 | m_freem(n); |
1964 | if (opts) |
1965 | m_freem(opts); |
1966 | } else |
1967 | sorwakeup(last->in6p_socket); |
1968 | opts = NULL; |
1969 | } |
1970 | } |
1971 | last = in6p; |
1972 | } |
1973 | if (last) { |
1974 | if (last->in6p_flags & IN6P_CONTROLOPTS) |
1975 | ip6_savecontrol(last, &opts, ip6, m); |
1976 | /* strip intermediate headers */ |
1977 | m_adj(m, off); |
1978 | if (sbappendaddr(&last->in6p_socket->so_rcv, |
1979 | sin6tosa(&rip6src), m, opts) == 0) { |
1980 | m_freem(m); |
1981 | if (opts) |
1982 | m_freem(opts); |
1983 | } else |
1984 | sorwakeup(last->in6p_socket); |
1985 | } else { |
1986 | m_freem(m); |
1987 | IP6_STATDEC(IP6_STAT_DELIVERED); |
1988 | } |
1989 | return IPPROTO_DONE; |
1990 | } |
1991 | |
1992 | /* |
1993 | * Reflect the ip6 packet back to the source. |
1994 | * OFF points to the icmp6 header, counted from the top of the mbuf. |
1995 | * |
1996 | * Note: RFC 1885 required that an echo reply should be truncated if it |
1997 | * did not fit in with (return) path MTU, and KAME code supported the |
1998 | * behavior. However, as a clarification after the RFC, this limitation |
1999 | * was removed in a revised version of the spec, RFC 2463. We had kept the |
2000 | * old behavior, with a (non-default) ifdef block, while the new version of |
2001 | * the spec was an internet-draft status, and even after the new RFC was |
2002 | * published. But it would rather make sense to clean the obsoleted part |
2003 | * up, and to make the code simpler at this stage. |
2004 | */ |
2005 | void |
2006 | icmp6_reflect(struct mbuf *m, size_t off) |
2007 | { |
2008 | struct ip6_hdr *ip6; |
2009 | struct icmp6_hdr *icmp6; |
2010 | const struct in6_ifaddr *ia; |
2011 | const struct ip6aux *ip6a; |
2012 | int plen; |
2013 | int type, code; |
2014 | struct ifnet *outif = NULL; |
2015 | struct in6_addr origdst; |
2016 | struct ifnet *rcvif; |
2017 | int s; |
2018 | bool ip6_src_filled = false; |
2019 | |
2020 | /* too short to reflect */ |
2021 | if (off < sizeof(struct ip6_hdr)) { |
2022 | nd6log(LOG_DEBUG, |
2023 | "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n" , |
2024 | (u_long)off, (u_long)sizeof(struct ip6_hdr), |
2025 | __FILE__, __LINE__); |
2026 | goto bad; |
2027 | } |
2028 | |
2029 | /* |
2030 | * If there are extra headers between IPv6 and ICMPv6, strip |
2031 | * off that header first. |
2032 | */ |
2033 | #ifdef DIAGNOSTIC |
2034 | if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN) |
2035 | panic("assumption failed in icmp6_reflect" ); |
2036 | #endif |
2037 | if (off > sizeof(struct ip6_hdr)) { |
2038 | size_t l; |
2039 | struct ip6_hdr nip6; |
2040 | |
2041 | l = off - sizeof(struct ip6_hdr); |
2042 | m_copydata(m, 0, sizeof(nip6), (void *)&nip6); |
2043 | m_adj(m, l); |
2044 | l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); |
2045 | if (m->m_len < l) { |
2046 | if ((m = m_pullup(m, l)) == NULL) |
2047 | return; |
2048 | } |
2049 | bcopy((void *)&nip6, mtod(m, void *), sizeof(nip6)); |
2050 | } else /* off == sizeof(struct ip6_hdr) */ { |
2051 | size_t l; |
2052 | l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); |
2053 | if (m->m_len < l) { |
2054 | if ((m = m_pullup(m, l)) == NULL) |
2055 | return; |
2056 | } |
2057 | } |
2058 | plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); |
2059 | ip6 = mtod(m, struct ip6_hdr *); |
2060 | ip6->ip6_nxt = IPPROTO_ICMPV6; |
2061 | icmp6 = (struct icmp6_hdr *)(ip6 + 1); |
2062 | type = icmp6->icmp6_type; /* keep type for statistics */ |
2063 | code = icmp6->icmp6_code; /* ditto. */ |
2064 | |
2065 | origdst = ip6->ip6_dst; |
2066 | /* |
2067 | * ip6_input() drops a packet if its src is multicast. |
2068 | * So, the src is never multicast. |
2069 | */ |
2070 | ip6->ip6_dst = ip6->ip6_src; |
2071 | |
2072 | /* |
2073 | * If the incoming packet was addressed directly to us (i.e. unicast), |
2074 | * use dst as the src for the reply. |
2075 | * The IN6_IFF_NOTREADY case should be VERY rare, but is possible |
2076 | * (for example) when we encounter an error while forwarding procedure |
2077 | * destined to a duplicated address of ours. |
2078 | * Note that ip6_getdstifaddr() may fail if we are in an error handling |
2079 | * procedure of an outgoing packet of our own, in which case we need |
2080 | * to search in the ifaddr list. |
2081 | */ |
2082 | if (IN6_IS_ADDR_MULTICAST(&origdst)) |
2083 | ; |
2084 | else if ((ip6a = ip6_getdstifaddr(m)) != NULL) { |
2085 | if ((ip6a->ip6a_flags & |
2086 | (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { |
2087 | ip6->ip6_src = ip6a->ip6a_src; |
2088 | ip6_src_filled = true; |
2089 | } |
2090 | } else { |
2091 | union { |
2092 | struct sockaddr_in6 sin6; |
2093 | struct sockaddr sa; |
2094 | } u; |
2095 | int _s; |
2096 | struct ifaddr *ifa; |
2097 | |
2098 | sockaddr_in6_init(&u.sin6, &origdst, 0, 0, 0); |
2099 | |
2100 | _s = pserialize_read_enter(); |
2101 | ifa = ifa_ifwithaddr(&u.sa); |
2102 | |
2103 | if (ifa != NULL) { |
2104 | ia = ifatoia6(ifa); |
2105 | if ((ia->ia6_flags & |
2106 | (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { |
2107 | ip6->ip6_src = ia->ia_addr.sin6_addr; |
2108 | ip6_src_filled = true; |
2109 | } |
2110 | } |
2111 | pserialize_read_exit(_s); |
2112 | } |
2113 | |
2114 | if (!ip6_src_filled) { |
2115 | int e; |
2116 | struct sockaddr_in6 sin6; |
2117 | struct route ro; |
2118 | |
2119 | /* |
2120 | * This case matches to multicasts, our anycast, or unicasts |
2121 | * that we do not own. Select a source address based on the |
2122 | * source address of the erroneous packet. |
2123 | */ |
2124 | /* zone ID should be embedded */ |
2125 | sockaddr_in6_init(&sin6, &ip6->ip6_dst, 0, 0, 0); |
2126 | |
2127 | memset(&ro, 0, sizeof(ro)); |
2128 | e = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, NULL, NULL, |
2129 | &ip6->ip6_src); |
2130 | rtcache_free(&ro); |
2131 | if (e != 0) { |
2132 | nd6log(LOG_DEBUG, |
2133 | "source can't be determined: " |
2134 | "dst=%s, error=%d\n" , |
2135 | ip6_sprintf(&sin6.sin6_addr), e); |
2136 | goto bad; |
2137 | } |
2138 | } |
2139 | |
2140 | ip6->ip6_flow = 0; |
2141 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; |
2142 | ip6->ip6_vfc |= IPV6_VERSION; |
2143 | ip6->ip6_nxt = IPPROTO_ICMPV6; |
2144 | rcvif = m_get_rcvif(m, &s); |
2145 | if (rcvif) { |
2146 | /* XXX: This may not be the outgoing interface */ |
2147 | ip6->ip6_hlim = ND_IFINFO(rcvif)->chlim; |
2148 | } else |
2149 | ip6->ip6_hlim = ip6_defhlim; |
2150 | m_put_rcvif(rcvif, &s); |
2151 | |
2152 | m->m_pkthdr.csum_flags = 0; |
2153 | icmp6->icmp6_cksum = 0; |
2154 | icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, |
2155 | sizeof(struct ip6_hdr), plen); |
2156 | |
2157 | /* |
2158 | * XXX option handling |
2159 | */ |
2160 | |
2161 | m->m_flags &= ~(M_BCAST|M_MCAST); |
2162 | |
2163 | /* |
2164 | * To avoid a "too big" situation at an intermediate router |
2165 | * and the path MTU discovery process, specify the IPV6_MINMTU flag. |
2166 | * Note that only echo and node information replies are affected, |
2167 | * since the length of ICMP6 errors is limited to the minimum MTU. |
2168 | */ |
2169 | if (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, &outif) |
2170 | != 0 && outif) |
2171 | icmp6_ifstat_inc(outif, ifs6_out_error); |
2172 | if (outif) |
2173 | icmp6_ifoutstat_inc(outif, type, code); |
2174 | |
2175 | return; |
2176 | |
2177 | bad: |
2178 | m_freem(m); |
2179 | return; |
2180 | } |
2181 | |
2182 | static const char * |
2183 | icmp6_redirect_diag(struct in6_addr *src6, struct in6_addr *dst6, |
2184 | struct in6_addr *tgt6) |
2185 | { |
2186 | static char buf[1024]; |
2187 | snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)" , |
2188 | ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6)); |
2189 | return buf; |
2190 | } |
2191 | |
2192 | void |
2193 | icmp6_redirect_input(struct mbuf *m, int off) |
2194 | { |
2195 | struct ifnet *ifp; |
2196 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
2197 | struct nd_redirect *nd_rd; |
2198 | int icmp6len = ntohs(ip6->ip6_plen); |
2199 | char *lladdr = NULL; |
2200 | int lladdrlen = 0; |
2201 | struct rtentry *rt = NULL; |
2202 | int is_router; |
2203 | int is_onlink; |
2204 | struct in6_addr src6 = ip6->ip6_src; |
2205 | struct in6_addr redtgt6; |
2206 | struct in6_addr reddst6; |
2207 | union nd_opts ndopts; |
2208 | struct psref psref; |
2209 | |
2210 | ifp = m_get_rcvif_psref(m, &psref); |
2211 | if (ifp == NULL) |
2212 | goto freeit; |
2213 | |
2214 | /* XXX if we are router, we don't update route by icmp6 redirect */ |
2215 | if (ip6_forwarding) |
2216 | goto freeit; |
2217 | if (!icmp6_rediraccept) |
2218 | goto freeit; |
2219 | |
2220 | IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); |
2221 | if (nd_rd == NULL) { |
2222 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
2223 | m_put_rcvif_psref(ifp, &psref); |
2224 | return; |
2225 | } |
2226 | redtgt6 = nd_rd->nd_rd_target; |
2227 | reddst6 = nd_rd->nd_rd_dst; |
2228 | |
2229 | if (in6_setscope(&redtgt6, ifp, NULL) || |
2230 | in6_setscope(&reddst6, ifp, NULL)) { |
2231 | goto freeit; |
2232 | } |
2233 | |
2234 | /* validation */ |
2235 | if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { |
2236 | nd6log(LOG_ERR, |
2237 | "ICMP6 redirect sent from %s rejected; " |
2238 | "must be from linklocal\n" , ip6_sprintf(&src6)); |
2239 | goto bad; |
2240 | } |
2241 | if (ip6->ip6_hlim != 255) { |
2242 | nd6log(LOG_ERR, |
2243 | "ICMP6 redirect sent from %s rejected; " |
2244 | "hlim=%d (must be 255)\n" , |
2245 | ip6_sprintf(&src6), ip6->ip6_hlim); |
2246 | goto bad; |
2247 | } |
2248 | { |
2249 | /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ |
2250 | struct sockaddr_in6 sin6; |
2251 | struct in6_addr *gw6; |
2252 | |
2253 | sockaddr_in6_init(&sin6, &reddst6, 0, 0, 0); |
2254 | rt = rtalloc1(sin6tosa(&sin6), 0); |
2255 | if (rt) { |
2256 | if (rt->rt_gateway == NULL || |
2257 | rt->rt_gateway->sa_family != AF_INET6) { |
2258 | nd6log(LOG_ERR, |
2259 | "ICMP6 redirect rejected; no route " |
2260 | "with inet6 gateway found for redirect dst: %s\n" , |
2261 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2262 | rtfree(rt); |
2263 | goto bad; |
2264 | } |
2265 | |
2266 | gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); |
2267 | if (memcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { |
2268 | nd6log(LOG_ERR, |
2269 | "ICMP6 redirect rejected; " |
2270 | "not equal to gw-for-src=%s (must be same): %s\n" , |
2271 | ip6_sprintf(gw6), |
2272 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2273 | rtfree(rt); |
2274 | goto bad; |
2275 | } |
2276 | } else { |
2277 | nd6log(LOG_ERR, "ICMP6 redirect rejected; " |
2278 | "no route found for redirect dst: %s\n" , |
2279 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2280 | goto bad; |
2281 | } |
2282 | rtfree(rt); |
2283 | rt = NULL; |
2284 | } |
2285 | if (IN6_IS_ADDR_MULTICAST(&reddst6)) { |
2286 | nd6log(LOG_ERR, "ICMP6 redirect rejected; " |
2287 | "redirect dst must be unicast: %s\n" , |
2288 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2289 | goto bad; |
2290 | } |
2291 | |
2292 | is_router = is_onlink = 0; |
2293 | if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) |
2294 | is_router = 1; /* router case */ |
2295 | if (memcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) |
2296 | is_onlink = 1; /* on-link destination case */ |
2297 | if (!is_router && !is_onlink) { |
2298 | nd6log(LOG_ERR, "ICMP6 redirect rejected; " |
2299 | "neither router case nor onlink case: %s\n" , |
2300 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2301 | goto bad; |
2302 | } |
2303 | /* validation passed */ |
2304 | |
2305 | icmp6len -= sizeof(*nd_rd); |
2306 | nd6_option_init(nd_rd + 1, icmp6len, &ndopts); |
2307 | if (nd6_options(&ndopts) < 0) { |
2308 | nd6log(LOG_INFO, "invalid ND option, rejected: %s\n" , |
2309 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2310 | /* nd6_options have incremented stats */ |
2311 | goto freeit; |
2312 | } |
2313 | |
2314 | if (ndopts.nd_opts_tgt_lladdr) { |
2315 | lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); |
2316 | lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; |
2317 | } |
2318 | |
2319 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
2320 | nd6log(LOG_INFO, "lladdrlen mismatch for %s " |
2321 | "(if %d, icmp6 packet %d): %s\n" , |
2322 | ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, |
2323 | icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); |
2324 | goto bad; |
2325 | } |
2326 | |
2327 | /* RFC 2461 8.3 */ |
2328 | nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, |
2329 | is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER); |
2330 | |
2331 | m_put_rcvif_psref(ifp, &psref); |
2332 | ifp = NULL; |
2333 | |
2334 | if (!is_onlink) { /* better router case. perform rtredirect. */ |
2335 | /* perform rtredirect */ |
2336 | struct sockaddr_in6 sdst; |
2337 | struct sockaddr_in6 sgw; |
2338 | struct sockaddr_in6 ssrc; |
2339 | unsigned long rtcount; |
2340 | struct rtentry *newrt = NULL; |
2341 | |
2342 | /* |
2343 | * do not install redirect route, if the number of entries |
2344 | * is too much (> hiwat). note that, the node (= host) will |
2345 | * work just fine even if we do not install redirect route |
2346 | * (there will be additional hops, though). |
2347 | */ |
2348 | rtcount = rt_timer_count(icmp6_redirect_timeout_q); |
2349 | if (0 <= ip6_maxdynroutes && rtcount >= ip6_maxdynroutes) |
2350 | goto freeit; |
2351 | if (0 <= icmp6_redirect_hiwat && rtcount > icmp6_redirect_hiwat) |
2352 | goto freeit; |
2353 | else if (0 <= icmp6_redirect_lowat && |
2354 | rtcount > icmp6_redirect_lowat) { |
2355 | /* |
2356 | * XXX nuke a victim, install the new one. |
2357 | */ |
2358 | } |
2359 | |
2360 | memset(&sdst, 0, sizeof(sdst)); |
2361 | memset(&sgw, 0, sizeof(sgw)); |
2362 | memset(&ssrc, 0, sizeof(ssrc)); |
2363 | sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6; |
2364 | sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len = |
2365 | sizeof(struct sockaddr_in6); |
2366 | bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); |
2367 | bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); |
2368 | bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); |
2369 | rtredirect(sin6tosa(&sdst), sin6tosa(&sgw), NULL, |
2370 | RTF_GATEWAY | RTF_HOST, sin6tosa(&ssrc), |
2371 | &newrt); |
2372 | |
2373 | if (newrt) { |
2374 | (void)rt_timer_add(newrt, icmp6_redirect_timeout, |
2375 | icmp6_redirect_timeout_q); |
2376 | rtfree(newrt); |
2377 | } |
2378 | } |
2379 | /* finally update cached route in each socket via pfctlinput */ |
2380 | { |
2381 | struct sockaddr_in6 sdst; |
2382 | |
2383 | sockaddr_in6_init(&sdst, &reddst6, 0, 0, 0); |
2384 | pfctlinput(PRC_REDIRECT_HOST, sin6tosa(&sdst)); |
2385 | #if defined(IPSEC) |
2386 | if (ipsec_used) |
2387 | key_sa_routechange(sin6tosa(&sdst)); |
2388 | #endif |
2389 | } |
2390 | |
2391 | freeit: |
2392 | if (ifp != NULL) |
2393 | m_put_rcvif_psref(ifp, &psref); |
2394 | m_freem(m); |
2395 | return; |
2396 | |
2397 | bad: |
2398 | m_put_rcvif_psref(ifp, &psref); |
2399 | ICMP6_STATINC(ICMP6_STAT_BADREDIRECT); |
2400 | m_freem(m); |
2401 | } |
2402 | |
2403 | void |
2404 | icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) |
2405 | { |
2406 | struct ifnet *ifp; /* my outgoing interface */ |
2407 | struct in6_addr *ifp_ll6; |
2408 | struct in6_addr *nexthop; |
2409 | struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ |
2410 | struct mbuf *m = NULL; /* newly allocated one */ |
2411 | struct ip6_hdr *ip6; /* m as struct ip6_hdr */ |
2412 | struct nd_redirect *nd_rd; |
2413 | size_t maxlen; |
2414 | u_char *p; |
2415 | struct sockaddr_in6 src_sa; |
2416 | |
2417 | icmp6_errcount(ICMP6_STAT_OUTERRHIST, ND_REDIRECT, 0); |
2418 | |
2419 | /* if we are not router, we don't send icmp6 redirect */ |
2420 | if (!ip6_forwarding) |
2421 | goto fail; |
2422 | |
2423 | /* sanity check */ |
2424 | if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) |
2425 | goto fail; |
2426 | |
2427 | /* |
2428 | * Address check: |
2429 | * the source address must identify a neighbor, and |
2430 | * the destination address must not be a multicast address |
2431 | * [RFC 2461, sec 8.2] |
2432 | */ |
2433 | sip6 = mtod(m0, struct ip6_hdr *); |
2434 | sockaddr_in6_init(&src_sa, &sip6->ip6_src, 0, 0, 0); |
2435 | if (nd6_is_addr_neighbor(&src_sa, ifp) == 0) |
2436 | goto fail; |
2437 | if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) |
2438 | goto fail; /* what should we do here? */ |
2439 | |
2440 | /* rate limit */ |
2441 | if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) |
2442 | goto fail; |
2443 | |
2444 | /* |
2445 | * Since we are going to append up to 1280 bytes (= IPV6_MMTU), |
2446 | * we almost always ask for an mbuf cluster for simplicity. |
2447 | * (MHLEN < IPV6_MMTU is almost always true) |
2448 | */ |
2449 | MGETHDR(m, M_DONTWAIT, MT_HEADER); |
2450 | if (m && IPV6_MMTU >= MHLEN) { |
2451 | #if IPV6_MMTU >= MCLBYTES |
2452 | _MCLGET(m, mcl_cache, IPV6_MMTU, M_DONTWAIT); |
2453 | #else |
2454 | MCLGET(m, M_DONTWAIT); |
2455 | #endif |
2456 | } |
2457 | |
2458 | if (!m) |
2459 | goto fail; |
2460 | m_reset_rcvif(m); |
2461 | m->m_len = 0; |
2462 | maxlen = M_TRAILINGSPACE(m); |
2463 | maxlen = min(IPV6_MMTU, maxlen); |
2464 | /* just for safety */ |
2465 | if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + |
2466 | ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { |
2467 | goto fail; |
2468 | } |
2469 | |
2470 | { |
2471 | /* get ip6 linklocal address for ifp(my outgoing interface). */ |
2472 | struct in6_ifaddr *ia; |
2473 | int s = pserialize_read_enter(); |
2474 | if ((ia = in6ifa_ifpforlinklocal(ifp, |
2475 | IN6_IFF_NOTREADY| |
2476 | IN6_IFF_ANYCAST)) == NULL) { |
2477 | pserialize_read_exit(s); |
2478 | goto fail; |
2479 | } |
2480 | ifp_ll6 = &ia->ia_addr.sin6_addr; |
2481 | pserialize_read_exit(s); |
2482 | } |
2483 | |
2484 | /* get ip6 linklocal address for the router. */ |
2485 | if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { |
2486 | struct sockaddr_in6 *sin6; |
2487 | sin6 = (struct sockaddr_in6 *)rt->rt_gateway; |
2488 | nexthop = &sin6->sin6_addr; |
2489 | if (!IN6_IS_ADDR_LINKLOCAL(nexthop)) |
2490 | nexthop = NULL; |
2491 | } else |
2492 | nexthop = NULL; |
2493 | |
2494 | /* ip6 */ |
2495 | ip6 = mtod(m, struct ip6_hdr *); |
2496 | ip6->ip6_flow = 0; |
2497 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; |
2498 | ip6->ip6_vfc |= IPV6_VERSION; |
2499 | /* ip6->ip6_plen will be set later */ |
2500 | ip6->ip6_nxt = IPPROTO_ICMPV6; |
2501 | ip6->ip6_hlim = 255; |
2502 | /* ip6->ip6_src must be linklocal addr for my outgoing if. */ |
2503 | bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); |
2504 | bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); |
2505 | |
2506 | /* ND Redirect */ |
2507 | nd_rd = (struct nd_redirect *)(ip6 + 1); |
2508 | nd_rd->nd_rd_type = ND_REDIRECT; |
2509 | nd_rd->nd_rd_code = 0; |
2510 | nd_rd->nd_rd_reserved = 0; |
2511 | if (rt->rt_flags & RTF_GATEWAY) { |
2512 | /* |
2513 | * nd_rd->nd_rd_target must be a link-local address in |
2514 | * better router cases. |
2515 | */ |
2516 | if (!nexthop) |
2517 | goto fail; |
2518 | bcopy(nexthop, &nd_rd->nd_rd_target, |
2519 | sizeof(nd_rd->nd_rd_target)); |
2520 | bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, |
2521 | sizeof(nd_rd->nd_rd_dst)); |
2522 | } else { |
2523 | /* make sure redtgt == reddst */ |
2524 | nexthop = &sip6->ip6_dst; |
2525 | bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, |
2526 | sizeof(nd_rd->nd_rd_target)); |
2527 | bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, |
2528 | sizeof(nd_rd->nd_rd_dst)); |
2529 | } |
2530 | |
2531 | p = (u_char *)(nd_rd + 1); |
2532 | |
2533 | { |
2534 | /* target lladdr option */ |
2535 | struct llentry *ln = NULL; |
2536 | int len; |
2537 | struct nd_opt_hdr *nd_opt; |
2538 | char *lladdr; |
2539 | |
2540 | ln = nd6_lookup(nexthop, ifp, false); |
2541 | if (ln == NULL) |
2542 | goto nolladdropt; |
2543 | len = sizeof(*nd_opt) + ifp->if_addrlen; |
2544 | len = (len + 7) & ~7; /* round by 8 */ |
2545 | /* safety check */ |
2546 | if (len + (p - (u_char *)ip6) > maxlen) { |
2547 | LLE_RUNLOCK(ln); |
2548 | goto nolladdropt; |
2549 | } |
2550 | if (ln->la_flags & LLE_VALID) { |
2551 | nd_opt = (struct nd_opt_hdr *)p; |
2552 | nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; |
2553 | nd_opt->nd_opt_len = len >> 3; |
2554 | lladdr = (char *)(nd_opt + 1); |
2555 | memcpy(lladdr, &ln->ll_addr, ifp->if_addrlen); |
2556 | p += len; |
2557 | } |
2558 | LLE_RUNLOCK(ln); |
2559 | } |
2560 | nolladdropt:; |
2561 | |
2562 | m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; |
2563 | |
2564 | /* just to be safe */ |
2565 | if (m0->m_flags & M_DECRYPTED) |
2566 | goto noredhdropt; |
2567 | if (p - (u_char *)ip6 > maxlen) |
2568 | goto noredhdropt; |
2569 | |
2570 | { |
2571 | /* redirected header option */ |
2572 | int len; |
2573 | struct nd_opt_rd_hdr *nd_opt_rh; |
2574 | |
2575 | /* |
2576 | * compute the maximum size for icmp6 redirect header option. |
2577 | * XXX room for auth header? |
2578 | */ |
2579 | len = maxlen - (p - (u_char *)ip6); |
2580 | len &= ~7; |
2581 | |
2582 | /* |
2583 | * Redirected header option spec (RFC2461 4.6.3) talks nothing |
2584 | * about padding/truncate rule for the original IP packet. |
2585 | * From the discussion on IPv6imp in Feb 1999, |
2586 | * the consensus was: |
2587 | * - "attach as much as possible" is the goal |
2588 | * - pad if not aligned (original size can be guessed by |
2589 | * original ip6 header) |
2590 | * Following code adds the padding if it is simple enough, |
2591 | * and truncates if not. |
2592 | */ |
2593 | if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { |
2594 | /* not enough room, truncate */ |
2595 | m_adj(m0, (len - sizeof(*nd_opt_rh)) - |
2596 | m0->m_pkthdr.len); |
2597 | } else { |
2598 | /* |
2599 | * enough room, truncate if not aligned. |
2600 | * we don't pad here for simplicity. |
2601 | */ |
2602 | size_t ; |
2603 | |
2604 | extra = m0->m_pkthdr.len % 8; |
2605 | if (extra) { |
2606 | /* truncate */ |
2607 | m_adj(m0, -extra); |
2608 | } |
2609 | len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); |
2610 | } |
2611 | |
2612 | nd_opt_rh = (struct nd_opt_rd_hdr *)p; |
2613 | memset(nd_opt_rh, 0, sizeof(*nd_opt_rh)); |
2614 | nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; |
2615 | nd_opt_rh->nd_opt_rh_len = len >> 3; |
2616 | p += sizeof(*nd_opt_rh); |
2617 | m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; |
2618 | |
2619 | /* connect m0 to m */ |
2620 | m->m_pkthdr.len += m0->m_pkthdr.len; |
2621 | m_cat(m, m0); |
2622 | m0 = NULL; |
2623 | } |
2624 | noredhdropt: |
2625 | if (m0) { |
2626 | m_freem(m0); |
2627 | m0 = NULL; |
2628 | } |
2629 | |
2630 | /* XXX: clear embedded link IDs in the inner header */ |
2631 | in6_clearscope(&sip6->ip6_src); |
2632 | in6_clearscope(&sip6->ip6_dst); |
2633 | in6_clearscope(&nd_rd->nd_rd_target); |
2634 | in6_clearscope(&nd_rd->nd_rd_dst); |
2635 | |
2636 | ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); |
2637 | |
2638 | nd_rd->nd_rd_cksum = 0; |
2639 | nd_rd->nd_rd_cksum |
2640 | = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); |
2641 | |
2642 | /* send the packet to outside... */ |
2643 | if (ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL) != 0) |
2644 | icmp6_ifstat_inc(ifp, ifs6_out_error); |
2645 | |
2646 | icmp6_ifstat_inc(ifp, ifs6_out_msg); |
2647 | icmp6_ifstat_inc(ifp, ifs6_out_redirect); |
2648 | ICMP6_STATINC(ICMP6_STAT_OUTHIST + ND_REDIRECT); |
2649 | |
2650 | return; |
2651 | |
2652 | fail: |
2653 | if (m) |
2654 | m_freem(m); |
2655 | if (m0) |
2656 | m_freem(m0); |
2657 | } |
2658 | |
2659 | /* |
2660 | * ICMPv6 socket option processing. |
2661 | */ |
2662 | int |
2663 | icmp6_ctloutput(int op, struct socket *so, struct sockopt *sopt) |
2664 | { |
2665 | int error = 0; |
2666 | struct in6pcb *in6p = sotoin6pcb(so); |
2667 | |
2668 | if (sopt->sopt_level != IPPROTO_ICMPV6) |
2669 | return rip6_ctloutput(op, so, sopt); |
2670 | |
2671 | switch (op) { |
2672 | case PRCO_SETOPT: |
2673 | switch (sopt->sopt_name) { |
2674 | case ICMP6_FILTER: |
2675 | { |
2676 | struct icmp6_filter fil; |
2677 | |
2678 | error = sockopt_get(sopt, &fil, sizeof(fil)); |
2679 | if (error) |
2680 | break; |
2681 | memcpy(in6p->in6p_icmp6filt, &fil, |
2682 | sizeof(struct icmp6_filter)); |
2683 | error = 0; |
2684 | break; |
2685 | } |
2686 | |
2687 | default: |
2688 | error = ENOPROTOOPT; |
2689 | break; |
2690 | } |
2691 | break; |
2692 | |
2693 | case PRCO_GETOPT: |
2694 | switch (sopt->sopt_name) { |
2695 | case ICMP6_FILTER: |
2696 | { |
2697 | if (in6p->in6p_icmp6filt == NULL) { |
2698 | error = EINVAL; |
2699 | break; |
2700 | } |
2701 | error = sockopt_set(sopt, in6p->in6p_icmp6filt, |
2702 | sizeof(struct icmp6_filter)); |
2703 | break; |
2704 | } |
2705 | |
2706 | default: |
2707 | error = ENOPROTOOPT; |
2708 | break; |
2709 | } |
2710 | break; |
2711 | } |
2712 | |
2713 | return (error); |
2714 | } |
2715 | |
2716 | /* |
2717 | * Perform rate limit check. |
2718 | * Returns 0 if it is okay to send the icmp6 packet. |
2719 | * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate |
2720 | * limitation. |
2721 | * |
2722 | * XXX per-destination/type check necessary? |
2723 | */ |
2724 | static int |
2725 | icmp6_ratelimit( |
2726 | const struct in6_addr *dst, /* not used at this moment */ |
2727 | const int type, /* not used at this moment */ |
2728 | const int code) /* not used at this moment */ |
2729 | { |
2730 | int ret; |
2731 | |
2732 | ret = 0; /* okay to send */ |
2733 | |
2734 | /* PPS limit */ |
2735 | if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count, |
2736 | icmp6errppslim)) { |
2737 | /* The packet is subject to rate limit */ |
2738 | ret++; |
2739 | } |
2740 | |
2741 | return ret; |
2742 | } |
2743 | |
2744 | static struct rtentry * |
2745 | icmp6_mtudisc_clone(struct sockaddr *dst) |
2746 | { |
2747 | struct rtentry *rt; |
2748 | int error; |
2749 | |
2750 | rt = rtalloc1(dst, 1); |
2751 | if (rt == 0) |
2752 | return NULL; |
2753 | |
2754 | /* If we didn't get a host route, allocate one */ |
2755 | if ((rt->rt_flags & RTF_HOST) == 0) { |
2756 | struct rtentry *nrt; |
2757 | |
2758 | error = rtrequest(RTM_ADD, dst, rt->rt_gateway, NULL, |
2759 | RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC, &nrt); |
2760 | if (error) { |
2761 | rtfree(rt); |
2762 | return NULL; |
2763 | } |
2764 | nrt->rt_rmx = rt->rt_rmx; |
2765 | rtfree(rt); |
2766 | rt = nrt; |
2767 | } |
2768 | error = rt_timer_add(rt, icmp6_mtudisc_timeout, |
2769 | icmp6_mtudisc_timeout_q); |
2770 | if (error) { |
2771 | rtfree(rt); |
2772 | return NULL; |
2773 | } |
2774 | |
2775 | return rt; /* caller need to call rtfree() */ |
2776 | } |
2777 | |
2778 | static void |
2779 | icmp6_mtudisc_timeout(struct rtentry *rt, struct rttimer *r) |
2780 | { |
2781 | |
2782 | KASSERT(rt != NULL); |
2783 | rt_assert_referenced(rt); |
2784 | |
2785 | if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) == |
2786 | (RTF_DYNAMIC | RTF_HOST)) { |
2787 | rtrequest(RTM_DELETE, rt_getkey(rt), |
2788 | rt->rt_gateway, rt_mask(rt), rt->rt_flags, NULL); |
2789 | } else { |
2790 | if (!(rt->rt_rmx.rmx_locks & RTV_MTU)) |
2791 | rt->rt_rmx.rmx_mtu = 0; |
2792 | } |
2793 | } |
2794 | |
2795 | static void |
2796 | icmp6_redirect_timeout(struct rtentry *rt, struct rttimer *r) |
2797 | { |
2798 | |
2799 | KASSERT(rt != NULL); |
2800 | rt_assert_referenced(rt); |
2801 | |
2802 | if ((rt->rt_flags & (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) == |
2803 | (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) { |
2804 | rtrequest(RTM_DELETE, rt_getkey(rt), |
2805 | rt->rt_gateway, rt_mask(rt), rt->rt_flags, NULL); |
2806 | } |
2807 | } |
2808 | |
2809 | /* |
2810 | * sysctl helper routine for the net.inet6.icmp6.nd6 nodes. silly? |
2811 | */ |
2812 | static int |
2813 | sysctl_net_inet6_icmp6_nd6(SYSCTLFN_ARGS) |
2814 | { |
2815 | (void)&name; |
2816 | (void)&l; |
2817 | (void)&oname; |
2818 | |
2819 | if (namelen != 0) |
2820 | return (EINVAL); |
2821 | |
2822 | return (nd6_sysctl(rnode->sysctl_num, oldp, oldlenp, |
2823 | /*XXXUNCONST*/ |
2824 | __UNCONST(newp), newlen)); |
2825 | } |
2826 | |
2827 | static int |
2828 | sysctl_net_inet6_icmp6_stats(SYSCTLFN_ARGS) |
2829 | { |
2830 | |
2831 | return (NETSTAT_SYSCTL(icmp6stat_percpu, ICMP6_NSTATS)); |
2832 | } |
2833 | |
2834 | static int |
2835 | sysctl_net_inet6_icmp6_redirtimeout(SYSCTLFN_ARGS) |
2836 | { |
2837 | int error, tmp; |
2838 | struct sysctlnode node; |
2839 | |
2840 | node = *rnode; |
2841 | node.sysctl_data = &tmp; |
2842 | tmp = icmp6_redirtimeout; |
2843 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
2844 | if (error || newp == NULL) |
2845 | return error; |
2846 | if (tmp < 0) |
2847 | return EINVAL; |
2848 | icmp6_redirtimeout = tmp; |
2849 | |
2850 | if (icmp6_redirect_timeout_q != NULL) { |
2851 | if (icmp6_redirtimeout == 0) { |
2852 | rt_timer_queue_destroy(icmp6_redirect_timeout_q); |
2853 | } else { |
2854 | rt_timer_queue_change(icmp6_redirect_timeout_q, |
2855 | icmp6_redirtimeout); |
2856 | } |
2857 | } else if (icmp6_redirtimeout > 0) { |
2858 | icmp6_redirect_timeout_q = |
2859 | rt_timer_queue_create(icmp6_redirtimeout); |
2860 | } |
2861 | |
2862 | return 0; |
2863 | } |
2864 | |
2865 | static void |
2866 | sysctl_net_inet6_icmp6_setup(struct sysctllog **clog) |
2867 | { |
2868 | extern int nd6_maxqueuelen; /* defined in nd6.c */ |
2869 | |
2870 | sysctl_createv(clog, 0, NULL, NULL, |
2871 | CTLFLAG_PERMANENT, |
2872 | CTLTYPE_NODE, "inet6" , NULL, |
2873 | NULL, 0, NULL, 0, |
2874 | CTL_NET, PF_INET6, CTL_EOL); |
2875 | sysctl_createv(clog, 0, NULL, NULL, |
2876 | CTLFLAG_PERMANENT, |
2877 | CTLTYPE_NODE, "icmp6" , |
2878 | SYSCTL_DESCR("ICMPv6 related settings" ), |
2879 | NULL, 0, NULL, 0, |
2880 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, CTL_EOL); |
2881 | |
2882 | sysctl_createv(clog, 0, NULL, NULL, |
2883 | CTLFLAG_PERMANENT, |
2884 | CTLTYPE_STRUCT, "stats" , |
2885 | SYSCTL_DESCR("ICMPv6 transmission statistics" ), |
2886 | sysctl_net_inet6_icmp6_stats, 0, NULL, 0, |
2887 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2888 | ICMPV6CTL_STATS, CTL_EOL); |
2889 | sysctl_createv(clog, 0, NULL, NULL, |
2890 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2891 | CTLTYPE_INT, "rediraccept" , |
2892 | SYSCTL_DESCR("Accept and process redirect messages" ), |
2893 | NULL, 0, &icmp6_rediraccept, 0, |
2894 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2895 | ICMPV6CTL_REDIRACCEPT, CTL_EOL); |
2896 | sysctl_createv(clog, 0, NULL, NULL, |
2897 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2898 | CTLTYPE_INT, "redirtimeout" , |
2899 | SYSCTL_DESCR("Redirect generated route lifetime" ), |
2900 | sysctl_net_inet6_icmp6_redirtimeout, 0, |
2901 | &icmp6_redirtimeout, 0, |
2902 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2903 | ICMPV6CTL_REDIRTIMEOUT, CTL_EOL); |
2904 | #if 0 /* obsoleted */ |
2905 | sysctl_createv(clog, 0, NULL, NULL, |
2906 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2907 | CTLTYPE_INT, "errratelimit" , NULL, |
2908 | NULL, 0, &icmp6_errratelimit, 0, |
2909 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2910 | ICMPV6CTL_ERRRATELIMIT, CTL_EOL); |
2911 | #endif |
2912 | sysctl_createv(clog, 0, NULL, NULL, |
2913 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2914 | CTLTYPE_INT, "nd6_prune" , |
2915 | SYSCTL_DESCR("Neighbor discovery prune interval" ), |
2916 | NULL, 0, &nd6_prune, 0, |
2917 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2918 | ICMPV6CTL_ND6_PRUNE, CTL_EOL); |
2919 | sysctl_createv(clog, 0, NULL, NULL, |
2920 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2921 | CTLTYPE_INT, "nd6_delay" , |
2922 | SYSCTL_DESCR("First probe delay time" ), |
2923 | NULL, 0, &nd6_delay, 0, |
2924 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2925 | ICMPV6CTL_ND6_DELAY, CTL_EOL); |
2926 | sysctl_createv(clog, 0, NULL, NULL, |
2927 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2928 | CTLTYPE_INT, "nd6_umaxtries" , |
2929 | SYSCTL_DESCR("Number of unicast discovery attempts" ), |
2930 | NULL, 0, &nd6_umaxtries, 0, |
2931 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2932 | ICMPV6CTL_ND6_UMAXTRIES, CTL_EOL); |
2933 | sysctl_createv(clog, 0, NULL, NULL, |
2934 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2935 | CTLTYPE_INT, "nd6_mmaxtries" , |
2936 | SYSCTL_DESCR("Number of multicast discovery attempts" ), |
2937 | NULL, 0, &nd6_mmaxtries, 0, |
2938 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2939 | ICMPV6CTL_ND6_MMAXTRIES, CTL_EOL); |
2940 | sysctl_createv(clog, 0, NULL, NULL, |
2941 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2942 | CTLTYPE_INT, "nd6_useloopback" , |
2943 | SYSCTL_DESCR("Use loopback interface for local traffic" ), |
2944 | NULL, 0, &nd6_useloopback, 0, |
2945 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2946 | ICMPV6CTL_ND6_USELOOPBACK, CTL_EOL); |
2947 | #if 0 /* obsoleted */ |
2948 | sysctl_createv(clog, 0, NULL, NULL, |
2949 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2950 | CTLTYPE_INT, "nd6_proxyall" , NULL, |
2951 | NULL, 0, &nd6_proxyall, 0, |
2952 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2953 | ICMPV6CTL_ND6_PROXYALL, CTL_EOL); |
2954 | #endif |
2955 | sysctl_createv(clog, 0, NULL, NULL, |
2956 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2957 | CTLTYPE_INT, "nodeinfo" , |
2958 | SYSCTL_DESCR("Respond to node information requests" ), |
2959 | NULL, 0, &icmp6_nodeinfo, 0, |
2960 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2961 | ICMPV6CTL_NODEINFO, CTL_EOL); |
2962 | sysctl_createv(clog, 0, NULL, NULL, |
2963 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2964 | CTLTYPE_INT, "errppslimit" , |
2965 | SYSCTL_DESCR("Maximum ICMP errors sent per second" ), |
2966 | NULL, 0, &icmp6errppslim, 0, |
2967 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2968 | ICMPV6CTL_ERRPPSLIMIT, CTL_EOL); |
2969 | sysctl_createv(clog, 0, NULL, NULL, |
2970 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2971 | CTLTYPE_INT, "nd6_maxnudhint" , |
2972 | SYSCTL_DESCR("Maximum neighbor unreachable hint count" ), |
2973 | NULL, 0, &nd6_maxnudhint, 0, |
2974 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2975 | ICMPV6CTL_ND6_MAXNUDHINT, CTL_EOL); |
2976 | sysctl_createv(clog, 0, NULL, NULL, |
2977 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2978 | CTLTYPE_INT, "mtudisc_hiwat" , |
2979 | SYSCTL_DESCR("Low mark on MTU Discovery route timers" ), |
2980 | NULL, 0, &icmp6_mtudisc_hiwat, 0, |
2981 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2982 | ICMPV6CTL_MTUDISC_HIWAT, CTL_EOL); |
2983 | sysctl_createv(clog, 0, NULL, NULL, |
2984 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2985 | CTLTYPE_INT, "mtudisc_lowat" , |
2986 | SYSCTL_DESCR("Low mark on MTU Discovery route timers" ), |
2987 | NULL, 0, &icmp6_mtudisc_lowat, 0, |
2988 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2989 | ICMPV6CTL_MTUDISC_LOWAT, CTL_EOL); |
2990 | sysctl_createv(clog, 0, NULL, NULL, |
2991 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
2992 | CTLTYPE_INT, "nd6_debug" , |
2993 | SYSCTL_DESCR("Enable neighbor discovery debug output" ), |
2994 | NULL, 0, &nd6_debug, 0, |
2995 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
2996 | ICMPV6CTL_ND6_DEBUG, CTL_EOL); |
2997 | sysctl_createv(clog, 0, NULL, NULL, |
2998 | CTLFLAG_PERMANENT, |
2999 | CTLTYPE_STRUCT, "nd6_drlist" , |
3000 | SYSCTL_DESCR("Default router list" ), |
3001 | sysctl_net_inet6_icmp6_nd6, 0, NULL, 0, |
3002 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
3003 | ICMPV6CTL_ND6_DRLIST, CTL_EOL); |
3004 | sysctl_createv(clog, 0, NULL, NULL, |
3005 | CTLFLAG_PERMANENT, |
3006 | CTLTYPE_STRUCT, "nd6_prlist" , |
3007 | SYSCTL_DESCR("Prefix list" ), |
3008 | sysctl_net_inet6_icmp6_nd6, 0, NULL, 0, |
3009 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
3010 | ICMPV6CTL_ND6_PRLIST, CTL_EOL); |
3011 | sysctl_createv(clog, 0, NULL, NULL, |
3012 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
3013 | CTLTYPE_INT, "maxqueuelen" , |
3014 | SYSCTL_DESCR("max packet queue len for a unresolved ND" ), |
3015 | NULL, 1, &nd6_maxqueuelen, 0, |
3016 | CTL_NET, PF_INET6, IPPROTO_ICMPV6, |
3017 | ICMPV6CTL_ND6_MAXQLEN, CTL_EOL); |
3018 | } |
3019 | |
3020 | void |
3021 | icmp6_statinc(u_int stat) |
3022 | { |
3023 | |
3024 | KASSERT(stat < ICMP6_NSTATS); |
3025 | ICMP6_STATINC(stat); |
3026 | } |
3027 | |