]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/net/bsd_tcpip/v2_0/src/sys/netinet6/icmp6.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / bsd_tcpip / v2_0 / src / sys / netinet6 / icmp6.c
1 //==========================================================================
2 //
3 //      src/sys/netinet6/icmp6.c
4 //
5 //==========================================================================
6 //####BSDCOPYRIGHTBEGIN####
7 //
8 // -------------------------------------------
9 //
10 // Portions of this software may have been derived from OpenBSD, 
11 // FreeBSD or other sources, and are covered by the appropriate
12 // copyright disclaimers included herein.
13 //
14 // Portions created by Red Hat are
15 // Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
16 //
17 // -------------------------------------------
18 //
19 //####BSDCOPYRIGHTEND####
20 //==========================================================================
21
22 /*      $KAME: icmp6.c,v 1.269 2001/12/18 02:19:16 jinmei Exp $ */
23
24 /*
25  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
26  * All rights reserved.
27  *
28  * Redistribution and use in source and binary forms, with or without
29  * modification, are permitted provided that the following conditions
30  * are met:
31  * 1. Redistributions of source code must retain the above copyright
32  *    notice, this list of conditions and the following disclaimer.
33  * 2. Redistributions in binary form must reproduce the above copyright
34  *    notice, this list of conditions and the following disclaimer in the
35  *    documentation and/or other materials provided with the distribution.
36  * 3. Neither the name of the project nor the names of its contributors
37  *    may be used to endorse or promote products derived from this software
38  *    without specific prior written permission.
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
44  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  */
52
53 /*
54  * Copyright (c) 1982, 1986, 1988, 1993
55  *      The Regents of the University of California.  All rights reserved.
56  *
57  * Redistribution and use in source and binary forms, with or without
58  * modification, are permitted provided that the following conditions
59  * are met:
60  * 1. Redistributions of source code must retain the above copyright
61  *    notice, this list of conditions and the following disclaimer.
62  * 2. Redistributions in binary form must reproduce the above copyright
63  *    notice, this list of conditions and the following disclaimer in the
64  *    documentation and/or other materials provided with the distribution.
65  * 3. All advertising materials mentioning features or use of this software
66  *    must display the following acknowledgement:
67  *      This product includes software developed by the University of
68  *      California, Berkeley and its contributors.
69  * 4. Neither the name of the University nor the names of its contributors
70  *    may be used to endorse or promote products derived from this software
71  *    without specific prior written permission.
72  *
73  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83  * SUCH DAMAGE.
84  *
85  *      @(#)ip_icmp.c   8.2 (Berkeley) 1/4/94
86  */
87
88 #include <sys/param.h>
89 #include <sys/malloc.h>
90 #include <sys/mbuf.h>
91 #include <sys/protosw.h>
92 #include <sys/socket.h>
93 #include <sys/socketvar.h>
94 #include <sys/domain.h>
95
96 #include <net/if.h>
97 #include <net/route.h>
98 #include <net/if_dl.h>
99 #include <net/if_types.h>
100
101 #include <netinet/in.h>
102 #include <netinet/in_var.h>
103 #if defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
104 #include <netinet/in_systm.h>
105 #include <netinet/ip.h>
106 #endif
107 #include <netinet/ip6.h>
108 #include <netinet6/ip6_var.h>
109 #include <netinet/icmp6.h>
110 #include <netinet6/mld6_var.h>
111 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
112 #include <netinet/in_pcb.h>
113 #include <netinet6/in6_pcb.h>
114 #elif defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802)
115 #include <netinet/in_pcb.h>
116 #else
117 #include <netinet6/in6_pcb.h>
118 #endif
119 #include <netinet6/nd6.h>
120 #include <netinet6/in6_ifattach.h>
121 #include <netinet6/ip6protosw.h>
122
123 #ifdef __OpenBSD__ /* KAME IPSEC */
124 #undef IPSEC
125 #endif
126
127 #ifdef IPSEC
128 #include <netinet6/ipsec.h>
129 #include <netkey/key.h>
130 #endif
131
132 #ifdef MIP6
133 #include <netinet6/mip6.h>
134 #endif
135
136 #ifdef HAVE_NRL_INPCB
137 /* inpcb members */
138 #define in6pcb          inpcb
139 #define in6p_laddr      inp_laddr6
140 #define in6p_faddr      inp_faddr6
141 #define in6p_icmp6filt  inp_icmp6filt
142 #define in6p_route      inp_route
143 #define in6p_socket     inp_socket
144 #define in6p_flags      inp_flags
145 #define in6p_moptions   inp_moptions6
146 #define in6p_outputopts inp_outputopts6
147 #define in6p_ip6        inp_ipv6
148 #define in6p_flowinfo   inp_flowinfo
149 #define in6p_sp         inp_sp
150 #define in6p_next       inp_next
151 #define in6p_prev       inp_prev
152
153 /*
154  * for KAME src sync over BSD*'s.  XXX: FreeBSD (>=3) are VERY different from
155  * others...
156  */
157 #define in6p_ip6_nxt    inp_ipv6.ip6_nxt
158 #endif
159
160 extern struct domain inet6domain;
161 extern struct ip6protosw inet6sw[];
162 extern u_char ip6_protox[];
163
164 struct icmp6stat icmp6stat;
165
166 #if defined (__OpenBSD__)
167 extern struct inpcbtable rawin6pcbtable;
168 #elif !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
169 extern struct in6pcb rawin6pcb;
170 #else
171 extern struct inpcbhead ripcb;
172 #endif
173 extern int icmp6errppslim;
174 static int icmp6errpps_count = 0;
175 static struct timeval icmp6errppslim_last;
176 extern int icmp6_nodeinfo;
177 #if defined(__NetBSD__) || defined(__OpenBSD__)
178 /*
179  * List of callbacks to notify when Path MTU changes are made.
180  */
181 struct icmp6_mtudisc_callback {
182         LIST_ENTRY(icmp6_mtudisc_callback) mc_list;
183         void (*mc_func) __P((struct in6_addr *));
184 };
185
186 LIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks =
187     LIST_HEAD_INITIALIZER(&icmp6_mtudisc_callbacks);
188
189 static struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
190 extern int pmtu_expire;
191
192 /* XXX do these values make any sense? */
193 static int icmp6_mtudisc_hiwat = 1280;
194 static int icmp6_mtudisc_lowat = 256;
195
196 /*
197  * keep track of # of redirect routes.
198  */
199 static struct rttimer_queue *icmp6_redirect_timeout_q = NULL;
200
201 /* XXX do these values make any sense? */
202 static int icmp6_redirect_hiwat = 1280;
203 static int icmp6_redirect_lowat = 1024;
204 #endif
205
206 static void icmp6_errcount __P((struct icmp6errstat *, int, int));
207 static int icmp6_rip6_input __P((struct mbuf **, int));
208 static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
209 static const char *icmp6_redirect_diag __P((struct in6_addr *,
210         struct in6_addr *, struct in6_addr *));
211 #ifndef HAVE_PPSRATECHECK
212 static int ppsratecheck __P((struct timeval *, int *, int));
213 #endif
214 static struct mbuf *ni6_input __P((struct mbuf *, int));
215 static struct mbuf *ni6_nametodns __P((const char *, int, int));
216 static int ni6_dnsmatch __P((const char *, int, const char *, int));
217 static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *,
218                           struct ifnet **, char *));
219 static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *,
220                                 struct ifnet *, int));
221 static int icmp6_notify_error __P((struct mbuf *, int, int, int));
222 static int icmp6_recover_src __P((struct mbuf *));
223 #if defined(__NetBSD__) || defined(__OpenBSD__)
224 static struct rtentry *icmp6_mtudisc_clone __P((struct sockaddr *));
225 static void icmp6_mtudisc_timeout __P((struct rtentry *, struct rttimer *));
226 static void icmp6_redirect_timeout __P((struct rtentry *, struct rttimer *));
227 #endif
228
229 void
230 icmp6_init()
231 {
232         mld6_init();
233 #if defined(__NetBSD__) || defined(__OpenBSD__)
234         icmp6_mtudisc_timeout_q = rt_timer_queue_create(pmtu_expire);
235         icmp6_redirect_timeout_q = rt_timer_queue_create(icmp6_redirtimeout);
236 #endif
237 }
238
239 static void
240 icmp6_errcount(stat, type, code)
241         struct icmp6errstat *stat;
242         int type, code;
243 {
244         switch (type) {
245         case ICMP6_DST_UNREACH:
246                 switch (code) {
247                 case ICMP6_DST_UNREACH_NOROUTE:
248                         stat->icp6errs_dst_unreach_noroute++;
249                         return;
250                 case ICMP6_DST_UNREACH_ADMIN:
251                         stat->icp6errs_dst_unreach_admin++;
252                         return;
253                 case ICMP6_DST_UNREACH_BEYONDSCOPE:
254                         stat->icp6errs_dst_unreach_beyondscope++;
255                         return;
256                 case ICMP6_DST_UNREACH_ADDR:
257                         stat->icp6errs_dst_unreach_addr++;
258                         return;
259                 case ICMP6_DST_UNREACH_NOPORT:
260                         stat->icp6errs_dst_unreach_noport++;
261                         return;
262                 }
263                 break;
264         case ICMP6_PACKET_TOO_BIG:
265                 stat->icp6errs_packet_too_big++;
266                 return;
267         case ICMP6_TIME_EXCEEDED:
268                 switch (code) {
269                 case ICMP6_TIME_EXCEED_TRANSIT:
270                         stat->icp6errs_time_exceed_transit++;
271                         return;
272                 case ICMP6_TIME_EXCEED_REASSEMBLY:
273                         stat->icp6errs_time_exceed_reassembly++;
274                         return;
275                 }
276                 break;
277         case ICMP6_PARAM_PROB:
278                 switch (code) {
279                 case ICMP6_PARAMPROB_HEADER:
280                         stat->icp6errs_paramprob_header++;
281                         return;
282                 case ICMP6_PARAMPROB_NEXTHEADER:
283                         stat->icp6errs_paramprob_nextheader++;
284                         return;
285                 case ICMP6_PARAMPROB_OPTION:
286                         stat->icp6errs_paramprob_option++;
287                         return;
288                 }
289                 break;
290         case ND_REDIRECT:
291                 stat->icp6errs_redirect++;
292                 return;
293         }
294         stat->icp6errs_unknown++;
295 }
296
297 #if defined(__NetBSD__) || defined(__OpenBSD__)
298 /*
299  * Register a Path MTU Discovery callback.
300  */
301 void
302 icmp6_mtudisc_callback_register(func)
303         void (*func) __P((struct in6_addr *));
304 {
305         struct icmp6_mtudisc_callback *mc;
306
307         for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
308              mc = LIST_NEXT(mc, mc_list)) {
309                 if (mc->mc_func == func)
310                         return;
311         }
312
313         mc = malloc(sizeof(*mc), M_PCB, M_NOWAIT);
314         if (mc == NULL)
315                 panic("icmp6_mtudisc_callback_register");
316
317         mc->mc_func = func;
318         LIST_INSERT_HEAD(&icmp6_mtudisc_callbacks, mc, mc_list);
319 }
320 #endif
321
322 /*
323  * Generate an error packet of type error in response to bad IP6 packet.
324  */
325 void
326 icmp6_error(m, type, code, param)
327         struct mbuf *m;
328         int type, code, param;
329 {
330         struct ip6_hdr *oip6, *nip6;
331         struct icmp6_hdr *icmp6;
332         struct mbuf *n;
333         struct ip6aux *ip6a;
334         struct in6_addr nip6_src, *nip6_srcp;
335         u_int preplen;
336         int off;
337         int nxt;
338
339         icmp6stat.icp6s_error++;
340
341         /* count per-type-code statistics */
342         icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code);
343
344 #ifdef M_DECRYPTED      /* not openbsd */
345         if (m->m_flags & M_DECRYPTED) {
346                 icmp6stat.icp6s_canterror++;
347                 goto freeit;
348         }
349 #endif
350
351 #ifndef PULLDOWN_TEST
352         IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
353 #else
354         if (m->m_len < sizeof(struct ip6_hdr)) {
355                 m = m_pullup(m, sizeof(struct ip6_hdr));
356                 if (m == NULL)
357                         return;
358         }
359 #endif
360         oip6 = mtod(m, struct ip6_hdr *);
361
362         /*
363          * If the destination address of the erroneous packet is a multicast
364          * address, or the packet was sent using link-layer multicast,
365          * we should basically suppress sending an error (RFC 2463, Section
366          * 2.4).
367          * We have two exceptions (the item e.2 in that section):
368          * - the Pakcet Too Big message can be sent for path MTU discovery.
369          * - the Parameter Problem Message that can be allowed an icmp6 error
370          *   in the option type field.  This check has been done in
371          *   ip6_unknown_opt(), so we can just check the type and code.
372          */
373         if ((m->m_flags & (M_BCAST|M_MCAST) ||
374              IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
375             (type != ICMP6_PACKET_TOO_BIG &&
376              (type != ICMP6_PARAM_PROB ||
377               code != ICMP6_PARAMPROB_OPTION)))
378                 goto freeit;
379
380         /*
381          * RFC 2463, 2.4 (e.5): source address check.
382          * XXX: the case of anycast source?
383          */
384         if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
385             IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
386                 goto freeit;
387
388         /*
389          * If we are about to send ICMPv6 against ICMPv6 error/redirect,
390          * don't do it.
391          */
392         nxt = -1;
393         off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
394         if (off >= 0 && nxt == IPPROTO_ICMPV6) {
395                 struct icmp6_hdr *icp;
396
397 #ifndef PULLDOWN_TEST
398                 IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
399                 icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
400 #else
401                 IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
402                         sizeof(*icp));
403                 if (icp == NULL) {
404                         icmp6stat.icp6s_tooshort++;
405                         return;
406                 }
407 #endif
408                 if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
409                     icp->icmp6_type == ND_REDIRECT) {
410                         /*
411                          * ICMPv6 error
412                          * Special case: for redirect (which is
413                          * informational) we must not send icmp6 error.
414                          */
415                         icmp6stat.icp6s_canterror++;
416                         goto freeit;
417                 } else {
418                         /* ICMPv6 informational - send the error */
419                 }
420         }
421 #if 0 /* controversial */
422         else if (off >= 0 && nxt == IPPROTO_ESP) {
423                 /*
424                  * It could be ICMPv6 error inside ESP.  Take a safer side,
425                  * don't respond.
426                  */
427                 icmp6stat.icp6s_canterror++;
428                 goto freeit;
429         }
430 #endif
431         else {
432                 /* non-ICMPv6 - send the error */
433         }
434
435         oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
436
437         /* Finally, do rate limitation check. */
438         if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
439                 icmp6stat.icp6s_toofreq++;
440                 goto freeit;
441         }
442
443         /*
444          * OK, ICMP6 can be generated.
445          */
446
447         /*
448          * Recover the original ip6_src if the src and the homeaddr
449          * have been swapped while dest6 processing (see dest6.c).  To
450          * avoid m_pulldown, we had better to swap addresses before
451          * m_prepend below.
452          *
453          * XXX: We should consider the other candidate to keep the
454          * icmp6 error packet correct that not swapping ip6_src and
455          * homeaddr in the dest6 processing.
456          */
457         nip6_srcp = &oip6->ip6_src;
458         n = ip6_findaux(m);
459         if (n != NULL) {
460                 ip6a = mtod(n, struct ip6aux *);
461                 if ((ip6a->ip6a_flags & IP6A_HASEEN) != 0 &&
462                     (ip6a->ip6a_flags & IP6A_SWAP) != 0) {
463                         nip6_src = oip6->ip6_src;
464                         nip6_srcp = &nip6_src;
465                         if (icmp6_recover_src(m)) {
466                                 /* mbuf is freed in icmp6_recover_src */
467                                 return;
468                         }
469                         oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
470                 }
471         }
472
473         if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
474                 m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
475
476         preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
477         M_PREPEND(m, preplen, M_DONTWAIT);
478         if (m && m->m_len < preplen)
479                 m = m_pullup(m, preplen);
480         if (m == NULL) {
481                 nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
482                 return;
483         }
484
485         nip6 = mtod(m, struct ip6_hdr *);
486         nip6->ip6_src  = *nip6_srcp;
487         nip6->ip6_dst  = oip6->ip6_dst;
488
489         if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
490                 oip6->ip6_src.s6_addr16[1] = 0;
491         if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
492                 oip6->ip6_dst.s6_addr16[1] = 0;
493
494         icmp6 = (struct icmp6_hdr *)(nip6 + 1);
495         icmp6->icmp6_type = type;
496         icmp6->icmp6_code = code;
497         icmp6->icmp6_pptr = htonl((u_int32_t)param);
498
499         /*
500          * icmp6_reflect() is designed to be in the input path.
501          * icmp6_error() can be called from both input and outut path,
502          * and if we are in output path rcvif could contain bogus value.
503          * clear m->m_pkthdr.rcvif for safety, we should have enough scope
504          * information in ip header (nip6).
505          */
506         m->m_pkthdr.rcvif = NULL;
507
508         icmp6stat.icp6s_outhist[type]++;
509         icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */
510
511         return;
512
513   freeit:
514         /*
515          * If we can't tell wheter or not we can generate ICMP6, free it.
516          */
517         m_freem(m);
518 }
519
520 /*
521  * Process a received ICMP6 message.
522  */
523 int
524 icmp6_input(mp, offp, proto)
525         struct mbuf **mp;
526         int *offp, proto;
527 {
528         struct mbuf *m = *mp, *n;
529         struct ip6_hdr *ip6, *nip6;
530         struct icmp6_hdr *icmp6, *nicmp6;
531         int off = *offp;
532         int icmp6len = m->m_pkthdr.len - *offp;
533         int code, sum, noff;
534
535         icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
536
537 #ifndef PULLDOWN_TEST
538         IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
539         /* m might change if M_LOOP.  So, call mtod after this */
540 #endif
541
542         /*
543          * Locate icmp6 structure in mbuf, and check
544          * that not corrupted and of at least minimum length
545          */
546
547         ip6 = mtod(m, struct ip6_hdr *);
548         if (icmp6len < sizeof(struct icmp6_hdr)) {
549                 icmp6stat.icp6s_tooshort++;
550                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
551                 goto freeit;
552         }
553
554         /*
555          * calculate the checksum
556          */
557 #ifndef PULLDOWN_TEST
558         icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
559 #else
560         IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
561         if (icmp6 == NULL) {
562                 icmp6stat.icp6s_tooshort++;
563                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
564                 return IPPROTO_DONE;
565         }
566 #endif
567         code = icmp6->icmp6_code;
568
569         if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
570                 nd6log((LOG_ERR,
571                     "ICMP6 checksum error(%d|%x) %s\n",
572                     icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)));
573                 icmp6stat.icp6s_checksum++;
574                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
575                 goto freeit;
576         }
577
578 #if defined(NFAITH) && 0 < NFAITH
579         if (faithprefix(&ip6->ip6_dst)) {
580                 /*
581                  * Deliver very specific ICMP6 type only.
582                  * This is important to deilver TOOBIG.  Otherwise PMTUD
583                  * will not work.
584                  */
585                 switch (icmp6->icmp6_type) {
586                 case ICMP6_DST_UNREACH:
587                 case ICMP6_PACKET_TOO_BIG:
588                 case ICMP6_TIME_EXCEEDED:
589                         break;
590                 default:
591                         goto freeit;
592                 }
593         }
594 #endif
595
596         icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
597
598 #ifdef MIP6
599         if (mip6_icmp6_tunnel_input(m, off, icmp6len)) {
600                 goto freeit;
601         }
602 #endif /* MIP6 */
603         switch (icmp6->icmp6_type) {
604         case ICMP6_DST_UNREACH:
605                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
606 #ifdef MIP6
607                 if (mip6_icmp6_input(m, off, icmp6len))
608                         goto freeit;
609 #endif /* MIP6 */
610                 switch (code) {
611                 case ICMP6_DST_UNREACH_NOROUTE:
612                         code = PRC_UNREACH_NET;
613                         break;
614                 case ICMP6_DST_UNREACH_ADMIN:
615                         icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
616                         code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
617                         break;
618                 case ICMP6_DST_UNREACH_ADDR:
619                         code = PRC_HOSTDEAD;
620                         break;
621 #ifdef COMPAT_RFC1885
622                 case ICMP6_DST_UNREACH_NOTNEIGHBOR:
623                         code = PRC_UNREACH_SRCFAIL;
624                         break;
625 #else
626                 case ICMP6_DST_UNREACH_BEYONDSCOPE:
627                         /* I mean "source address was incorrect." */
628                         code = PRC_UNREACH_NET;
629                         break;
630 #endif
631                 case ICMP6_DST_UNREACH_NOPORT:
632                         code = PRC_UNREACH_PORT;
633                         break;
634                 default:
635                         goto badcode;
636                 }
637                 goto deliver;
638                 break;
639
640         case ICMP6_PACKET_TOO_BIG:
641                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
642                 if (code != 0)
643                         goto badcode;
644
645                 code = PRC_MSGSIZE;
646
647                 /*
648                  * Updating the path MTU will be done after examining
649                  * intermediate extension headers.
650                  */
651                 goto deliver;
652                 break;
653
654         case ICMP6_TIME_EXCEEDED:
655                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
656                 switch (code) {
657                 case ICMP6_TIME_EXCEED_TRANSIT:
658                         code = PRC_TIMXCEED_INTRANS;
659                         break;
660                 case ICMP6_TIME_EXCEED_REASSEMBLY:
661                         code = PRC_TIMXCEED_REASS;
662                         break;
663                 default:
664                         goto badcode;
665                 }
666                 goto deliver;
667                 break;
668
669         case ICMP6_PARAM_PROB:
670                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
671                 switch (code) {
672                 case ICMP6_PARAMPROB_NEXTHEADER:
673                         code = PRC_UNREACH_PROTOCOL;
674                         break;
675                 case ICMP6_PARAMPROB_HEADER:
676                 case ICMP6_PARAMPROB_OPTION:
677 #ifdef MIP6
678                         if (mip6_icmp6_input(m, off, icmp6len))
679                                 goto freeit;
680 #endif /* MIP6 */
681                         code = PRC_PARAMPROB;
682                         break;
683                 default:
684                         goto badcode;
685                 }
686                 goto deliver;
687                 break;
688
689         case ICMP6_ECHO_REQUEST:
690                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
691                 if (code != 0)
692                         goto badcode;
693                 /*
694                  * Copy mbuf to send to two data paths: userland socket(s),
695                  * and to the querier (echo reply).
696                  * m: a copy for socket, n: a copy for querier
697                  */
698                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
699                         /* Give up local */
700                         n = m;
701                         m = NULL;
702                         goto deliverecho;
703                 }
704                 /*
705                  * If the first mbuf is shared, or the first mbuf is too short,
706                  * copy the first part of the data into a fresh mbuf.
707                  * Otherwise, we will wrongly overwrite both copies.
708                  */
709                 if ((n->m_flags & M_EXT) != 0 ||
710                     n->m_len < off + sizeof(struct icmp6_hdr)) {
711                         struct mbuf *n0 = n;
712                         const int maxlen = sizeof(*nip6) + sizeof(*nicmp6);
713
714                         /*
715                          * Prepare an internal mbuf.  m_pullup() doesn't
716                          * always copy the length we specified.
717                          */
718                         if (maxlen >= MCLBYTES) {
719                                 /* Give up remote */
720                                 m_freem(n0);
721                                 break;
722                         }
723                         MGETHDR(n, M_DONTWAIT, n0->m_type);
724                         if (n && maxlen >= MHLEN) {
725                                 MCLGET(n, M_DONTWAIT);
726                                 if ((n->m_flags & M_EXT) == 0) {
727                                         m_free(n);
728                                         n = NULL;
729                                 }
730                         }
731                         if (n == NULL) {
732                                 /* Give up local */
733                                 m_freem(n0);
734                                 n = m;
735                                 m = NULL;
736                                 goto deliverecho;
737                         }
738 #ifdef __OpenBSD__
739                         M_MOVE_PKTHDR(n, n0);
740 #else
741                         M_COPY_PKTHDR(n, n0);
742 #endif
743                         /*
744                          * Copy IPv6 and ICMPv6 only.
745                          */
746                         nip6 = mtod(n, struct ip6_hdr *);
747                         bcopy(ip6, nip6, sizeof(struct ip6_hdr));
748                         nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
749                         bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
750                         noff = sizeof(struct ip6_hdr);
751                         n->m_len = noff + sizeof(struct icmp6_hdr);
752                         /*
753                          * Adjust mbuf.  ip6_plen will be adjusted in
754                          * ip6_output().
755                          * n->m_pkthdr.len == n0->m_pkthdr.len at this point.
756                          */
757                         n->m_pkthdr.len += noff + sizeof(struct icmp6_hdr);
758                         n->m_pkthdr.len -= (off + sizeof(struct icmp6_hdr));
759                         m_adj(n0, off + sizeof(struct icmp6_hdr));
760                         n->m_next = n0;
761                         n0->m_flags &= ~M_PKTHDR;
762                 } else {
763          deliverecho:
764                         nip6 = mtod(n, struct ip6_hdr *);
765                         nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
766                         noff = off;
767                 }
768                 nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
769                 nicmp6->icmp6_code = 0;
770                 if (n) {
771                         icmp6stat.icp6s_reflect++;
772                         icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
773                         icmp6_reflect(n, noff);
774                 }
775                 if (!m)
776                         goto freeit;
777                 break;
778
779         case ICMP6_ECHO_REPLY:
780                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
781                 if (code != 0)
782                         goto badcode;
783                 break;
784
785 #ifdef MIP6
786         case ICMP6_HADISCOV_REQUEST:
787                 if (icmp6len < sizeof(struct ha_discov_req))
788                         goto badlen;
789                 if (code != 0)
790                         goto badcode;
791                 if (mip6_icmp6_input(m, off, icmp6len))
792                         goto freeit;
793                 break;
794         case ICMP6_HADISCOV_REPLY:
795                 if (icmp6len < sizeof(struct ha_discov_rep))
796                         goto badlen;
797                 if (code != 0)
798                         goto badcode;
799                 if (mip6_icmp6_input(m, off, icmp6len))
800                         goto freeit;
801                 break;
802 #ifndef MIP6_DRAFT13
803         case ICMP6_MOBILEPREFIX_SOLICIT:
804                 if (icmp6len < sizeof(struct mobile_prefix_solicit))
805                         goto badlen;
806                 if (code != 0)
807                         goto badcode;
808                 if (mip6_icmp6_input(m, off, icmp6len))
809                         goto freeit;
810                 break;
811
812         case ICMP6_MOBILEPREFIX_ADVERT:
813                 if (icmp6len < sizeof (struct mobile_prefix_advert))
814                         goto badlen;
815                 if (code != 0)
816                         goto badcode;
817                 if (mip6_icmp6_input(m, off, icmp6len))
818                         goto freeit;
819                 break;
820 #endif /* !MIP6_DRAFT13 */
821 #endif /* MIP6 */
822
823         case MLD_LISTENER_QUERY:
824         case MLD_LISTENER_REPORT:
825                 if (icmp6len < sizeof(struct mld_hdr))
826                         goto badlen;
827                 if (icmp6->icmp6_type == MLD_LISTENER_QUERY) /* XXX: ugly... */
828                         icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
829                 else
830                         icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
831                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
832                         /* give up local */
833                         mld6_input(m, off);
834                         m = NULL;
835                         goto freeit;
836                 }
837                 mld6_input(n, off);
838                 /* m stays. */
839                 break;
840
841         case MLD_LISTENER_DONE:
842                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
843                 if (icmp6len < sizeof(struct mld_hdr))  /* necessary? */
844                         goto badlen;
845                 break;          /* nothing to be done in kernel */
846
847         case MLD_MTRACE_RESP:
848         case MLD_MTRACE:
849                 /* XXX: these two are experimental.  not officially defined. */
850                 /* XXX: per-interface statistics? */
851                 break;          /* just pass it to applications */
852
853         case ICMP6_WRUREQUEST:  /* ICMP6_FQDN_QUERY */
854             {
855                 enum { WRU, FQDN } mode;
856
857                 if (!icmp6_nodeinfo)
858                         break;
859
860                 if (icmp6len == sizeof(struct icmp6_hdr) + 4)
861                         mode = WRU;
862                 else if (icmp6len >= sizeof(struct icmp6_nodeinfo))
863                         mode = FQDN;
864                 else
865                         goto badlen;
866
867 #ifdef __FreeBSD__
868 #define hostnamelen     strlen(hostname)
869 #endif
870                 if (mode == FQDN) {
871 #ifndef PULLDOWN_TEST
872                         IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
873                                          IPPROTO_DONE);
874 #endif
875                         n = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
876                         if (n)
877                                 n = ni6_input(n, off);
878                         /* XXX meaningless if n == NULL */
879                         noff = sizeof(struct ip6_hdr);
880                 } else {
881                         u_char *p;
882                         int maxlen, maxhlen;
883
884                         if ((icmp6_nodeinfo & 5) != 5) 
885                                 break;
886
887                         if (code != 0)
888                                 goto badcode;
889                         maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4;
890                         if (maxlen >= MCLBYTES) {
891                                 /* Give up remote */
892                                 break;
893                         }
894                         MGETHDR(n, M_DONTWAIT, m->m_type);
895                         if (n && maxlen > MHLEN) {
896                                 MCLGET(n, M_DONTWAIT);
897                                 if ((n->m_flags & M_EXT) == 0) {
898                                         m_free(n);
899                                         n = NULL;
900                                 }
901                         }
902                         if (n == NULL) {
903                                 /* Give up remote */
904                                 break;
905                         }
906                         n->m_pkthdr.rcvif = NULL;
907                         n->m_len = 0;
908                         maxhlen = M_TRAILINGSPACE(n) - maxlen;
909                         if (maxhlen > hostnamelen)
910                                 maxhlen = hostnamelen;
911                         /*
912                          * Copy IPv6 and ICMPv6 only.
913                          */
914                         nip6 = mtod(n, struct ip6_hdr *);
915                         bcopy(ip6, nip6, sizeof(struct ip6_hdr));
916                         nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
917                         bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
918                         p = (u_char *)(nicmp6 + 1);
919                         bzero(p, 4);
920                         bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */
921                         noff = sizeof(struct ip6_hdr);
922 #ifdef __OpenBSD__
923                         M_DUP_PKTHDR(n, m); /* just for rcvif */
924 #else
925                         M_COPY_PKTHDR(n, m); /* just for rcvif */
926 #endif
927                         n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
928                                 sizeof(struct icmp6_hdr) + 4 + maxhlen;
929                         nicmp6->icmp6_type = ICMP6_WRUREPLY;
930                         nicmp6->icmp6_code = 0;
931                 }
932 #undef hostnamelen
933                 if (n) {
934                         icmp6stat.icp6s_reflect++;
935                         icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
936                         icmp6_reflect(n, noff);
937                 }
938                 break;
939             }
940
941         case ICMP6_WRUREPLY:
942                 if (code != 0)
943                         goto badcode;
944                 break;
945
946         case ND_ROUTER_SOLICIT:
947                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
948                 if (code != 0)
949                         goto badcode;
950                 if (icmp6len < sizeof(struct nd_router_solicit))
951                         goto badlen;
952                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
953                         /* give up local */
954                         nd6_rs_input(m, off, icmp6len);
955                         m = NULL;
956                         goto freeit;
957                 }
958                 nd6_rs_input(n, off, icmp6len);
959                 /* m stays. */
960                 break;
961
962         case ND_ROUTER_ADVERT:
963                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
964                 if (code != 0)
965                         goto badcode;
966                 if (icmp6len < sizeof(struct nd_router_advert))
967                         goto badlen;
968                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
969                         /* give up local */
970                         nd6_ra_input(m, off, icmp6len);
971                         m = NULL;
972                         goto freeit;
973                 }
974                 nd6_ra_input(n, off, icmp6len);
975                 /* m stays. */
976                 break;
977
978         case ND_NEIGHBOR_SOLICIT:
979                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
980                 if (code != 0)
981                         goto badcode;
982                 if (icmp6len < sizeof(struct nd_neighbor_solicit))
983                         goto badlen;
984                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
985                         /* give up local */
986                         nd6_ns_input(m, off, icmp6len);
987                         m = NULL;
988                         goto freeit;
989                 }
990                 nd6_ns_input(n, off, icmp6len);
991                 /* m stays. */
992                 break;
993
994         case ND_NEIGHBOR_ADVERT:
995                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
996                 if (code != 0)
997                         goto badcode;
998                 if (icmp6len < sizeof(struct nd_neighbor_advert))
999                         goto badlen;
1000                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
1001                         /* give up local */
1002                         nd6_na_input(m, off, icmp6len);
1003                         m = NULL;
1004                         goto freeit;
1005                 }
1006                 nd6_na_input(n, off, icmp6len);
1007                 /* m stays. */
1008                 break;
1009
1010         case ND_REDIRECT:
1011                 icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
1012                 if (code != 0)
1013                         goto badcode;
1014                 if (icmp6len < sizeof(struct nd_redirect))
1015                         goto badlen;
1016                 if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
1017                         /* give up local */
1018                         icmp6_redirect_input(m, off);
1019                         m = NULL;
1020                         goto freeit;
1021                 }
1022                 icmp6_redirect_input(n, off);
1023                 /* m stays. */
1024                 break;
1025
1026         case ICMP6_ROUTER_RENUMBERING:
1027                 if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
1028                     code != ICMP6_ROUTER_RENUMBERING_RESULT)
1029                         goto badcode;
1030                 if (icmp6len < sizeof(struct icmp6_router_renum))
1031                         goto badlen;
1032                 break;
1033
1034         default:
1035                 nd6log((LOG_DEBUG,
1036                     "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
1037                     icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
1038                     ip6_sprintf(&ip6->ip6_dst),
1039                     m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0));
1040                 if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
1041                         /* ICMPv6 error: MUST deliver it by spec... */
1042                         code = PRC_NCMDS;
1043                         /* deliver */
1044                 } else {
1045                         /* ICMPv6 informational: MUST not deliver */
1046                         break;
1047                 }
1048         deliver:
1049                 if (icmp6_notify_error(m, off, icmp6len, code)) {
1050                         /* In this case, m should've been freed. */
1051                         return(IPPROTO_DONE);
1052                 }
1053                 break;
1054
1055         badcode:
1056                 icmp6stat.icp6s_badcode++;
1057                 break;
1058
1059         badlen:
1060                 icmp6stat.icp6s_badlen++;
1061                 break;
1062         }
1063
1064         /* deliver the packet to appropriate sockets */
1065         icmp6_rip6_input(&m, *offp);
1066
1067         return IPPROTO_DONE;
1068
1069  freeit:
1070         m_freem(m);
1071         return IPPROTO_DONE;
1072 }
1073
1074 static int
1075 icmp6_notify_error(m, off, icmp6len, code)
1076         struct mbuf *m;
1077         int off, icmp6len;
1078 {
1079         struct icmp6_hdr *icmp6;
1080         struct ip6_hdr *eip6;
1081         u_int32_t notifymtu;
1082         int64_t zoneid;
1083         struct sockaddr_in6 icmp6src, icmp6dst;
1084
1085         if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
1086                 icmp6stat.icp6s_tooshort++;
1087                 goto freeit;
1088         }
1089 #ifndef PULLDOWN_TEST
1090         IP6_EXTHDR_CHECK(m, off,
1091                          sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
1092                          -1);
1093         icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
1094 #else
1095         IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
1096                        sizeof(*icmp6) + sizeof(struct ip6_hdr));
1097         if (icmp6 == NULL) {
1098                 icmp6stat.icp6s_tooshort++;
1099                 return(-1);
1100         }
1101 #endif
1102         eip6 = (struct ip6_hdr *)(icmp6 + 1);
1103
1104         /* Detect the upper level protocol */
1105         {
1106                 void (*ctlfunc) __P((int, struct sockaddr *, void *));
1107                 u_int8_t nxt = eip6->ip6_nxt;
1108                 int eoff = off + sizeof(struct icmp6_hdr) +
1109                         sizeof(struct ip6_hdr);
1110                 struct ip6ctlparam ip6cp;
1111                 struct in6_addr *finaldst = NULL;
1112                 int icmp6type = icmp6->icmp6_type;
1113                 struct ip6_frag *fh;
1114                 struct ip6_rthdr *rth;
1115                 struct ip6_rthdr0 *rth0;
1116                 int rthlen;
1117
1118                 while (1) { /* XXX: should avoid infinite loop explicitly? */
1119                         struct ip6_ext *eh;
1120
1121                         switch (nxt) {
1122                         case IPPROTO_HOPOPTS:
1123                         case IPPROTO_DSTOPTS:
1124                         case IPPROTO_AH:
1125 #ifndef PULLDOWN_TEST
1126                                 IP6_EXTHDR_CHECK(m, 0, eoff +
1127                                                  sizeof(struct ip6_ext),
1128                                                  -1);
1129                                 eh = (struct ip6_ext *)(mtod(m, caddr_t)
1130                                                         + eoff);
1131 #else
1132                                 IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
1133                                                eoff, sizeof(*eh));
1134                                 if (eh == NULL) {
1135                                         icmp6stat.icp6s_tooshort++;
1136                                         return(-1);
1137                                 }
1138 #endif
1139                                 
1140                                 if (nxt == IPPROTO_AH)
1141                                         eoff += (eh->ip6e_len + 2) << 2;
1142                                 else
1143                                         eoff += (eh->ip6e_len + 1) << 3;
1144                                 nxt = eh->ip6e_nxt;
1145                                 break;
1146                         case IPPROTO_ROUTING:
1147                                 /*
1148                                  * When the erroneous packet contains a
1149                                  * routing header, we should examine the
1150                                  * header to determine the final destination.
1151                                  * Otherwise, we can't properly update
1152                                  * information that depends on the final
1153                                  * destination (e.g. path MTU).
1154                                  */
1155 #ifndef PULLDOWN_TEST
1156                                 IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth),
1157                                                  -1);
1158                                 rth = (struct ip6_rthdr *)(mtod(m, caddr_t)
1159                                                            + eoff);
1160 #else
1161                                 IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
1162                                                eoff, sizeof(*rth));
1163                                 if (rth == NULL) {
1164                                         icmp6stat.icp6s_tooshort++;
1165                                         return(-1);
1166                                 }
1167 #endif
1168                                 rthlen = (rth->ip6r_len + 1) << 3;
1169                                 /*
1170                                  * XXX: currently there is no
1171                                  * officially defined type other
1172                                  * than type-0.
1173                                  * Note that if the segment left field
1174                                  * is 0, all intermediate hops must
1175                                  * have been passed.
1176                                  */
1177                                 if (rth->ip6r_segleft &&
1178                                     rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
1179                                         int hops;
1180
1181 #ifndef PULLDOWN_TEST
1182                                         IP6_EXTHDR_CHECK(m, 0, eoff + rthlen,
1183                                                          -1);
1184                                         rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff);
1185 #else
1186                                         IP6_EXTHDR_GET(rth0,
1187                                                        struct ip6_rthdr0 *, m,
1188                                                        eoff, rthlen);
1189                                         if (rth0 == NULL) {
1190                                                 icmp6stat.icp6s_tooshort++;
1191                                                 return(-1);
1192                                         }
1193 #endif
1194                                         /* just ignore a bogus header */
1195                                         if ((rth0->ip6r0_len % 2) == 0 &&
1196                                             (hops = rth0->ip6r0_len/2))
1197                                                 finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
1198                                 }
1199                                 eoff += rthlen;
1200                                 nxt = rth->ip6r_nxt;
1201                                 break;
1202                         case IPPROTO_FRAGMENT:
1203 #ifndef PULLDOWN_TEST
1204                                 IP6_EXTHDR_CHECK(m, 0, eoff +
1205                                                  sizeof(struct ip6_frag),
1206                                                  -1);
1207                                 fh = (struct ip6_frag *)(mtod(m, caddr_t)
1208                                                          + eoff);
1209 #else
1210                                 IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
1211                                                eoff, sizeof(*fh));
1212                                 if (fh == NULL) {
1213                                         icmp6stat.icp6s_tooshort++;
1214                                         return(-1);
1215                                 }
1216 #endif
1217                                 /*
1218                                  * Data after a fragment header is meaningless
1219                                  * unless it is the first fragment, but
1220                                  * we'll go to the notify label for path MTU
1221                                  * discovery.
1222                                  */
1223                                 if (fh->ip6f_offlg & IP6F_OFF_MASK)
1224                                         goto notify;
1225
1226                                 eoff += sizeof(struct ip6_frag);
1227                                 nxt = fh->ip6f_nxt;
1228                                 break;
1229                         default:
1230                                 /*
1231                                  * This case includes ESP and the No Next
1232                                  * Header.  In such cases going to the notify
1233                                  * label does not have any meaning
1234                                  * (i.e. ctlfunc will be NULL), but we go
1235                                  * anyway since we might have to update
1236                                  * path MTU information.
1237                                  */
1238                                 goto notify;
1239                         }
1240                 }
1241           notify:
1242 #ifndef PULLDOWN_TEST
1243                 icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
1244 #else
1245                 IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
1246                                sizeof(*icmp6) + sizeof(struct ip6_hdr));
1247                 if (icmp6 == NULL) {
1248                         icmp6stat.icp6s_tooshort++;
1249                         return(-1);
1250                 }
1251 #endif
1252
1253                 eip6 = (struct ip6_hdr *)(icmp6 + 1);
1254                 bzero(&icmp6dst, sizeof(icmp6dst));
1255                 icmp6dst.sin6_len = sizeof(struct sockaddr_in6);
1256                 icmp6dst.sin6_family = AF_INET6;
1257                 if (finaldst == NULL)
1258                         icmp6dst.sin6_addr = eip6->ip6_dst;
1259                 else
1260                         icmp6dst.sin6_addr = *finaldst;
1261                 if ((zoneid = in6_addr2zoneid(m->m_pkthdr.rcvif,
1262                                               &icmp6dst.sin6_addr)) < 0)
1263                         goto freeit;
1264                 icmp6dst.sin6_scope_id = zoneid;
1265                 if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst)) {
1266                         /* should be impossbile */
1267                         nd6log((LOG_DEBUG,
1268                             "icmp6_notify_error: in6_embedscope failed\n"));
1269                         goto freeit;
1270                 }
1271
1272                 /*
1273                  * retrieve parameters from the inner IPv6 header, and convert
1274                  * them into sockaddr structures.
1275                  */
1276                 bzero(&icmp6src, sizeof(icmp6src));
1277                 icmp6src.sin6_len = sizeof(struct sockaddr_in6);
1278                 icmp6src.sin6_family = AF_INET6;
1279                 icmp6src.sin6_addr = eip6->ip6_src;
1280                 if ((zoneid = in6_addr2zoneid(m->m_pkthdr.rcvif,
1281                                               &icmp6src.sin6_addr)) < 0)
1282                         goto freeit;
1283                 icmp6src.sin6_scope_id = zoneid;
1284                 if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src)) {
1285                         /* should be impossbile */
1286                         nd6log((LOG_DEBUG,
1287                             "icmp6_notify_error: in6_embedscope failed\n"));
1288                         goto freeit;
1289                 }
1290                 icmp6src.sin6_flowinfo =
1291                         (eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
1292
1293                 if (finaldst == NULL)
1294                         finaldst = &eip6->ip6_dst;
1295                 ip6cp.ip6c_m = m;
1296                 ip6cp.ip6c_icmp6 = icmp6;
1297                 ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
1298                 ip6cp.ip6c_off = eoff;
1299                 ip6cp.ip6c_finaldst = finaldst;
1300                 ip6cp.ip6c_src = &icmp6src;
1301                 ip6cp.ip6c_nxt = nxt;
1302
1303                 if (icmp6type == ICMP6_PACKET_TOO_BIG) {
1304                         notifymtu = ntohl(icmp6->icmp6_mtu);
1305                         ip6cp.ip6c_cmdarg = (void *)&notifymtu;
1306 #if !(defined(__NetBSD__) || defined(__OpenBSD__))
1307                         icmp6_mtudisc_update(&ip6cp, 1);        /* XXX */
1308 #endif
1309                 }
1310
1311                 ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
1312                         (inet6sw[ip6_protox[nxt]].pr_ctlinput);
1313                 if (ctlfunc) {
1314                         (void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
1315                                           &ip6cp);
1316                 }
1317         }
1318         return(0);
1319
1320   freeit:
1321         m_freem(m);
1322         return(-1);
1323 }
1324
1325 void
1326 icmp6_mtudisc_update(ip6cp, validated)
1327         struct ip6ctlparam *ip6cp;
1328         int validated;
1329 {
1330 #if defined(__NetBSD__) || defined(__OpenBSD__)
1331         unsigned long rtcount;
1332         struct icmp6_mtudisc_callback *mc;
1333 #endif
1334         struct in6_addr *dst = ip6cp->ip6c_finaldst;
1335         struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
1336         struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */
1337         u_int mtu = ntohl(icmp6->icmp6_mtu);
1338         struct rtentry *rt = NULL;
1339         struct sockaddr_in6 sin6;
1340 #ifdef __bsdi__
1341 #ifdef NEW_STRUCT_ROUTE
1342         struct route ro6;
1343 #else
1344         struct route_in6 ro6;
1345 #endif
1346 #endif
1347
1348 #if defined(__NetBSD__) || defined(__OpenBSD__)
1349         /*
1350          * allow non-validated cases if memory is plenty, to make traffic
1351          * from non-connected pcb happy.
1352          */
1353         rtcount = rt_timer_count(icmp6_mtudisc_timeout_q);
1354         if (validated) {
1355                 if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat)
1356                         return;
1357                 else if (0 <= icmp6_mtudisc_lowat &&
1358                     rtcount > icmp6_mtudisc_lowat) {
1359                         /*
1360                          * XXX nuke a victim, install the new one.
1361                          */
1362                 }
1363         } else {
1364                 if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat)
1365                         return;
1366         }
1367 #else
1368         if (!validated)
1369                 return;
1370 #endif
1371
1372         bzero(&sin6, sizeof(sin6));
1373         sin6.sin6_family = PF_INET6;
1374         sin6.sin6_len = sizeof(struct sockaddr_in6);
1375         sin6.sin6_addr = *dst;
1376         /* XXX normally, this won't happen */
1377         if (IN6_IS_ADDR_LINKLOCAL(dst)) {
1378                 sin6.sin6_addr.s6_addr16[1] =
1379                     htons(m->m_pkthdr.rcvif->if_index);
1380         }
1381         /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */
1382 #if defined(__NetBSD__) || defined(__OpenBSD__)
1383         rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
1384 #else
1385 #ifdef __FreeBSD__
1386         rt = rtalloc1((struct sockaddr *)&sin6, 0,
1387                       RTF_CLONING | RTF_PRCLONING);
1388 #else
1389 #ifdef __bsdi__
1390         bcopy(&sin6, &ro6.ro_dst, sizeof(struct sockaddr_in6));
1391         ro6.ro_rt = 0;
1392         /* rtcalloc((struct route *)&ro6); */
1393         rtalloc((struct route *)&ro6);
1394         rt = ro6.ro_rt;
1395 #else
1396 #error no case for this particular operating system
1397 #endif
1398 #endif
1399 #endif
1400
1401         if (rt && (rt->rt_flags & RTF_HOST)
1402             && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
1403                 if (mtu < IPV6_MMTU) {
1404                                 /* xxx */
1405                         rt->rt_rmx.rmx_locks |= RTV_MTU;
1406                 } else if (mtu < rt->rt_ifp->if_mtu &&
1407                            rt->rt_rmx.rmx_mtu > mtu) {
1408                         icmp6stat.icp6s_pmtuchg++;
1409                         rt->rt_rmx.rmx_mtu = mtu;
1410                 }
1411         }
1412         if (rt) { /* XXX: need braces to avoid conflict with else in RTFREE. */
1413                 RTFREE(rt);
1414         }
1415
1416 #if defined(__NetBSD__) || defined(__OpenBSD__)
1417         /*
1418          * Notify protocols that the MTU for this destination
1419          * has changed.
1420          */
1421         for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
1422              mc = LIST_NEXT(mc, mc_list))
1423                 (*mc->mc_func)(&sin6.sin6_addr);
1424 #endif
1425 }
1426
1427 /*
1428  * Process a Node Information Query packet, based on
1429  * draft-ietf-ipngwg-icmp-name-lookups-07.
1430  * 
1431  * Spec incompatibilities:
1432  * - IPv6 Subject address handling
1433  * - IPv4 Subject address handling support missing
1434  * - Proxy reply (answer even if it's not for me)
1435  * - joins NI group address at in6_ifattach() time only, does not cope
1436  *   with hostname changes by sethostname(3)
1437  */
1438 #ifdef __FreeBSD__
1439 #define hostnamelen     strlen(hostname)
1440 #endif
1441 #ifndef offsetof                /* XXX */
1442 #define offsetof(type, member)  ((size_t)(&((type *)0)->member))
1443 #endif
1444 static struct mbuf *
1445 ni6_input(m, off)
1446         struct mbuf *m;
1447         int off;
1448 {
1449         struct icmp6_nodeinfo *ni6, *nni6;
1450         struct mbuf *n = NULL;
1451         u_int16_t qtype;
1452         int subjlen;
1453         int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
1454         struct ni_reply_fqdn *fqdn;
1455         int addrs;              /* for NI_QTYPE_NODEADDR */
1456         struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
1457         struct sockaddr_in6 sin6; /* double meaning; ip6_dst and subjectaddr */
1458         struct sockaddr_in6 sin6_d; /* XXX: we should retrieve this from m_aux */
1459         struct ip6_hdr *ip6;
1460         int oldfqdn = 0;        /* if 1, return pascal string (03 draft) */
1461         char *subj = NULL;
1462         struct in6_ifaddr *ia6 = NULL;
1463         int64_t zoneid;
1464
1465         ip6 = mtod(m, struct ip6_hdr *);
1466 #ifndef PULLDOWN_TEST
1467         ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off);
1468 #else
1469         IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6));
1470         if (ni6 == NULL) {
1471                 /* m is already reclaimed */
1472                 return NULL;
1473         }
1474 #endif
1475
1476         /*
1477          * Validate IPv6 destination address.
1478          *
1479          * The Responder must discard the Query without further processing
1480          * unless it is one of the Responder's unicast or anycast addresses, or
1481          * a link-local scope multicast address which the Responder has joined.
1482          * [icmp-name-lookups-07, Section 4.]
1483          */
1484         bzero(&sin6, sizeof(sin6));
1485         sin6.sin6_family = AF_INET6;
1486         sin6.sin6_len = sizeof(struct sockaddr_in6);
1487         bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
1488         /* XXX scopeid */
1489         if ((ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)&sin6)) != NULL) {
1490                 /* unicast/anycast, fine */
1491                 if ((ia6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
1492                     (icmp6_nodeinfo & 4) == 0) {
1493                         nd6log((LOG_DEBUG, "ni6_input: ignore node info to "
1494                                 "a temporary address in %s:%d",
1495                                __FILE__, __LINE__));
1496                         goto bad;
1497                 }
1498         } else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr))
1499                 ; /* link-local multicast, fine */
1500         else
1501                 goto bad;
1502
1503         /* validate query Subject field. */
1504         qtype = ntohs(ni6->ni_qtype);
1505         subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo);
1506         switch (qtype) {
1507         case NI_QTYPE_NOOP:
1508         case NI_QTYPE_SUPTYPES:
1509                 /* 07 draft */
1510                 if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0)
1511                         break;
1512                 /* FALLTHROUGH */
1513         case NI_QTYPE_FQDN:
1514         case NI_QTYPE_NODEADDR:
1515         case NI_QTYPE_IPV4ADDR:
1516                 switch (ni6->ni_code) {
1517                 case ICMP6_NI_SUBJ_IPV6:
1518 #if ICMP6_NI_SUBJ_IPV6 != 0
1519                 case 0:
1520 #endif
1521                         /*
1522                          * backward compatibility - try to accept 03 draft
1523                          * format, where no Subject is present.
1524                          */
1525                         if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 &&
1526                             subjlen == 0) {
1527                                 oldfqdn++;
1528                                 break;
1529                         }
1530 #if ICMP6_NI_SUBJ_IPV6 != 0
1531                         if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6)
1532                                 goto bad;
1533 #endif
1534
1535                         if (subjlen != sizeof(sin6.sin6_addr))
1536                                 goto bad;
1537
1538                         /*
1539                          * Validate Subject address.
1540                          *
1541                          * Not sure what exactly "address belongs to the node"
1542                          * means in the spec, is it just unicast, or what?
1543                          *
1544                          * At this moment we consider Subject address as
1545                          * "belong to the node" if the Subject address equals
1546                          * to the IPv6 destination address; validation for
1547                          * IPv6 destination address should have done enough
1548                          * check for us.
1549                          *
1550                          * We do not do proxy at this moment.
1551                          */
1552                         /* m_pulldown instead of copy? */
1553                         m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
1554                             subjlen, (caddr_t)&sin6.sin6_addr);
1555                         if ((zoneid = in6_addr2zoneid(m->m_pkthdr.rcvif,
1556                                                       &sin6.sin6_addr)) < 0)
1557                                 goto bad;
1558                         sin6.sin6_scope_id = zoneid;
1559                                                              
1560                         if (in6_embedscope(&sin6.sin6_addr, &sin6))
1561                                 goto bad; /* XXX should not happen */
1562                         bzero(&sin6_d, sizeof(sin6_d));
1563                         sin6_d.sin6_family = AF_INET6; /* not used, actually */
1564                         sin6_d.sin6_len = sizeof(sin6_d); /* ditto */
1565                         sin6_d.sin6_addr = ip6->ip6_dst;
1566                         if ((zoneid = in6_addr2zoneid(m->m_pkthdr.rcvif,
1567                                                       &ip6->ip6_dst)) < 0)
1568                                 goto bad;
1569                         sin6_d.sin6_scope_id = zoneid;
1570                         if (in6_embedscope(&sin6_d.sin6_addr, &sin6_d))
1571                                 goto bad; /* XXX should not happen */
1572                         subj = (char *)&sin6;
1573                         if (SA6_ARE_ADDR_EQUAL(&sin6, &sin6_d))
1574                                 break;
1575
1576                         /*
1577                          * XXX if we are to allow other cases, we should really
1578                          * be careful about scope here.
1579                          * basically, we should disallow queries toward IPv6
1580                          * destination X with subject Y, if scope(X) > scope(Y).
1581                          * if we allow scope(X) > scope(Y), it will result in
1582                          * information leakage across scope boundary.
1583                          */
1584                         goto bad;
1585
1586                 case ICMP6_NI_SUBJ_FQDN:
1587                         /*
1588                          * Validate Subject name with gethostname(3).
1589                          *
1590                          * The behavior may need some debate, since:
1591                          * - we are not sure if the node has FQDN as
1592                          *   hostname (returned by gethostname(3)).
1593                          * - the code does wildcard match for truncated names.
1594                          *   however, we are not sure if we want to perform
1595                          *   wildcard match, if gethostname(3) side has
1596                          *   truncated hostname.
1597                          */
1598                         n = ni6_nametodns(hostname, hostnamelen, 0);
1599                         if (!n || n->m_next || n->m_len == 0)
1600                                 goto bad;
1601                         IP6_EXTHDR_GET(subj, char *, m,
1602                             off + sizeof(struct icmp6_nodeinfo), subjlen);
1603                         if (subj == NULL)
1604                                 goto bad;
1605                         if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *),
1606                                         n->m_len)) {
1607                                 goto bad;
1608                         }
1609                         m_freem(n);
1610                         n = NULL;
1611                         break;
1612
1613                 case ICMP6_NI_SUBJ_IPV4:        /* XXX: to be implemented? */
1614                 default:
1615                         goto bad;
1616                 }
1617                 break;
1618         }
1619
1620         /* refuse based on configuration.  XXX ICMP6_NI_REFUSED? */
1621         switch (qtype) {
1622         case NI_QTYPE_FQDN:
1623                 if ((icmp6_nodeinfo & 1) == 0)
1624                         goto bad;
1625                 break;
1626         case NI_QTYPE_NODEADDR:
1627         case NI_QTYPE_IPV4ADDR:
1628                 if ((icmp6_nodeinfo & 2) == 0)
1629                         goto bad;
1630                 break;
1631         }
1632
1633         /* guess reply length */
1634         switch (qtype) {
1635         case NI_QTYPE_NOOP:
1636                 break;          /* no reply data */
1637         case NI_QTYPE_SUPTYPES:
1638                 replylen += sizeof(u_int32_t);
1639                 break;
1640         case NI_QTYPE_FQDN:
1641                 /* XXX will append an mbuf */
1642                 replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
1643                 break;
1644         case NI_QTYPE_NODEADDR:
1645                 addrs = ni6_addrs(ni6, m, &ifp, subj);
1646                 if ((replylen += addrs * (sizeof(struct in6_addr) +
1647                                           sizeof(u_int32_t))) > MCLBYTES)
1648                         replylen = MCLBYTES; /* XXX: will truncate pkt later */
1649                 break;
1650         case NI_QTYPE_IPV4ADDR:
1651                 /* unsupported - should respond with unknown Qtype? */
1652                 goto bad;
1653         default:
1654                 /*
1655                  * XXX: We must return a reply with the ICMP6 code
1656                  * `unknown Qtype' in this case.  However we regard the case
1657                  * as an FQDN query for backward compatibility.
1658                  * Older versions set a random value to this field,
1659                  * so it rarely varies in the defined qtypes.
1660                  * But the mechanism is not reliable...
1661                  * maybe we should obsolete older versions.
1662                  */
1663                 qtype = NI_QTYPE_FQDN;
1664                 /* XXX will append an mbuf */
1665                 replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
1666                 oldfqdn++;
1667                 break;
1668         }
1669
1670         /* allocate an mbuf to reply. */
1671         MGETHDR(n, M_DONTWAIT, m->m_type);
1672         if (n == NULL) {
1673                 m_freem(m);
1674                 return(NULL);
1675         }
1676 #ifdef __OpenBSD__
1677         M_DUP_PKTHDR(n, m); /* just for rcvif */
1678 #else
1679         M_COPY_PKTHDR(n, m); /* just for rcvif */
1680 #endif
1681         if (replylen > MHLEN) {
1682                 if (replylen > MCLBYTES) {
1683                         /*
1684                          * XXX: should we try to allocate more? But MCLBYTES
1685                          * is probably much larger than IPV6_MMTU...
1686                          */
1687                         goto bad;
1688                 }
1689                 MCLGET(n, M_DONTWAIT);
1690                 if ((n->m_flags & M_EXT) == 0) {
1691                         goto bad;
1692                 }
1693         }
1694         n->m_pkthdr.len = n->m_len = replylen;
1695
1696         /* copy mbuf header and IPv6 + Node Information base headers */
1697         bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr));
1698         nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1);
1699         bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo));
1700
1701         /* qtype dependent procedure */
1702         switch (qtype) {
1703         case NI_QTYPE_NOOP:
1704                 nni6->ni_code = ICMP6_NI_SUCCESS;
1705                 nni6->ni_flags = 0;
1706                 break;
1707         case NI_QTYPE_SUPTYPES:
1708         {
1709                 u_int32_t v;
1710                 nni6->ni_code = ICMP6_NI_SUCCESS;
1711                 nni6->ni_flags = htons(0x0000); /* raw bitmap */
1712                 /* supports NOOP, SUPTYPES, FQDN, and NODEADDR */
1713                 v = (u_int32_t)htonl(0x0000000f);
1714                 bcopy(&v, nni6 + 1, sizeof(u_int32_t));
1715                 break;
1716         }
1717         case NI_QTYPE_FQDN:
1718                 nni6->ni_code = ICMP6_NI_SUCCESS;
1719                 fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
1720                                                 sizeof(struct ip6_hdr) +
1721                                                 sizeof(struct icmp6_nodeinfo));
1722                 nni6->ni_flags = 0; /* XXX: meaningless TTL */
1723                 fqdn->ni_fqdn_ttl = 0;  /* ditto. */
1724                 /*
1725                  * XXX do we really have FQDN in variable "hostname"?
1726                  */
1727                 n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn);
1728                 if (n->m_next == NULL)
1729                         goto bad;
1730                 /* XXX we assume that n->m_next is not a chain */
1731                 if (n->m_next->m_next != NULL)
1732                         goto bad;
1733                 n->m_pkthdr.len += n->m_next->m_len;
1734                 break;
1735         case NI_QTYPE_NODEADDR:
1736         {
1737                 int lenlim, copied;
1738
1739                 nni6->ni_code = ICMP6_NI_SUCCESS;
1740                 n->m_pkthdr.len = n->m_len =
1741                     sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
1742                 lenlim = M_TRAILINGSPACE(n);
1743                 copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
1744                 /* XXX: reset mbuf length */
1745                 n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
1746                         sizeof(struct icmp6_nodeinfo) + copied;
1747                 break;
1748         }
1749         default:
1750                 break;          /* XXX impossible! */
1751         }
1752
1753         nni6->ni_type = ICMP6_NI_REPLY;
1754         m_freem(m);
1755         return(n);
1756
1757   bad:
1758         m_freem(m);
1759         if (n)
1760                 m_freem(n);
1761         return(NULL);
1762 }
1763 #undef hostnamelen
1764
1765 /*
1766  * make a mbuf with DNS-encoded string.  no compression support.
1767  *
1768  * XXX names with less than 2 dots (like "foo" or "foo.section") will be
1769  * treated as truncated name (two \0 at the end).  this is a wild guess.
1770  */
1771 static struct mbuf *
1772 ni6_nametodns(name, namelen, old)
1773         const char *name;
1774         int namelen;
1775         int old;        /* return pascal string if non-zero */
1776 {
1777         struct mbuf *m;
1778         char *cp, *ep;
1779         const char *p, *q;
1780         int i, len, nterm;
1781
1782         if (old)
1783                 len = namelen + 1;
1784         else
1785                 len = MCLBYTES;
1786
1787         /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */
1788         MGET(m, M_DONTWAIT, MT_DATA);
1789         if (m && len > MLEN) {
1790                 MCLGET(m, M_DONTWAIT);
1791                 if ((m->m_flags & M_EXT) == 0)
1792                         goto fail;
1793         }
1794         if (!m)
1795                 goto fail;
1796         m->m_next = NULL;
1797
1798         if (old) {
1799                 m->m_len = len;
1800                 *mtod(m, char *) = namelen;
1801                 bcopy(name, mtod(m, char *) + 1, namelen);
1802                 return m;
1803         } else {
1804                 m->m_len = 0;
1805                 cp = mtod(m, char *);
1806                 ep = mtod(m, char *) + M_TRAILINGSPACE(m);
1807
1808                 /* if not certain about my name, return empty buffer */
1809                 if (namelen == 0)
1810                         return m;
1811
1812                 /*
1813                  * guess if it looks like shortened hostname, or FQDN.
1814                  * shortened hostname needs two trailing "\0".
1815                  */
1816                 i = 0;
1817                 for (p = name; p < name + namelen; p++) {
1818                         if (*p && *p == '.')
1819                                 i++;
1820                 }
1821                 if (i < 2)
1822                         nterm = 2;
1823                 else
1824                         nterm = 1;
1825
1826                 p = name;
1827                 while (cp < ep && p < name + namelen) {
1828                         i = 0;
1829                         for (q = p; q < name + namelen && *q && *q != '.'; q++)
1830                                 i++;
1831                         /* result does not fit into mbuf */
1832                         if (cp + i + 1 >= ep)
1833                                 goto fail;
1834                         /*
1835                          * DNS label length restriction, RFC1035 page 8.
1836                          * "i == 0" case is included here to avoid returning
1837                          * 0-length label on "foo..bar".
1838                          */
1839                         if (i <= 0 || i >= 64)
1840                                 goto fail;
1841                         *cp++ = i;
1842                         bcopy(p, cp, i);
1843                         cp += i;
1844                         p = q;
1845                         if (p < name + namelen && *p == '.')
1846                                 p++;
1847                 }
1848                 /* termination */
1849                 if (cp + nterm >= ep)
1850                         goto fail;
1851                 while (nterm-- > 0)
1852                         *cp++ = '\0';
1853                 m->m_len = cp - mtod(m, char *);
1854                 return m;
1855         }
1856
1857         panic("should not reach here");
1858         /* NOTREACHED */
1859
1860  fail:
1861         if (m)
1862                 m_freem(m);
1863         return NULL;
1864 }
1865
1866 /*
1867  * check if two DNS-encoded string matches.  takes care of truncated
1868  * form (with \0\0 at the end).  no compression support.
1869  * XXX upper/lowercase match (see RFC2065)
1870  */
1871 static int
1872 ni6_dnsmatch(a, alen, b, blen)
1873         const char *a;
1874         int alen;
1875         const char *b;
1876         int blen;
1877 {
1878         const char *a0, *b0;
1879         int l;
1880
1881         /* simplest case - need validation? */
1882         if (alen == blen && bcmp(a, b, alen) == 0)
1883                 return 1;
1884
1885         a0 = a;
1886         b0 = b;
1887
1888         /* termination is mandatory */
1889         if (alen < 2 || blen < 2)
1890                 return 0;
1891         if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0')
1892                 return 0;
1893         alen--;
1894         blen--;
1895
1896         while (a - a0 < alen && b - b0 < blen) {
1897                 if (a - a0 + 1 > alen || b - b0 + 1 > blen)
1898                         return 0;
1899
1900                 if ((signed char)a[0] < 0 || (signed char)b[0] < 0)
1901                         return 0;
1902                 /* we don't support compression yet */
1903                 if (a[0] >= 64 || b[0] >= 64)
1904                         return 0;
1905
1906                 /* truncated case */
1907                 if (a[0] == 0 && a - a0 == alen - 1)
1908                         return 1;
1909                 if (b[0] == 0 && b - b0 == blen - 1)
1910                         return 1;
1911                 if (a[0] == 0 || b[0] == 0)
1912                         return 0;
1913
1914                 if (a[0] != b[0])
1915                         return 0;
1916                 l = a[0];
1917                 if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen)
1918                         return 0;
1919                 if (bcmp(a + 1, b + 1, l) != 0)
1920                         return 0;
1921
1922                 a += 1 + l;
1923                 b += 1 + l;
1924         }
1925
1926         if (a - a0 == alen && b - b0 == blen)
1927                 return 1;
1928         else
1929                 return 0;
1930 }
1931
1932 /*
1933  * calculate the number of addresses to be returned in the node info reply.
1934  */
1935 static int
1936 ni6_addrs(ni6, m, ifpp, subj)
1937         struct icmp6_nodeinfo *ni6;
1938         struct mbuf *m;
1939         struct ifnet **ifpp;
1940         char *subj;
1941 {
1942         struct ifnet *ifp;
1943         struct in6_ifaddr *ifa6;
1944         struct ifaddr *ifa;
1945         struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */
1946         int addrs = 0, addrsofif, iffound = 0;
1947         int niflags = ni6->ni_flags;
1948
1949         if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) {
1950                 switch (ni6->ni_code) {
1951                 case ICMP6_NI_SUBJ_IPV6:
1952                         if (subj == NULL) /* must be impossible... */
1953                                 return(0);
1954                         subj_ip6 = (struct sockaddr_in6 *)subj;
1955                         break;
1956                 default:
1957                         /*
1958                          * XXX: we only support IPv6 subject address for
1959                          * this Qtype.
1960                          */
1961                         return(0);
1962                 }
1963         }
1964
1965 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
1966         for (ifp = ifnet; ifp; ifp = ifp->if_next)
1967 #else
1968         for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
1969 #endif
1970         {
1971                 addrsofif = 0;
1972 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
1973                 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
1974 #elif defined(__FreeBSD__) && __FreeBSD__ >= 4
1975                 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
1976 #else
1977                 for (ifa = ifp->if_addrlist.tqh_first; ifa;
1978                      ifa = ifa->ifa_list.tqe_next)
1979 #endif
1980                 {
1981                         if (ifa->ifa_addr->sa_family != AF_INET6)
1982                                 continue;
1983                         ifa6 = (struct in6_ifaddr *)ifa;
1984
1985                         if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 &&
1986                             IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr,
1987                                                &ifa6->ia_addr.sin6_addr))
1988                                 iffound = 1;
1989
1990                         /*
1991                          * IPv4-mapped addresses can only be returned by a
1992                          * Node Information proxy, since they represent
1993                          * addresses of IPv4-only nodes, which perforce do
1994                          * not implement this protocol.
1995                          * [icmp-name-lookups-07, Section 5.4]
1996                          * So we don't support NI_NODEADDR_FLAG_COMPAT in
1997                          * this function at this moment.
1998                          */
1999
2000                         /* What do we have to do about ::1? */
2001                         switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
2002                         case IPV6_ADDR_SCOPE_LINKLOCAL:
2003                                 if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
2004                                         continue;
2005                                 break;
2006                         case IPV6_ADDR_SCOPE_SITELOCAL:
2007                                 if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
2008                                         continue;
2009                                 break;
2010                         case IPV6_ADDR_SCOPE_GLOBAL:
2011                                 if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
2012                                         continue;
2013                                 break;
2014                         default:
2015                                 continue;
2016                         }
2017
2018                         /*
2019                          * check if anycast is okay.
2020                          * XXX: just experimental.  not in the spec.
2021                          */
2022                         if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
2023                             (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
2024                                 continue; /* we need only unicast addresses */
2025                         if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
2026                             (icmp6_nodeinfo & 4) == 0) {
2027                                 continue;
2028                         }
2029                         addrsofif++; /* count the address */
2030                 }
2031                 if (iffound) {
2032                         *ifpp = ifp;
2033                         return(addrsofif);
2034                 }
2035
2036                 addrs += addrsofif;
2037         }
2038
2039         return(addrs);
2040 }
2041
2042 static int
2043 ni6_store_addrs(ni6, nni6, ifp0, resid)
2044         struct icmp6_nodeinfo *ni6, *nni6;
2045         struct ifnet *ifp0;
2046         int resid;
2047 {
2048 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
2049         struct ifnet *ifp = ifp0 ? ifp0 : ifnet;
2050 #else
2051         struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
2052 #endif
2053         struct in6_ifaddr *ifa6;
2054         struct ifaddr *ifa;
2055         struct ifnet *ifp_dep = NULL;
2056         int copied = 0, allow_deprecated = 0;
2057         u_char *cp = (u_char *)(nni6 + 1);
2058         int niflags = ni6->ni_flags;
2059         u_int32_t ltime;
2060 #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
2061         long time_second = time.tv_sec;
2062 #endif
2063
2064         if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL))
2065                 return(0);      /* needless to copy */
2066
2067   again:
2068
2069 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
2070         for (; ifp; ifp = ifp->if_next)
2071 #else
2072         for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
2073 #endif
2074         {
2075 #if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
2076                 for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
2077 #else
2078                 for (ifa = ifp->if_addrlist.tqh_first; ifa;
2079                      ifa = ifa->ifa_list.tqe_next)
2080 #endif
2081                 {
2082                         if (ifa->ifa_addr->sa_family != AF_INET6)
2083                                 continue;
2084                         ifa6 = (struct in6_ifaddr *)ifa;
2085
2086                         if (IFA6_IS_DEPRECATED(ifa6) &&
2087                             allow_deprecated == 0) {
2088                                 /*
2089                                  * prefererred address should be put before
2090                                  * deprecated addresses.
2091                                  */
2092
2093                                 /* record the interface for later search */
2094                                 if (ifp_dep == NULL)
2095                                         ifp_dep = ifp;
2096
2097                                 continue;
2098                         }
2099                         else if (!IFA6_IS_DEPRECATED(ifa6) &&
2100                                  allow_deprecated != 0)
2101                                 continue; /* we now collect deprecated addrs */
2102
2103                         /* What do we have to do about ::1? */
2104                         switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
2105                         case IPV6_ADDR_SCOPE_LINKLOCAL:
2106                                 if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
2107                                         continue;
2108                                 break;
2109                         case IPV6_ADDR_SCOPE_SITELOCAL:
2110                                 if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
2111                                         continue;
2112                                 break;
2113                         case IPV6_ADDR_SCOPE_GLOBAL:
2114                                 if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
2115                                         continue;
2116                                 break;
2117                         default:
2118                                 continue;
2119                         }
2120
2121                         /*
2122                          * check if anycast is okay.
2123                          * XXX: just experimental.  not in the spec.
2124                          */
2125                         if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
2126                             (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
2127                                 continue;
2128                         if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0 &&
2129                             (icmp6_nodeinfo & 4) == 0) {
2130                                 continue;
2131                         }
2132
2133                         /* now we can copy the address */
2134                         if (resid < sizeof(struct in6_addr) +
2135                             sizeof(u_int32_t)) {
2136                                 /*
2137                                  * We give up much more copy.
2138                                  * Set the truncate flag and return.
2139                                  */
2140                                 nni6->ni_flags |=
2141                                         NI_NODEADDR_FLAG_TRUNCATE;
2142                                 return(copied);
2143                         }
2144
2145                         /*
2146                          * Set the TTL of the address.
2147                          * The TTL value should be one of the following
2148                          * according to the specification:
2149                          *
2150                          * 1. The remaining lifetime of a DHCP lease on the
2151                          *    address, or
2152                          * 2. The remaining Valid Lifetime of a prefix from
2153                          *    which the address was derived through Stateless
2154                          *    Autoconfiguration.
2155                          *
2156                          * Note that we currently do not support stateful
2157                          * address configuration by DHCPv6, so the former
2158                          * case can't happen.
2159                          *
2160                          * TTL must be 2^31 > TTL >= 0.
2161                          */
2162                         if (ifa6->ia6_lifetime.ia6t_expire == 0)
2163                                 ltime = ND6_INFINITE_LIFETIME;
2164                         else {
2165                                 if (ifa6->ia6_lifetime.ia6t_expire >
2166                                     time_second) {
2167                                         ltime = ifa6->ia6_lifetime.ia6t_expire
2168                                                 - time_second;
2169                                 } else
2170                                         ltime = 0;
2171                         }
2172                         if (ltime > 0x7fffffff)
2173                                 ltime = 0x7fffffff;
2174                         ltime = htonl(ltime);
2175                         
2176                         bcopy(&ltime, cp, sizeof(u_int32_t));
2177                         cp += sizeof(u_int32_t);
2178
2179                         /* copy the address itself */
2180                         bcopy(&ifa6->ia_addr.sin6_addr, cp,
2181                               sizeof(struct in6_addr));
2182                         /* XXX: KAME link-local hack; remove ifindex */
2183                         if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
2184                                 ((struct in6_addr *)cp)->s6_addr16[1] = 0;
2185                         cp += sizeof(struct in6_addr);
2186                         
2187                         resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t));
2188                         copied += (sizeof(struct in6_addr) +
2189                                    sizeof(u_int32_t));
2190                 }
2191                 if (ifp0)       /* we need search only on the specified IF */
2192                         break;
2193         }
2194
2195         if (allow_deprecated == 0 && ifp_dep != NULL) {
2196                 ifp = ifp_dep;
2197                 allow_deprecated = 1;
2198
2199                 goto again;
2200         }
2201
2202         return(copied);
2203 }
2204
2205 /*
2206  * XXX almost dup'ed code with rip6_input.
2207  */
2208 static int
2209 icmp6_rip6_input(mp, off)
2210         struct  mbuf **mp;
2211         int     off;
2212 {
2213         struct mbuf *m = *mp;
2214         struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
2215         struct in6pcb *in6p;
2216         struct in6pcb *last = NULL;
2217         struct sockaddr_in6 rip6src;
2218         struct icmp6_hdr *icmp6;
2219         struct ip6_recvpktopts opts;
2220
2221 #ifndef PULLDOWN_TEST
2222         /* this is assumed to be safe. */
2223         icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
2224 #else
2225         IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
2226         if (icmp6 == NULL) {
2227                 /* m is already reclaimed */
2228                 return IPPROTO_DONE;
2229         }
2230 #endif
2231
2232         bzero(&opts, sizeof(opts));
2233         bzero(&rip6src, sizeof(rip6src));
2234         rip6src.sin6_len = sizeof(struct sockaddr_in6);
2235         rip6src.sin6_family = AF_INET6;
2236         /* KAME hack: recover scopeid */
2237         (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif);
2238
2239 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
2240         LIST_FOREACH(in6p, &ripcb, inp_list)
2241 #elif defined(__OpenBSD__)
2242         for (in6p = rawin6pcbtable.inpt_queue.cqh_first;
2243              in6p != (struct inpcb *)&rawin6pcbtable.inpt_queue;
2244              in6p = in6p->inp_queue.cqe_next)
2245 #else
2246         for (in6p = rawin6pcb.in6p_next;
2247              in6p != &rawin6pcb; in6p = in6p->in6p_next)
2248 #endif
2249         {
2250 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
2251                 if ((in6p->inp_vflag & INP_IPV6) == 0)
2252                         continue;
2253 #endif
2254 #ifdef HAVE_NRL_INPCB
2255                 if (!(in6p->in6p_flags & INP_IPV6))
2256                         continue;
2257 #endif
2258                 if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6)
2259                         continue;
2260                 if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
2261                    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
2262                         continue;
2263                 if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
2264                    !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
2265                         continue;
2266                 if (in6p->in6p_icmp6filt
2267                     && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type,
2268                                  in6p->in6p_icmp6filt))
2269                         continue;
2270                 if (last) {
2271                         struct  mbuf *n;
2272                         if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
2273                                 if (last->in6p_flags & IN6P_CONTROLOPTS)
2274                                         ip6_savecontrol(last, ip6, n, &opts,
2275                                                         NULL);
2276                                 /* strip intermediate headers */
2277                                 m_adj(n, off);
2278                                 if (sbappendaddr(&last->in6p_socket->so_rcv,
2279                                                  (struct sockaddr *)&rip6src,
2280                                                  n, opts.head) == 0) {
2281                                         /* should notify about lost packet */
2282                                         m_freem(n);
2283                                         if (opts.head) {
2284                                                 m_freem(opts.head);
2285                                         }
2286                                 } else
2287                                         sorwakeup(last->in6p_socket);
2288                                 bzero(&opts, sizeof(opts));
2289                         }
2290                 }
2291                 last = in6p;
2292         }
2293         if (last) {
2294                 if (last->in6p_flags & IN6P_CONTROLOPTS)
2295                         ip6_savecontrol(last, ip6, m, &opts, NULL);
2296                 /* strip intermediate headers */
2297                 m_adj(m, off);
2298                 if (sbappendaddr(&last->in6p_socket->so_rcv,
2299                                  (struct sockaddr *)&rip6src,
2300                                  m, opts.head) == 0) {
2301                         m_freem(m);
2302                         if (opts.head)
2303                                 m_freem(opts.head);
2304                 } else
2305                         sorwakeup(last->in6p_socket);
2306         } else {
2307                 m_freem(m);
2308                 ip6stat.ip6s_delivered--;
2309         }
2310         return IPPROTO_DONE;
2311 }
2312
2313 /*
2314  * Reflect the ip6 packet back to the source.
2315  * OFF points to the icmp6 header, counted from the top of the mbuf.
2316  *
2317  * Note: RFC 1885 required that an echo reply should be truncated if it
2318  * did not fit in with (return) path MTU, and KAME code supported the
2319  * behavior.  However, as a clarification after the RFC, this limitation
2320  * was removed in a revised version of the spec, RFC 2463.  We had kept the
2321  * old behavior, with a (non-default) ifdef block, while the new version of
2322  * the spec was an internet-draft status, and even after the new RFC was
2323  * published.  But it would rather make sense to clean the obsoleted part
2324  * up, and to make the code simpler at this stage.
2325  */
2326 void
2327 icmp6_reflect(m, off)
2328         struct  mbuf *m;
2329         size_t off;
2330 {
2331         struct ip6_hdr *ip6;
2332         struct icmp6_hdr *icmp6;
2333         struct in6_ifaddr *ia;
2334         struct in6_addr t, *src = 0;
2335         int plen;
2336         int type, code;
2337         struct ifnet *outif = NULL;
2338         struct sockaddr_in6 sa6_src, sa6_dst;
2339
2340         /* too short to reflect */
2341         if (off < sizeof(struct ip6_hdr)) {
2342                 nd6log((LOG_DEBUG,
2343                     "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
2344                     (u_long)off, (u_long)sizeof(struct ip6_hdr),
2345                     __FILE__, __LINE__));
2346                 goto bad;
2347         }
2348
2349         /*
2350          * If there are extra headers between IPv6 and ICMPv6, strip
2351          * off that header first.
2352          */
2353 #ifdef DIAGNOSTIC
2354         if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN)
2355                 panic("assumption failed in icmp6_reflect");
2356 #endif
2357         if (off > sizeof(struct ip6_hdr)) {
2358                 size_t l;
2359                 struct ip6_hdr nip6;
2360
2361                 l = off - sizeof(struct ip6_hdr);
2362                 m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6);
2363                 m_adj(m, l);
2364                 l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
2365                 if (m->m_len < l) {
2366                         if ((m = m_pullup(m, l)) == NULL)
2367                                 return;
2368                 }
2369                 bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6));
2370         } else /* off == sizeof(struct ip6_hdr) */ {
2371                 size_t l;
2372                 l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
2373                 if (m->m_len < l) {
2374                         if ((m = m_pullup(m, l)) == NULL)
2375                                 return;
2376                 }
2377         }
2378         plen = m->m_pkthdr.len - sizeof(struct ip6_hdr);
2379         ip6 = mtod(m, struct ip6_hdr *);
2380         ip6->ip6_nxt = IPPROTO_ICMPV6;
2381         icmp6 = (struct icmp6_hdr *)(ip6 + 1);
2382         type = icmp6->icmp6_type; /* keep type for statistics */
2383         code = icmp6->icmp6_code; /* ditto. */
2384
2385         t = ip6->ip6_dst;
2386         /*
2387          * ip6_input() drops a packet if its src is multicast.
2388          * So, the src is never multicast.
2389          */
2390         ip6->ip6_dst = ip6->ip6_src;
2391
2392         /*
2393          * XXX: make sure to embed scope zone information, using
2394          * already embedded IDs or the received interface (if any).
2395          * Note that rcvif may be NULL.
2396          * TODO: scoped routing case (XXX).
2397          */
2398         bzero(&sa6_src, sizeof(sa6_src));
2399         sa6_src.sin6_family = AF_INET6;
2400         sa6_src.sin6_len = sizeof(sa6_src);
2401         sa6_src.sin6_addr = ip6->ip6_dst;
2402         in6_recoverscope(&sa6_src, &ip6->ip6_dst, m->m_pkthdr.rcvif);
2403         in6_embedscope(&sa6_src.sin6_addr, &sa6_src);
2404         ip6->ip6_dst = sa6_src.sin6_addr;
2405
2406         bzero(&sa6_dst, sizeof(sa6_dst));
2407         sa6_dst.sin6_family = AF_INET6;
2408         sa6_dst.sin6_len = sizeof(sa6_dst);
2409         sa6_dst.sin6_addr = t;
2410         in6_recoverscope(&sa6_dst, &t, m->m_pkthdr.rcvif);
2411         in6_embedscope(&t, &sa6_dst);
2412
2413         /*
2414          * If the incoming packet was addressed directly to us (i.e. unicast),
2415          * use dst as the src for the reply.
2416          * The IN6_IFF_NOTREADY case would be VERY rare, but is possible
2417          * (for example) when we encounter an error while forwarding procedure
2418          * destined to a duplicated address of ours.
2419          */
2420         for (ia = in6_ifaddr; ia; ia = ia->ia_next)
2421                 if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
2422                     (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
2423                         src = &t;
2424                         break;
2425                 }
2426         if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
2427                 /*
2428                  * This is the case if the dst is our link-local address
2429                  * and the sender is also ourselves.
2430                  */
2431                 src = &t;
2432         }
2433
2434         if (src == 0) {
2435                 int e;
2436 #ifdef NEW_STRUCT_ROUTE
2437                 struct route ro;
2438 #else
2439                 struct route_in6 ro;
2440 #endif
2441
2442                 /*
2443                  * This case matches to multicasts, our anycast, or unicasts
2444                  * that we do not own.  Select a source address based on the
2445                  * source address of the erroneous packet.
2446                  */
2447                 bzero(&ro, sizeof(ro));
2448                 src = in6_selectsrc(&sa6_src, NULL, NULL, &ro, NULL, NULL, &e);
2449                 if (ro.ro_rt) { /* XXX: see comments in icmp6_mtudisc_update */
2450                         RTFREE(ro.ro_rt); /* XXX: we could use this */
2451                 }
2452                 if (src == NULL) {
2453                         nd6log((LOG_DEBUG,
2454                             "icmp6_reflect: source can't be determined: "
2455                             "dst=%s, error=%d\n",
2456                             ip6_sprintf(&sa6_src.sin6_addr), e));
2457                         goto bad;
2458                 }
2459         }
2460
2461         ip6->ip6_src = *src;
2462
2463         ip6->ip6_flow = 0;
2464         ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
2465         ip6->ip6_vfc |= IPV6_VERSION;
2466         ip6->ip6_nxt = IPPROTO_ICMPV6;
2467         if (m->m_pkthdr.rcvif) {
2468                 /* XXX: This may not be the outgoing interface */
2469                 ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim;
2470         } else
2471                 ip6->ip6_hlim = ip6_defhlim;
2472
2473         icmp6->icmp6_cksum = 0;
2474         icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
2475                                         sizeof(struct ip6_hdr), plen);
2476
2477         /*
2478          * XXX option handling
2479          */
2480
2481         m->m_flags &= ~(M_BCAST|M_MCAST);
2482 #ifdef IPSEC
2483         /* Don't lookup socket */
2484         (void)ipsec_setsocket(m, NULL);
2485 #endif /* IPSEC */
2486
2487         /*
2488          * To avoid a "too big" situation at an intermediate router
2489          * and the path MTU discovery process, specify the IPV6_MINMTU flag.
2490          * Note that only echo and node information replies are affected,
2491          * since the length of ICMP6 errors is limited to the minimum MTU.
2492          */
2493         if (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, &outif) != 0 && outif)
2494                 icmp6_ifstat_inc(outif, ifs6_out_error);
2495
2496         if (outif)
2497                 icmp6_ifoutstat_inc(outif, type, code);
2498
2499         return;
2500
2501  bad:
2502         m_freem(m);
2503         return;
2504 }
2505
2506 void
2507 icmp6_fasttimo()
2508 {
2509
2510         mld6_fasttimeo();
2511 }
2512
2513 static const char *
2514 icmp6_redirect_diag(src6, dst6, tgt6)
2515         struct in6_addr *src6;
2516         struct in6_addr *dst6;
2517         struct in6_addr *tgt6;
2518 {
2519         static char buf[1024];
2520 #ifndef __bsdi__
2521         snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
2522                 ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
2523 #else
2524         sprintf(buf, "(src=%s dst=%s tgt=%s)",
2525                 ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
2526 #endif
2527         return buf;
2528 }
2529
2530 void
2531 icmp6_redirect_input(m, off)
2532         struct mbuf *m;
2533         int off;
2534 {
2535         struct ifnet *ifp = m->m_pkthdr.rcvif;
2536         struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
2537         struct nd_redirect *nd_rd;
2538         int icmp6len = ntohs(ip6->ip6_plen);
2539         char *lladdr = NULL;
2540         int lladdrlen = 0;
2541         u_char *redirhdr = NULL;
2542         int redirhdrlen = 0;
2543         struct rtentry *rt = NULL;
2544         int is_router;
2545         int is_onlink;
2546         struct in6_addr src6 = ip6->ip6_src;
2547         struct in6_addr redtgt6;
2548         struct in6_addr reddst6;
2549         union nd_opts ndopts;
2550
2551         if (!m || !ifp)
2552                 return;
2553
2554         /* XXX if we are router, we don't update route by icmp6 redirect */
2555         if (ip6_forwarding)
2556                 goto freeit;
2557         if (!icmp6_rediraccept)
2558                 goto freeit;
2559
2560 #ifndef PULLDOWN_TEST
2561         IP6_EXTHDR_CHECK(m, off, icmp6len,);
2562         nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
2563 #else
2564         IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
2565         if (nd_rd == NULL) {
2566                 icmp6stat.icp6s_tooshort++;
2567                 return;
2568         }
2569 #endif
2570         redtgt6 = nd_rd->nd_rd_target;
2571         reddst6 = nd_rd->nd_rd_dst;
2572
2573         if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
2574                 redtgt6.s6_addr16[1] = htons(ifp->if_index);
2575         if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
2576                 reddst6.s6_addr16[1] = htons(ifp->if_index);
2577
2578         /* validation */
2579         if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
2580                 nd6log((LOG_ERR,
2581                         "ICMP6 redirect sent from %s rejected; "
2582                         "must be from linklocal\n", ip6_sprintf(&src6)));
2583                 goto bad;
2584         }
2585         if (ip6->ip6_hlim != 255) {
2586                 nd6log((LOG_ERR,
2587                         "ICMP6 redirect sent from %s rejected; "
2588                         "hlim=%d (must be 255)\n",
2589                         ip6_sprintf(&src6), ip6->ip6_hlim));
2590                 goto bad;
2591         }
2592     {
2593         /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
2594         struct sockaddr_in6 sin6;
2595         struct in6_addr *gw6;
2596
2597         bzero(&sin6, sizeof(sin6));
2598         sin6.sin6_family = AF_INET6;
2599         sin6.sin6_len = sizeof(struct sockaddr_in6);
2600         bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
2601         rt = rtalloc1((struct sockaddr *)&sin6, 0
2602 #ifdef __FreeBSD__
2603                       , 0UL
2604 #endif
2605                       );
2606         if (rt) {
2607                 if (rt->rt_gateway == NULL ||
2608                     rt->rt_gateway->sa_family != AF_INET6) {
2609                         nd6log((LOG_ERR,
2610                             "ICMP6 redirect rejected; no route "
2611                             "with inet6 gateway found for redirect dst: %s\n",
2612                             icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2613                         RTFREE(rt);
2614                         goto bad;
2615                 }
2616
2617                 gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
2618                 if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
2619                         nd6log((LOG_ERR,
2620                                 "ICMP6 redirect rejected; "
2621                                 "not equal to gw-for-src=%s (must be same): "
2622                                 "%s\n",
2623                                 ip6_sprintf(gw6),
2624                                 icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2625                         RTFREE(rt);
2626                         goto bad;
2627                 }
2628         } else {
2629                 nd6log((LOG_ERR,
2630                         "ICMP6 redirect rejected; "
2631                         "no route found for redirect dst: %s\n",
2632                         icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2633                 goto bad;
2634         }
2635         RTFREE(rt);
2636         rt = NULL;
2637     }
2638         if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
2639                 nd6log((LOG_ERR,
2640                         "ICMP6 redirect rejected; "
2641                         "redirect dst must be unicast: %s\n",
2642                         icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2643                 goto bad;
2644         }
2645
2646         is_router = is_onlink = 0;
2647         if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
2648                 is_router = 1;  /* router case */
2649         if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
2650                 is_onlink = 1;  /* on-link destination case */
2651         if (!is_router && !is_onlink) {
2652                 nd6log((LOG_ERR,
2653                         "ICMP6 redirect rejected; "
2654                         "neither router case nor onlink case: %s\n",
2655                         icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2656                 goto bad;
2657         }
2658         /* validation passed */
2659
2660         icmp6len -= sizeof(*nd_rd);
2661         nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
2662         if (nd6_options(&ndopts) < 0) {
2663                 nd6log((LOG_INFO, "icmp6_redirect_input: "
2664                         "invalid ND option, rejected: %s\n",
2665                         icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2666                 /* nd6_options have incremented stats */
2667                 goto freeit;
2668         }
2669
2670         if (ndopts.nd_opts_tgt_lladdr) {
2671                 lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
2672                 lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
2673         }
2674
2675         if (ndopts.nd_opts_rh) {
2676                 redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len;
2677                 redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */
2678         }
2679
2680         if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
2681                 nd6log((LOG_INFO,
2682                         "icmp6_redirect_input: lladdrlen mismatch for %s "
2683                         "(if %d, icmp6 packet %d): %s\n",
2684                         ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2,
2685                         icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
2686                 goto bad;
2687         }
2688
2689         /* RFC 2461 8.3 */
2690         nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
2691                          is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
2692
2693         if (!is_onlink) {       /* better router case.  perform rtredirect. */
2694                 /* perform rtredirect */
2695                 struct sockaddr_in6 sdst;
2696                 struct sockaddr_in6 sgw;
2697                 struct sockaddr_in6 ssrc;
2698 #ifdef __bsdi__
2699                 extern int icmp_redirtimeout;   /* XXX */
2700 #endif
2701 #if defined(__NetBSD__) || defined(__OpenBSD__)
2702                 unsigned long rtcount;
2703                 struct rtentry *newrt = NULL;
2704 #endif
2705
2706 #if defined(__NetBSD__) || defined(__OpenBSD__)
2707                 /*
2708                  * do not install redirect route, if the number of entries
2709                  * is too much (> hiwat).  note that, the node (= host) will
2710                  * work just fine even if we do not install redirect route
2711                  * (there will be additional hops, though).
2712                  */
2713                 rtcount = rt_timer_count(icmp6_redirect_timeout_q);
2714                 if (0 <= icmp6_redirect_hiwat && rtcount > icmp6_redirect_hiwat)
2715                         return;
2716                 else if (0 <= icmp6_redirect_lowat &&
2717                     rtcount > icmp6_redirect_lowat) {
2718                         /*
2719                          * XXX nuke a victim, install the new one.
2720                          */
2721                 }
2722 #endif
2723
2724                 bzero(&sdst, sizeof(sdst));
2725                 bzero(&sgw, sizeof(sgw));
2726                 bzero(&ssrc, sizeof(ssrc));
2727                 sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
2728                 sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
2729                         sizeof(struct sockaddr_in6);
2730                 bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
2731                 bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
2732                 bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
2733                 rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
2734                            (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
2735                            (struct sockaddr *)&ssrc,
2736 #ifdef __bsdi__
2737                            icmp_redirtimeout
2738 #elif defined(__NetBSD__) || defined(__OpenBSD__)
2739                            &newrt
2740 #else
2741                            (struct rtentry **)NULL
2742 #endif
2743                            );
2744
2745 #if defined(__NetBSD__) || defined(__OpenBSD__)
2746                 if (newrt) {
2747                         (void)rt_timer_add(newrt, icmp6_redirect_timeout,
2748                             icmp6_redirect_timeout_q);
2749                         rtfree(newrt);
2750                 }
2751 #endif
2752         }
2753         /* finally update cached route in each socket via pfctlinput */
2754         {
2755                 struct sockaddr_in6 sdst;
2756
2757                 bzero(&sdst, sizeof(sdst));
2758                 sdst.sin6_family = AF_INET6;
2759                 sdst.sin6_len = sizeof(struct sockaddr_in6);
2760                 bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
2761                 pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
2762 #ifdef IPSEC
2763                 key_sa_routechange((struct sockaddr *)&sdst);
2764 #endif
2765         }
2766
2767  freeit:
2768         m_freem(m);
2769         return;
2770
2771  bad:
2772         icmp6stat.icp6s_badredirect++;
2773         m_freem(m);
2774 }
2775
2776 void
2777 icmp6_redirect_output(m0, rt)
2778         struct mbuf *m0;
2779         struct rtentry *rt;
2780 {
2781         struct ifnet *ifp;      /* my outgoing interface */
2782         struct in6_addr *ifp_ll6;
2783         struct in6_addr *router_ll6;
2784         struct ip6_hdr *sip6;   /* m0 as struct ip6_hdr */
2785         struct mbuf *m = NULL;  /* newly allocated one */
2786         struct ip6_hdr *ip6;    /* m as struct ip6_hdr */
2787         struct nd_redirect *nd_rd;
2788         size_t maxlen;
2789         u_char *p;
2790         struct sockaddr_in6 src_sa;
2791         int64_t zoneid;
2792
2793         icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
2794
2795         /* if we are not router, we don't send icmp6 redirect */
2796         if (!ip6_forwarding || ip6_accept_rtadv)
2797                 goto fail;
2798
2799         /* sanity check */
2800         if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
2801                 goto fail;
2802
2803         /*
2804          * Address check:
2805          *  the source address must identify a neighbor, and
2806          *  the destination address must not be a multicast address
2807          *  [RFC 2461, sec 8.2]
2808          */
2809         sip6 = mtod(m0, struct ip6_hdr *);
2810         bzero(&src_sa, sizeof(src_sa));
2811         src_sa.sin6_family = AF_INET6;
2812         src_sa.sin6_len = sizeof(src_sa);
2813         src_sa.sin6_addr = sip6->ip6_src;
2814         /* we don't currently use sin6_scope_id, but eventually use it */
2815         if ((zoneid = in6_addr2zoneid(ifp, &sip6->ip6_src)) < 0)
2816                 goto fail;
2817         src_sa.sin6_scope_id = zoneid;
2818         if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
2819                 goto fail;
2820         if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
2821                 goto fail;      /* what should we do here? */
2822
2823         /* rate limit */
2824         if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
2825                 goto fail;
2826
2827         /*
2828          * Since we are going to append up to 1280 bytes (= IPV6_MMTU),
2829          * we almost always ask for an mbuf cluster for simplicity.
2830          * (MHLEN < IPV6_MMTU is almost always true)
2831          */
2832 #if IPV6_MMTU >= MCLBYTES
2833 # error assumption failed about IPV6_MMTU and MCLBYTES
2834 #endif
2835         MGETHDR(m, M_DONTWAIT, MT_HEADER);
2836         if (m && IPV6_MMTU >= MHLEN)
2837                 MCLGET(m, M_DONTWAIT);
2838         if (!m)
2839                 goto fail;
2840         m->m_pkthdr.rcvif = NULL;
2841         m->m_len = 0;
2842         maxlen = M_TRAILINGSPACE(m);
2843         maxlen = min(IPV6_MMTU, maxlen);
2844         /* just for safety */
2845         if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
2846             ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
2847                 goto fail;
2848         }
2849
2850         {
2851                 /* get ip6 linklocal address for ifp(my outgoing interface). */
2852                 struct in6_ifaddr *ia;
2853                 if ((ia = in6ifa_ifpforlinklocal(ifp,
2854                                                  IN6_IFF_NOTREADY|
2855                                                  IN6_IFF_ANYCAST)) == NULL)
2856                         goto fail;
2857                 ifp_ll6 = &ia->ia_addr.sin6_addr;
2858         }
2859
2860         /* get ip6 linklocal address for the router. */
2861         if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
2862                 struct sockaddr_in6 *sin6;
2863                 sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
2864                 router_ll6 = &sin6->sin6_addr;
2865                 if (!IN6_IS_ADDR_LINKLOCAL(router_ll6))
2866                         router_ll6 = (struct in6_addr *)NULL;
2867         } else
2868                 router_ll6 = (struct in6_addr *)NULL;
2869
2870         /* ip6 */
2871         ip6 = mtod(m, struct ip6_hdr *);
2872         ip6->ip6_flow = 0;
2873         ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
2874         ip6->ip6_vfc |= IPV6_VERSION;
2875         /* ip6->ip6_plen will be set later */
2876         ip6->ip6_nxt = IPPROTO_ICMPV6;
2877         ip6->ip6_hlim = 255;
2878         /* ip6->ip6_src must be linklocal addr for my outgoing if. */
2879         bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
2880         bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
2881
2882         /* ND Redirect */
2883         nd_rd = (struct nd_redirect *)(ip6 + 1);
2884         nd_rd->nd_rd_type = ND_REDIRECT;
2885         nd_rd->nd_rd_code = 0;
2886         nd_rd->nd_rd_reserved = 0;
2887         if (rt->rt_flags & RTF_GATEWAY) {
2888                 /*
2889                  * nd_rd->nd_rd_target must be a link-local address in
2890                  * better router cases.
2891                  */
2892                 if (!router_ll6)
2893                         goto fail;
2894                 bcopy(router_ll6, &nd_rd->nd_rd_target,
2895                       sizeof(nd_rd->nd_rd_target));
2896                 bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
2897                       sizeof(nd_rd->nd_rd_dst));
2898         } else {
2899                 /* make sure redtgt == reddst */
2900                 bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
2901                       sizeof(nd_rd->nd_rd_target));
2902                 bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
2903                       sizeof(nd_rd->nd_rd_dst));
2904         }
2905
2906         p = (u_char *)(nd_rd + 1);
2907
2908         if (!router_ll6)
2909                 goto nolladdropt;
2910
2911     {
2912         /* target lladdr option */
2913         struct rtentry *rt_router = NULL;
2914         int len;
2915         struct sockaddr_dl *sdl;
2916         struct nd_opt_hdr *nd_opt;
2917         char *lladdr;
2918
2919         rt_router = nd6_lookup(router_ll6, 0, ifp);
2920         if (!rt_router)
2921                 goto nolladdropt;
2922         len = sizeof(*nd_opt) + ifp->if_addrlen;
2923         len = (len + 7) & ~7;   /* round by 8 */
2924         /* safety check */
2925         if (len + (p - (u_char *)ip6) > maxlen)
2926                 goto nolladdropt;
2927         if (!(rt_router->rt_flags & RTF_GATEWAY) &&
2928             (rt_router->rt_flags & RTF_LLINFO) &&
2929             (rt_router->rt_gateway->sa_family == AF_LINK) &&
2930             (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) &&
2931             sdl->sdl_alen) {
2932                 nd_opt = (struct nd_opt_hdr *)p;
2933                 nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
2934                 nd_opt->nd_opt_len = len >> 3;
2935                 lladdr = (char *)(nd_opt + 1);
2936                 bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
2937                 p += len;
2938         }
2939     }
2940 nolladdropt:;
2941
2942         m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
2943
2944         /* just to be safe */
2945 #ifdef M_DECRYPTED      /* not openbsd */
2946         if (m0->m_flags & M_DECRYPTED)
2947                 goto noredhdropt;
2948 #endif
2949         if (p - (u_char *)ip6 > maxlen)
2950                 goto noredhdropt;
2951
2952     {
2953         /* redirected header option */
2954         int len;
2955         struct nd_opt_rd_hdr *nd_opt_rh;
2956
2957         /*
2958          * compute the maximum size for icmp6 redirect header option.
2959          * XXX room for auth header?
2960          */
2961         len = maxlen - (p - (u_char *)ip6);
2962         len &= ~7;
2963
2964         /* This is just for simplicity. */
2965         if (m0->m_pkthdr.len != m0->m_len) {
2966                 if (m0->m_next) {
2967                         m_freem(m0->m_next);
2968                         m0->m_next = NULL;
2969                 }
2970                 m0->m_pkthdr.len = m0->m_len;
2971         }
2972
2973         /*
2974          * Redirected header option spec (RFC2461 4.6.3) talks nothing
2975          * about padding/truncate rule for the original IP packet.
2976          * From the discussion on IPv6imp in Feb 1999, the consensus was:
2977          * - "attach as much as possible" is the goal
2978          * - pad if not aligned (original size can be guessed by original
2979          *   ip6 header)
2980          * Following code adds the padding if it is simple enough,
2981          * and truncates if not.
2982          */
2983         if (m0->m_next || m0->m_pkthdr.len != m0->m_len)
2984                 panic("assumption failed in %s:%d\n", __FILE__, __LINE__);
2985
2986         if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
2987                 /* not enough room, truncate */
2988                 m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
2989         } else {
2990                 /* enough room, pad or truncate */
2991                 size_t extra;
2992
2993                 extra = m0->m_pkthdr.len % 8;
2994                 if (extra) {
2995                         /* pad if easy enough, truncate if not */
2996                         if (8 - extra <= M_TRAILINGSPACE(m0)) {
2997                                 /* pad */
2998                                 m0->m_len += (8 - extra);
2999                                 m0->m_pkthdr.len += (8 - extra);
3000                         } else {
3001                                 /* truncate */
3002                                 m0->m_pkthdr.len -= extra;
3003                                 m0->m_len -= extra;
3004                         }
3005                 }
3006                 len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
3007                 m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
3008         }
3009
3010         nd_opt_rh = (struct nd_opt_rd_hdr *)p;
3011         bzero(nd_opt_rh, sizeof(*nd_opt_rh));
3012         nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
3013         nd_opt_rh->nd_opt_rh_len = len >> 3;
3014         p += sizeof(*nd_opt_rh);
3015         m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
3016
3017         /* connect m0 to m */
3018         m->m_next = m0;
3019         m->m_pkthdr.len = m->m_len + m0->m_len;
3020     }
3021 noredhdropt:;
3022
3023         if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
3024                 sip6->ip6_src.s6_addr16[1] = 0;
3025         if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst))
3026                 sip6->ip6_dst.s6_addr16[1] = 0;
3027 #if 0
3028         if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src))
3029                 ip6->ip6_src.s6_addr16[1] = 0;
3030         if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
3031                 ip6->ip6_dst.s6_addr16[1] = 0;
3032 #endif
3033         if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target))
3034                 nd_rd->nd_rd_target.s6_addr16[1] = 0;
3035         if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst))
3036                 nd_rd->nd_rd_dst.s6_addr16[1] = 0;
3037
3038         ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
3039
3040         nd_rd->nd_rd_cksum = 0;
3041         nd_rd->nd_rd_cksum
3042                 = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen));
3043
3044         /* send the packet to outside... */
3045 #ifdef IPSEC
3046         /* Don't lookup socket */
3047         (void)ipsec_setsocket(m, NULL);
3048 #endif /* IPSEC */
3049         if (ip6_output(m, NULL, NULL, 0, NULL, NULL) != 0)
3050                 icmp6_ifstat_inc(ifp, ifs6_out_error);
3051
3052         icmp6_ifstat_inc(ifp, ifs6_out_msg);
3053         icmp6_ifstat_inc(ifp, ifs6_out_redirect);
3054         icmp6stat.icp6s_outhist[ND_REDIRECT]++;
3055
3056         return;
3057
3058 fail:
3059         if (m)
3060                 m_freem(m);
3061         if (m0)
3062                 m_freem(m0);
3063 }
3064
3065 #ifdef HAVE_NRL_INPCB
3066 #define sotoin6pcb      sotoinpcb
3067 #define in6pcb          inpcb
3068 #define in6p_icmp6filt  inp_icmp6filt
3069 #endif
3070 /*
3071  * ICMPv6 socket option processing.
3072  */
3073 int
3074 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
3075 icmp6_ctloutput(so, sopt)
3076         struct socket *so;
3077         struct sockopt *sopt;
3078 #else
3079 icmp6_ctloutput(op, so, level, optname, mp)
3080         int op;
3081         struct socket *so;
3082         int level, optname;
3083         struct mbuf **mp;
3084 #endif
3085 {
3086         int error = 0;
3087         int optlen;
3088 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
3089         struct inpcb *inp = sotoinpcb(so);
3090         int level, op, optname;
3091
3092         if (sopt) {
3093                 level = sopt->sopt_level;
3094                 op = sopt->sopt_dir;
3095                 optname = sopt->sopt_name;
3096                 optlen = sopt->sopt_valsize;
3097         } else
3098                 level = op = optname = optlen = 0;
3099 #else
3100         struct in6pcb *in6p = sotoin6pcb(so);
3101         struct mbuf *m = *mp;
3102
3103         optlen = m ? m->m_len : 0;
3104 #endif
3105
3106         if (level != IPPROTO_ICMPV6) {
3107 #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
3108                 if (op == PRCO_SETOPT && m)
3109                         (void)m_free(m);
3110 #endif
3111                 return EINVAL;
3112         }
3113
3114         switch (op) {
3115         case PRCO_SETOPT:
3116                 switch (optname) {
3117                 case ICMP6_FILTER:
3118                     {
3119                         struct icmp6_filter *p;
3120
3121                         if (optlen != sizeof(*p)) {
3122                                 error = EMSGSIZE;
3123                                 break;
3124                         }
3125 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
3126                         if (inp->in6p_icmp6filt == NULL) {
3127                                 error = EINVAL;
3128                                 break;
3129                         }
3130                         error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen,
3131                                 optlen);
3132 #else
3133                         p = mtod(m, struct icmp6_filter *);
3134                         if (!p || !in6p->in6p_icmp6filt) {
3135                                 error = EINVAL;
3136                                 break;
3137                         }
3138                         bcopy(p, in6p->in6p_icmp6filt,
3139                                 sizeof(struct icmp6_filter));
3140                         error = 0;
3141 #endif
3142                         break;
3143                     }
3144
3145                 default:
3146                         error = ENOPROTOOPT;
3147                         break;
3148                 }
3149 #if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
3150                 if (m)
3151                         (void)m_freem(m);
3152 #endif          
3153                 break;
3154
3155         case PRCO_GETOPT:
3156                 switch (optname) {
3157                 case ICMP6_FILTER:
3158                     {
3159 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
3160                         if (inp->in6p_icmp6filt == NULL) {
3161                                 error = EINVAL;
3162                                 break;
3163                         }
3164                         error = sooptcopyout(sopt, inp->in6p_icmp6filt,
3165                                 sizeof(struct icmp6_filter));
3166 #else
3167                         struct icmp6_filter *p;
3168
3169                         if (!in6p->in6p_icmp6filt) {
3170                                 error = EINVAL;
3171                                 break;
3172                         }
3173                         *mp = m = m_get(M_WAIT, MT_SOOPTS);
3174                         m->m_len = sizeof(struct icmp6_filter);
3175                         p = mtod(m, struct icmp6_filter *);
3176                         bcopy(in6p->in6p_icmp6filt, p,
3177                                 sizeof(struct icmp6_filter));
3178                         error = 0;
3179 #endif
3180                         break;
3181                     }
3182
3183                 default:
3184                         error = ENOPROTOOPT;
3185                         break;
3186                 }
3187                 break;
3188         }
3189
3190         return(error);
3191 }
3192 #ifdef HAVE_NRL_INPCB
3193 #undef sotoin6pcb
3194 #undef in6pcb
3195 #undef in6p_icmp6filt
3196 #endif
3197
3198 #ifndef HAVE_PPSRATECHECK
3199 #ifndef timersub
3200 #define timersub(tvp, uvp, vvp)                                         \
3201         do {                                                            \
3202                 (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;          \
3203                 (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;       \
3204                 if ((vvp)->tv_usec < 0) {                               \
3205                         (vvp)->tv_sec--;                                \
3206                         (vvp)->tv_usec += 1000000;                      \
3207                 }                                                       \
3208         } while (0)
3209 #endif
3210
3211 /*
3212  * ppsratecheck(): packets (or events) per second limitation.
3213  */
3214 static int
3215 ppsratecheck(lasttime, curpps, maxpps)
3216         struct timeval *lasttime;
3217         int *curpps;
3218         int maxpps;     /* maximum pps allowed */
3219 {
3220         struct timeval tv, delta;
3221         int s, rv;
3222
3223         s = splclock(); 
3224 #ifndef __FreeBSD__
3225         tv = mono_time;
3226 #else
3227         microtime(&tv);
3228 #endif
3229         splx(s);
3230
3231         timersub(&tv, lasttime, &delta);
3232
3233         /*
3234          * Check for 0,0 so that the message will be seen at least once.
3235          * If more than one second has passed since the last update of
3236          * lasttime, reset the counter.
3237          *
3238          * We do increment *curpps even in *curpps < maxpps case, as some may
3239          * try to use *curpps for stat purposes as well.
3240          */
3241         if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) ||
3242             delta.tv_sec >= 1) {
3243                 *lasttime = tv;
3244                 *curpps = 0;
3245                 rv = 1;
3246         } else if (maxpps < 0)
3247                 rv = 1;
3248         else if (*curpps < maxpps)
3249                 rv = 1;
3250         else
3251                 rv = 0;
3252
3253 #if 1 /* DIAGNOSTIC? */
3254         /* be careful about wrap-around */
3255         if (*curpps + 1 > *curpps)
3256                 *curpps = *curpps + 1;
3257 #else
3258         /*
3259          * assume that there's not too many calls to this function.
3260          * not sure if the assumption holds, as it depends on *caller's*
3261          * behavior, not the behavior of this function.
3262          * IMHO it is wrong to make assumption on the caller's behavior,
3263          * so the above #if is #if 1, not #ifdef DIAGNOSTIC.
3264          */
3265         *curpps = *curpps + 1;
3266 #endif
3267
3268         return (rv);
3269 }
3270 #endif
3271
3272 /*
3273  * Perform rate limit check.
3274  * Returns 0 if it is okay to send the icmp6 packet.
3275  * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
3276  * limitation.
3277  *
3278  * XXX per-destination/type check necessary?
3279  */
3280 static int
3281 icmp6_ratelimit(dst, type, code)
3282         const struct in6_addr *dst;     /* not used at this moment */
3283         const int type;                 /* not used at this moment */
3284         const int code;                 /* not used at this moment */
3285 {
3286         int ret;
3287
3288         ret = 0;        /* okay to send */
3289
3290         /* PPS limit */
3291         if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
3292             icmp6errppslim)) {
3293                 /* The packet is subject to rate limit */
3294                 ret++;
3295         }
3296
3297         return ret;
3298 }
3299
3300 /*
3301  * Swap ip6_src and the address in the homeaddress destination option.
3302  * Since the dest6 processing routine swaps ip6_src and homeaddr (if
3303  * any), we must swap them again when we send back the ip datagram to
3304  * the sender (ex. icmp6_error).
3305  *
3306  * returns 0 if succeeded.
3307  */
3308 static int
3309 icmp6_recover_src(m)
3310         struct mbuf *m; /* original ip6 packet */
3311 {
3312         int off, nxt, finished = 0;
3313         struct ip6_hdr *oip6;
3314         struct ip6_ext *exts;
3315         struct ip6_dest *dstopts;
3316         int dstoptlen;
3317         u_int8_t *opt;
3318         int optlen;
3319         struct ip6_opt_home_address *haopt;
3320         struct in6_addr t;
3321         int error = 0;
3322
3323 #ifndef PULLDOWN_TEST 
3324         IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ENOBUFS);
3325         oip6 = mtod(m, struct ip6_hdr *);
3326 #else
3327         IP6_EXTHDR_GET(oip6, struct ip6_hdr *, m, 0, sizeof(*oip6));
3328         if (oip6 == NULL) {
3329                 error = ENOBUFS;
3330                 goto bad;
3331         }
3332 #endif
3333
3334         off = sizeof(struct ip6_hdr);
3335         nxt = oip6->ip6_nxt;
3336         while (off + 2 < m->m_pkthdr.len) {
3337                 switch (nxt) {
3338                 case IPPROTO_DSTOPTS:
3339 #ifndef PULLDOWN_TEST 
3340                         IP6_EXTHDR_CHECK(m, off, sizeof(struct ip6_dest),
3341                                          ENOBUFS);
3342                         dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
3343 #else
3344                         IP6_EXTHDR_GET(dstopts, struct ip6_dest *,
3345                                        m, off, sizeof(*dstopts));
3346                         if (dstopts == NULL) {
3347                                 error = ENOBUFS;
3348                                 goto bad;
3349                         }
3350 #endif
3351
3352                         dstoptlen = (dstopts->ip6d_len + 1) << 3;
3353 #ifndef PULLDOWN_TEST 
3354                         IP6_EXTHDR_CHECK(m, off, dstoptlen, ENOBUFS);
3355                         dstopts = (struct ip6_dest *)(mtod(m, caddr_t) + off);
3356 #else
3357                         IP6_EXTHDR_GET(dstopts, struct ip6_dest *,
3358                                        m, off, dstoptlen);
3359                         if (dstopts == NULL) {
3360                                 error = ENOBUFS;
3361                                 goto bad;
3362                         }
3363 #endif
3364                         off += dstoptlen;
3365                         nxt = dstopts->ip6d_nxt;
3366
3367                         /* find homeaddress dstopt */
3368                         dstoptlen -= sizeof(struct ip6_dest);
3369                         opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest);
3370                         for (optlen = 0; (dstoptlen > 0 && finished == 0);
3371                              dstoptlen -= optlen, opt += optlen) {
3372                                 if ((*opt != IP6OPT_PAD1) &&
3373                                     (dstoptlen < IP6OPT_MINLEN || *(opt + 1) + 2 > dstoptlen)) {
3374                                         error = EINVAL;
3375                                         goto bad;
3376                                 }
3377                                 switch (*opt) {
3378                                 case IP6OPT_PAD1:
3379                                         optlen = 1;
3380                                         break;
3381                                 case IP6OPT_PADN:
3382                                         optlen = *(opt + 1) + 2;
3383                                         break;
3384                                 case IP6OPT_HOME_ADDRESS:
3385                                         haopt = (struct ip6_opt_home_address *)opt;
3386                                         optlen = haopt->ip6oh_len + 2;
3387                                         if (optlen < sizeof(*haopt)) {
3388                                                 error = EINVAL;
3389                                                 goto bad;
3390                                         }
3391
3392                                         /* swap */
3393                                         bcopy(haopt->ip6oh_addr, &t,
3394                                               sizeof(haopt->ip6oh_addr));
3395                                         bcopy(&oip6->ip6_src,
3396                                               haopt->ip6oh_addr,
3397                                               sizeof(oip6->ip6_src));
3398                                         bcopy(&t, &oip6->ip6_src,
3399                                               sizeof(t));
3400                                         finished = 1;
3401                                         break;
3402                                 default:
3403                                         optlen = ip6_unknown_opt(opt, m,
3404                                             opt - mtod(m, u_int8_t *));
3405                                         if (optlen == -1) {
3406                                                 error = EINVAL;
3407                                                 goto bad;
3408                                         }
3409                                         optlen += 2;
3410                                         break;
3411                                 }
3412                         }
3413                         break;
3414
3415                 default:
3416 #ifndef PULLDOWN_TEST 
3417                         IP6_EXTHDR_CHECK(m, off, sizeof(struct ip6_ext),
3418                                          ENOBUFS);
3419                         exts = (struct ip6_ext *)(mtod(m, caddr_t) + off);
3420 #else
3421                         IP6_EXTHDR_GET(exts, struct ip6_ext *,
3422                                        m, off, sizeof(*exts));
3423                         if (exts == NULL) {
3424                                 error = ENOBUFS;
3425                                 goto bad;
3426                         }
3427 #endif
3428                         if (nxt == IPPROTO_AH)
3429                                 off += (exts->ip6e_len + 2) << 2;
3430                         else
3431                                 off += (exts->ip6e_len + 1) << 3;
3432                         nxt = exts->ip6e_nxt;
3433                         break;
3434                 }
3435                 if (finished)
3436                         break;
3437         }
3438
3439         return (0);
3440  bad:
3441         ip6_delaux(m);
3442         m_freem(m);
3443         return (error);
3444 }
3445
3446 #if defined(__NetBSD__) || defined(__OpenBSD__)
3447 static struct rtentry *
3448 icmp6_mtudisc_clone(dst)
3449         struct sockaddr *dst;
3450 {
3451         struct rtentry *rt;
3452         int    error;
3453
3454         rt = rtalloc1(dst, 1);
3455         if (rt == 0)
3456                 return NULL;
3457
3458         /* If we didn't get a host route, allocate one */
3459         if ((rt->rt_flags & RTF_HOST) == 0) {
3460                 struct rtentry *nrt;
3461
3462                 error = rtrequest((int) RTM_ADD, dst,
3463                     (struct sockaddr *) rt->rt_gateway,
3464                     (struct sockaddr *) 0,
3465                     RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC, &nrt);
3466                 if (error) {
3467                         rtfree(rt);
3468                         return NULL;
3469                 }
3470                 nrt->rt_rmx = rt->rt_rmx;
3471                 rtfree(rt);
3472                 rt = nrt;
3473         }
3474         error = rt_timer_add(rt, icmp6_mtudisc_timeout,
3475                         icmp6_mtudisc_timeout_q);
3476         if (error) {
3477                 rtfree(rt);
3478                 return NULL;
3479         }
3480
3481         return rt;      /* caller need to call rtfree() */
3482 }
3483
3484 static void
3485 icmp6_mtudisc_timeout(rt, r)
3486         struct rtentry *rt;
3487         struct rttimer *r;
3488 {
3489         if (rt == NULL)
3490                 panic("icmp6_mtudisc_timeout: bad route to timeout");
3491         if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
3492             (RTF_DYNAMIC | RTF_HOST)) {
3493                 rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
3494                     rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
3495         } else {
3496                 if ((rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
3497                         rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu;
3498         }
3499 }
3500
3501 static void
3502 icmp6_redirect_timeout(rt, r)
3503         struct rtentry *rt;
3504         struct rttimer *r;
3505 {
3506         if (rt == NULL)
3507                 panic("icmp6_redirect_timeout: bad route to timeout");
3508         if ((rt->rt_flags & (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) ==
3509             (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) {
3510                 rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
3511                     rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
3512         }
3513 }
3514 #endif /* __NetBSD__ || __OpenBSD__ */
3515
3516 #ifdef __bsdi__
3517 void
3518 icmp6_mtuexpire(rt, rtt)
3519         struct rtentry *rt;
3520         struct rttimer *rtt;
3521 {
3522         rt->rt_flags |= RTF_PROBEMTU;
3523         R_Free(rtt);
3524 }
3525
3526 int *icmp6_sysvars[] = ICMPV6CTL_VARS;
3527
3528 int
3529 icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
3530         int *name;
3531         u_int namelen;
3532         void *oldp;
3533         size_t *oldlenp;
3534         void *newp;
3535         size_t newlen;
3536 {
3537         if (name[0] >= ICMPV6CTL_MAXID)
3538                 return (EOPNOTSUPP);
3539         switch (name[0]) {
3540 #if 0
3541         ICMPV6CTL_ND6_PRUNE:
3542         ICMPV6CTL_ND6_DELAY:
3543         ICMPV6CTL_ND6_UMAXTRIES:
3544         ICMPV6CTL_ND6_MMAXTRIES:
3545         ICMPV6CTL_ND6_USELOOPBACK:
3546                 /* need to check the value. */
3547 #endif
3548         case ICMPV6CTL_STATS:
3549                 return sysctl_rdtrunc(oldp, oldlenp, newp, &icmp6stat,
3550                     sizeof(icmp6stat));
3551         case ICMPV6CTL_ND6_DRLIST:
3552         case ICMPV6CTL_ND6_PRLIST:
3553                 return nd6_sysctl(name[0], oldp, oldlenp, newp, newlen);
3554         default:
3555                 return (sysctl_int_arr(icmp6_sysvars, name, namelen,
3556                     oldp, oldlenp, newp, newlen));
3557         }
3558 }
3559 #endif /* __bsdi__ */
3560
3561 #if defined(__NetBSD__) || defined(__OpenBSD__)
3562 #include <vm/vm.h>
3563 #include <sys/sysctl.h>
3564 int
3565 icmp6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
3566         int *name;
3567         u_int namelen;
3568         void *oldp;
3569         size_t *oldlenp;
3570         void *newp;
3571         size_t newlen;
3572 {
3573
3574         /* All sysctl names at this level are terminal. */
3575         if (namelen != 1)
3576                 return ENOTDIR;
3577
3578         switch (name[0]) {
3579
3580         case ICMPV6CTL_REDIRACCEPT:
3581                 return sysctl_int(oldp, oldlenp, newp, newlen,
3582                                 &icmp6_rediraccept);
3583         case ICMPV6CTL_REDIRTIMEOUT:
3584                 return sysctl_int(oldp, oldlenp, newp, newlen,
3585                                 &icmp6_redirtimeout);
3586         case ICMPV6CTL_STATS:
3587                 return sysctl_rdstruct(oldp, oldlenp, newp,
3588                                 &icmp6stat, sizeof(icmp6stat));
3589         case ICMPV6CTL_ND6_PRUNE:
3590                 return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_prune);
3591         case ICMPV6CTL_ND6_DELAY:
3592                 return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_delay);
3593         case ICMPV6CTL_ND6_UMAXTRIES:
3594                 return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_umaxtries);
3595         case ICMPV6CTL_ND6_MMAXTRIES:
3596                 return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_mmaxtries);
3597         case ICMPV6CTL_ND6_USELOOPBACK:
3598                 return sysctl_int(oldp, oldlenp, newp, newlen,
3599                                 &nd6_useloopback);
3600         case ICMPV6CTL_NODEINFO:
3601                 return sysctl_int(oldp, oldlenp, newp, newlen, &icmp6_nodeinfo);
3602         case ICMPV6CTL_ERRPPSLIMIT:
3603                 return sysctl_int(oldp, oldlenp, newp, newlen, &icmp6errppslim);
3604         case ICMPV6CTL_ND6_MAXNUDHINT:
3605                 return sysctl_int(oldp, oldlenp, newp, newlen,
3606                                 &nd6_maxnudhint);
3607         case ICMPV6CTL_MTUDISC_HIWAT:
3608                 return sysctl_int(oldp, oldlenp, newp, newlen,
3609                                 &icmp6_mtudisc_hiwat);
3610         case ICMPV6CTL_MTUDISC_LOWAT:
3611                 return sysctl_int(oldp, oldlenp, newp, newlen,
3612                                 &icmp6_mtudisc_lowat);
3613         case ICMPV6CTL_ND6_DEBUG:
3614                 return sysctl_int(oldp, oldlenp, newp, newlen, &nd6_debug);
3615         case ICMPV6CTL_ND6_DRLIST:
3616         case ICMPV6CTL_ND6_PRLIST:
3617                 return nd6_sysctl(name[0], oldp, oldlenp, newp, newlen);
3618         default:
3619                 return ENOPROTOOPT;
3620         }
3621         /* NOTREACHED */
3622 }
3623 #endif /* __NetBSD__ */