icmp6.c revision 1.121.4.2
11.121.4.2Syamt/*	$NetBSD: icmp6.c,v 1.121.4.2 2006/12/10 07:19:14 yamt Exp $	*/
21.66Sitojun/*	$KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $	*/
31.3Sthorpej
41.2Sitojun/*
51.2Sitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
61.2Sitojun * All rights reserved.
71.29Sitojun *
81.2Sitojun * Redistribution and use in source and binary forms, with or without
91.2Sitojun * modification, are permitted provided that the following conditions
101.2Sitojun * are met:
111.2Sitojun * 1. Redistributions of source code must retain the above copyright
121.2Sitojun *    notice, this list of conditions and the following disclaimer.
131.2Sitojun * 2. Redistributions in binary form must reproduce the above copyright
141.2Sitojun *    notice, this list of conditions and the following disclaimer in the
151.2Sitojun *    documentation and/or other materials provided with the distribution.
161.2Sitojun * 3. Neither the name of the project nor the names of its contributors
171.2Sitojun *    may be used to endorse or promote products derived from this software
181.2Sitojun *    without specific prior written permission.
191.29Sitojun *
201.2Sitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
211.2Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221.2Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231.2Sitojun * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
241.2Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251.2Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261.2Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271.2Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281.2Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291.2Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301.2Sitojun * SUCH DAMAGE.
311.2Sitojun */
321.2Sitojun
331.2Sitojun/*
341.2Sitojun * Copyright (c) 1982, 1986, 1988, 1993
351.2Sitojun *	The Regents of the University of California.  All rights reserved.
361.2Sitojun *
371.2Sitojun * Redistribution and use in source and binary forms, with or without
381.2Sitojun * modification, are permitted provided that the following conditions
391.2Sitojun * are met:
401.2Sitojun * 1. Redistributions of source code must retain the above copyright
411.2Sitojun *    notice, this list of conditions and the following disclaimer.
421.2Sitojun * 2. Redistributions in binary form must reproduce the above copyright
431.2Sitojun *    notice, this list of conditions and the following disclaimer in the
441.2Sitojun *    documentation and/or other materials provided with the distribution.
451.96Sagc * 3. Neither the name of the University nor the names of its contributors
461.2Sitojun *    may be used to endorse or promote products derived from this software
471.2Sitojun *    without specific prior written permission.
481.2Sitojun *
491.2Sitojun * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
501.2Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
511.2Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
521.2Sitojun * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
531.2Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
541.2Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
551.2Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
561.2Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
571.2Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
581.2Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
591.2Sitojun * SUCH DAMAGE.
601.2Sitojun *
611.2Sitojun *	@(#)ip_icmp.c	8.2 (Berkeley) 1/4/94
621.2Sitojun */
631.71Slukem
641.71Slukem#include <sys/cdefs.h>
651.121.4.2Syamt__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.121.4.2 2006/12/10 07:19:14 yamt Exp $");
661.2Sitojun
671.2Sitojun#include "opt_inet.h"
681.6Sthorpej#include "opt_ipsec.h"
691.2Sitojun
701.2Sitojun#include <sys/param.h>
711.2Sitojun#include <sys/systm.h>
721.2Sitojun#include <sys/malloc.h>
731.2Sitojun#include <sys/mbuf.h>
741.2Sitojun#include <sys/protosw.h>
751.2Sitojun#include <sys/socket.h>
761.2Sitojun#include <sys/socketvar.h>
771.2Sitojun#include <sys/time.h>
781.2Sitojun#include <sys/kernel.h>
791.2Sitojun#include <sys/syslog.h>
801.12Sitojun#include <sys/domain.h>
811.70Ssimonb#include <sys/sysctl.h>
821.2Sitojun
831.2Sitojun#include <net/if.h>
841.2Sitojun#include <net/route.h>
851.2Sitojun#include <net/if_dl.h>
861.2Sitojun#include <net/if_types.h>
871.2Sitojun
881.2Sitojun#include <netinet/in.h>
891.2Sitojun#include <netinet/in_var.h>
901.19Sitojun#include <netinet/ip6.h>
911.2Sitojun#include <netinet6/ip6_var.h>
921.19Sitojun#include <netinet/icmp6.h>
931.2Sitojun#include <netinet6/mld6_var.h>
941.2Sitojun#include <netinet6/in6_pcb.h>
951.2Sitojun#include <netinet6/nd6.h>
961.2Sitojun#include <netinet6/in6_ifattach.h>
971.10Sitojun#include <netinet6/ip6protosw.h>
981.113Srpaulo#include <netinet6/scope6_var.h>
991.12Sitojun
1001.2Sitojun#ifdef IPSEC
1011.12Sitojun#include <netinet6/ipsec.h>
1021.2Sitojun#include <netkey/key.h>
1031.2Sitojun#endif
1041.2Sitojun
1051.2Sitojun#include "faith.h"
1061.64Sitojun#if defined(NFAITH) && 0 < NFAITH
1071.64Sitojun#include <net/if_faith.h>
1081.64Sitojun#endif
1091.2Sitojun
1101.12Sitojun#include <net/net_osdep.h>
1111.12Sitojun
1121.12Sitojunextern struct domain inet6domain;
1131.2Sitojun
1141.2Sitojunstruct icmp6stat icmp6stat;
1151.2Sitojun
1161.101Sitojunextern struct inpcbtable raw6cbtable;
1171.35Sitojunextern int icmp6errppslim;
1181.35Sitojunstatic int icmp6errpps_count = 0;
1191.37Sitojunstatic struct timeval icmp6errppslim_last;
1201.14Sitojunextern int icmp6_nodeinfo;
1211.46Sitojun
1221.46Sitojun/*
1231.46Sitojun * List of callbacks to notify when Path MTU changes are made.
1241.46Sitojun */
1251.46Sitojunstruct icmp6_mtudisc_callback {
1261.46Sitojun	LIST_ENTRY(icmp6_mtudisc_callback) mc_list;
1271.46Sitojun	void (*mc_func) __P((struct in6_addr *));
1281.46Sitojun};
1291.46Sitojun
1301.46SitojunLIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks =
1311.46Sitojun    LIST_HEAD_INITIALIZER(&icmp6_mtudisc_callbacks);
1321.46Sitojun
1331.8Sitojunstatic struct rttimer_queue *icmp6_mtudisc_timeout_q = NULL;
1341.8Sitojunextern int pmtu_expire;
1351.8Sitojun
1361.48Sitojun/* XXX do these values make any sense? */
1371.55Sitojunstatic int icmp6_mtudisc_hiwat = 1280;
1381.55Sitojunstatic int icmp6_mtudisc_lowat = 256;
1391.55Sitojun
1401.55Sitojun/*
1411.55Sitojun * keep track of # of redirect routes.
1421.55Sitojun */
1431.55Sitojunstatic struct rttimer_queue *icmp6_redirect_timeout_q = NULL;
1441.55Sitojun
1451.55Sitojun/* XXX experimental, turned off */
1461.55Sitojunstatic int icmp6_redirect_hiwat = -1;
1471.55Sitojunstatic int icmp6_redirect_lowat = -1;
1481.48Sitojun
1491.35Sitojunstatic void icmp6_errcount __P((struct icmp6errstat *, int, int));
1501.2Sitojunstatic int icmp6_rip6_input __P((struct mbuf **, int));
1511.2Sitojunstatic int icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
1521.12Sitojunstatic const char *icmp6_redirect_diag __P((struct in6_addr *,
1531.12Sitojun	struct in6_addr *, struct in6_addr *));
1541.31Sitojunstatic struct mbuf *ni6_input __P((struct mbuf *, int));
1551.31Sitojunstatic struct mbuf *ni6_nametodns __P((const char *, int, int));
1561.31Sitojunstatic int ni6_dnsmatch __P((const char *, int, const char *, int));
1571.2Sitojunstatic int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *,
1581.47Sitojun			  struct ifnet **, char *));
1591.2Sitojunstatic int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *,
1601.2Sitojun				struct ifnet *, int));
1611.58Sitojunstatic int icmp6_notify_error __P((struct mbuf *, int, int, int));
1621.8Sitojunstatic struct rtentry *icmp6_mtudisc_clone __P((struct sockaddr *));
1631.8Sitojunstatic void icmp6_mtudisc_timeout __P((struct rtentry *, struct rttimer *));
1641.55Sitojunstatic void icmp6_redirect_timeout __P((struct rtentry *, struct rttimer *));
1651.2Sitojun
1661.116Schristos
1671.2Sitojunvoid
1681.2Sitojunicmp6_init()
1691.2Sitojun{
1701.115Srpaulo	mld_init();
1711.8Sitojun	icmp6_mtudisc_timeout_q = rt_timer_queue_create(pmtu_expire);
1721.55Sitojun	icmp6_redirect_timeout_q = rt_timer_queue_create(icmp6_redirtimeout);
1731.2Sitojun}
1741.2Sitojun
1751.35Sitojunstatic void
1761.35Sitojunicmp6_errcount(stat, type, code)
1771.35Sitojun	struct icmp6errstat *stat;
1781.35Sitojun	int type, code;
1791.35Sitojun{
1801.56Sitojun	switch (type) {
1811.35Sitojun	case ICMP6_DST_UNREACH:
1821.35Sitojun		switch (code) {
1831.35Sitojun		case ICMP6_DST_UNREACH_NOROUTE:
1841.35Sitojun			stat->icp6errs_dst_unreach_noroute++;
1851.35Sitojun			return;
1861.35Sitojun		case ICMP6_DST_UNREACH_ADMIN:
1871.35Sitojun			stat->icp6errs_dst_unreach_admin++;
1881.35Sitojun			return;
1891.35Sitojun		case ICMP6_DST_UNREACH_BEYONDSCOPE:
1901.35Sitojun			stat->icp6errs_dst_unreach_beyondscope++;
1911.35Sitojun			return;
1921.35Sitojun		case ICMP6_DST_UNREACH_ADDR:
1931.35Sitojun			stat->icp6errs_dst_unreach_addr++;
1941.35Sitojun			return;
1951.35Sitojun		case ICMP6_DST_UNREACH_NOPORT:
1961.35Sitojun			stat->icp6errs_dst_unreach_noport++;
1971.35Sitojun			return;
1981.35Sitojun		}
1991.35Sitojun		break;
2001.35Sitojun	case ICMP6_PACKET_TOO_BIG:
2011.35Sitojun		stat->icp6errs_packet_too_big++;
2021.35Sitojun		return;
2031.35Sitojun	case ICMP6_TIME_EXCEEDED:
2041.56Sitojun		switch (code) {
2051.35Sitojun		case ICMP6_TIME_EXCEED_TRANSIT:
2061.35Sitojun			stat->icp6errs_time_exceed_transit++;
2071.35Sitojun			return;
2081.35Sitojun		case ICMP6_TIME_EXCEED_REASSEMBLY:
2091.35Sitojun			stat->icp6errs_time_exceed_reassembly++;
2101.35Sitojun			return;
2111.35Sitojun		}
2121.35Sitojun		break;
2131.35Sitojun	case ICMP6_PARAM_PROB:
2141.56Sitojun		switch (code) {
2151.35Sitojun		case ICMP6_PARAMPROB_HEADER:
2161.35Sitojun			stat->icp6errs_paramprob_header++;
2171.35Sitojun			return;
2181.35Sitojun		case ICMP6_PARAMPROB_NEXTHEADER:
2191.35Sitojun			stat->icp6errs_paramprob_nextheader++;
2201.35Sitojun			return;
2211.35Sitojun		case ICMP6_PARAMPROB_OPTION:
2221.35Sitojun			stat->icp6errs_paramprob_option++;
2231.35Sitojun			return;
2241.35Sitojun		}
2251.35Sitojun		break;
2261.35Sitojun	case ND_REDIRECT:
2271.35Sitojun		stat->icp6errs_redirect++;
2281.35Sitojun		return;
2291.35Sitojun	}
2301.35Sitojun	stat->icp6errs_unknown++;
2311.35Sitojun}
2321.35Sitojun
2331.2Sitojun/*
2341.46Sitojun * Register a Path MTU Discovery callback.
2351.46Sitojun */
2361.46Sitojunvoid
2371.46Sitojunicmp6_mtudisc_callback_register(func)
2381.46Sitojun	void (*func) __P((struct in6_addr *));
2391.46Sitojun{
2401.46Sitojun	struct icmp6_mtudisc_callback *mc;
2411.46Sitojun
2421.46Sitojun	for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
2431.46Sitojun	     mc = LIST_NEXT(mc, mc_list)) {
2441.46Sitojun		if (mc->mc_func == func)
2451.46Sitojun			return;
2461.46Sitojun	}
2471.46Sitojun
2481.46Sitojun	mc = malloc(sizeof(*mc), M_PCB, M_NOWAIT);
2491.46Sitojun	if (mc == NULL)
2501.46Sitojun		panic("icmp6_mtudisc_callback_register");
2511.46Sitojun
2521.46Sitojun	mc->mc_func = func;
2531.46Sitojun	LIST_INSERT_HEAD(&icmp6_mtudisc_callbacks, mc, mc_list);
2541.46Sitojun}
2551.46Sitojun
2561.46Sitojun/*
2571.113Srpaulo * A wrapper function for icmp6_error() necessary when the erroneous packet
2581.113Srpaulo * may not contain enough scope zone information.
2591.113Srpaulo */
2601.113Srpaulovoid
2611.113Srpauloicmp6_error2(m, type, code, param, ifp)
2621.113Srpaulo	struct mbuf *m;
2631.113Srpaulo	int type, code, param;
2641.113Srpaulo	struct ifnet *ifp;
2651.113Srpaulo{
2661.113Srpaulo	struct ip6_hdr *ip6;
2671.113Srpaulo
2681.113Srpaulo	if (ifp == NULL)
2691.113Srpaulo		return;
2701.113Srpaulo
2711.113Srpaulo	if (m->m_len < sizeof(struct ip6_hdr)) {
2721.113Srpaulo		m = m_pullup(m, sizeof(struct ip6_hdr));
2731.113Srpaulo		if (m == NULL)
2741.113Srpaulo			return;
2751.113Srpaulo	}
2761.113Srpaulo
2771.113Srpaulo	ip6 = mtod(m, struct ip6_hdr *);
2781.113Srpaulo
2791.113Srpaulo	if (in6_setscope(&ip6->ip6_src, ifp, NULL) != 0)
2801.113Srpaulo		return;
2811.113Srpaulo	if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
2821.113Srpaulo		return;
2831.113Srpaulo
2841.113Srpaulo	icmp6_error(m, type, code, param);
2851.113Srpaulo}
2861.113Srpaulo
2871.113Srpaulo/*
2881.2Sitojun * Generate an error packet of type error in response to bad IP6 packet.
2891.2Sitojun */
2901.2Sitojunvoid
2911.2Sitojunicmp6_error(m, type, code, param)
2921.2Sitojun	struct mbuf *m;
2931.2Sitojun	int type, code, param;
2941.2Sitojun{
2951.2Sitojun	struct ip6_hdr *oip6, *nip6;
2961.2Sitojun	struct icmp6_hdr *icmp6;
2971.17Sitohy	u_int preplen;
2981.2Sitojun	int off;
2991.27Sitojun	int nxt;
3001.2Sitojun
3011.2Sitojun	icmp6stat.icp6s_error++;
3021.2Sitojun
3031.35Sitojun	/* count per-type-code statistics */
3041.35Sitojun	icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code);
3051.35Sitojun
3061.23Sitojun	if (m->m_flags & M_DECRYPTED) {
3071.23Sitojun		icmp6stat.icp6s_canterror++;
3081.2Sitojun		goto freeit;
3091.23Sitojun	}
3101.2Sitojun
3111.12Sitojun	if (m->m_len < sizeof(struct ip6_hdr)) {
3121.12Sitojun		m = m_pullup(m, sizeof(struct ip6_hdr));
3131.12Sitojun		if (m == NULL)
3141.12Sitojun			return;
3151.12Sitojun	}
3161.2Sitojun	oip6 = mtod(m, struct ip6_hdr *);
3171.2Sitojun
3181.2Sitojun	/*
3191.67Sitojun	 * If the destination address of the erroneous packet is a multicast
3201.67Sitojun	 * address, or the packet was sent using link-layer multicast,
3211.67Sitojun	 * we should basically suppress sending an error (RFC 2463, Section
3221.67Sitojun	 * 2.4).
3231.67Sitojun	 * We have two exceptions (the item e.2 in that section):
3241.67Sitojun	 * - the Pakcet Too Big message can be sent for path MTU discovery.
3251.67Sitojun	 * - the Parameter Problem Message that can be allowed an icmp6 error
3261.67Sitojun	 *   in the option type field.  This check has been done in
3271.67Sitojun	 *   ip6_unknown_opt(), so we can just check the type and code.
3281.2Sitojun	 */
3291.2Sitojun	if ((m->m_flags & (M_BCAST|M_MCAST) ||
3301.2Sitojun	     IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
3311.2Sitojun	    (type != ICMP6_PACKET_TOO_BIG &&
3321.2Sitojun	     (type != ICMP6_PARAM_PROB ||
3331.2Sitojun	      code != ICMP6_PARAMPROB_OPTION)))
3341.2Sitojun		goto freeit;
3351.2Sitojun
3361.67Sitojun	/*
3371.67Sitojun	 * RFC 2463, 2.4 (e.5): source address check.
3381.67Sitojun	 * XXX: the case of anycast source?
3391.67Sitojun	 */
3401.2Sitojun	if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
3411.2Sitojun	    IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
3421.2Sitojun		goto freeit;
3431.2Sitojun
3441.2Sitojun	/*
3451.27Sitojun	 * If we are about to send ICMPv6 against ICMPv6 error/redirect,
3461.27Sitojun	 * don't do it.
3471.2Sitojun	 */
3481.27Sitojun	nxt = -1;
3491.28Sitojun	off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
3501.27Sitojun	if (off >= 0 && nxt == IPPROTO_ICMPV6) {
3511.2Sitojun		struct icmp6_hdr *icp;
3521.2Sitojun
3531.27Sitojun		IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
3541.27Sitojun			sizeof(*icp));
3551.27Sitojun		if (icp == NULL) {
3561.27Sitojun			icmp6stat.icp6s_tooshort++;
3571.27Sitojun			return;
3581.27Sitojun		}
3591.27Sitojun		if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
3601.27Sitojun		    icp->icmp6_type == ND_REDIRECT) {
3611.27Sitojun			/*
3621.27Sitojun			 * ICMPv6 error
3631.27Sitojun			 * Special case: for redirect (which is
3641.27Sitojun			 * informational) we must not send icmp6 error.
3651.27Sitojun			 */
3661.27Sitojun			icmp6stat.icp6s_canterror++;
3671.27Sitojun			goto freeit;
3681.27Sitojun		} else {
3691.27Sitojun			/* ICMPv6 informational - send the error */
3701.2Sitojun		}
3711.67Sitojun	}
3721.67Sitojun#if 0 /* controversial */
3731.67Sitojun	else if (off >= 0 && nxt == IPPROTO_ESP) {
3741.67Sitojun		/*
3751.67Sitojun		 * It could be ICMPv6 error inside ESP.  Take a safer side,
3761.67Sitojun		 * don't respond.
3771.67Sitojun		 */
3781.67Sitojun		icmp6stat.icp6s_canterror++;
3791.67Sitojun		goto freeit;
3801.67Sitojun	}
3811.67Sitojun#endif
3821.67Sitojun	else {
3831.27Sitojun		/* non-ICMPv6 - send the error */
3841.2Sitojun	}
3851.2Sitojun
3861.2Sitojun	oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
3871.2Sitojun
3881.2Sitojun	/* Finally, do rate limitation check. */
3891.2Sitojun	if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
3901.2Sitojun		icmp6stat.icp6s_toofreq++;
3911.2Sitojun		goto freeit;
3921.2Sitojun	}
3931.2Sitojun
3941.2Sitojun	/*
3951.2Sitojun	 * OK, ICMP6 can be generated.
3961.2Sitojun	 */
3971.2Sitojun
3981.2Sitojun	if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
3991.2Sitojun		m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
4001.2Sitojun
4011.17Sitohy	preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
4021.17Sitohy	M_PREPEND(m, preplen, M_DONTWAIT);
4031.17Sitohy	if (m && m->m_len < preplen)
4041.17Sitohy		m = m_pullup(m, preplen);
4051.2Sitojun	if (m == NULL) {
4061.53Sitojun		nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__));
4071.2Sitojun		return;
4081.2Sitojun	}
4091.2Sitojun
4101.2Sitojun	nip6 = mtod(m, struct ip6_hdr *);
4111.2Sitojun	nip6->ip6_src  = oip6->ip6_src;
4121.2Sitojun	nip6->ip6_dst  = oip6->ip6_dst;
4131.2Sitojun
4141.113Srpaulo	in6_clearscope(&oip6->ip6_src);
4151.113Srpaulo	in6_clearscope(&oip6->ip6_dst);
4161.2Sitojun
4171.2Sitojun	icmp6 = (struct icmp6_hdr *)(nip6 + 1);
4181.2Sitojun	icmp6->icmp6_type = type;
4191.2Sitojun	icmp6->icmp6_code = code;
4201.7Sitojun	icmp6->icmp6_pptr = htonl((u_int32_t)param);
4211.63Sitojun
4221.63Sitojun	/*
4231.63Sitojun	 * icmp6_reflect() is designed to be in the input path.
4241.114Srpaulo	 * icmp6_error() can be called from both input and output path,
4251.63Sitojun	 * and if we are in output path rcvif could contain bogus value.
4261.63Sitojun	 * clear m->m_pkthdr.rcvif for safety, we should have enough scope
4271.63Sitojun	 * information in ip header (nip6).
4281.63Sitojun	 */
4291.63Sitojun	m->m_pkthdr.rcvif = NULL;
4301.2Sitojun
4311.2Sitojun	icmp6stat.icp6s_outhist[type]++;
4321.67Sitojun	icmp6_reflect(m, sizeof(struct ip6_hdr)); /* header order: IPv6 - ICMPv6 */
4331.27Sitojun
4341.27Sitojun	return;
4351.27Sitojun
4361.27Sitojun  freeit:
4371.27Sitojun	/*
4381.114Srpaulo	 * If we can't tell whether or not we can generate ICMP6, free it.
4391.27Sitojun	 */
4401.27Sitojun	m_freem(m);
4411.2Sitojun}
4421.2Sitojun
4431.2Sitojun/*
4441.2Sitojun * Process a received ICMP6 message.
4451.2Sitojun */
4461.2Sitojunint
4471.121.4.2Syamticmp6_input(struct mbuf **mp, int *offp, int proto)
4481.2Sitojun{
4491.2Sitojun	struct mbuf *m = *mp, *n;
4501.2Sitojun	struct ip6_hdr *ip6, *nip6;
4511.2Sitojun	struct icmp6_hdr *icmp6, *nicmp6;
4521.2Sitojun	int off = *offp;
4531.2Sitojun	int icmp6len = m->m_pkthdr.len - *offp;
4541.2Sitojun	int code, sum, noff;
4551.2Sitojun
4561.116Schristos#define ICMP6_MAXLEN (sizeof(*nip6) + sizeof(*nicmp6) + 4)
4571.116Schristos	KASSERT(ICMP6_MAXLEN < MCLBYTES);
4581.72Sitojun	icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
4591.72Sitojun
4601.2Sitojun	/*
4611.2Sitojun	 * Locate icmp6 structure in mbuf, and check
4621.2Sitojun	 * that not corrupted and of at least minimum length
4631.2Sitojun	 */
4641.2Sitojun
4651.2Sitojun	ip6 = mtod(m, struct ip6_hdr *);
4661.2Sitojun	if (icmp6len < sizeof(struct icmp6_hdr)) {
4671.2Sitojun		icmp6stat.icp6s_tooshort++;
4681.72Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
4691.2Sitojun		goto freeit;
4701.2Sitojun	}
4711.2Sitojun
4721.2Sitojun	/*
4731.2Sitojun	 * calculate the checksum
4741.2Sitojun	 */
4751.12Sitojun	IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
4761.12Sitojun	if (icmp6 == NULL) {
4771.12Sitojun		icmp6stat.icp6s_tooshort++;
4781.106Sitojun		/* m is invalid */
4791.106Sitojun		/*icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);*/
4801.12Sitojun		return IPPROTO_DONE;
4811.12Sitojun	}
4821.83Sthorpej	KASSERT(IP6_HDR_ALIGNED_P(icmp6));
4831.2Sitojun	code = icmp6->icmp6_code;
4841.2Sitojun
4851.2Sitojun	if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
4861.53Sitojun		nd6log((LOG_ERR,
4871.2Sitojun		    "ICMP6 checksum error(%d|%x) %s\n",
4881.53Sitojun		    icmp6->icmp6_type, sum, ip6_sprintf(&ip6->ip6_src)));
4891.2Sitojun		icmp6stat.icp6s_checksum++;
4901.72Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
4911.2Sitojun		goto freeit;
4921.2Sitojun	}
4931.2Sitojun
4941.2Sitojun#if defined(NFAITH) && 0 < NFAITH
4951.64Sitojun	if (faithprefix(&ip6->ip6_dst)) {
4961.2Sitojun		/*
4971.2Sitojun		 * Deliver very specific ICMP6 type only.
4981.114Srpaulo		 * This is important to deliver TOOBIG.  Otherwise PMTUD
4991.2Sitojun		 * will not work.
5001.2Sitojun		 */
5011.2Sitojun		switch (icmp6->icmp6_type) {
5021.2Sitojun		case ICMP6_DST_UNREACH:
5031.2Sitojun		case ICMP6_PACKET_TOO_BIG:
5041.2Sitojun		case ICMP6_TIME_EXCEEDED:
5051.2Sitojun			break;
5061.2Sitojun		default:
5071.2Sitojun			goto freeit;
5081.2Sitojun		}
5091.2Sitojun	}
5101.2Sitojun#endif
5111.2Sitojun
5121.2Sitojun	icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
5131.2Sitojun
5141.2Sitojun	switch (icmp6->icmp6_type) {
5151.2Sitojun	case ICMP6_DST_UNREACH:
5161.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
5171.2Sitojun		switch (code) {
5181.2Sitojun		case ICMP6_DST_UNREACH_NOROUTE:
5191.2Sitojun			code = PRC_UNREACH_NET;
5201.2Sitojun			break;
5211.2Sitojun		case ICMP6_DST_UNREACH_ADMIN:
5221.12Sitojun			icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
5231.23Sitojun			code = PRC_UNREACH_PROTOCOL; /* is this a good code? */
5241.23Sitojun			break;
5251.2Sitojun		case ICMP6_DST_UNREACH_ADDR:
5261.23Sitojun			code = PRC_HOSTDEAD;
5271.2Sitojun			break;
5281.23Sitojun#ifdef COMPAT_RFC1885
5291.2Sitojun		case ICMP6_DST_UNREACH_NOTNEIGHBOR:
5301.2Sitojun			code = PRC_UNREACH_SRCFAIL;
5311.2Sitojun			break;
5321.23Sitojun#else
5331.23Sitojun		case ICMP6_DST_UNREACH_BEYONDSCOPE:
5341.23Sitojun			/* I mean "source address was incorrect." */
5351.67Sitojun			code = PRC_UNREACH_NET;
5361.23Sitojun			break;
5371.29Sitojun#endif
5381.2Sitojun		case ICMP6_DST_UNREACH_NOPORT:
5391.2Sitojun			code = PRC_UNREACH_PORT;
5401.2Sitojun			break;
5411.2Sitojun		default:
5421.2Sitojun			goto badcode;
5431.2Sitojun		}
5441.2Sitojun		goto deliver;
5451.2Sitojun
5461.2Sitojun	case ICMP6_PACKET_TOO_BIG:
5471.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
5481.2Sitojun
5491.2Sitojun		code = PRC_MSGSIZE;
5501.2Sitojun
5511.23Sitojun		/*
5521.23Sitojun		 * Updating the path MTU will be done after examining
5531.23Sitojun		 * intermediate extension headers.
5541.23Sitojun		 */
5551.2Sitojun		goto deliver;
5561.2Sitojun
5571.2Sitojun	case ICMP6_TIME_EXCEEDED:
5581.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
5591.2Sitojun		switch (code) {
5601.2Sitojun		case ICMP6_TIME_EXCEED_TRANSIT:
5611.67Sitojun			code = PRC_TIMXCEED_INTRANS;
5621.67Sitojun			break;
5631.2Sitojun		case ICMP6_TIME_EXCEED_REASSEMBLY:
5641.67Sitojun			code = PRC_TIMXCEED_REASS;
5651.2Sitojun			break;
5661.2Sitojun		default:
5671.2Sitojun			goto badcode;
5681.2Sitojun		}
5691.2Sitojun		goto deliver;
5701.2Sitojun
5711.2Sitojun	case ICMP6_PARAM_PROB:
5721.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
5731.2Sitojun		switch (code) {
5741.2Sitojun		case ICMP6_PARAMPROB_NEXTHEADER:
5751.2Sitojun			code = PRC_UNREACH_PROTOCOL;
5761.2Sitojun			break;
5771.2Sitojun		case ICMP6_PARAMPROB_HEADER:
5781.2Sitojun		case ICMP6_PARAMPROB_OPTION:
5791.2Sitojun			code = PRC_PARAMPROB;
5801.2Sitojun			break;
5811.2Sitojun		default:
5821.2Sitojun			goto badcode;
5831.2Sitojun		}
5841.2Sitojun		goto deliver;
5851.2Sitojun
5861.2Sitojun	case ICMP6_ECHO_REQUEST:
5871.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
5881.2Sitojun		if (code != 0)
5891.2Sitojun			goto badcode;
5901.67Sitojun		/*
5911.67Sitojun		 * Copy mbuf to send to two data paths: userland socket(s),
5921.67Sitojun		 * and to the querier (echo reply).
5931.67Sitojun		 * m: a copy for socket, n: a copy for querier
5941.120Sdyoung		 *
5951.120Sdyoung		 * If the first mbuf is shared, or the first mbuf is too short,
5961.120Sdyoung		 * copy the first part of the data into a fresh mbuf.
5971.120Sdyoung		 * Otherwise, we will wrongly overwrite both copies.
5981.67Sitojun		 */
5991.67Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
6001.67Sitojun			/* Give up local */
6011.67Sitojun			n = m;
6021.67Sitojun			m = NULL;
6031.120Sdyoung		} else if (M_READONLY(n) ||
6041.67Sitojun		    n->m_len < off + sizeof(struct icmp6_hdr)) {
6051.2Sitojun			struct mbuf *n0 = n;
6061.2Sitojun
6071.2Sitojun			/*
6081.67Sitojun			 * Prepare an internal mbuf.  m_pullup() doesn't
6091.2Sitojun			 * always copy the length we specified.
6101.2Sitojun			 */
6111.121Sdyoung			if ((n = m_dup(n0, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
6121.67Sitojun				/* Give up local */
6131.67Sitojun				n = m;
6141.67Sitojun				m = NULL;
6151.2Sitojun			}
6161.120Sdyoung			m_freem(n0);
6171.2Sitojun		}
6181.120Sdyoung		nip6 = mtod(n, struct ip6_hdr *);
6191.120Sdyoung		nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
6201.2Sitojun		nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
6211.2Sitojun		nicmp6->icmp6_code = 0;
6221.10Sitojun		if (n) {
6231.10Sitojun			icmp6stat.icp6s_reflect++;
6241.10Sitojun			icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
6251.120Sdyoung			icmp6_reflect(n, off);
6261.10Sitojun		}
6271.67Sitojun		if (!m)
6281.67Sitojun			goto freeit;
6291.2Sitojun		break;
6301.2Sitojun
6311.2Sitojun	case ICMP6_ECHO_REPLY:
6321.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
6331.2Sitojun		if (code != 0)
6341.2Sitojun			goto badcode;
6351.2Sitojun		break;
6361.2Sitojun
6371.92Sitojun	case MLD_LISTENER_QUERY:
6381.92Sitojun	case MLD_LISTENER_REPORT:
6391.92Sitojun		if (icmp6len < sizeof(struct mld_hdr))
6401.2Sitojun			goto badlen;
6411.92Sitojun		if (icmp6->icmp6_type == MLD_LISTENER_QUERY) /* XXX: ugly... */
6421.12Sitojun			icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
6431.12Sitojun		else
6441.12Sitojun			icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
6451.23Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
6461.23Sitojun			/* give up local */
6471.115Srpaulo			mld_input(m, off);
6481.23Sitojun			m = NULL;
6491.23Sitojun			goto freeit;
6501.23Sitojun		}
6511.115Srpaulo		mld_input(n, off);
6521.2Sitojun		/* m stays. */
6531.2Sitojun		break;
6541.2Sitojun
6551.92Sitojun	case MLD_LISTENER_DONE:
6561.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
6571.92Sitojun		if (icmp6len < sizeof(struct mld_hdr))	/* necessary? */
6581.2Sitojun			goto badlen;
6591.2Sitojun		break;		/* nothing to be done in kernel */
6601.2Sitojun
6611.92Sitojun	case MLD_MTRACE_RESP:
6621.92Sitojun	case MLD_MTRACE:
6631.74Sitojun		/* XXX: these two are experimental.  not officially defined. */
6641.12Sitojun		/* XXX: per-interface statistics? */
6651.23Sitojun		break;		/* just pass it to applications */
6661.12Sitojun
6671.2Sitojun	case ICMP6_WRUREQUEST:	/* ICMP6_FQDN_QUERY */
6681.2Sitojun	    {
6691.2Sitojun		enum { WRU, FQDN } mode;
6701.2Sitojun
6711.14Sitojun		if (!icmp6_nodeinfo)
6721.14Sitojun			break;
6731.14Sitojun
6741.2Sitojun		if (icmp6len == sizeof(struct icmp6_hdr) + 4)
6751.2Sitojun			mode = WRU;
6761.31Sitojun		else if (icmp6len >= sizeof(struct icmp6_nodeinfo))
6771.2Sitojun			mode = FQDN;
6781.2Sitojun		else
6791.2Sitojun			goto badlen;
6801.2Sitojun
6811.2Sitojun		if (mode == FQDN) {
6821.67Sitojun			n = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
6831.14Sitojun			if (n)
6841.14Sitojun				n = ni6_input(n, off);
6851.31Sitojun			/* XXX meaningless if n == NULL */
6861.31Sitojun			noff = sizeof(struct ip6_hdr);
6871.14Sitojun		} else {
6881.2Sitojun			u_char *p;
6891.116Schristos			int maxhlen;
6901.2Sitojun
6911.81Sitojun			if ((icmp6_nodeinfo & 5) != 5)
6921.67Sitojun				break;
6931.67Sitojun
6941.31Sitojun			if (code != 0)
6951.31Sitojun				goto badcode;
6961.2Sitojun			MGETHDR(n, M_DONTWAIT, m->m_type);
6971.116Schristos			if (n && ICMP6_MAXLEN > MHLEN) {
6981.23Sitojun				MCLGET(n, M_DONTWAIT);
6991.23Sitojun				if ((n->m_flags & M_EXT) == 0) {
7001.23Sitojun					m_free(n);
7011.23Sitojun					n = NULL;
7021.23Sitojun				}
7031.23Sitojun			}
7041.2Sitojun			if (n == NULL) {
7051.2Sitojun				/* Give up remote */
7061.2Sitojun				break;
7071.2Sitojun			}
7081.67Sitojun			n->m_pkthdr.rcvif = NULL;
7091.23Sitojun			n->m_len = 0;
7101.116Schristos			maxhlen = M_TRAILINGSPACE(n) - ICMP6_MAXLEN;
7111.23Sitojun			if (maxhlen > hostnamelen)
7121.23Sitojun				maxhlen = hostnamelen;
7131.2Sitojun			/*
7141.2Sitojun			 * Copy IPv6 and ICMPv6 only.
7151.2Sitojun			 */
7161.2Sitojun			nip6 = mtod(n, struct ip6_hdr *);
7171.2Sitojun			bcopy(ip6, nip6, sizeof(struct ip6_hdr));
7181.2Sitojun			nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
7191.2Sitojun			bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
7201.2Sitojun			p = (u_char *)(nicmp6 + 1);
7211.2Sitojun			bzero(p, 4);
7221.67Sitojun			bcopy(hostname, p + 4, maxhlen); /* meaningless TTL */
7231.2Sitojun			noff = sizeof(struct ip6_hdr);
7241.74Sitojun			M_COPY_PKTHDR(n, m); /* just for rcvif */
7251.2Sitojun			n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
7261.23Sitojun				sizeof(struct icmp6_hdr) + 4 + maxhlen;
7271.2Sitojun			nicmp6->icmp6_type = ICMP6_WRUREPLY;
7281.2Sitojun			nicmp6->icmp6_code = 0;
7291.2Sitojun		}
7301.2Sitojun#undef hostnamelen
7311.2Sitojun		if (n) {
7321.2Sitojun			icmp6stat.icp6s_reflect++;
7331.2Sitojun			icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
7341.2Sitojun			icmp6_reflect(n, noff);
7351.2Sitojun		}
7361.2Sitojun		break;
7371.2Sitojun	    }
7381.2Sitojun
7391.2Sitojun	case ICMP6_WRUREPLY:
7401.2Sitojun		if (code != 0)
7411.2Sitojun			goto badcode;
7421.2Sitojun		break;
7431.2Sitojun
7441.2Sitojun	case ND_ROUTER_SOLICIT:
7451.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
7461.2Sitojun		if (code != 0)
7471.2Sitojun			goto badcode;
7481.2Sitojun		if (icmp6len < sizeof(struct nd_router_solicit))
7491.2Sitojun			goto badlen;
7501.23Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
7511.23Sitojun			/* give up local */
7521.23Sitojun			nd6_rs_input(m, off, icmp6len);
7531.23Sitojun			m = NULL;
7541.23Sitojun			goto freeit;
7551.23Sitojun		}
7561.23Sitojun		nd6_rs_input(n, off, icmp6len);
7571.2Sitojun		/* m stays. */
7581.2Sitojun		break;
7591.2Sitojun
7601.2Sitojun	case ND_ROUTER_ADVERT:
7611.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
7621.2Sitojun		if (code != 0)
7631.2Sitojun			goto badcode;
7641.2Sitojun		if (icmp6len < sizeof(struct nd_router_advert))
7651.2Sitojun			goto badlen;
7661.23Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
7671.23Sitojun			/* give up local */
7681.23Sitojun			nd6_ra_input(m, off, icmp6len);
7691.23Sitojun			m = NULL;
7701.23Sitojun			goto freeit;
7711.23Sitojun		}
7721.23Sitojun		nd6_ra_input(n, off, icmp6len);
7731.2Sitojun		/* m stays. */
7741.2Sitojun		break;
7751.2Sitojun
7761.2Sitojun	case ND_NEIGHBOR_SOLICIT:
7771.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
7781.2Sitojun		if (code != 0)
7791.2Sitojun			goto badcode;
7801.2Sitojun		if (icmp6len < sizeof(struct nd_neighbor_solicit))
7811.2Sitojun			goto badlen;
7821.23Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
7831.23Sitojun			/* give up local */
7841.23Sitojun			nd6_ns_input(m, off, icmp6len);
7851.23Sitojun			m = NULL;
7861.23Sitojun			goto freeit;
7871.23Sitojun		}
7881.23Sitojun		nd6_ns_input(n, off, icmp6len);
7891.2Sitojun		/* m stays. */
7901.2Sitojun		break;
7911.2Sitojun
7921.2Sitojun	case ND_NEIGHBOR_ADVERT:
7931.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
7941.2Sitojun		if (code != 0)
7951.2Sitojun			goto badcode;
7961.2Sitojun		if (icmp6len < sizeof(struct nd_neighbor_advert))
7971.2Sitojun			goto badlen;
7981.23Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
7991.23Sitojun			/* give up local */
8001.23Sitojun			nd6_na_input(m, off, icmp6len);
8011.23Sitojun			m = NULL;
8021.23Sitojun			goto freeit;
8031.23Sitojun		}
8041.23Sitojun		nd6_na_input(n, off, icmp6len);
8051.2Sitojun		/* m stays. */
8061.2Sitojun		break;
8071.2Sitojun
8081.2Sitojun	case ND_REDIRECT:
8091.12Sitojun		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
8101.2Sitojun		if (code != 0)
8111.2Sitojun			goto badcode;
8121.2Sitojun		if (icmp6len < sizeof(struct nd_redirect))
8131.2Sitojun			goto badlen;
8141.23Sitojun		if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) {
8151.23Sitojun			/* give up local */
8161.23Sitojun			icmp6_redirect_input(m, off);
8171.23Sitojun			m = NULL;
8181.23Sitojun			goto freeit;
8191.23Sitojun		}
8201.23Sitojun		icmp6_redirect_input(n, off);
8211.2Sitojun		/* m stays. */
8221.2Sitojun		break;
8231.2Sitojun
8241.2Sitojun	case ICMP6_ROUTER_RENUMBERING:
8251.2Sitojun		if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
8261.2Sitojun		    code != ICMP6_ROUTER_RENUMBERING_RESULT)
8271.2Sitojun			goto badcode;
8281.2Sitojun		if (icmp6len < sizeof(struct icmp6_router_renum))
8291.2Sitojun			goto badlen;
8301.2Sitojun		break;
8311.2Sitojun
8321.2Sitojun	default:
8331.53Sitojun		nd6log((LOG_DEBUG,
8341.53Sitojun		    "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
8351.53Sitojun		    icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
8361.53Sitojun		    ip6_sprintf(&ip6->ip6_dst),
8371.53Sitojun		    m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0));
8381.2Sitojun		if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
8391.2Sitojun			/* ICMPv6 error: MUST deliver it by spec... */
8401.2Sitojun			code = PRC_NCMDS;
8411.2Sitojun			/* deliver */
8421.2Sitojun		} else {
8431.2Sitojun			/* ICMPv6 informational: MUST not deliver */
8441.2Sitojun			break;
8451.2Sitojun		}
8461.2Sitojun	deliver:
8471.58Sitojun		if (icmp6_notify_error(m, off, icmp6len, code)) {
8481.58Sitojun			/* In this case, m should've been freed. */
8491.86Sitojun			return (IPPROTO_DONE);
8501.2Sitojun		}
8511.58Sitojun		break;
8521.58Sitojun
8531.58Sitojun	badcode:
8541.58Sitojun		icmp6stat.icp6s_badcode++;
8551.58Sitojun		break;
8561.58Sitojun
8571.58Sitojun	badlen:
8581.58Sitojun		icmp6stat.icp6s_badlen++;
8591.58Sitojun		break;
8601.58Sitojun	}
8611.58Sitojun
8621.58Sitojun	/* deliver the packet to appropriate sockets */
8631.58Sitojun	icmp6_rip6_input(&m, *offp);
8641.58Sitojun
8651.58Sitojun	return IPPROTO_DONE;
8661.58Sitojun
8671.58Sitojun freeit:
8681.58Sitojun	m_freem(m);
8691.58Sitojun	return IPPROTO_DONE;
8701.58Sitojun}
8711.58Sitojun
8721.58Sitojunstatic int
8731.58Sitojunicmp6_notify_error(m, off, icmp6len, code)
8741.58Sitojun	struct mbuf *m;
8751.119Schristos	int off, icmp6len, code;
8761.58Sitojun{
8771.58Sitojun	struct icmp6_hdr *icmp6;
8781.58Sitojun	struct ip6_hdr *eip6;
8791.58Sitojun	u_int32_t notifymtu;
8801.58Sitojun	struct sockaddr_in6 icmp6src, icmp6dst;
8811.58Sitojun
8821.58Sitojun	if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
8831.58Sitojun		icmp6stat.icp6s_tooshort++;
8841.58Sitojun		goto freeit;
8851.58Sitojun	}
8861.58Sitojun	IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
8871.58Sitojun		       sizeof(*icmp6) + sizeof(struct ip6_hdr));
8881.58Sitojun	if (icmp6 == NULL) {
8891.58Sitojun		icmp6stat.icp6s_tooshort++;
8901.86Sitojun		return (-1);
8911.58Sitojun	}
8921.58Sitojun	eip6 = (struct ip6_hdr *)(icmp6 + 1);
8931.2Sitojun
8941.58Sitojun	/* Detect the upper level protocol */
8951.58Sitojun	{
8961.12Sitojun		void (*ctlfunc) __P((int, struct sockaddr *, void *));
8971.2Sitojun		u_int8_t nxt = eip6->ip6_nxt;
8981.2Sitojun		int eoff = off + sizeof(struct icmp6_hdr) +
8991.2Sitojun			sizeof(struct ip6_hdr);
9001.12Sitojun		struct ip6ctlparam ip6cp;
9011.23Sitojun		struct in6_addr *finaldst = NULL;
9021.58Sitojun		int icmp6type = icmp6->icmp6_type;
9031.23Sitojun		struct ip6_frag *fh;
9041.23Sitojun		struct ip6_rthdr *rth;
9051.23Sitojun		struct ip6_rthdr0 *rth0;
9061.23Sitojun		int rthlen;
9071.2Sitojun
9081.67Sitojun		while (1) { /* XXX: should avoid infinite loop explicitly? */
9091.2Sitojun			struct ip6_ext *eh;
9101.2Sitojun
9111.56Sitojun			switch (nxt) {
9121.2Sitojun			case IPPROTO_HOPOPTS:
9131.2Sitojun			case IPPROTO_DSTOPTS:
9141.2Sitojun			case IPPROTO_AH:
9151.12Sitojun				IP6_EXTHDR_GET(eh, struct ip6_ext *, m,
9161.58Sitojun					       eoff, sizeof(*eh));
9171.12Sitojun				if (eh == NULL) {
9181.12Sitojun					icmp6stat.icp6s_tooshort++;
9191.86Sitojun					return (-1);
9201.12Sitojun				}
9211.82Sitojun
9221.2Sitojun				if (nxt == IPPROTO_AH)
9231.2Sitojun					eoff += (eh->ip6e_len + 2) << 2;
9241.2Sitojun				else
9251.2Sitojun					eoff += (eh->ip6e_len + 1) << 3;
9261.2Sitojun				nxt = eh->ip6e_nxt;
9271.2Sitojun				break;
9281.23Sitojun			case IPPROTO_ROUTING:
9291.23Sitojun				/*
9301.23Sitojun				 * When the erroneous packet contains a
9311.23Sitojun				 * routing header, we should examine the
9321.23Sitojun				 * header to determine the final destination.
9331.23Sitojun				 * Otherwise, we can't properly update
9341.23Sitojun				 * information that depends on the final
9351.23Sitojun				 * destination (e.g. path MTU).
9361.23Sitojun				 */
9371.23Sitojun				IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m,
9381.58Sitojun					       eoff, sizeof(*rth));
9391.23Sitojun				if (rth == NULL) {
9401.23Sitojun					icmp6stat.icp6s_tooshort++;
9411.86Sitojun					return (-1);
9421.23Sitojun				}
9431.23Sitojun				rthlen = (rth->ip6r_len + 1) << 3;
9441.23Sitojun				/*
9451.23Sitojun				 * XXX: currently there is no
9461.23Sitojun				 * officially defined type other
9471.23Sitojun				 * than type-0.
9481.23Sitojun				 * Note that if the segment left field
9491.23Sitojun				 * is 0, all intermediate hops must
9501.23Sitojun				 * have been passed.
9511.23Sitojun				 */
9521.23Sitojun				if (rth->ip6r_segleft &&
9531.23Sitojun				    rth->ip6r_type == IPV6_RTHDR_TYPE_0) {
9541.23Sitojun					int hops;
9551.23Sitojun
9561.23Sitojun					IP6_EXTHDR_GET(rth0,
9571.23Sitojun						       struct ip6_rthdr0 *, m,
9581.23Sitojun						       eoff, rthlen);
9591.23Sitojun					if (rth0 == NULL) {
9601.23Sitojun						icmp6stat.icp6s_tooshort++;
9611.86Sitojun						return (-1);
9621.23Sitojun					}
9631.23Sitojun					/* just ignore a bogus header */
9641.23Sitojun					if ((rth0->ip6r0_len % 2) == 0 &&
9651.23Sitojun					    (hops = rth0->ip6r0_len/2))
9661.23Sitojun						finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1);
9671.23Sitojun				}
9681.23Sitojun				eoff += rthlen;
9691.23Sitojun				nxt = rth->ip6r_nxt;
9701.23Sitojun				break;
9711.23Sitojun			case IPPROTO_FRAGMENT:
9721.23Sitojun				IP6_EXTHDR_GET(fh, struct ip6_frag *, m,
9731.58Sitojun					       eoff, sizeof(*fh));
9741.23Sitojun				if (fh == NULL) {
9751.23Sitojun					icmp6stat.icp6s_tooshort++;
9761.86Sitojun					return (-1);
9771.23Sitojun				}
9781.23Sitojun				/*
9791.23Sitojun				 * Data after a fragment header is meaningless
9801.23Sitojun				 * unless it is the first fragment, but
9811.23Sitojun				 * we'll go to the notify label for path MTU
9821.23Sitojun				 * discovery.
9831.23Sitojun				 */
9841.23Sitojun				if (fh->ip6f_offlg & IP6F_OFF_MASK)
9851.23Sitojun					goto notify;
9861.23Sitojun
9871.23Sitojun				eoff += sizeof(struct ip6_frag);
9881.23Sitojun				nxt = fh->ip6f_nxt;
9891.23Sitojun				break;
9901.2Sitojun			default:
9911.23Sitojun				/*
9921.23Sitojun				 * This case includes ESP and the No Next
9931.67Sitojun				 * Header.  In such cases going to the notify
9941.23Sitojun				 * label does not have any meaning
9951.23Sitojun				 * (i.e. ctlfunc will be NULL), but we go
9961.23Sitojun				 * anyway since we might have to update
9971.23Sitojun				 * path MTU information.
9981.23Sitojun				 */
9991.2Sitojun				goto notify;
10001.2Sitojun			}
10011.2Sitojun		}
10021.58Sitojun	  notify:
10031.12Sitojun		IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off,
10041.58Sitojun			       sizeof(*icmp6) + sizeof(struct ip6_hdr));
10051.12Sitojun		if (icmp6 == NULL) {
10061.12Sitojun			icmp6stat.icp6s_tooshort++;
10071.86Sitojun			return (-1);
10081.58Sitojun		}
10091.58Sitojun
10101.113Srpaulo		/*
10111.113Srpaulo		 * retrieve parameters from the inner IPv6 header, and convert
10121.113Srpaulo		 * them into sockaddr structures.
10131.113Srpaulo		 * XXX: there is no guarantee that the source or destination
10141.113Srpaulo		 * addresses of the inner packet are in the same scope zone as
10151.113Srpaulo		 * the addresses of the icmp packet.  But there is no other
10161.113Srpaulo		 * way to determine the zone.
10171.113Srpaulo		 */
10181.58Sitojun		eip6 = (struct ip6_hdr *)(icmp6 + 1);
10191.113Srpaulo
10201.58Sitojun		bzero(&icmp6dst, sizeof(icmp6dst));
10211.58Sitojun		icmp6dst.sin6_len = sizeof(struct sockaddr_in6);
10221.58Sitojun		icmp6dst.sin6_family = AF_INET6;
10231.58Sitojun		if (finaldst == NULL)
10241.58Sitojun			icmp6dst.sin6_addr = eip6->ip6_dst;
10251.58Sitojun		else
10261.58Sitojun			icmp6dst.sin6_addr = *finaldst;
10271.113Srpaulo		if (in6_setscope(&icmp6dst.sin6_addr, m->m_pkthdr.rcvif, NULL))
10281.58Sitojun			goto freeit;
10291.58Sitojun		bzero(&icmp6src, sizeof(icmp6src));
10301.58Sitojun		icmp6src.sin6_len = sizeof(struct sockaddr_in6);
10311.58Sitojun		icmp6src.sin6_family = AF_INET6;
10321.58Sitojun		icmp6src.sin6_addr = eip6->ip6_src;
10331.113Srpaulo		if (in6_setscope(&icmp6src.sin6_addr, m->m_pkthdr.rcvif, NULL))
10341.58Sitojun			goto freeit;
10351.58Sitojun		icmp6src.sin6_flowinfo =
10361.58Sitojun			(eip6->ip6_flow & IPV6_FLOWLABEL_MASK);
10371.58Sitojun
10381.46Sitojun		if (finaldst == NULL)
10391.58Sitojun			finaldst = &eip6->ip6_dst;
10401.46Sitojun		ip6cp.ip6c_m = m;
10411.46Sitojun		ip6cp.ip6c_icmp6 = icmp6;
10421.46Sitojun		ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
10431.46Sitojun		ip6cp.ip6c_off = eoff;
10441.46Sitojun		ip6cp.ip6c_finaldst = finaldst;
10451.58Sitojun		ip6cp.ip6c_src = &icmp6src;
10461.58Sitojun		ip6cp.ip6c_nxt = nxt;
10471.58Sitojun
10481.58Sitojun		if (icmp6type == ICMP6_PACKET_TOO_BIG) {
10491.58Sitojun			notifymtu = ntohl(icmp6->icmp6_mtu);
10501.58Sitojun			ip6cp.ip6c_cmdarg = (void *)&notifymtu;
10511.58Sitojun		}
10521.23Sitojun
10531.12Sitojun		ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
10541.2Sitojun			(inet6sw[ip6_protox[nxt]].pr_ctlinput);
10551.12Sitojun		if (ctlfunc) {
10561.58Sitojun			(void) (*ctlfunc)(code, (struct sockaddr *)&icmp6dst,
10571.58Sitojun					  &ip6cp);
10581.12Sitojun		}
10591.2Sitojun	}
10601.86Sitojun	return (0);
10611.2Sitojun
10621.58Sitojun  freeit:
10631.2Sitojun	m_freem(m);
10641.86Sitojun	return (-1);
10651.2Sitojun}
10661.2Sitojun
10671.46Sitojunvoid
10681.48Sitojunicmp6_mtudisc_update(ip6cp, validated)
10691.46Sitojun	struct ip6ctlparam *ip6cp;
10701.48Sitojun	int validated;
10711.23Sitojun{
10721.48Sitojun	unsigned long rtcount;
10731.46Sitojun	struct icmp6_mtudisc_callback *mc;
10741.46Sitojun	struct in6_addr *dst = ip6cp->ip6c_finaldst;
10751.46Sitojun	struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6;
10761.46Sitojun	struct mbuf *m = ip6cp->ip6c_m;	/* will be necessary for scope issue */
10771.23Sitojun	u_int mtu = ntohl(icmp6->icmp6_mtu);
10781.23Sitojun	struct rtentry *rt = NULL;
10791.23Sitojun	struct sockaddr_in6 sin6;
10801.23Sitojun
10811.48Sitojun	/*
10821.48Sitojun	 * allow non-validated cases if memory is plenty, to make traffic
10831.48Sitojun	 * from non-connected pcb happy.
10841.48Sitojun	 */
10851.48Sitojun	rtcount = rt_timer_count(icmp6_mtudisc_timeout_q);
10861.48Sitojun	if (validated) {
10871.55Sitojun		if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat)
10881.48Sitojun			return;
10891.55Sitojun		else if (0 <= icmp6_mtudisc_lowat &&
10901.55Sitojun		    rtcount > icmp6_mtudisc_lowat) {
10911.48Sitojun			/*
10921.48Sitojun			 * XXX nuke a victim, install the new one.
10931.48Sitojun			 */
10941.48Sitojun		}
10951.48Sitojun	} else {
10961.55Sitojun		if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat)
10971.48Sitojun			return;
10981.48Sitojun	}
10991.48Sitojun
11001.23Sitojun	bzero(&sin6, sizeof(sin6));
11011.23Sitojun	sin6.sin6_family = PF_INET6;
11021.23Sitojun	sin6.sin6_len = sizeof(struct sockaddr_in6);
11031.23Sitojun	sin6.sin6_addr = *dst;
11041.113Srpaulo	if (in6_setscope(&sin6.sin6_addr, m->m_pkthdr.rcvif, NULL))
11051.113Srpaulo		return;
11061.113Srpaulo
11071.49Sitojun	rt = icmp6_mtudisc_clone((struct sockaddr *)&sin6);
11081.23Sitojun
11091.80Sitojun	if (rt && (rt->rt_flags & RTF_HOST) &&
11101.80Sitojun	    !(rt->rt_rmx.rmx_locks & RTV_MTU) &&
11111.80Sitojun	    (rt->rt_rmx.rmx_mtu > mtu || rt->rt_rmx.rmx_mtu == 0)) {
11121.80Sitojun		if (mtu < IN6_LINKMTU(rt->rt_ifp)) {
11131.46Sitojun			icmp6stat.icp6s_pmtuchg++;
11141.23Sitojun			rt->rt_rmx.rmx_mtu = mtu;
11151.23Sitojun		}
11161.23Sitojun	}
11171.67Sitojun	if (rt) { /* XXX: need braces to avoid conflict with else in RTFREE. */
11181.23Sitojun		RTFREE(rt);
11191.67Sitojun	}
11201.46Sitojun
11211.46Sitojun	/*
11221.46Sitojun	 * Notify protocols that the MTU for this destination
11231.46Sitojun	 * has changed.
11241.46Sitojun	 */
11251.46Sitojun	for (mc = LIST_FIRST(&icmp6_mtudisc_callbacks); mc != NULL;
11261.46Sitojun	     mc = LIST_NEXT(mc, mc_list))
11271.46Sitojun		(*mc->mc_func)(&sin6.sin6_addr);
11281.23Sitojun}
11291.23Sitojun
11301.2Sitojun/*
11311.40Sitojun * Process a Node Information Query packet, based on
11321.47Sitojun * draft-ietf-ipngwg-icmp-name-lookups-07.
11331.81Sitojun *
11341.31Sitojun * Spec incompatibilities:
11351.31Sitojun * - IPv6 Subject address handling
11361.31Sitojun * - IPv4 Subject address handling support missing
11371.31Sitojun * - Proxy reply (answer even if it's not for me)
11381.31Sitojun * - joins NI group address at in6_ifattach() time only, does not cope
11391.31Sitojun *   with hostname changes by sethostname(3)
11401.2Sitojun */
11411.2Sitojun#ifndef offsetof		/* XXX */
11421.2Sitojun#define	offsetof(type, member)	((size_t)(&((type *)0)->member))
11431.29Sitojun#endif
11441.2Sitojunstatic struct mbuf *
11451.2Sitojunni6_input(m, off)
11461.2Sitojun	struct mbuf *m;
11471.2Sitojun	int off;
11481.2Sitojun{
11491.12Sitojun	struct icmp6_nodeinfo *ni6, *nni6;
11501.2Sitojun	struct mbuf *n = NULL;
11511.12Sitojun	u_int16_t qtype;
11521.31Sitojun	int subjlen;
11531.2Sitojun	int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
11541.2Sitojun	struct ni_reply_fqdn *fqdn;
11551.2Sitojun	int addrs;		/* for NI_QTYPE_NODEADDR */
11561.2Sitojun	struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
11571.113Srpaulo	struct sockaddr_in6 sin6; /* ip6_dst */
11581.113Srpaulo	struct in6_addr in6_subj; /* subject address */
11591.31Sitojun	struct ip6_hdr *ip6;
11601.31Sitojun	int oldfqdn = 0;	/* if 1, return pascal string (03 draft) */
11611.47Sitojun	char *subj = NULL;
11621.2Sitojun
11631.31Sitojun	ip6 = mtod(m, struct ip6_hdr *);
11641.14Sitojun	IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6));
11651.14Sitojun	if (ni6 == NULL) {
11661.14Sitojun		/* m is already reclaimed */
11671.12Sitojun		return NULL;
11681.14Sitojun	}
11691.31Sitojun
11701.31Sitojun	/*
11711.31Sitojun	 * Validate IPv6 destination address.
11721.31Sitojun	 *
11731.47Sitojun	 * The Responder must discard the Query without further processing
11741.47Sitojun	 * unless it is one of the Responder's unicast or anycast addresses, or
11751.47Sitojun	 * a link-local scope multicast address which the Responder has joined.
11761.47Sitojun	 * [icmp-name-lookups-07, Section 4.]
11771.31Sitojun	 */
11781.31Sitojun	bzero(&sin6, sizeof(sin6));
11791.31Sitojun	sin6.sin6_family = AF_INET6;
11801.31Sitojun	sin6.sin6_len = sizeof(struct sockaddr_in6);
11811.31Sitojun	bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
11821.31Sitojun	/* XXX scopeid */
11831.31Sitojun	if (ifa_ifwithaddr((struct sockaddr *)&sin6))
11841.47Sitojun		; /* unicast/anycast, fine */
11851.31Sitojun	else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr))
11861.47Sitojun		; /* link-local multicast, fine */
11871.31Sitojun	else
11881.31Sitojun		goto bad;
11891.31Sitojun
11901.47Sitojun	/* validate query Subject field. */
11911.12Sitojun	qtype = ntohs(ni6->ni_qtype);
11921.31Sitojun	subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo);
11931.31Sitojun	switch (qtype) {
11941.31Sitojun	case NI_QTYPE_NOOP:
11951.31Sitojun	case NI_QTYPE_SUPTYPES:
11961.47Sitojun		/* 07 draft */
11971.40Sitojun		if (ni6->ni_code == ICMP6_NI_SUBJ_FQDN && subjlen == 0)
11981.40Sitojun			break;
11991.67Sitojun		/* FALLTHROUGH */
12001.31Sitojun	case NI_QTYPE_FQDN:
12011.31Sitojun	case NI_QTYPE_NODEADDR:
12021.115Srpaulo	case NI_QTYPE_IPV4ADDR:
12031.31Sitojun		switch (ni6->ni_code) {
12041.31Sitojun		case ICMP6_NI_SUBJ_IPV6:
12051.31Sitojun#if ICMP6_NI_SUBJ_IPV6 != 0
12061.31Sitojun		case 0:
12071.31Sitojun#endif
12081.31Sitojun			/*
12091.31Sitojun			 * backward compatibility - try to accept 03 draft
12101.31Sitojun			 * format, where no Subject is present.
12111.31Sitojun			 */
12121.40Sitojun			if (qtype == NI_QTYPE_FQDN && ni6->ni_code == 0 &&
12131.40Sitojun			    subjlen == 0) {
12141.31Sitojun				oldfqdn++;
12151.31Sitojun				break;
12161.31Sitojun			}
12171.40Sitojun#if ICMP6_NI_SUBJ_IPV6 != 0
12181.40Sitojun			if (ni6->ni_code != ICMP6_NI_SUBJ_IPV6)
12191.40Sitojun				goto bad;
12201.40Sitojun#endif
12211.31Sitojun
12221.31Sitojun			if (subjlen != sizeof(sin6.sin6_addr))
12231.31Sitojun				goto bad;
12241.31Sitojun
12251.31Sitojun			/*
12261.31Sitojun			 * Validate Subject address.
12271.31Sitojun			 *
12281.53Sitojun			 * Not sure what exactly "address belongs to the node"
12291.53Sitojun			 * means in the spec, is it just unicast, or what?
12301.31Sitojun			 *
12311.31Sitojun			 * At this moment we consider Subject address as
12321.31Sitojun			 * "belong to the node" if the Subject address equals
12331.31Sitojun			 * to the IPv6 destination address; validation for
12341.31Sitojun			 * IPv6 destination address should have done enough
12351.31Sitojun			 * check for us.
12361.31Sitojun			 *
12371.31Sitojun			 * We do not do proxy at this moment.
12381.31Sitojun			 */
12391.31Sitojun			/* m_pulldown instead of copy? */
12401.31Sitojun			m_copydata(m, off + sizeof(struct icmp6_nodeinfo),
12411.113Srpaulo			    subjlen, (caddr_t)&in6_subj);
12421.113Srpaulo			if (in6_setscope(&in6_subj, m->m_pkthdr.rcvif, NULL))
12431.113Srpaulo				goto bad;
12441.113Srpaulo
12451.113Srpaulo			subj = (char *)&in6_subj;
12461.113Srpaulo			if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &in6_subj))
12471.31Sitojun				break;
12481.47Sitojun
12491.31Sitojun			/*
12501.31Sitojun			 * XXX if we are to allow other cases, we should really
12511.31Sitojun			 * be careful about scope here.
12521.31Sitojun			 * basically, we should disallow queries toward IPv6
12531.31Sitojun			 * destination X with subject Y, if scope(X) > scope(Y).
12541.31Sitojun			 * if we allow scope(X) > scope(Y), it will result in
12551.31Sitojun			 * information leakage across scope boundary.
12561.31Sitojun			 */
12571.31Sitojun			goto bad;
12581.31Sitojun
12591.31Sitojun		case ICMP6_NI_SUBJ_FQDN:
12601.31Sitojun			/*
12611.31Sitojun			 * Validate Subject name with gethostname(3).
12621.31Sitojun			 *
12631.31Sitojun			 * The behavior may need some debate, since:
12641.31Sitojun			 * - we are not sure if the node has FQDN as
12651.31Sitojun			 *   hostname (returned by gethostname(3)).
12661.31Sitojun			 * - the code does wildcard match for truncated names.
12671.31Sitojun			 *   however, we are not sure if we want to perform
12681.31Sitojun			 *   wildcard match, if gethostname(3) side has
12691.31Sitojun			 *   truncated hostname.
12701.31Sitojun			 */
12711.31Sitojun			n = ni6_nametodns(hostname, hostnamelen, 0);
12721.31Sitojun			if (!n || n->m_next || n->m_len == 0)
12731.31Sitojun				goto bad;
12741.31Sitojun			IP6_EXTHDR_GET(subj, char *, m,
12751.31Sitojun			    off + sizeof(struct icmp6_nodeinfo), subjlen);
12761.31Sitojun			if (subj == NULL)
12771.31Sitojun				goto bad;
12781.31Sitojun			if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *),
12791.31Sitojun					n->m_len)) {
12801.31Sitojun				goto bad;
12811.31Sitojun			}
12821.31Sitojun			m_freem(n);
12831.31Sitojun			n = NULL;
12841.31Sitojun			break;
12851.31Sitojun
12861.47Sitojun		case ICMP6_NI_SUBJ_IPV4:	/* XXX: to be implemented? */
12871.31Sitojun		default:
12881.31Sitojun			goto bad;
12891.31Sitojun		}
12901.31Sitojun		break;
12911.2Sitojun	}
12921.2Sitojun
12931.67Sitojun	/* refuse based on configuration.  XXX ICMP6_NI_REFUSED? */
12941.67Sitojun	switch (qtype) {
12951.67Sitojun	case NI_QTYPE_FQDN:
12961.67Sitojun		if ((icmp6_nodeinfo & 1) == 0)
12971.67Sitojun			goto bad;
12981.67Sitojun		break;
12991.67Sitojun	case NI_QTYPE_NODEADDR:
13001.115Srpaulo	case NI_QTYPE_IPV4ADDR:
13011.67Sitojun		if ((icmp6_nodeinfo & 2) == 0)
13021.67Sitojun			goto bad;
13031.67Sitojun		break;
13041.67Sitojun	}
13051.67Sitojun
13061.47Sitojun	/* guess reply length */
13071.47Sitojun	switch (qtype) {
13081.47Sitojun	case NI_QTYPE_NOOP:
13091.47Sitojun		break;		/* no reply data */
13101.47Sitojun	case NI_QTYPE_SUPTYPES:
13111.47Sitojun		replylen += sizeof(u_int32_t);
13121.47Sitojun		break;
13131.47Sitojun	case NI_QTYPE_FQDN:
13141.47Sitojun		/* XXX will append an mbuf */
13151.47Sitojun		replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
13161.47Sitojun		break;
13171.47Sitojun	case NI_QTYPE_NODEADDR:
13181.47Sitojun		addrs = ni6_addrs(ni6, m, &ifp, subj);
13191.47Sitojun		if ((replylen += addrs * (sizeof(struct in6_addr) +
13201.47Sitojun					  sizeof(u_int32_t))) > MCLBYTES)
13211.47Sitojun			replylen = MCLBYTES; /* XXX: will truncate pkt later */
13221.47Sitojun		break;
13231.115Srpaulo	case NI_QTYPE_IPV4ADDR:
13241.115Srpaulo		/* unsupported - should respond with unknown Qtype? */
13251.115Srpaulo		goto bad;
13261.47Sitojun	default:
13271.47Sitojun		/*
13281.47Sitojun		 * XXX: We must return a reply with the ICMP6 code
13291.67Sitojun		 * `unknown Qtype' in this case.  However we regard the case
13301.47Sitojun		 * as an FQDN query for backward compatibility.
13311.47Sitojun		 * Older versions set a random value to this field,
13321.47Sitojun		 * so it rarely varies in the defined qtypes.
13331.47Sitojun		 * But the mechanism is not reliable...
13341.47Sitojun		 * maybe we should obsolete older versions.
13351.47Sitojun		 */
13361.47Sitojun		qtype = NI_QTYPE_FQDN;
13371.47Sitojun		/* XXX will append an mbuf */
13381.47Sitojun		replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen);
13391.47Sitojun		oldfqdn++;
13401.47Sitojun		break;
13411.47Sitojun	}
13421.47Sitojun
13431.47Sitojun	/* allocate an mbuf to reply. */
13441.2Sitojun	MGETHDR(n, M_DONTWAIT, m->m_type);
13451.14Sitojun	if (n == NULL) {
13461.14Sitojun		m_freem(m);
13471.86Sitojun		return (NULL);
13481.14Sitojun	}
13491.110Syamt	M_MOVE_PKTHDR(n, m); /* just for rcvif */
13501.2Sitojun	if (replylen > MHLEN) {
13511.31Sitojun		if (replylen > MCLBYTES) {
13521.53Sitojun			/*
13531.53Sitojun			 * XXX: should we try to allocate more? But MCLBYTES
13541.53Sitojun			 * is probably much larger than IPV6_MMTU...
13551.53Sitojun			 */
13561.2Sitojun			goto bad;
13571.31Sitojun		}
13581.2Sitojun		MCLGET(n, M_DONTWAIT);
13591.2Sitojun		if ((n->m_flags & M_EXT) == 0) {
13601.2Sitojun			goto bad;
13611.2Sitojun		}
13621.2Sitojun	}
13631.2Sitojun	n->m_pkthdr.len = n->m_len = replylen;
13641.2Sitojun
13651.2Sitojun	/* copy mbuf header and IPv6 + Node Information base headers */
13661.2Sitojun	bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr));
13671.29Sitojun	nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1);
13681.12Sitojun	bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo));
13691.2Sitojun
13701.2Sitojun	/* qtype dependent procedure */
13711.2Sitojun	switch (qtype) {
13721.31Sitojun	case NI_QTYPE_NOOP:
13731.40Sitojun		nni6->ni_code = ICMP6_NI_SUCCESS;
13741.31Sitojun		nni6->ni_flags = 0;
13751.31Sitojun		break;
13761.31Sitojun	case NI_QTYPE_SUPTYPES:
13771.42Sitojun	{
13781.42Sitojun		u_int32_t v;
13791.40Sitojun		nni6->ni_code = ICMP6_NI_SUCCESS;
13801.40Sitojun		nni6->ni_flags = htons(0x0000);	/* raw bitmap */
13811.40Sitojun		/* supports NOOP, SUPTYPES, FQDN, and NODEADDR */
13821.42Sitojun		v = (u_int32_t)htonl(0x0000000f);
13831.42Sitojun		bcopy(&v, nni6 + 1, sizeof(u_int32_t));
13841.31Sitojun		break;
13851.42Sitojun	}
13861.31Sitojun	case NI_QTYPE_FQDN:
13871.40Sitojun		nni6->ni_code = ICMP6_NI_SUCCESS;
13881.31Sitojun		fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
13891.31Sitojun						sizeof(struct ip6_hdr) +
13901.31Sitojun						sizeof(struct icmp6_nodeinfo));
13911.31Sitojun		nni6->ni_flags = 0; /* XXX: meaningless TTL */
13921.31Sitojun		fqdn->ni_fqdn_ttl = 0;	/* ditto. */
13931.31Sitojun		/*
13941.31Sitojun		 * XXX do we really have FQDN in variable "hostname"?
13951.31Sitojun		 */
13961.31Sitojun		n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn);
13971.31Sitojun		if (n->m_next == NULL)
13981.31Sitojun			goto bad;
13991.31Sitojun		/* XXX we assume that n->m_next is not a chain */
14001.31Sitojun		if (n->m_next->m_next != NULL)
14011.31Sitojun			goto bad;
14021.31Sitojun		n->m_pkthdr.len += n->m_next->m_len;
14031.31Sitojun		break;
14041.31Sitojun	case NI_QTYPE_NODEADDR:
14051.31Sitojun	{
14061.31Sitojun		int lenlim, copied;
14071.31Sitojun
14081.40Sitojun		nni6->ni_code = ICMP6_NI_SUCCESS;
14091.42Sitojun		n->m_pkthdr.len = n->m_len =
14101.42Sitojun		    sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
14111.42Sitojun		lenlim = M_TRAILINGSPACE(n);
14121.31Sitojun		copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
14131.31Sitojun		/* XXX: reset mbuf length */
14141.31Sitojun		n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
14151.31Sitojun			sizeof(struct icmp6_nodeinfo) + copied;
14161.31Sitojun		break;
14171.31Sitojun	}
14181.31Sitojun	default:
14191.31Sitojun		break;		/* XXX impossible! */
14201.2Sitojun	}
14211.2Sitojun
14221.2Sitojun	nni6->ni_type = ICMP6_NI_REPLY;
14231.14Sitojun	m_freem(m);
14241.86Sitojun	return (n);
14251.2Sitojun
14261.2Sitojun  bad:
14271.14Sitojun	m_freem(m);
14281.2Sitojun	if (n)
14291.2Sitojun		m_freem(n);
14301.86Sitojun	return (NULL);
14311.2Sitojun}
14321.2Sitojun#undef hostnamelen
14331.2Sitojun
14341.76Sitojun#define isupper(x) ('A' <= (x) && (x) <= 'Z')
14351.76Sitojun#define isalpha(x) (('A' <= (x) && (x) <= 'Z') || ('a' <= (x) && (x) <= 'z'))
14361.76Sitojun#define isalnum(x) (isalpha(x) || ('0' <= (x) && (x) <= '9'))
14371.76Sitojun#define tolower(x) (isupper(x) ? (x) + 'a' - 'A' : (x))
14381.76Sitojun
14391.2Sitojun/*
14401.31Sitojun * make a mbuf with DNS-encoded string.  no compression support.
14411.31Sitojun *
14421.31Sitojun * XXX names with less than 2 dots (like "foo" or "foo.section") will be
14431.31Sitojun * treated as truncated name (two \0 at the end).  this is a wild guess.
14441.31Sitojun */
14451.31Sitojunstatic struct mbuf *
14461.31Sitojunni6_nametodns(name, namelen, old)
14471.31Sitojun	const char *name;
14481.31Sitojun	int namelen;
14491.31Sitojun	int old;	/* return pascal string if non-zero */
14501.31Sitojun{
14511.31Sitojun	struct mbuf *m;
14521.31Sitojun	char *cp, *ep;
14531.31Sitojun	const char *p, *q;
14541.31Sitojun	int i, len, nterm;
14551.31Sitojun
14561.31Sitojun	if (old)
14571.31Sitojun		len = namelen + 1;
14581.31Sitojun	else
14591.31Sitojun		len = MCLBYTES;
14601.31Sitojun
14611.31Sitojun	/* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */
14621.31Sitojun	MGET(m, M_DONTWAIT, MT_DATA);
14631.31Sitojun	if (m && len > MLEN) {
14641.31Sitojun		MCLGET(m, M_DONTWAIT);
14651.31Sitojun		if ((m->m_flags & M_EXT) == 0)
14661.31Sitojun			goto fail;
14671.31Sitojun	}
14681.31Sitojun	if (!m)
14691.31Sitojun		goto fail;
14701.31Sitojun	m->m_next = NULL;
14711.31Sitojun
14721.31Sitojun	if (old) {
14731.31Sitojun		m->m_len = len;
14741.31Sitojun		*mtod(m, char *) = namelen;
14751.31Sitojun		bcopy(name, mtod(m, char *) + 1, namelen);
14761.31Sitojun		return m;
14771.31Sitojun	} else {
14781.31Sitojun		m->m_len = 0;
14791.31Sitojun		cp = mtod(m, char *);
14801.31Sitojun		ep = mtod(m, char *) + M_TRAILINGSPACE(m);
14811.31Sitojun
14821.31Sitojun		/* if not certain about my name, return empty buffer */
14831.31Sitojun		if (namelen == 0)
14841.31Sitojun			return m;
14851.31Sitojun
14861.31Sitojun		/*
14871.31Sitojun		 * guess if it looks like shortened hostname, or FQDN.
14881.31Sitojun		 * shortened hostname needs two trailing "\0".
14891.31Sitojun		 */
14901.31Sitojun		i = 0;
14911.31Sitojun		for (p = name; p < name + namelen; p++) {
14921.31Sitojun			if (*p && *p == '.')
14931.31Sitojun				i++;
14941.31Sitojun		}
14951.31Sitojun		if (i < 2)
14961.31Sitojun			nterm = 2;
14971.31Sitojun		else
14981.31Sitojun			nterm = 1;
14991.31Sitojun
15001.31Sitojun		p = name;
15011.31Sitojun		while (cp < ep && p < name + namelen) {
15021.31Sitojun			i = 0;
15031.31Sitojun			for (q = p; q < name + namelen && *q && *q != '.'; q++)
15041.31Sitojun				i++;
15051.31Sitojun			/* result does not fit into mbuf */
15061.31Sitojun			if (cp + i + 1 >= ep)
15071.31Sitojun				goto fail;
15081.67Sitojun			/*
15091.67Sitojun			 * DNS label length restriction, RFC1035 page 8.
15101.67Sitojun			 * "i == 0" case is included here to avoid returning
15111.67Sitojun			 * 0-length label on "foo..bar".
15121.67Sitojun			 */
15131.67Sitojun			if (i <= 0 || i >= 64)
15141.31Sitojun				goto fail;
15151.31Sitojun			*cp++ = i;
15161.76Sitojun			if (!isalpha(p[0]) || !isalnum(p[i - 1]))
15171.76Sitojun				goto fail;
15181.76Sitojun			while (i > 0) {
15191.76Sitojun				if (!isalnum(*p) && *p != '-')
15201.76Sitojun					goto fail;
15211.84Sitojun				if (isupper(*p)) {
15221.84Sitojun					*cp++ = tolower(*p);
15231.84Sitojun					p++;
15241.84Sitojun				} else
15251.76Sitojun					*cp++ = *p++;
15261.76Sitojun				i--;
15271.76Sitojun			}
15281.31Sitojun			p = q;
15291.31Sitojun			if (p < name + namelen && *p == '.')
15301.31Sitojun				p++;
15311.31Sitojun		}
15321.31Sitojun		/* termination */
15331.31Sitojun		if (cp + nterm >= ep)
15341.31Sitojun			goto fail;
15351.31Sitojun		while (nterm-- > 0)
15361.31Sitojun			*cp++ = '\0';
15371.31Sitojun		m->m_len = cp - mtod(m, char *);
15381.31Sitojun		return m;
15391.31Sitojun	}
15401.31Sitojun
15411.31Sitojun	panic("should not reach here");
15421.67Sitojun	/* NOTREACHED */
15431.31Sitojun
15441.31Sitojun fail:
15451.31Sitojun	if (m)
15461.31Sitojun		m_freem(m);
15471.31Sitojun	return NULL;
15481.31Sitojun}
15491.31Sitojun
15501.31Sitojun/*
15511.31Sitojun * check if two DNS-encoded string matches.  takes care of truncated
15521.31Sitojun * form (with \0\0 at the end).  no compression support.
15531.40Sitojun * XXX upper/lowercase match (see RFC2065)
15541.31Sitojun */
15551.31Sitojunstatic int
15561.31Sitojunni6_dnsmatch(a, alen, b, blen)
15571.31Sitojun	const char *a;
15581.31Sitojun	int alen;
15591.31Sitojun	const char *b;
15601.31Sitojun	int blen;
15611.31Sitojun{
15621.31Sitojun	const char *a0, *b0;
15631.31Sitojun	int l;
15641.31Sitojun
15651.31Sitojun	/* simplest case - need validation? */
15661.31Sitojun	if (alen == blen && bcmp(a, b, alen) == 0)
15671.31Sitojun		return 1;
15681.31Sitojun
15691.31Sitojun	a0 = a;
15701.31Sitojun	b0 = b;
15711.31Sitojun
15721.31Sitojun	/* termination is mandatory */
15731.31Sitojun	if (alen < 2 || blen < 2)
15741.31Sitojun		return 0;
15751.31Sitojun	if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0')
15761.31Sitojun		return 0;
15771.31Sitojun	alen--;
15781.31Sitojun	blen--;
15791.31Sitojun
15801.31Sitojun	while (a - a0 < alen && b - b0 < blen) {
15811.31Sitojun		if (a - a0 + 1 > alen || b - b0 + 1 > blen)
15821.31Sitojun			return 0;
15831.31Sitojun
15841.33Sitojun		if ((signed char)a[0] < 0 || (signed char)b[0] < 0)
15851.31Sitojun			return 0;
15861.31Sitojun		/* we don't support compression yet */
15871.31Sitojun		if (a[0] >= 64 || b[0] >= 64)
15881.31Sitojun			return 0;
15891.31Sitojun
15901.31Sitojun		/* truncated case */
15911.31Sitojun		if (a[0] == 0 && a - a0 == alen - 1)
15921.31Sitojun			return 1;
15931.31Sitojun		if (b[0] == 0 && b - b0 == blen - 1)
15941.31Sitojun			return 1;
15951.31Sitojun		if (a[0] == 0 || b[0] == 0)
15961.31Sitojun			return 0;
15971.31Sitojun
15981.31Sitojun		if (a[0] != b[0])
15991.31Sitojun			return 0;
16001.31Sitojun		l = a[0];
16011.31Sitojun		if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen)
16021.31Sitojun			return 0;
16031.31Sitojun		if (bcmp(a + 1, b + 1, l) != 0)
16041.31Sitojun			return 0;
16051.31Sitojun
16061.31Sitojun		a += 1 + l;
16071.31Sitojun		b += 1 + l;
16081.31Sitojun	}
16091.31Sitojun
16101.31Sitojun	if (a - a0 == alen && b - b0 == blen)
16111.31Sitojun		return 1;
16121.31Sitojun	else
16131.31Sitojun		return 0;
16141.31Sitojun}
16151.31Sitojun
16161.31Sitojun/*
16171.2Sitojun * calculate the number of addresses to be returned in the node info reply.
16181.2Sitojun */
16191.2Sitojunstatic int
16201.121.4.2Syamtni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m,
16211.121.4.1Syamt    struct ifnet **ifpp, char *subj)
16221.2Sitojun{
16231.53Sitojun	struct ifnet *ifp;
16241.53Sitojun	struct in6_ifaddr *ifa6;
16251.53Sitojun	struct ifaddr *ifa;
16261.47Sitojun	struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */
16271.2Sitojun	int addrs = 0, addrsofif, iffound = 0;
16281.47Sitojun	int niflags = ni6->ni_flags;
16291.47Sitojun
16301.47Sitojun	if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) {
16311.56Sitojun		switch (ni6->ni_code) {
16321.47Sitojun		case ICMP6_NI_SUBJ_IPV6:
16331.47Sitojun			if (subj == NULL) /* must be impossible... */
16341.86Sitojun				return (0);
16351.47Sitojun			subj_ip6 = (struct sockaddr_in6 *)subj;
16361.47Sitojun			break;
16371.47Sitojun		default:
16381.47Sitojun			/*
16391.47Sitojun			 * XXX: we only support IPv6 subject address for
16401.47Sitojun			 * this Qtype.
16411.47Sitojun			 */
16421.86Sitojun			return (0);
16431.47Sitojun		}
16441.47Sitojun	}
16451.2Sitojun
16461.2Sitojun	for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
16471.2Sitojun	{
16481.2Sitojun		addrsofif = 0;
16491.2Sitojun		for (ifa = ifp->if_addrlist.tqh_first; ifa;
16501.2Sitojun		     ifa = ifa->ifa_list.tqe_next)
16511.2Sitojun		{
16521.2Sitojun			if (ifa->ifa_addr->sa_family != AF_INET6)
16531.2Sitojun				continue;
16541.2Sitojun			ifa6 = (struct in6_ifaddr *)ifa;
16551.2Sitojun
16561.47Sitojun			if ((niflags & NI_NODEADDR_FLAG_ALL) == 0 &&
16571.47Sitojun			    IN6_ARE_ADDR_EQUAL(&subj_ip6->sin6_addr,
16581.2Sitojun					       &ifa6->ia_addr.sin6_addr))
16591.2Sitojun				iffound = 1;
16601.24Sitojun
16611.24Sitojun			/*
16621.24Sitojun			 * IPv4-mapped addresses can only be returned by a
16631.24Sitojun			 * Node Information proxy, since they represent
16641.24Sitojun			 * addresses of IPv4-only nodes, which perforce do
16651.24Sitojun			 * not implement this protocol.
16661.47Sitojun			 * [icmp-name-lookups-07, Section 5.4]
16671.24Sitojun			 * So we don't support NI_NODEADDR_FLAG_COMPAT in
16681.24Sitojun			 * this function at this moment.
16691.24Sitojun			 */
16701.2Sitojun
16711.2Sitojun			/* What do we have to do about ::1? */
16721.56Sitojun			switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
16731.47Sitojun			case IPV6_ADDR_SCOPE_LINKLOCAL:
16741.53Sitojun				if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
16751.47Sitojun					continue;
16761.2Sitojun				break;
16771.47Sitojun			case IPV6_ADDR_SCOPE_SITELOCAL:
16781.53Sitojun				if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
16791.47Sitojun					continue;
16801.47Sitojun				break;
16811.47Sitojun			case IPV6_ADDR_SCOPE_GLOBAL:
16821.53Sitojun				if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
16831.47Sitojun					continue;
16841.2Sitojun				break;
16851.47Sitojun			default:
16861.47Sitojun				continue;
16871.2Sitojun			}
16881.47Sitojun
16891.47Sitojun			/*
16901.47Sitojun			 * check if anycast is okay.
16911.67Sitojun			 * XXX: just experimental.  not in the spec.
16921.47Sitojun			 */
16931.47Sitojun			if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
16941.47Sitojun			    (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
16951.47Sitojun				continue; /* we need only unicast addresses */
16961.47Sitojun
16971.47Sitojun			addrsofif++; /* count the address */
16981.2Sitojun		}
16991.2Sitojun		if (iffound) {
17001.2Sitojun			*ifpp = ifp;
17011.86Sitojun			return (addrsofif);
17021.2Sitojun		}
17031.2Sitojun
17041.2Sitojun		addrs += addrsofif;
17051.2Sitojun	}
17061.2Sitojun
17071.86Sitojun	return (addrs);
17081.2Sitojun}
17091.2Sitojun
17101.2Sitojunstatic int
17111.2Sitojunni6_store_addrs(ni6, nni6, ifp0, resid)
17121.2Sitojun	struct icmp6_nodeinfo *ni6, *nni6;
17131.2Sitojun	struct ifnet *ifp0;
17141.2Sitojun	int resid;
17151.2Sitojun{
17161.53Sitojun	struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
17171.53Sitojun	struct in6_ifaddr *ifa6;
17181.53Sitojun	struct ifaddr *ifa;
17191.47Sitojun	struct ifnet *ifp_dep = NULL;
17201.47Sitojun	int copied = 0, allow_deprecated = 0;
17211.2Sitojun	u_char *cp = (u_char *)(nni6 + 1);
17221.47Sitojun	int niflags = ni6->ni_flags;
17231.47Sitojun	u_int32_t ltime;
17241.2Sitojun
17251.47Sitojun	if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL))
17261.86Sitojun		return (0);	/* needless to copy */
17271.2Sitojun
17281.47Sitojun  again:
17291.47Sitojun
17301.2Sitojun	for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
17311.2Sitojun	{
17321.2Sitojun		for (ifa = ifp->if_addrlist.tqh_first; ifa;
17331.2Sitojun		     ifa = ifa->ifa_list.tqe_next)
17341.2Sitojun		{
17351.2Sitojun			if (ifa->ifa_addr->sa_family != AF_INET6)
17361.2Sitojun				continue;
17371.2Sitojun			ifa6 = (struct in6_ifaddr *)ifa;
17381.2Sitojun
17391.47Sitojun			if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) != 0 &&
17401.47Sitojun			    allow_deprecated == 0) {
17411.47Sitojun				/*
17421.47Sitojun				 * prefererred address should be put before
17431.47Sitojun				 * deprecated addresses.
17441.47Sitojun				 */
17451.47Sitojun
17461.47Sitojun				/* record the interface for later search */
17471.47Sitojun				if (ifp_dep == NULL)
17481.47Sitojun					ifp_dep = ifp;
17491.47Sitojun
17501.47Sitojun				continue;
17511.12Sitojun			}
17521.47Sitojun			else if ((ifa6->ia6_flags & IN6_IFF_DEPRECATED) == 0 &&
17531.47Sitojun				 allow_deprecated != 0)
17541.47Sitojun				continue; /* we now collect deprecated addrs */
17551.2Sitojun
17561.2Sitojun			/* What do we have to do about ::1? */
17571.56Sitojun			switch (in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
17581.47Sitojun			case IPV6_ADDR_SCOPE_LINKLOCAL:
17591.53Sitojun				if ((niflags & NI_NODEADDR_FLAG_LINKLOCAL) == 0)
17601.47Sitojun					continue;
17611.2Sitojun				break;
17621.47Sitojun			case IPV6_ADDR_SCOPE_SITELOCAL:
17631.53Sitojun				if ((niflags & NI_NODEADDR_FLAG_SITELOCAL) == 0)
17641.47Sitojun					continue;
17651.47Sitojun				break;
17661.47Sitojun			case IPV6_ADDR_SCOPE_GLOBAL:
17671.53Sitojun				if ((niflags & NI_NODEADDR_FLAG_GLOBAL) == 0)
17681.47Sitojun					continue;
17691.2Sitojun				break;
17701.47Sitojun			default:
17711.47Sitojun				continue;
17721.47Sitojun			}
17731.47Sitojun
17741.47Sitojun			/*
17751.47Sitojun			 * check if anycast is okay.
17761.67Sitojun			 * XXX: just experimental.  not in the spec.
17771.47Sitojun			 */
17781.47Sitojun			if ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0 &&
17791.47Sitojun			    (niflags & NI_NODEADDR_FLAG_ANYCAST) == 0)
17801.47Sitojun				continue;
17811.47Sitojun
17821.47Sitojun			/* now we can copy the address */
17831.47Sitojun			if (resid < sizeof(struct in6_addr) +
17841.47Sitojun			    sizeof(u_int32_t)) {
17851.47Sitojun				/*
17861.47Sitojun				 * We give up much more copy.
17871.47Sitojun				 * Set the truncate flag and return.
17881.47Sitojun				 */
17891.115Srpaulo				nni6->ni_flags |= NI_NODEADDR_FLAG_TRUNCATE;
17901.86Sitojun				return (copied);
17911.2Sitojun			}
17921.2Sitojun
17931.47Sitojun			/*
17941.47Sitojun			 * Set the TTL of the address.
17951.47Sitojun			 * The TTL value should be one of the following
17961.47Sitojun			 * according to the specification:
17971.47Sitojun			 *
17981.47Sitojun			 * 1. The remaining lifetime of a DHCP lease on the
17991.47Sitojun			 *    address, or
18001.47Sitojun			 * 2. The remaining Valid Lifetime of a prefix from
18011.47Sitojun			 *    which the address was derived through Stateless
18021.47Sitojun			 *    Autoconfiguration.
18031.47Sitojun			 *
18041.47Sitojun			 * Note that we currently do not support stateful
18051.47Sitojun			 * address configuration by DHCPv6, so the former
18061.47Sitojun			 * case can't happen.
18071.67Sitojun			 *
18081.67Sitojun			 * TTL must be 2^31 > TTL >= 0.
18091.47Sitojun			 */
18101.47Sitojun			if (ifa6->ia6_lifetime.ia6t_expire == 0)
18111.47Sitojun				ltime = ND6_INFINITE_LIFETIME;
18121.47Sitojun			else {
18131.47Sitojun				if (ifa6->ia6_lifetime.ia6t_expire >
18141.117Skardel				    time_second)
18151.117Skardel					ltime = ifa6->ia6_lifetime.ia6t_expire -
18161.117Skardel					    time_second;
18171.47Sitojun				else
18181.47Sitojun					ltime = 0;
18191.2Sitojun			}
18201.67Sitojun			if (ltime > 0x7fffffff)
18211.67Sitojun				ltime = 0x7fffffff;
18221.67Sitojun			ltime = htonl(ltime);
18231.82Sitojun
18241.47Sitojun			bcopy(&ltime, cp, sizeof(u_int32_t));
18251.47Sitojun			cp += sizeof(u_int32_t);
18261.47Sitojun
18271.47Sitojun			/* copy the address itself */
18281.47Sitojun			bcopy(&ifa6->ia_addr.sin6_addr, cp,
18291.47Sitojun			      sizeof(struct in6_addr));
18301.113Srpaulo			in6_clearscope((struct in6_addr *)cp); /* XXX */
18311.47Sitojun			cp += sizeof(struct in6_addr);
18321.82Sitojun
18331.47Sitojun			resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t));
18341.115Srpaulo			copied += (sizeof(struct in6_addr) + sizeof(u_int32_t));
18351.2Sitojun		}
18361.2Sitojun		if (ifp0)	/* we need search only on the specified IF */
18371.2Sitojun			break;
18381.47Sitojun	}
18391.47Sitojun
18401.47Sitojun	if (allow_deprecated == 0 && ifp_dep != NULL) {
18411.47Sitojun		ifp = ifp_dep;
18421.47Sitojun		allow_deprecated = 1;
18431.47Sitojun
18441.47Sitojun		goto again;
18451.2Sitojun	}
18461.2Sitojun
18471.86Sitojun	return (copied);
18481.2Sitojun}
18491.2Sitojun
18501.2Sitojun/*
18511.2Sitojun * XXX almost dup'ed code with rip6_input.
18521.2Sitojun */
18531.2Sitojunstatic int
18541.2Sitojunicmp6_rip6_input(mp, off)
18551.2Sitojun	struct	mbuf **mp;
18561.2Sitojun	int	off;
18571.2Sitojun{
18581.2Sitojun	struct mbuf *m = *mp;
18591.53Sitojun	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
18601.101Sitojun	struct inpcb_hdr *inph;
18611.53Sitojun	struct in6pcb *in6p;
18621.2Sitojun	struct in6pcb *last = NULL;
18631.2Sitojun	struct sockaddr_in6 rip6src;
18641.2Sitojun	struct icmp6_hdr *icmp6;
18651.2Sitojun	struct mbuf *opts = NULL;
18661.2Sitojun
18671.23Sitojun	IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6));
18681.23Sitojun	if (icmp6 == NULL) {
18691.23Sitojun		/* m is already reclaimed */
18701.23Sitojun		return IPPROTO_DONE;
18711.23Sitojun	}
18721.2Sitojun
18731.113Srpaulo	/*
18741.113Srpaulo	 * XXX: the address may have embedded scope zone ID, which should be
18751.113Srpaulo	 * hidden from applications.
18761.113Srpaulo	 */
18771.2Sitojun	bzero(&rip6src, sizeof(rip6src));
18781.2Sitojun	rip6src.sin6_len = sizeof(struct sockaddr_in6);
18791.2Sitojun	rip6src.sin6_family = AF_INET6;
18801.113Srpaulo	rip6src.sin6_addr = ip6->ip6_src;
18811.113Srpaulo	if (sa6_recoverscope(&rip6src)) {
18821.113Srpaulo		m_freem(m);
18831.113Srpaulo		return (IPPROTO_DONE);
18841.113Srpaulo	}
18851.2Sitojun
18861.101Sitojun	CIRCLEQ_FOREACH(inph, &raw6cbtable.inpt_queue, inph_queue) {
18871.101Sitojun		in6p = (struct in6pcb *)inph;
18881.101Sitojun		if (in6p->in6p_af != AF_INET6)
18891.101Sitojun			continue;
18901.100Sitojun		if (in6p->in6p_ip6.ip6_nxt != IPPROTO_ICMPV6)
18911.2Sitojun			continue;
18921.12Sitojun		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
18931.2Sitojun		   !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
18941.2Sitojun			continue;
18951.12Sitojun		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
18961.2Sitojun		   !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
18971.2Sitojun			continue;
18981.2Sitojun		if (in6p->in6p_icmp6filt
18991.2Sitojun		    && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type,
19001.2Sitojun				 in6p->in6p_icmp6filt))
19011.2Sitojun			continue;
19021.2Sitojun		if (last) {
19031.2Sitojun			struct	mbuf *n;
19041.2Sitojun			if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
19051.2Sitojun				if (last->in6p_flags & IN6P_CONTROLOPTS)
19061.2Sitojun					ip6_savecontrol(last, &opts, ip6, n);
19071.2Sitojun				/* strip intermediate headers */
19081.2Sitojun				m_adj(n, off);
19091.2Sitojun				if (sbappendaddr(&last->in6p_socket->so_rcv,
19101.2Sitojun						 (struct sockaddr *)&rip6src,
19111.2Sitojun						 n, opts) == 0) {
19121.2Sitojun					/* should notify about lost packet */
19131.2Sitojun					m_freem(n);
19141.2Sitojun					if (opts)
19151.2Sitojun						m_freem(opts);
19161.2Sitojun				} else
19171.2Sitojun					sorwakeup(last->in6p_socket);
19181.2Sitojun				opts = NULL;
19191.2Sitojun			}
19201.2Sitojun		}
19211.2Sitojun		last = in6p;
19221.2Sitojun	}
19231.2Sitojun	if (last) {
19241.2Sitojun		if (last->in6p_flags & IN6P_CONTROLOPTS)
19251.2Sitojun			ip6_savecontrol(last, &opts, ip6, m);
19261.2Sitojun		/* strip intermediate headers */
19271.2Sitojun		m_adj(m, off);
19281.2Sitojun		if (sbappendaddr(&last->in6p_socket->so_rcv,
19291.2Sitojun				(struct sockaddr *)&rip6src, m, opts) == 0) {
19301.2Sitojun			m_freem(m);
19311.2Sitojun			if (opts)
19321.2Sitojun				m_freem(opts);
19331.2Sitojun		} else
19341.2Sitojun			sorwakeup(last->in6p_socket);
19351.2Sitojun	} else {
19361.2Sitojun		m_freem(m);
19371.2Sitojun		ip6stat.ip6s_delivered--;
19381.2Sitojun	}
19391.2Sitojun	return IPPROTO_DONE;
19401.2Sitojun}
19411.2Sitojun
19421.2Sitojun/*
19431.2Sitojun * Reflect the ip6 packet back to the source.
19441.14Sitojun * OFF points to the icmp6 header, counted from the top of the mbuf.
19451.66Sitojun *
19461.66Sitojun * Note: RFC 1885 required that an echo reply should be truncated if it
19471.66Sitojun * did not fit in with (return) path MTU, and KAME code supported the
19481.66Sitojun * behavior.  However, as a clarification after the RFC, this limitation
19491.66Sitojun * was removed in a revised version of the spec, RFC 2463.  We had kept the
19501.66Sitojun * old behavior, with a (non-default) ifdef block, while the new version of
19511.66Sitojun * the spec was an internet-draft status, and even after the new RFC was
19521.66Sitojun * published.  But it would rather make sense to clean the obsoleted part
19531.66Sitojun * up, and to make the code simpler at this stage.
19541.2Sitojun */
19551.2Sitojunvoid
19561.2Sitojunicmp6_reflect(m, off)
19571.2Sitojun	struct	mbuf *m;
19581.2Sitojun	size_t off;
19591.2Sitojun{
19601.12Sitojun	struct ip6_hdr *ip6;
19611.2Sitojun	struct icmp6_hdr *icmp6;
19621.2Sitojun	struct in6_ifaddr *ia;
19631.12Sitojun	int plen;
19641.12Sitojun	int type, code;
19651.12Sitojun	struct ifnet *outif = NULL;
19661.113Srpaulo	struct in6_addr origdst, *src = NULL;
19671.2Sitojun
19681.12Sitojun	/* too short to reflect */
19691.12Sitojun	if (off < sizeof(struct ip6_hdr)) {
19701.53Sitojun		nd6log((LOG_DEBUG,
19711.53Sitojun		    "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n",
19721.53Sitojun		    (u_long)off, (u_long)sizeof(struct ip6_hdr),
19731.53Sitojun		    __FILE__, __LINE__));
19741.12Sitojun		goto bad;
19751.12Sitojun	}
19761.12Sitojun
19771.2Sitojun	/*
19781.2Sitojun	 * If there are extra headers between IPv6 and ICMPv6, strip
19791.2Sitojun	 * off that header first.
19801.2Sitojun	 */
19811.35Sitojun#ifdef DIAGNOSTIC
19821.35Sitojun	if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN)
19831.35Sitojun		panic("assumption failed in icmp6_reflect");
19841.35Sitojun#endif
19851.12Sitojun	if (off > sizeof(struct ip6_hdr)) {
19861.12Sitojun		size_t l;
19871.12Sitojun		struct ip6_hdr nip6;
19881.12Sitojun
19891.12Sitojun		l = off - sizeof(struct ip6_hdr);
19901.12Sitojun		m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6);
19911.12Sitojun		m_adj(m, l);
19921.12Sitojun		l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
19931.12Sitojun		if (m->m_len < l) {
19941.12Sitojun			if ((m = m_pullup(m, l)) == NULL)
19951.12Sitojun				return;
19961.12Sitojun		}
19971.12Sitojun		bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6));
19981.12Sitojun	} else /* off == sizeof(struct ip6_hdr) */ {
19991.12Sitojun		size_t l;
20001.12Sitojun		l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
20011.12Sitojun		if (m->m_len < l) {
20021.12Sitojun			if ((m = m_pullup(m, l)) == NULL)
20031.12Sitojun				return;
20041.2Sitojun		}
20051.2Sitojun	}
20061.12Sitojun	plen = m->m_pkthdr.len - sizeof(struct ip6_hdr);
20071.12Sitojun	ip6 = mtod(m, struct ip6_hdr *);
20081.12Sitojun	ip6->ip6_nxt = IPPROTO_ICMPV6;
20091.2Sitojun	icmp6 = (struct icmp6_hdr *)(ip6 + 1);
20101.12Sitojun	type = icmp6->icmp6_type; /* keep type for statistics */
20111.12Sitojun	code = icmp6->icmp6_code; /* ditto. */
20121.2Sitojun
20131.113Srpaulo	origdst = ip6->ip6_dst;
20141.2Sitojun	/*
20151.2Sitojun	 * ip6_input() drops a packet if its src is multicast.
20161.2Sitojun	 * So, the src is never multicast.
20171.2Sitojun	 */
20181.2Sitojun	ip6->ip6_dst = ip6->ip6_src;
20191.2Sitojun
20201.55Sitojun	/*
20211.67Sitojun	 * If the incoming packet was addressed directly to us (i.e. unicast),
20221.2Sitojun	 * use dst as the src for the reply.
20231.114Srpaulo	 * The IN6_IFF_NOTREADY case should be VERY rare, but is possible
20241.23Sitojun	 * (for example) when we encounter an error while forwarding procedure
20251.23Sitojun	 * destined to a duplicated address of ours.
20261.113Srpaulo	 * Note that ip6_getdstifaddr() may fail if we are in an error handling
20271.113Srpaulo	 * procedure of an outgoing packet of our own, in which case we need
20281.113Srpaulo	 * to search in the ifaddr list.
20291.2Sitojun	 */
20301.113Srpaulo	if (!IN6_IS_ADDR_MULTICAST(&origdst)) {
20311.113Srpaulo		if ((ia = ip6_getdstifaddr(m))) {
20321.113Srpaulo			if (!(ia->ia6_flags &
20331.113Srpaulo			    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)))
20341.113Srpaulo				src = &ia->ia_addr.sin6_addr;
20351.113Srpaulo		} else {
20361.113Srpaulo			struct sockaddr_in6 d;
20371.113Srpaulo
20381.113Srpaulo			bzero(&d, sizeof(d));
20391.113Srpaulo			d.sin6_family = AF_INET6;
20401.113Srpaulo			d.sin6_len = sizeof(d);
20411.113Srpaulo			d.sin6_addr = origdst;
20421.113Srpaulo			ia = (struct in6_ifaddr *)
20431.113Srpaulo			    ifa_ifwithaddr((struct sockaddr *)&d);
20441.113Srpaulo			if (ia &&
20451.113Srpaulo			    !(ia->ia6_flags &
20461.113Srpaulo			    (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY))) {
20471.113Srpaulo				src = &ia->ia_addr.sin6_addr;
20481.113Srpaulo			}
20491.2Sitojun		}
20501.2Sitojun	}
20511.2Sitojun
20521.113Srpaulo	if (src == NULL) {
20531.55Sitojun		int e;
20541.113Srpaulo		struct sockaddr_in6 sin6;
20551.55Sitojun		struct route_in6 ro;
20561.55Sitojun
20571.2Sitojun		/*
20581.23Sitojun		 * This case matches to multicasts, our anycast, or unicasts
20591.67Sitojun		 * that we do not own.  Select a source address based on the
20601.55Sitojun		 * source address of the erroneous packet.
20611.2Sitojun		 */
20621.113Srpaulo		bzero(&sin6, sizeof(sin6));
20631.113Srpaulo		sin6.sin6_family = AF_INET6;
20641.113Srpaulo		sin6.sin6_len = sizeof(sin6);
20651.113Srpaulo		sin6.sin6_addr = ip6->ip6_dst; /* zone ID should be embedded */
20661.113Srpaulo
20671.55Sitojun		bzero(&ro, sizeof(ro));
20681.113Srpaulo		src = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, &outif, &e);
20691.121.4.2Syamt		if (ro.ro_rt != NULL)
20701.121.4.2Syamt			rtflush((struct route *)&ro);
20711.55Sitojun		if (src == NULL) {
20721.55Sitojun			nd6log((LOG_DEBUG,
20731.55Sitojun			    "icmp6_reflect: source can't be determined: "
20741.55Sitojun			    "dst=%s, error=%d\n",
20751.113Srpaulo			    ip6_sprintf(&sin6.sin6_addr), e));
20761.55Sitojun			goto bad;
20771.55Sitojun		}
20781.55Sitojun	}
20791.2Sitojun
20801.2Sitojun	ip6->ip6_src = *src;
20811.2Sitojun	ip6->ip6_flow = 0;
20821.13Sitojun	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
20831.13Sitojun	ip6->ip6_vfc |= IPV6_VERSION;
20841.2Sitojun	ip6->ip6_nxt = IPPROTO_ICMPV6;
20851.2Sitojun	if (m->m_pkthdr.rcvif) {
20861.2Sitojun		/* XXX: This may not be the outgoing interface */
20871.79Schristos		ip6->ip6_hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim;
20881.65Sitojun	} else
20891.65Sitojun		ip6->ip6_hlim = ip6_defhlim;
20901.2Sitojun
20911.118Stron	m->m_pkthdr.csum_flags = 0;
20921.2Sitojun	icmp6->icmp6_cksum = 0;
20931.2Sitojun	icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
20941.2Sitojun					sizeof(struct ip6_hdr), plen);
20951.2Sitojun
20961.2Sitojun	/*
20971.67Sitojun	 * XXX option handling
20981.2Sitojun	 */
20991.2Sitojun
21001.2Sitojun	m->m_flags &= ~(M_BCAST|M_MCAST);
21011.2Sitojun
21021.73Sitojun	/*
21031.73Sitojun	 * To avoid a "too big" situation at an intermediate router
21041.73Sitojun	 * and the path MTU discovery process, specify the IPV6_MINMTU flag.
21051.73Sitojun	 * Note that only echo and node information replies are affected,
21061.73Sitojun	 * since the length of ICMP6 errors is limited to the minimum MTU.
21071.73Sitojun	 */
21081.115Srpaulo	if (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, &outif) != 0 &&
21091.115Srpaulo	    outif)
21101.72Sitojun		icmp6_ifstat_inc(outif, ifs6_out_error);
21111.66Sitojun
21121.12Sitojun	if (outif)
21131.12Sitojun		icmp6_ifoutstat_inc(outif, type, code);
21141.2Sitojun
21151.2Sitojun	return;
21161.2Sitojun
21171.2Sitojun bad:
21181.2Sitojun	m_freem(m);
21191.2Sitojun	return;
21201.2Sitojun}
21211.2Sitojun
21221.12Sitojunstatic const char *
21231.12Sitojunicmp6_redirect_diag(src6, dst6, tgt6)
21241.11Sitojun	struct in6_addr *src6;
21251.11Sitojun	struct in6_addr *dst6;
21261.11Sitojun	struct in6_addr *tgt6;
21271.11Sitojun{
21281.12Sitojun	static char buf[1024];
21291.12Sitojun	snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
21301.12Sitojun		ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
21311.12Sitojun	return buf;
21321.11Sitojun}
21331.11Sitojun
21341.2Sitojunvoid
21351.2Sitojunicmp6_redirect_input(m, off)
21361.53Sitojun	struct mbuf *m;
21371.2Sitojun	int off;
21381.2Sitojun{
21391.2Sitojun	struct ifnet *ifp = m->m_pkthdr.rcvif;
21401.2Sitojun	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
21411.23Sitojun	struct nd_redirect *nd_rd;
21421.2Sitojun	int icmp6len = ntohs(ip6->ip6_plen);
21431.2Sitojun	char *lladdr = NULL;
21441.2Sitojun	int lladdrlen = 0;
21451.2Sitojun	struct rtentry *rt = NULL;
21461.2Sitojun	int is_router;
21471.2Sitojun	int is_onlink;
21481.2Sitojun	struct in6_addr src6 = ip6->ip6_src;
21491.23Sitojun	struct in6_addr redtgt6;
21501.23Sitojun	struct in6_addr reddst6;
21511.2Sitojun	union nd_opts ndopts;
21521.2Sitojun
21531.85Sitojun	if (!ifp)
21541.2Sitojun		return;
21551.2Sitojun
21561.2Sitojun	/* XXX if we are router, we don't update route by icmp6 redirect */
21571.2Sitojun	if (ip6_forwarding)
21581.23Sitojun		goto freeit;
21591.2Sitojun	if (!icmp6_rediraccept)
21601.23Sitojun		goto freeit;
21611.2Sitojun
21621.23Sitojun	IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len);
21631.23Sitojun	if (nd_rd == NULL) {
21641.23Sitojun		icmp6stat.icp6s_tooshort++;
21651.23Sitojun		return;
21661.23Sitojun	}
21671.23Sitojun	redtgt6 = nd_rd->nd_rd_target;
21681.23Sitojun	reddst6 = nd_rd->nd_rd_dst;
21691.25Sitojun
21701.113Srpaulo	if (in6_setscope(&redtgt6, m->m_pkthdr.rcvif, NULL) ||
21711.113Srpaulo	    in6_setscope(&reddst6, m->m_pkthdr.rcvif, NULL)) {
21721.113Srpaulo		goto freeit;
21731.113Srpaulo	}
21741.23Sitojun
21751.2Sitojun	/* validation */
21761.2Sitojun	if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
21771.53Sitojun		nd6log((LOG_ERR,
21781.2Sitojun			"ICMP6 redirect sent from %s rejected; "
21791.53Sitojun			"must be from linklocal\n", ip6_sprintf(&src6)));
21801.53Sitojun		goto bad;
21811.2Sitojun	}
21821.2Sitojun	if (ip6->ip6_hlim != 255) {
21831.53Sitojun		nd6log((LOG_ERR,
21841.2Sitojun			"ICMP6 redirect sent from %s rejected; "
21851.2Sitojun			"hlim=%d (must be 255)\n",
21861.53Sitojun			ip6_sprintf(&src6), ip6->ip6_hlim));
21871.53Sitojun		goto bad;
21881.2Sitojun	}
21891.2Sitojun    {
21901.2Sitojun	/* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
21911.2Sitojun	struct sockaddr_in6 sin6;
21921.2Sitojun	struct in6_addr *gw6;
21931.2Sitojun
21941.2Sitojun	bzero(&sin6, sizeof(sin6));
21951.2Sitojun	sin6.sin6_family = AF_INET6;
21961.2Sitojun	sin6.sin6_len = sizeof(struct sockaddr_in6);
21971.2Sitojun	bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
21981.23Sitojun	rt = rtalloc1((struct sockaddr *)&sin6, 0);
21991.2Sitojun	if (rt) {
22001.43Sitojun		if (rt->rt_gateway == NULL ||
22011.43Sitojun		    rt->rt_gateway->sa_family != AF_INET6) {
22021.53Sitojun			nd6log((LOG_ERR,
22031.43Sitojun			    "ICMP6 redirect rejected; no route "
22041.43Sitojun			    "with inet6 gateway found for redirect dst: %s\n",
22051.53Sitojun			    icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22061.43Sitojun			RTFREE(rt);
22071.53Sitojun			goto bad;
22081.43Sitojun		}
22091.43Sitojun
22101.2Sitojun		gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
22111.2Sitojun		if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
22121.53Sitojun			nd6log((LOG_ERR,
22131.12Sitojun				"ICMP6 redirect rejected; "
22141.12Sitojun				"not equal to gw-for-src=%s (must be same): "
22151.12Sitojun				"%s\n",
22161.12Sitojun				ip6_sprintf(gw6),
22171.53Sitojun				icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22181.2Sitojun			RTFREE(rt);
22191.53Sitojun			goto bad;
22201.2Sitojun		}
22211.2Sitojun	} else {
22221.53Sitojun		nd6log((LOG_ERR,
22231.12Sitojun			"ICMP6 redirect rejected; "
22241.12Sitojun			"no route found for redirect dst: %s\n",
22251.53Sitojun			icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22261.53Sitojun		goto bad;
22271.2Sitojun	}
22281.2Sitojun	RTFREE(rt);
22291.2Sitojun	rt = NULL;
22301.2Sitojun    }
22311.2Sitojun	if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
22321.53Sitojun		nd6log((LOG_ERR,
22331.12Sitojun			"ICMP6 redirect rejected; "
22341.12Sitojun			"redirect dst must be unicast: %s\n",
22351.53Sitojun			icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22361.53Sitojun		goto bad;
22371.2Sitojun	}
22381.2Sitojun
22391.2Sitojun	is_router = is_onlink = 0;
22401.2Sitojun	if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
22411.2Sitojun		is_router = 1;	/* router case */
22421.2Sitojun	if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
22431.2Sitojun		is_onlink = 1;	/* on-link destination case */
22441.2Sitojun	if (!is_router && !is_onlink) {
22451.53Sitojun		nd6log((LOG_ERR,
22461.12Sitojun			"ICMP6 redirect rejected; "
22471.12Sitojun			"neither router case nor onlink case: %s\n",
22481.53Sitojun			icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22491.53Sitojun		goto bad;
22501.2Sitojun	}
22511.2Sitojun	/* validation passed */
22521.2Sitojun
22531.2Sitojun	icmp6len -= sizeof(*nd_rd);
22541.2Sitojun	nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
22551.2Sitojun	if (nd6_options(&ndopts) < 0) {
22561.53Sitojun		nd6log((LOG_INFO, "icmp6_redirect_input: "
22571.12Sitojun			"invalid ND option, rejected: %s\n",
22581.53Sitojun			icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22591.53Sitojun		/* nd6_options have incremented stats */
22601.23Sitojun		goto freeit;
22611.2Sitojun	}
22621.2Sitojun
22631.2Sitojun	if (ndopts.nd_opts_tgt_lladdr) {
22641.2Sitojun		lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
22651.2Sitojun		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
22661.2Sitojun	}
22671.2Sitojun
22681.2Sitojun	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
22691.53Sitojun		nd6log((LOG_INFO,
22701.2Sitojun			"icmp6_redirect_input: lladdrlen mismatch for %s "
22711.12Sitojun			"(if %d, icmp6 packet %d): %s\n",
22721.12Sitojun			ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2,
22731.53Sitojun			icmp6_redirect_diag(&src6, &reddst6, &redtgt6)));
22741.53Sitojun		goto bad;
22751.2Sitojun	}
22761.2Sitojun
22771.2Sitojun	/* RFC 2461 8.3 */
22781.10Sitojun	nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
22791.10Sitojun			 is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
22801.2Sitojun
22811.67Sitojun	if (!is_onlink) {	/* better router case.  perform rtredirect. */
22821.2Sitojun		/* perform rtredirect */
22831.2Sitojun		struct sockaddr_in6 sdst;
22841.2Sitojun		struct sockaddr_in6 sgw;
22851.2Sitojun		struct sockaddr_in6 ssrc;
22861.55Sitojun		unsigned long rtcount;
22871.55Sitojun		struct rtentry *newrt = NULL;
22881.55Sitojun
22891.55Sitojun		/*
22901.55Sitojun		 * do not install redirect route, if the number of entries
22911.55Sitojun		 * is too much (> hiwat).  note that, the node (= host) will
22921.55Sitojun		 * work just fine even if we do not install redirect route
22931.55Sitojun		 * (there will be additional hops, though).
22941.55Sitojun		 */
22951.55Sitojun		rtcount = rt_timer_count(icmp6_redirect_timeout_q);
22961.55Sitojun		if (0 <= icmp6_redirect_hiwat && rtcount > icmp6_redirect_hiwat)
22971.55Sitojun			return;
22981.55Sitojun		else if (0 <= icmp6_redirect_lowat &&
22991.55Sitojun		    rtcount > icmp6_redirect_lowat) {
23001.55Sitojun			/*
23011.55Sitojun			 * XXX nuke a victim, install the new one.
23021.55Sitojun			 */
23031.55Sitojun		}
23041.2Sitojun
23051.2Sitojun		bzero(&sdst, sizeof(sdst));
23061.2Sitojun		bzero(&sgw, sizeof(sgw));
23071.2Sitojun		bzero(&ssrc, sizeof(ssrc));
23081.2Sitojun		sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
23091.2Sitojun		sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
23101.2Sitojun			sizeof(struct sockaddr_in6);
23111.2Sitojun		bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
23121.2Sitojun		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
23131.2Sitojun		bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
23141.2Sitojun		rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
23151.2Sitojun			   (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
23161.2Sitojun			   (struct sockaddr *)&ssrc,
23171.55Sitojun			   &newrt);
23181.55Sitojun
23191.55Sitojun		if (newrt) {
23201.55Sitojun			(void)rt_timer_add(newrt, icmp6_redirect_timeout,
23211.55Sitojun			    icmp6_redirect_timeout_q);
23221.55Sitojun			rtfree(newrt);
23231.55Sitojun		}
23241.2Sitojun	}
23251.2Sitojun	/* finally update cached route in each socket via pfctlinput */
23261.74Sitojun	{
23271.74Sitojun		struct sockaddr_in6 sdst;
23281.2Sitojun
23291.74Sitojun		bzero(&sdst, sizeof(sdst));
23301.74Sitojun		sdst.sin6_family = AF_INET6;
23311.74Sitojun		sdst.sin6_len = sizeof(struct sockaddr_in6);
23321.74Sitojun		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
23331.74Sitojun		pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
23341.2Sitojun#ifdef IPSEC
23351.74Sitojun		key_sa_routechange((struct sockaddr *)&sdst);
23361.2Sitojun#endif
23371.74Sitojun	}
23381.23Sitojun
23391.23Sitojun freeit:
23401.23Sitojun	m_freem(m);
23411.53Sitojun	return;
23421.53Sitojun
23431.53Sitojun bad:
23441.53Sitojun	icmp6stat.icp6s_badredirect++;
23451.53Sitojun	m_freem(m);
23461.2Sitojun}
23471.2Sitojun
23481.2Sitojunvoid
23491.2Sitojunicmp6_redirect_output(m0, rt)
23501.2Sitojun	struct mbuf *m0;
23511.2Sitojun	struct rtentry *rt;
23521.2Sitojun{
23531.2Sitojun	struct ifnet *ifp;	/* my outgoing interface */
23541.2Sitojun	struct in6_addr *ifp_ll6;
23551.75Sitojun	struct in6_addr *nexthop;
23561.2Sitojun	struct ip6_hdr *sip6;	/* m0 as struct ip6_hdr */
23571.2Sitojun	struct mbuf *m = NULL;	/* newly allocated one */
23581.2Sitojun	struct ip6_hdr *ip6;	/* m as struct ip6_hdr */
23591.2Sitojun	struct nd_redirect *nd_rd;
23601.2Sitojun	size_t maxlen;
23611.2Sitojun	u_char *p;
23621.29Sitojun	struct sockaddr_in6 src_sa;
23631.2Sitojun
23641.35Sitojun	icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
23651.35Sitojun
23661.2Sitojun	/* if we are not router, we don't send icmp6 redirect */
23671.94Sitojun	if (!ip6_forwarding)
23681.2Sitojun		goto fail;
23691.2Sitojun
23701.2Sitojun	/* sanity check */
23711.2Sitojun	if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
23721.2Sitojun		goto fail;
23731.5Sitojun
23741.5Sitojun	/*
23751.5Sitojun	 * Address check:
23761.5Sitojun	 *  the source address must identify a neighbor, and
23771.5Sitojun	 *  the destination address must not be a multicast address
23781.5Sitojun	 *  [RFC 2461, sec 8.2]
23791.5Sitojun	 */
23801.2Sitojun	sip6 = mtod(m0, struct ip6_hdr *);
23811.29Sitojun	bzero(&src_sa, sizeof(src_sa));
23821.29Sitojun	src_sa.sin6_family = AF_INET6;
23831.29Sitojun	src_sa.sin6_len = sizeof(src_sa);
23841.29Sitojun	src_sa.sin6_addr = sip6->ip6_src;
23851.29Sitojun	if (nd6_is_addr_neighbor(&src_sa, ifp) == 0)
23861.5Sitojun		goto fail;
23871.2Sitojun	if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
23881.2Sitojun		goto fail;	/* what should we do here? */
23891.2Sitojun
23901.2Sitojun	/* rate limit */
23911.2Sitojun	if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
23921.2Sitojun		goto fail;
23931.2Sitojun
23941.2Sitojun	/*
23951.2Sitojun	 * Since we are going to append up to 1280 bytes (= IPV6_MMTU),
23961.2Sitojun	 * we almost always ask for an mbuf cluster for simplicity.
23971.2Sitojun	 * (MHLEN < IPV6_MMTU is almost always true)
23981.2Sitojun	 */
23991.23Sitojun#if IPV6_MMTU >= MCLBYTES
24001.23Sitojun# error assumption failed about IPV6_MMTU and MCLBYTES
24011.23Sitojun#endif
24021.2Sitojun	MGETHDR(m, M_DONTWAIT, MT_HEADER);
24031.23Sitojun	if (m && IPV6_MMTU >= MHLEN)
24041.23Sitojun		MCLGET(m, M_DONTWAIT);
24051.2Sitojun	if (!m)
24061.2Sitojun		goto fail;
24071.74Sitojun	m->m_pkthdr.rcvif = NULL;
24081.42Sitojun	m->m_len = 0;
24091.42Sitojun	maxlen = M_TRAILINGSPACE(m);
24101.2Sitojun	maxlen = min(IPV6_MMTU, maxlen);
24111.2Sitojun	/* just for safety */
24121.23Sitojun	if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) +
24131.23Sitojun	    ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) {
24141.2Sitojun		goto fail;
24151.23Sitojun	}
24161.2Sitojun
24171.2Sitojun	{
24181.2Sitojun		/* get ip6 linklocal address for ifp(my outgoing interface). */
24191.23Sitojun		struct in6_ifaddr *ia;
24201.23Sitojun		if ((ia = in6ifa_ifpforlinklocal(ifp,
24211.23Sitojun						 IN6_IFF_NOTREADY|
24221.23Sitojun						 IN6_IFF_ANYCAST)) == NULL)
24231.2Sitojun			goto fail;
24241.2Sitojun		ifp_ll6 = &ia->ia_addr.sin6_addr;
24251.2Sitojun	}
24261.2Sitojun
24271.2Sitojun	/* get ip6 linklocal address for the router. */
24281.2Sitojun	if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
24291.2Sitojun		struct sockaddr_in6 *sin6;
24301.2Sitojun		sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
24311.75Sitojun		nexthop = &sin6->sin6_addr;
24321.75Sitojun		if (!IN6_IS_ADDR_LINKLOCAL(nexthop))
24331.75Sitojun			nexthop = NULL;
24341.2Sitojun	} else
24351.75Sitojun		nexthop = NULL;
24361.2Sitojun
24371.2Sitojun	/* ip6 */
24381.2Sitojun	ip6 = mtod(m, struct ip6_hdr *);
24391.2Sitojun	ip6->ip6_flow = 0;
24401.13Sitojun	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
24411.13Sitojun	ip6->ip6_vfc |= IPV6_VERSION;
24421.2Sitojun	/* ip6->ip6_plen will be set later */
24431.2Sitojun	ip6->ip6_nxt = IPPROTO_ICMPV6;
24441.2Sitojun	ip6->ip6_hlim = 255;
24451.2Sitojun	/* ip6->ip6_src must be linklocal addr for my outgoing if. */
24461.2Sitojun	bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
24471.2Sitojun	bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
24481.2Sitojun
24491.2Sitojun	/* ND Redirect */
24501.2Sitojun	nd_rd = (struct nd_redirect *)(ip6 + 1);
24511.2Sitojun	nd_rd->nd_rd_type = ND_REDIRECT;
24521.2Sitojun	nd_rd->nd_rd_code = 0;
24531.2Sitojun	nd_rd->nd_rd_reserved = 0;
24541.2Sitojun	if (rt->rt_flags & RTF_GATEWAY) {
24551.2Sitojun		/*
24561.2Sitojun		 * nd_rd->nd_rd_target must be a link-local address in
24571.2Sitojun		 * better router cases.
24581.2Sitojun		 */
24591.75Sitojun		if (!nexthop)
24601.2Sitojun			goto fail;
24611.75Sitojun		bcopy(nexthop, &nd_rd->nd_rd_target,
24621.2Sitojun		      sizeof(nd_rd->nd_rd_target));
24631.2Sitojun		bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
24641.2Sitojun		      sizeof(nd_rd->nd_rd_dst));
24651.2Sitojun	} else {
24661.2Sitojun		/* make sure redtgt == reddst */
24671.75Sitojun		nexthop = &sip6->ip6_dst;
24681.2Sitojun		bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
24691.2Sitojun		      sizeof(nd_rd->nd_rd_target));
24701.2Sitojun		bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
24711.2Sitojun		      sizeof(nd_rd->nd_rd_dst));
24721.2Sitojun	}
24731.2Sitojun
24741.2Sitojun	p = (u_char *)(nd_rd + 1);
24751.2Sitojun
24761.75Sitojun	{
24771.75Sitojun		/* target lladdr option */
24781.75Sitojun		struct rtentry *rt_nexthop = NULL;
24791.75Sitojun		int len;
24801.75Sitojun		struct sockaddr_dl *sdl;
24811.75Sitojun		struct nd_opt_hdr *nd_opt;
24821.75Sitojun		char *lladdr;
24831.75Sitojun
24841.75Sitojun		rt_nexthop = nd6_lookup(nexthop, 0, ifp);
24851.75Sitojun		if (!rt_nexthop)
24861.75Sitojun			goto nolladdropt;
24871.75Sitojun		len = sizeof(*nd_opt) + ifp->if_addrlen;
24881.75Sitojun		len = (len + 7) & ~7;	/* round by 8 */
24891.75Sitojun		/* safety check */
24901.75Sitojun		if (len + (p - (u_char *)ip6) > maxlen)
24911.75Sitojun			goto nolladdropt;
24921.75Sitojun		if (!(rt_nexthop->rt_flags & RTF_GATEWAY) &&
24931.75Sitojun		    (rt_nexthop->rt_flags & RTF_LLINFO) &&
24941.75Sitojun		    (rt_nexthop->rt_gateway->sa_family == AF_LINK) &&
24951.75Sitojun		    (sdl = (struct sockaddr_dl *)rt_nexthop->rt_gateway) &&
24961.75Sitojun		    sdl->sdl_alen) {
24971.75Sitojun			nd_opt = (struct nd_opt_hdr *)p;
24981.75Sitojun			nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
24991.75Sitojun			nd_opt->nd_opt_len = len >> 3;
25001.75Sitojun			lladdr = (char *)(nd_opt + 1);
25011.75Sitojun			bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
25021.75Sitojun			p += len;
25031.75Sitojun		}
25041.2Sitojun	}
25051.75Sitojun  nolladdropt:;
25061.2Sitojun
25071.2Sitojun	m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
25081.2Sitojun
25091.2Sitojun	/* just to be safe */
25101.2Sitojun	if (m0->m_flags & M_DECRYPTED)
25111.2Sitojun		goto noredhdropt;
25121.23Sitojun	if (p - (u_char *)ip6 > maxlen)
25131.23Sitojun		goto noredhdropt;
25141.2Sitojun
25151.91Sitojun	{
25161.91Sitojun		/* redirected header option */
25171.91Sitojun		int len;
25181.91Sitojun		struct nd_opt_rd_hdr *nd_opt_rh;
25191.2Sitojun
25201.91Sitojun		/*
25211.91Sitojun		 * compute the maximum size for icmp6 redirect header option.
25221.91Sitojun		 * XXX room for auth header?
25231.91Sitojun		 */
25241.91Sitojun		len = maxlen - (p - (u_char *)ip6);
25251.91Sitojun		len &= ~7;
25261.2Sitojun
25271.91Sitojun		/*
25281.91Sitojun		 * Redirected header option spec (RFC2461 4.6.3) talks nothing
25291.91Sitojun		 * about padding/truncate rule for the original IP packet.
25301.91Sitojun		 * From the discussion on IPv6imp in Feb 1999,
25311.91Sitojun		 * the consensus was:
25321.91Sitojun		 * - "attach as much as possible" is the goal
25331.91Sitojun		 * - pad if not aligned (original size can be guessed by
25341.91Sitojun		 *   original ip6 header)
25351.91Sitojun		 * Following code adds the padding if it is simple enough,
25361.91Sitojun		 * and truncates if not.
25371.91Sitojun		 */
25381.91Sitojun		if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
25391.91Sitojun			/* not enough room, truncate */
25401.91Sitojun			m_adj(m0, (len - sizeof(*nd_opt_rh)) -
25411.91Sitojun			    m0->m_pkthdr.len);
25421.91Sitojun		} else {
25431.91Sitojun			/*
25441.115Srpaulo			 * enough room, truncate if not aligned.
25451.91Sitojun			 * we don't pad here for simplicity.
25461.91Sitojun			 */
25471.91Sitojun			size_t extra;
25481.2Sitojun
25491.91Sitojun			extra = m0->m_pkthdr.len % 8;
25501.91Sitojun			if (extra) {
25511.2Sitojun				/* truncate */
25521.91Sitojun				m_adj(m0, -extra);
25531.2Sitojun			}
25541.91Sitojun			len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
25551.2Sitojun		}
25561.91Sitojun
25571.91Sitojun		nd_opt_rh = (struct nd_opt_rd_hdr *)p;
25581.91Sitojun		bzero(nd_opt_rh, sizeof(*nd_opt_rh));
25591.91Sitojun		nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
25601.91Sitojun		nd_opt_rh->nd_opt_rh_len = len >> 3;
25611.91Sitojun		p += sizeof(*nd_opt_rh);
25621.91Sitojun		m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
25631.91Sitojun
25641.91Sitojun		/* connect m0 to m */
25651.95Sitojun		m->m_pkthdr.len += m0->m_pkthdr.len;
25661.91Sitojun		m_cat(m, m0);
25671.91Sitojun		m0 = NULL;
25681.2Sitojun	}
25691.91Sitojunnoredhdropt:
25701.89Sitojun	if (m0) {
25711.89Sitojun		m_freem(m0);
25721.89Sitojun		m0 = NULL;
25731.89Sitojun	}
25741.2Sitojun
25751.113Srpaulo	/* XXX: clear embedded link IDs in the inner header */
25761.113Srpaulo	in6_clearscope(&sip6->ip6_src);
25771.113Srpaulo	in6_clearscope(&sip6->ip6_dst);
25781.113Srpaulo	in6_clearscope(&nd_rd->nd_rd_target);
25791.113Srpaulo	in6_clearscope(&nd_rd->nd_rd_dst);
25801.2Sitojun
25811.2Sitojun	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
25821.2Sitojun
25831.2Sitojun	nd_rd->nd_rd_cksum = 0;
25841.2Sitojun	nd_rd->nd_rd_cksum
25851.2Sitojun		= in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen));
25861.2Sitojun
25871.2Sitojun	/* send the packet to outside... */
25881.97Sjonathan	if (ip6_output(m, NULL, NULL, 0,
25891.98Sitojun		(struct ip6_moptions *)NULL, (struct socket *)NULL, NULL) != 0)
25901.72Sitojun		icmp6_ifstat_inc(ifp, ifs6_out_error);
25911.72Sitojun
25921.68Sitojun	icmp6_ifstat_inc(ifp, ifs6_out_msg);
25931.68Sitojun	icmp6_ifstat_inc(ifp, ifs6_out_redirect);
25941.2Sitojun	icmp6stat.icp6s_outhist[ND_REDIRECT]++;
25951.2Sitojun
25961.2Sitojun	return;
25971.2Sitojun
25981.2Sitojunfail:
25991.2Sitojun	if (m)
26001.2Sitojun		m_freem(m);
26011.2Sitojun	if (m0)
26021.2Sitojun		m_freem(m0);
26031.2Sitojun}
26041.2Sitojun
26051.2Sitojun/*
26061.2Sitojun * ICMPv6 socket option processing.
26071.2Sitojun */
26081.2Sitojunint
26091.2Sitojunicmp6_ctloutput(op, so, level, optname, mp)
26101.2Sitojun	int op;
26111.2Sitojun	struct socket *so;
26121.2Sitojun	int level, optname;
26131.2Sitojun	struct mbuf **mp;
26141.2Sitojun{
26151.2Sitojun	int error = 0;
26161.56Sitojun	int optlen;
26171.56Sitojun	struct in6pcb *in6p = sotoin6pcb(so);
26181.56Sitojun	struct mbuf *m = *mp;
26191.56Sitojun
26201.56Sitojun	optlen = m ? m->m_len : 0;
26211.2Sitojun
26221.2Sitojun	if (level != IPPROTO_ICMPV6) {
26231.2Sitojun		if (op == PRCO_SETOPT && m)
26241.2Sitojun			(void)m_free(m);
26251.56Sitojun		return EINVAL;
26261.56Sitojun	}
26271.56Sitojun
26281.56Sitojun	switch (op) {
26291.56Sitojun	case PRCO_SETOPT:
26301.56Sitojun		switch (optname) {
26311.56Sitojun		case ICMP6_FILTER:
26321.56Sitojun		    {
26331.56Sitojun			struct icmp6_filter *p;
26341.56Sitojun
26351.56Sitojun			if (optlen != sizeof(*p)) {
26361.56Sitojun				error = EMSGSIZE;
26371.56Sitojun				break;
26381.56Sitojun			}
26391.56Sitojun			p = mtod(m, struct icmp6_filter *);
26401.56Sitojun			if (!p || !in6p->in6p_icmp6filt) {
26411.56Sitojun				error = EINVAL;
26421.56Sitojun				break;
26431.56Sitojun			}
26441.56Sitojun			bcopy(p, in6p->in6p_icmp6filt,
26451.2Sitojun				sizeof(struct icmp6_filter));
26461.56Sitojun			error = 0;
26471.56Sitojun			break;
26481.56Sitojun		    }
26491.56Sitojun
26501.56Sitojun		default:
26511.56Sitojun			error = ENOPROTOOPT;
26521.56Sitojun			break;
26531.56Sitojun		}
26541.56Sitojun		if (m)
26551.56Sitojun			(void)m_freem(m);
26561.56Sitojun		break;
26571.56Sitojun
26581.56Sitojun	case PRCO_GETOPT:
26591.56Sitojun		switch (optname) {
26601.56Sitojun		case ICMP6_FILTER:
26611.56Sitojun		    {
26621.56Sitojun			struct icmp6_filter *p;
26631.56Sitojun
26641.56Sitojun			if (!in6p->in6p_icmp6filt) {
26651.56Sitojun				error = EINVAL;
26661.56Sitojun				break;
26671.56Sitojun			}
26681.56Sitojun			*mp = m = m_get(M_WAIT, MT_SOOPTS);
26691.56Sitojun			m->m_len = sizeof(struct icmp6_filter);
26701.56Sitojun			p = mtod(m, struct icmp6_filter *);
26711.56Sitojun			bcopy(in6p->in6p_icmp6filt, p,
26721.2Sitojun				sizeof(struct icmp6_filter));
26731.56Sitojun			error = 0;
26741.56Sitojun			break;
26751.56Sitojun		    }
26761.56Sitojun
26771.56Sitojun		default:
26781.56Sitojun			error = ENOPROTOOPT;
26791.56Sitojun			break;
26801.56Sitojun		}
26811.56Sitojun		break;
26821.2Sitojun	}
26831.2Sitojun
26841.86Sitojun	return (error);
26851.2Sitojun}
26861.2Sitojun
26871.2Sitojun/*
26881.2Sitojun * Perform rate limit check.
26891.2Sitojun * Returns 0 if it is okay to send the icmp6 packet.
26901.2Sitojun * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
26911.2Sitojun * limitation.
26921.35Sitojun *
26931.2Sitojun * XXX per-destination/type check necessary?
26941.2Sitojun */
26951.2Sitojunstatic int
26961.121.4.1Syamticmp6_ratelimit(
26971.121.4.2Syamt	const struct in6_addr *dst,	/* not used at this moment */
26981.121.4.2Syamt	const int type,		/* not used at this moment */
26991.121.4.2Syamt	const int code)		/* not used at this moment */
27001.2Sitojun{
27011.35Sitojun	int ret;
27021.35Sitojun
27031.67Sitojun	ret = 0;	/* okay to send */
27041.2Sitojun
27051.35Sitojun	/* PPS limit */
27061.37Sitojun	if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count,
27071.37Sitojun	    icmp6errppslim)) {
27081.37Sitojun		/* The packet is subject to rate limit */
27091.35Sitojun		ret++;
27101.35Sitojun	}
27111.35Sitojun
27121.35Sitojun	return ret;
27131.2Sitojun}
27141.8Sitojun
27151.8Sitojunstatic struct rtentry *
27161.8Sitojunicmp6_mtudisc_clone(dst)
27171.8Sitojun	struct sockaddr *dst;
27181.8Sitojun{
27191.8Sitojun	struct rtentry *rt;
27201.8Sitojun	int    error;
27211.8Sitojun
27221.8Sitojun	rt = rtalloc1(dst, 1);
27231.8Sitojun	if (rt == 0)
27241.8Sitojun		return NULL;
27251.29Sitojun
27261.8Sitojun	/* If we didn't get a host route, allocate one */
27271.8Sitojun	if ((rt->rt_flags & RTF_HOST) == 0) {
27281.8Sitojun		struct rtentry *nrt;
27291.8Sitojun
27301.29Sitojun		error = rtrequest((int) RTM_ADD, dst,
27311.8Sitojun		    (struct sockaddr *) rt->rt_gateway,
27321.29Sitojun		    (struct sockaddr *) 0,
27331.8Sitojun		    RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC, &nrt);
27341.8Sitojun		if (error) {
27351.8Sitojun			rtfree(rt);
27361.8Sitojun			return NULL;
27371.8Sitojun		}
27381.8Sitojun		nrt->rt_rmx = rt->rt_rmx;
27391.8Sitojun		rtfree(rt);
27401.8Sitojun		rt = nrt;
27411.8Sitojun	}
27421.8Sitojun	error = rt_timer_add(rt, icmp6_mtudisc_timeout,
27431.8Sitojun			icmp6_mtudisc_timeout_q);
27441.8Sitojun	if (error) {
27451.8Sitojun		rtfree(rt);
27461.8Sitojun		return NULL;
27471.8Sitojun	}
27481.8Sitojun
27491.8Sitojun	return rt;	/* caller need to call rtfree() */
27501.8Sitojun}
27511.8Sitojun
27521.8Sitojunstatic void
27531.121.4.2Syamticmp6_mtudisc_timeout(struct rtentry *rt, struct rttimer *r)
27541.8Sitojun{
27551.8Sitojun	if (rt == NULL)
27561.8Sitojun		panic("icmp6_mtudisc_timeout: bad route to timeout");
27571.29Sitojun	if ((rt->rt_flags & (RTF_DYNAMIC | RTF_HOST)) ==
27581.8Sitojun	    (RTF_DYNAMIC | RTF_HOST)) {
27591.8Sitojun		rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
27601.8Sitojun		    rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
27611.8Sitojun	} else {
27621.78Sitojun		if (!(rt->rt_rmx.rmx_locks & RTV_MTU))
27631.78Sitojun			rt->rt_rmx.rmx_mtu = 0;
27641.55Sitojun	}
27651.55Sitojun}
27661.55Sitojun
27671.55Sitojunstatic void
27681.121.4.2Syamticmp6_redirect_timeout(struct rtentry *rt, struct rttimer *r)
27691.55Sitojun{
27701.55Sitojun	if (rt == NULL)
27711.55Sitojun		panic("icmp6_redirect_timeout: bad route to timeout");
27721.55Sitojun	if ((rt->rt_flags & (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) ==
27731.55Sitojun	    (RTF_GATEWAY | RTF_DYNAMIC | RTF_HOST)) {
27741.55Sitojun		rtrequest((int) RTM_DELETE, (struct sockaddr *)rt_key(rt),
27751.55Sitojun		    rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0);
27761.8Sitojun	}
27771.8Sitojun}
27781.34Smrg
27791.103Satatat/*
27801.103Satatat * sysctl helper routine for the net.inet6.icmp6.nd6 nodes.  silly?
27811.103Satatat */
27821.103Satatatstatic int
27831.103Satatatsysctl_net_inet6_icmp6_nd6(SYSCTLFN_ARGS)
27841.2Sitojun{
27851.121.4.1Syamt	(void)&name;
27861.121.4.1Syamt	(void)&l;
27871.121.4.1Syamt	(void)&oname;
27881.2Sitojun
27891.104Slha	if (namelen != 0)
27901.103Satatat		return (EINVAL);
27911.103Satatat
27921.104Slha	return (nd6_sysctl(rnode->sysctl_num, oldp, oldlenp,
27931.109Schristos	    /*XXXUNCONST*/
27941.109Schristos	    __UNCONST(newp), newlen));
27951.103Satatat}
27961.2Sitojun
27971.103SatatatSYSCTL_SETUP(sysctl_net_inet6_icmp6_setup,
27981.103Satatat	     "sysctl net.inet6.icmp6 subtree setup")
27991.103Satatat{
28001.115Srpaulo	extern int nd6_maxqueuelen; /* defined in nd6.c */
28011.2Sitojun
28021.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28031.105Satatat		       CTLFLAG_PERMANENT,
28041.103Satatat		       CTLTYPE_NODE, "net", NULL,
28051.103Satatat		       NULL, 0, NULL, 0,
28061.103Satatat		       CTL_NET, CTL_EOL);
28071.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28081.105Satatat		       CTLFLAG_PERMANENT,
28091.103Satatat		       CTLTYPE_NODE, "inet6", NULL,
28101.103Satatat		       NULL, 0, NULL, 0,
28111.103Satatat		       CTL_NET, PF_INET6, CTL_EOL);
28121.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28131.105Satatat		       CTLFLAG_PERMANENT,
28141.107Satatat		       CTLTYPE_NODE, "icmp6",
28151.107Satatat		       SYSCTL_DESCR("ICMPv6 related settings"),
28161.103Satatat		       NULL, 0, NULL, 0,
28171.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6, CTL_EOL);
28181.103Satatat
28191.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28201.105Satatat		       CTLFLAG_PERMANENT,
28211.107Satatat		       CTLTYPE_STRUCT, "stats",
28221.107Satatat		       SYSCTL_DESCR("ICMPv6 transmission statistics"),
28231.103Satatat		       NULL, 0, &icmp6stat, sizeof(icmp6stat),
28241.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28251.103Satatat		       ICMPV6CTL_STATS, CTL_EOL);
28261.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28271.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28281.107Satatat		       CTLTYPE_INT, "rediraccept",
28291.107Satatat		       SYSCTL_DESCR("Accept and process redirect messages"),
28301.103Satatat		       NULL, 0, &icmp6_rediraccept, 0,
28311.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28321.103Satatat		       ICMPV6CTL_REDIRACCEPT, CTL_EOL);
28331.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28341.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28351.107Satatat		       CTLTYPE_INT, "redirtimeout",
28361.107Satatat		       SYSCTL_DESCR("Redirect generated route lifetime"),
28371.103Satatat		       NULL, 0, &icmp6_redirtimeout, 0,
28381.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28391.103Satatat		       ICMPV6CTL_REDIRTIMEOUT, CTL_EOL);
28401.103Satatat#if 0 /* obsoleted */
28411.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28421.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28431.103Satatat		       CTLTYPE_INT, "errratelimit", NULL,
28441.103Satatat		       NULL, 0, &icmp6_errratelimit, 0,
28451.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28461.103Satatat		       ICMPV6CTL_ERRRATELIMIT, CTL_EOL);
28471.103Satatat#endif
28481.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28491.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28501.107Satatat		       CTLTYPE_INT, "nd6_prune",
28511.107Satatat		       SYSCTL_DESCR("Neighbor discovery prune interval"),
28521.103Satatat		       NULL, 0, &nd6_prune, 0,
28531.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28541.103Satatat		       ICMPV6CTL_ND6_PRUNE, CTL_EOL);
28551.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28561.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28571.107Satatat		       CTLTYPE_INT, "nd6_delay",
28581.107Satatat		       SYSCTL_DESCR("First probe delay time"),
28591.103Satatat		       NULL, 0, &nd6_delay, 0,
28601.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28611.103Satatat		       ICMPV6CTL_ND6_DELAY, CTL_EOL);
28621.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28631.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28641.107Satatat		       CTLTYPE_INT, "nd6_umaxtries",
28651.107Satatat		       SYSCTL_DESCR("Number of unicast discovery attempts"),
28661.103Satatat		       NULL, 0, &nd6_umaxtries, 0,
28671.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28681.103Satatat		       ICMPV6CTL_ND6_UMAXTRIES, CTL_EOL);
28691.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28701.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28711.107Satatat		       CTLTYPE_INT, "nd6_mmaxtries",
28721.107Satatat		       SYSCTL_DESCR("Number of multicast discovery attempts"),
28731.103Satatat		       NULL, 0, &nd6_mmaxtries, 0,
28741.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28751.103Satatat		       ICMPV6CTL_ND6_MMAXTRIES, CTL_EOL);
28761.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28771.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28781.107Satatat		       CTLTYPE_INT, "nd6_useloopback",
28791.107Satatat		       SYSCTL_DESCR("Use loopback interface for local traffic"),
28801.103Satatat		       NULL, 0, &nd6_useloopback, 0,
28811.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28821.103Satatat		       ICMPV6CTL_ND6_USELOOPBACK, CTL_EOL);
28831.103Satatat#if 0 /* obsoleted */
28841.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28851.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28861.103Satatat		       CTLTYPE_INT, "nd6_proxyall", NULL,
28871.103Satatat		       NULL, 0, &nd6_proxyall, 0,
28881.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28891.103Satatat		       ICMPV6CTL_ND6_PROXYALL, CTL_EOL);
28901.103Satatat#endif
28911.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28921.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
28931.107Satatat		       CTLTYPE_INT, "nodeinfo",
28941.107Satatat		       SYSCTL_DESCR("Respond to node information requests"),
28951.103Satatat		       NULL, 0, &icmp6_nodeinfo, 0,
28961.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
28971.103Satatat		       ICMPV6CTL_NODEINFO, CTL_EOL);
28981.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
28991.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
29001.107Satatat		       CTLTYPE_INT, "errppslimit",
29011.107Satatat		       SYSCTL_DESCR("Maximum ICMP errors sent per second"),
29021.103Satatat		       NULL, 0, &icmp6errppslim, 0,
29031.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29041.103Satatat		       ICMPV6CTL_ERRPPSLIMIT, CTL_EOL);
29051.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
29061.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
29071.107Satatat		       CTLTYPE_INT, "nd6_maxnudhint",
29081.107Satatat		       SYSCTL_DESCR("Maximum neighbor unreachable hint count"),
29091.103Satatat		       NULL, 0, &nd6_maxnudhint, 0,
29101.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29111.103Satatat		       ICMPV6CTL_ND6_MAXNUDHINT, CTL_EOL);
29121.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
29131.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
29141.107Satatat		       CTLTYPE_INT, "mtudisc_hiwat",
29151.107Satatat		       SYSCTL_DESCR("Low mark on MTU Discovery route timers"),
29161.103Satatat		       NULL, 0, &icmp6_mtudisc_hiwat, 0,
29171.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29181.103Satatat		       ICMPV6CTL_MTUDISC_HIWAT, CTL_EOL);
29191.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
29201.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
29211.107Satatat		       CTLTYPE_INT, "mtudisc_lowat",
29221.107Satatat		       SYSCTL_DESCR("Low mark on MTU Discovery route timers"),
29231.103Satatat		       NULL, 0, &icmp6_mtudisc_lowat, 0,
29241.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29251.103Satatat		       ICMPV6CTL_MTUDISC_LOWAT, CTL_EOL);
29261.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
29271.105Satatat		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
29281.107Satatat		       CTLTYPE_INT, "nd6_debug",
29291.107Satatat		       SYSCTL_DESCR("Enable neighbor discovery debug output"),
29301.103Satatat		       NULL, 0, &nd6_debug, 0,
29311.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29321.103Satatat		       ICMPV6CTL_ND6_DEBUG, CTL_EOL);
29331.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
29341.105Satatat		       CTLFLAG_PERMANENT,
29351.107Satatat		       CTLTYPE_STRUCT, "nd6_drlist",
29361.107Satatat		       SYSCTL_DESCR("Default router list"),
29371.103Satatat		       sysctl_net_inet6_icmp6_nd6, 0, NULL, 0,
29381.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29391.103Satatat		       ICMPV6CTL_ND6_DRLIST, CTL_EOL);
29401.105Satatat	sysctl_createv(clog, 0, NULL, NULL,
29411.105Satatat		       CTLFLAG_PERMANENT,
29421.107Satatat		       CTLTYPE_STRUCT, "nd6_prlist",
29431.107Satatat		       SYSCTL_DESCR("Prefix list"),
29441.103Satatat		       sysctl_net_inet6_icmp6_nd6, 0, NULL, 0,
29451.103Satatat		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29461.103Satatat		       ICMPV6CTL_ND6_PRLIST, CTL_EOL);
29471.115Srpaulo	sysctl_createv(clog, 0, NULL, NULL,
29481.115Srpaulo		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
29491.115Srpaulo		       CTLTYPE_INT, "maxqueuelen",
29501.115Srpaulo		       SYSCTL_DESCR("max packet queue len for a unresolved ND"),
29511.115Srpaulo		       NULL, 1, &nd6_maxqueuelen, 0,
29521.115Srpaulo		       CTL_NET, PF_INET6, IPPROTO_ICMPV6,
29531.115Srpaulo		       ICMPV6CTL_ND6_MAXQLEN, CTL_EOL);
29541.2Sitojun}
2955