icmp6.c revision 55009
153541Sshin/*
253541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
353541Sshin * All rights reserved.
453541Sshin *
553541Sshin * Redistribution and use in source and binary forms, with or without
653541Sshin * modification, are permitted provided that the following conditions
753541Sshin * are met:
853541Sshin * 1. Redistributions of source code must retain the above copyright
953541Sshin *    notice, this list of conditions and the following disclaimer.
1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright
1153541Sshin *    notice, this list of conditions and the following disclaimer in the
1253541Sshin *    documentation and/or other materials provided with the distribution.
1353541Sshin * 3. Neither the name of the project nor the names of its contributors
1453541Sshin *    may be used to endorse or promote products derived from this software
1553541Sshin *    without specific prior written permission.
1653541Sshin *
1753541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2053541Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2753541Sshin * SUCH DAMAGE.
2853541Sshin *
2953541Sshin * $FreeBSD: head/sys/netinet6/icmp6.c 55009 1999-12-22 19:13:38Z shin $
3053541Sshin */
3153541Sshin
3253541Sshin/*
3353541Sshin * Copyright (c) 1982, 1986, 1988, 1993
3453541Sshin *	The Regents of the University of California.  All rights reserved.
3553541Sshin *
3653541Sshin * Redistribution and use in source and binary forms, with or without
3753541Sshin * modification, are permitted provided that the following conditions
3853541Sshin * are met:
3953541Sshin * 1. Redistributions of source code must retain the above copyright
4053541Sshin *    notice, this list of conditions and the following disclaimer.
4153541Sshin * 2. Redistributions in binary form must reproduce the above copyright
4253541Sshin *    notice, this list of conditions and the following disclaimer in the
4353541Sshin *    documentation and/or other materials provided with the distribution.
4453541Sshin * 3. All advertising materials mentioning features or use of this software
4553541Sshin *    must display the following acknowledgement:
4653541Sshin *	This product includes software developed by the University of
4753541Sshin *	California, Berkeley and its contributors.
4853541Sshin * 4. Neither the name of the University nor the names of its contributors
4953541Sshin *    may be used to endorse or promote products derived from this software
5053541Sshin *    without specific prior written permission.
5153541Sshin *
5253541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5353541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5453541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5553541Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5653541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5753541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5853541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5953541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6053541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6153541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6253541Sshin * SUCH DAMAGE.
6353541Sshin *
6453541Sshin *	@(#)ip_icmp.c	8.2 (Berkeley) 1/4/94
6553541Sshin */
6653541Sshin
6755009Sshin#include "opt_ipsec.h"
6853541Sshin
6953541Sshin#include <sys/param.h>
7053541Sshin#include <sys/systm.h>
7153541Sshin#include <sys/malloc.h>
7253541Sshin#include <sys/mbuf.h>
7353541Sshin#include <sys/protosw.h>
7453541Sshin#include <sys/socket.h>
7553541Sshin#include <sys/socketvar.h>
7653541Sshin#include <sys/time.h>
7753541Sshin#include <sys/kernel.h>
7853541Sshin#include <sys/syslog.h>
7953541Sshin#include <sys/domain.h>
8053541Sshin
8153541Sshin#include <net/if.h>
8253541Sshin#include <net/route.h>
8353541Sshin#include <net/if_dl.h>
8453541Sshin#include <net/if_types.h>
8553541Sshin
8653541Sshin#include <netinet/in.h>
8753541Sshin#include <netinet/in_var.h>
8853541Sshin#include <netinet6/ip6.h>
8953541Sshin#include <netinet6/ip6_var.h>
9053541Sshin#include <netinet6/icmp6.h>
9153541Sshin#include <netinet6/mld6_var.h>
9253541Sshin#include <netinet/in_pcb.h>
9353541Sshin#include <netinet6/nd6.h>
9453541Sshin#include <netinet6/in6_ifattach.h>
9553541Sshin#include <netinet6/ip6protosw.h>
9653541Sshin
9753541Sshin#ifdef IPSEC
9853541Sshin#include <netinet6/ipsec.h>
9955009Sshin#include <netinet6/ah.h>
10053541Sshin#include <netinet6/ipsec6.h>
10155009Sshin#include <netinet6/ah6.h>
10253541Sshin#include <netkey/key.h>
10355009Sshin#ifdef IPSEC_DEBUG
10453541Sshin#include <netkey/key_debug.h>
10553541Sshin#else
10655009Sshin#define KEYDEBUG(lev,arg)
10755009Sshin#endif
10853541Sshin#endif /* IPSEC */
10953541Sshin
11054263Sshin#include "faith.h"
11153541Sshin
11253541Sshin#include <net/net_osdep.h>
11353541Sshin
11453541Sshinextern struct	domain inet6domain;
11553541Sshinextern struct	ip6protosw inet6sw[];
11653541Sshinextern u_char	ip6_protox[];
11753541Sshin
11853541Sshinstruct	icmp6stat icmp6stat;
11953541Sshin
12053541Sshinextern struct	inpcbhead ripcb;
12153541Sshinextern u_int	icmp6errratelim;
12253541Sshin
12353541Sshinstatic int	icmp6_rip6_input __P((struct mbuf **, int));
12453541Sshinstatic int	icmp6_ratelimit __P((const struct in6_addr *, const int, const int));
12553541Sshinstatic const char	*icmp6_redirect_diag __P((struct in6_addr *,
12653541Sshin						  struct in6_addr *,
12753541Sshin						  struct in6_addr *));
12853541Sshinstatic struct	mbuf *ni6_input __P((struct mbuf *, int));
12953541Sshinstatic int	ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *,
13053541Sshin			       struct ifnet **));
13153541Sshinstatic int	ni6_store_addrs __P((struct icmp6_nodeinfo *,
13253541Sshin				     struct icmp6_nodeinfo *,
13353541Sshin				     struct ifnet *, int));
13453541Sshin
13553541Sshin#ifdef COMPAT_RFC1885
13653541Sshinstatic struct	route_in6 icmp6_reflect_rt;
13753541Sshin#endif
13853541Sshinstatic struct	timeval icmp6_nextsend = {0, 0};
13953541Sshin
14053541Sshinvoid
14153541Sshinicmp6_init()
14253541Sshin{
14353541Sshin	mld6_init();
14453541Sshin}
14553541Sshin
14653541Sshin/*
14753541Sshin * Generate an error packet of type error in response to bad IP6 packet.
14853541Sshin */
14953541Sshinvoid
15053541Sshinicmp6_error(m, type, code, param)
15153541Sshin	struct mbuf *m;
15253541Sshin	int type, code, param;
15353541Sshin{
15453541Sshin	struct ip6_hdr *oip6, *nip6;
15553541Sshin	struct icmp6_hdr *icmp6;
15653541Sshin	u_int prep;
15753541Sshin	int off;
15853541Sshin	u_char nxt;
15953541Sshin
16053541Sshin	icmp6stat.icp6s_error++;
16153541Sshin
16253541Sshin	if (m->m_flags & M_DECRYPTED)
16353541Sshin		goto freeit;
16453541Sshin
16553541Sshin	oip6 = mtod(m, struct ip6_hdr *);
16653541Sshin
16753541Sshin	/*
16853541Sshin	 * Multicast destination check. For unrecognized option errors,
16953541Sshin	 * this check has already done in ip6_unknown_opt(), so we can
17053541Sshin	 * check only for other errors.
17153541Sshin	 */
17253541Sshin	if ((m->m_flags & (M_BCAST|M_MCAST) ||
17353541Sshin	     IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) &&
17453541Sshin	    (type != ICMP6_PACKET_TOO_BIG &&
17553541Sshin	     (type != ICMP6_PARAM_PROB ||
17653541Sshin	      code != ICMP6_PARAMPROB_OPTION)))
17753541Sshin		goto freeit;
17853541Sshin
17953541Sshin	/* Source address check. XXX: the case of anycast source? */
18053541Sshin	if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) ||
18153541Sshin	    IN6_IS_ADDR_MULTICAST(&oip6->ip6_src))
18253541Sshin		goto freeit;
18353541Sshin
18453541Sshin	/*
18553541Sshin	 * If the erroneous packet is also an ICMP error, discard it.
18653541Sshin	 */
18753541Sshin	IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), );
18853541Sshin	off = sizeof(struct ip6_hdr);
18953541Sshin	nxt = oip6->ip6_nxt;
19053541Sshin	while(1) {		/* XXX: should avoid inf. loop explicitly? */
19153541Sshin		struct ip6_ext *ip6e;
19253541Sshin		struct icmp6_hdr *icp;
19353541Sshin
19453541Sshin		switch(nxt) {
19553541Sshin		case IPPROTO_IPV6:
19653541Sshin		case IPPROTO_IPV4:
19753541Sshin		case IPPROTO_UDP:
19853541Sshin		case IPPROTO_TCP:
19953541Sshin		case IPPROTO_ESP:
20053541Sshin		case IPPROTO_FRAGMENT:
20153541Sshin			/*
20253541Sshin			 * ICMPv6 error must not be fragmented.
20353541Sshin			 * XXX: but can we trust the sender?
20453541Sshin			 */
20553541Sshin		default:
20653541Sshin			/* What if unknown header followed by ICMP error? */
20753541Sshin			goto generate;
20853541Sshin		case IPPROTO_ICMPV6:
20953541Sshin			IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
21053541Sshin			icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
21153541Sshin			if (icp->icmp6_type < ICMP6_ECHO_REQUEST
21253541Sshin			 || icp->icmp6_type == ND_REDIRECT) {
21353541Sshin				/*
21453541Sshin				 * ICMPv6 error
21553541Sshin				 * Special case: for redirect (which is
21653541Sshin				 * informational) we must not send icmp6 error.
21753541Sshin				 */
21853541Sshin				icmp6stat.icp6s_canterror++;
21953541Sshin				goto freeit;
22053541Sshin			} else {
22153541Sshin				/* ICMPv6 informational */
22253541Sshin				goto generate;
22353541Sshin			}
22453541Sshin		case IPPROTO_HOPOPTS:
22553541Sshin		case IPPROTO_DSTOPTS:
22653541Sshin		case IPPROTO_ROUTING:
22753541Sshin		case IPPROTO_AH:
22853541Sshin			IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), );
22953541Sshin			ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
23053541Sshin			if (nxt == IPPROTO_AH)
23153541Sshin				off += (ip6e->ip6e_len + 2) << 2;
23253541Sshin			else
23353541Sshin				off += (ip6e->ip6e_len + 1) << 3;
23453541Sshin			nxt = ip6e->ip6e_nxt;
23553541Sshin			break;
23653541Sshin		}
23753541Sshin	}
23853541Sshin
23953541Sshin  freeit:
24053541Sshin	/*
24153541Sshin	 * If we can't tell wheter or not we can generate ICMP6, free it.
24253541Sshin	 */
24353541Sshin	m_freem(m);
24453541Sshin	return;
24553541Sshin
24653541Sshin  generate:
24753541Sshin	oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
24853541Sshin
24953541Sshin	/* Finally, do rate limitation check. */
25053541Sshin	if (icmp6_ratelimit(&oip6->ip6_src, type, code)) {
25153541Sshin		icmp6stat.icp6s_toofreq++;
25253541Sshin		goto freeit;
25353541Sshin	}
25453541Sshin
25553541Sshin	/*
25653541Sshin	 * OK, ICMP6 can be generated.
25753541Sshin	 */
25853541Sshin
25953541Sshin	if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN)
26053541Sshin		m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len);
26153541Sshin
26253541Sshin	prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr);
26353541Sshin	M_PREPEND(m, prep, M_DONTWAIT);
26453541Sshin	if (m && m->m_len < prep)
26553541Sshin		m = m_pullup(m, prep);
26653541Sshin	if (m == NULL) {
26753541Sshin		printf("ENOBUFS in icmp6_error %d\n", __LINE__);
26853541Sshin		return;
26953541Sshin	}
27053541Sshin
27153541Sshin	nip6 = mtod(m, struct ip6_hdr *);
27253541Sshin	nip6->ip6_src  = oip6->ip6_src;
27353541Sshin	nip6->ip6_dst  = oip6->ip6_dst;
27453541Sshin
27553541Sshin	if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
27653541Sshin		oip6->ip6_src.s6_addr16[1] = 0;
27753541Sshin	if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
27853541Sshin		oip6->ip6_dst.s6_addr16[1] = 0;
27953541Sshin
28053541Sshin	icmp6 = (struct icmp6_hdr *)(nip6 + 1);
28153541Sshin	icmp6->icmp6_type = type;
28253541Sshin	icmp6->icmp6_code = code;
28353541Sshin	icmp6->icmp6_pptr = htonl((u_int32_t)param);
28453541Sshin
28553541Sshin	icmp6stat.icp6s_outhist[type]++;
28653541Sshin	icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/
28753541Sshin}
28853541Sshin
28953541Sshin/*
29053541Sshin * Process a received ICMP6 message.
29153541Sshin */
29253541Sshinint
29353541Sshinicmp6_input(mp, offp, proto)
29453541Sshin	struct mbuf **mp;
29553541Sshin	int *offp, proto;
29653541Sshin{
29753541Sshin	struct mbuf *m = *mp, *n;
29853541Sshin	struct ip6_hdr *ip6, *nip6;
29953541Sshin	struct icmp6_hdr *icmp6, *nicmp6;
30053541Sshin	int off = *offp;
30153541Sshin	int icmp6len = m->m_pkthdr.len - *offp;
30253541Sshin	int code, sum, noff;
30353541Sshin	struct sockaddr_in6 icmp6src;
30453541Sshin
30553541Sshin	IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE);
30653541Sshin	/* m might change if M_LOOP. So, call mtod after this */
30753541Sshin
30853541Sshin	/*
30953541Sshin	 * Locate icmp6 structure in mbuf, and check
31053541Sshin	 * that not corrupted and of at least minimum length
31153541Sshin	 */
31253541Sshin
31353541Sshin	ip6 = mtod(m, struct ip6_hdr *);
31453541Sshin	if (icmp6len < sizeof(struct icmp6_hdr)) {
31553541Sshin		icmp6stat.icp6s_tooshort++;
31653541Sshin		goto freeit;
31753541Sshin	}
31853541Sshin
31953541Sshin	/*
32053541Sshin	 * calculate the checksum
32153541Sshin	 */
32253541Sshin
32353541Sshin	icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
32453541Sshin	code = icmp6->icmp6_code;
32553541Sshin
32653541Sshin	if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) {
32753541Sshin		log(LOG_ERR,
32853541Sshin		    "ICMP6 checksum error(%d|%x) %s\n",
32953541Sshin		    icmp6->icmp6_type,
33053541Sshin		    sum,
33153541Sshin		    ip6_sprintf(&ip6->ip6_src));
33253541Sshin		icmp6stat.icp6s_checksum++;
33353541Sshin		goto freeit;
33453541Sshin	}
33553541Sshin
33653541Sshin#if defined(NFAITH) && 0 < NFAITH
33753541Sshin	if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) {
33853541Sshin		/*
33953541Sshin		 * Deliver very specific ICMP6 type only.
34053541Sshin		 * This is important to deilver TOOBIG.  Otherwise PMTUD
34153541Sshin		 * will not work.
34253541Sshin		 */
34353541Sshin		switch (icmp6->icmp6_type) {
34453541Sshin		case ICMP6_DST_UNREACH:
34553541Sshin		case ICMP6_PACKET_TOO_BIG:
34653541Sshin		case ICMP6_TIME_EXCEEDED:
34753541Sshin			break;
34853541Sshin		default:
34953541Sshin			goto freeit;
35053541Sshin		}
35153541Sshin	}
35253541Sshin#endif
35353541Sshin
35453541Sshin#ifdef IPSEC
35553541Sshin	/* drop it if it does not match the default policy */
35653541Sshin	if (ipsec6_in_reject(m, NULL)) {
35753541Sshin		ipsecstat.in_polvio++;
35853541Sshin		goto freeit;
35953541Sshin	}
36053541Sshin#endif
36153541Sshin
36253541Sshin	icmp6stat.icp6s_inhist[icmp6->icmp6_type]++;
36353541Sshin	icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg);
36453541Sshin	if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK)
36553541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error);
36653541Sshin
36753541Sshin	switch (icmp6->icmp6_type) {
36853541Sshin
36953541Sshin	case ICMP6_DST_UNREACH:
37053541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach);
37153541Sshin		switch (code) {
37253541Sshin		case ICMP6_DST_UNREACH_NOROUTE:
37353541Sshin			code = PRC_UNREACH_NET;
37453541Sshin			break;
37553541Sshin		case ICMP6_DST_UNREACH_ADMIN:
37653541Sshin			icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib);
37753541Sshin		case ICMP6_DST_UNREACH_ADDR:
37853541Sshin			code = PRC_UNREACH_HOST;
37953541Sshin			break;
38053541Sshin		case ICMP6_DST_UNREACH_NOTNEIGHBOR:
38153541Sshin			code = PRC_UNREACH_SRCFAIL;
38253541Sshin			break;
38353541Sshin		case ICMP6_DST_UNREACH_NOPORT:
38453541Sshin			code = PRC_UNREACH_PORT;
38553541Sshin			break;
38653541Sshin		default:
38753541Sshin			goto badcode;
38853541Sshin		}
38953541Sshin		goto deliver;
39053541Sshin		break;
39153541Sshin
39253541Sshin	case ICMP6_PACKET_TOO_BIG:
39353541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig);
39453541Sshin		if (code != 0)
39553541Sshin			goto badcode;
39653541Sshin	    {
39753541Sshin		u_int mtu = ntohl(icmp6->icmp6_mtu);
39853541Sshin		struct rtentry *rt = NULL;
39953541Sshin		struct sockaddr_in6 sin6;
40053541Sshin
40153541Sshin		code = PRC_MSGSIZE;
40253541Sshin		bzero(&sin6, sizeof(sin6));
40353541Sshin		sin6.sin6_family = PF_INET6;
40453541Sshin		sin6.sin6_len = sizeof(struct sockaddr_in6);
40553541Sshin		sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
40653541Sshin		rt = rtalloc1((struct sockaddr *)&sin6, 0,
40753541Sshin			RTF_CLONING | RTF_PRCLONING);
40853541Sshin		if (rt && (rt->rt_flags & RTF_HOST)
40953541Sshin		    && !(rt->rt_rmx.rmx_locks & RTV_MTU)) {
41053541Sshin			if (mtu < IPV6_MMTU) {
41153541Sshin				/* xxx */
41253541Sshin				rt->rt_rmx.rmx_locks |= RTV_MTU;
41353541Sshin			} else if (mtu < rt->rt_ifp->if_mtu &&
41453541Sshin				   rt->rt_rmx.rmx_mtu > mtu) {
41553541Sshin				rt->rt_rmx.rmx_mtu = mtu;
41653541Sshin			}
41753541Sshin		}
41853541Sshin		if (rt)
41953541Sshin			RTFREE(rt);
42053541Sshin
42153541Sshin		goto deliver;
42253541Sshin	    }
42353541Sshin		break;
42453541Sshin
42553541Sshin	case ICMP6_TIME_EXCEEDED:
42653541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed);
42753541Sshin		switch (code) {
42853541Sshin		case ICMP6_TIME_EXCEED_TRANSIT:
42953541Sshin		case ICMP6_TIME_EXCEED_REASSEMBLY:
43053541Sshin			code += PRC_TIMXCEED_INTRANS;
43153541Sshin			break;
43253541Sshin		default:
43353541Sshin			goto badcode;
43453541Sshin		}
43553541Sshin		goto deliver;
43653541Sshin		break;
43753541Sshin
43853541Sshin	case ICMP6_PARAM_PROB:
43953541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob);
44053541Sshin		switch (code) {
44153541Sshin		case ICMP6_PARAMPROB_NEXTHEADER:
44253541Sshin			code = PRC_UNREACH_PROTOCOL;
44353541Sshin			break;
44453541Sshin		case ICMP6_PARAMPROB_HEADER:
44553541Sshin		case ICMP6_PARAMPROB_OPTION:
44653541Sshin			code = PRC_PARAMPROB;
44753541Sshin			break;
44853541Sshin		default:
44953541Sshin			goto badcode;
45053541Sshin		}
45153541Sshin		goto deliver;
45253541Sshin		break;
45353541Sshin
45453541Sshin	case ICMP6_ECHO_REQUEST:
45553541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo);
45653541Sshin		if (code != 0)
45753541Sshin			goto badcode;
45853541Sshin		if ((n = m_copy(m, 0, M_COPYALL)) == NULL) {
45953541Sshin			/* Give up remote */
46053541Sshin			break;
46153541Sshin		}
46253541Sshin		if (n->m_flags & M_EXT) {
46353541Sshin			int gap, move;
46453541Sshin			struct mbuf *n0 = n;
46553541Sshin
46653541Sshin			/*
46753541Sshin			 * Prepare an internal mbuf. m_pullup() doesn't
46853541Sshin			 * always copy the length we specified.
46953541Sshin			 */
47053541Sshin			MGETHDR(n, M_DONTWAIT, n0->m_type);
47153541Sshin			if (n == NULL) {
47253541Sshin				/* Give up remote */
47353541Sshin				m_freem(n0);
47453541Sshin				break;
47553541Sshin			}
47653541Sshin			M_COPY_PKTHDR(n, n0);
47753541Sshin			n0->m_flags &= ~M_PKTHDR;
47853541Sshin			n->m_next = n0;
47953541Sshin			/*
48053541Sshin			 * Copy IPv6 and ICMPv6 only.
48153541Sshin			 */
48253541Sshin			nip6 = mtod(n, struct ip6_hdr *);
48353541Sshin			bcopy(ip6, nip6, sizeof(struct ip6_hdr));
48453541Sshin			nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
48553541Sshin			bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
48653541Sshin			/*
48753541Sshin			 * Adjust mbuf. ip6_plen will be adjusted.
48853541Sshin			 */
48953541Sshin			noff = sizeof(struct ip6_hdr);
49053541Sshin			n->m_len = noff + sizeof(struct icmp6_hdr);
49153541Sshin			move = off + sizeof(struct icmp6_hdr);
49253541Sshin			n0->m_len -= move;
49353541Sshin			n0->m_data += move;
49453541Sshin			gap = off - noff;
49553541Sshin			n->m_pkthdr.len -= gap;
49653541Sshin		} else {
49753541Sshin			nip6 = mtod(n, struct ip6_hdr *);
49853541Sshin			nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off);
49953541Sshin			noff = off;
50053541Sshin		}
50153541Sshin		nicmp6->icmp6_type = ICMP6_ECHO_REPLY;
50253541Sshin		nicmp6->icmp6_code = 0;
50353541Sshin		if (n) {
50453541Sshin			icmp6stat.icp6s_reflect++;
50553541Sshin			icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++;
50653541Sshin			icmp6_reflect(n, noff);
50753541Sshin		}
50853541Sshin		break;
50953541Sshin
51053541Sshin	case ICMP6_ECHO_REPLY:
51153541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply);
51253541Sshin		if (code != 0)
51353541Sshin			goto badcode;
51453541Sshin		break;
51553541Sshin
51653541Sshin	case MLD6_LISTENER_QUERY:
51753541Sshin	case MLD6_LISTENER_REPORT:
51853541Sshin		if (icmp6len < sizeof(struct mld6_hdr))
51953541Sshin			goto badlen;
52053541Sshin		if (icmp6->icmp6_type == MLD6_LISTENER_QUERY) /* XXX: ugly... */
52153541Sshin			icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery);
52253541Sshin		else
52353541Sshin			icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport);
52453541Sshin		IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
52553541Sshin		mld6_input(m, off);
52653541Sshin		/* m stays. */
52753541Sshin		break;
52853541Sshin
52953541Sshin	case MLD6_LISTENER_DONE:
53053541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone);
53153541Sshin		if (icmp6len < sizeof(struct mld6_hdr))	/* necessary? */
53253541Sshin			goto badlen;
53353541Sshin		break;		/* nothing to be done in kernel */
53453541Sshin
53553541Sshin	case MLD6_MTRACE_RESP:
53653541Sshin	case MLD6_MTRACE:
53753541Sshin		/* XXX: these two are experimental. not officially defind. */
53853541Sshin		/* XXX: per-interface statistics? */
53953541Sshin		break;		/* just pass it to the userland daemon */
54053541Sshin
54153541Sshin	case ICMP6_WRUREQUEST:	/* ICMP6_FQDN_QUERY */
54253541Sshin	    {
54353541Sshin		enum { WRU, FQDN } mode;
54453541Sshin
54553541Sshin		if (code != 0)
54653541Sshin			goto badcode;
54753541Sshin		if (icmp6len == sizeof(struct icmp6_hdr) + 4)
54853541Sshin			mode = WRU;
54953541Sshin		else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */
55053541Sshin			mode = FQDN;
55153541Sshin		else
55253541Sshin			goto badlen;
55353541Sshin
55453541Sshin#define hostnamelen	strlen(hostname)
55553541Sshin		if (mode == FQDN) {
55653541Sshin			IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo),
55753541Sshin					 IPPROTO_DONE);
55853541Sshin			n = ni6_input(m, off);
55953541Sshin			noff = sizeof(struct ip6_hdr);
56053541Sshin		} else {
56153541Sshin			u_char *p;
56253541Sshin
56353541Sshin			MGETHDR(n, M_DONTWAIT, m->m_type);
56453541Sshin			if (n == NULL) {
56553541Sshin				/* Give up remote */
56653541Sshin				break;
56753541Sshin			}
56853541Sshin			/*
56953541Sshin			 * Copy IPv6 and ICMPv6 only.
57053541Sshin			 */
57153541Sshin			nip6 = mtod(n, struct ip6_hdr *);
57253541Sshin			bcopy(ip6, nip6, sizeof(struct ip6_hdr));
57353541Sshin			nicmp6 = (struct icmp6_hdr *)(nip6 + 1);
57453541Sshin			bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr));
57553541Sshin			p = (u_char *)(nicmp6 + 1);
57653541Sshin			bzero(p, 4);
57753541Sshin			bcopy(hostname, p + 4, hostnamelen);
57853541Sshin			noff = sizeof(struct ip6_hdr);
57953541Sshin			M_COPY_PKTHDR(n, m); /* just for recvif */
58053541Sshin			n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
58153541Sshin				sizeof(struct icmp6_hdr) + 4 + hostnamelen;
58253541Sshin			nicmp6->icmp6_type = ICMP6_WRUREPLY;
58353541Sshin			nicmp6->icmp6_code = 0;
58453541Sshin		}
58553541Sshin#undef hostnamelen
58653541Sshin		if (n) {
58753541Sshin			icmp6stat.icp6s_reflect++;
58853541Sshin			icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++;
58953541Sshin			icmp6_reflect(n, noff);
59053541Sshin		}
59153541Sshin		break;
59253541Sshin	    }
59353541Sshin
59453541Sshin	case ICMP6_WRUREPLY:
59553541Sshin		if (code != 0)
59653541Sshin			goto badcode;
59753541Sshin		break;
59853541Sshin
59953541Sshin	case ND_ROUTER_SOLICIT:
60053541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit);
60153541Sshin		if (code != 0)
60253541Sshin			goto badcode;
60353541Sshin		if (icmp6len < sizeof(struct nd_router_solicit))
60453541Sshin			goto badlen;
60553541Sshin		IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
60653541Sshin		nd6_rs_input(m, off, icmp6len);
60753541Sshin		/* m stays. */
60853541Sshin		break;
60953541Sshin
61053541Sshin	case ND_ROUTER_ADVERT:
61153541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert);
61253541Sshin		if (code != 0)
61353541Sshin			goto badcode;
61453541Sshin		if (icmp6len < sizeof(struct nd_router_advert))
61553541Sshin			goto badlen;
61653541Sshin		IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
61753541Sshin		nd6_ra_input(m, off, icmp6len);
61853541Sshin		/* m stays. */
61953541Sshin		break;
62053541Sshin
62153541Sshin	case ND_NEIGHBOR_SOLICIT:
62253541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit);
62353541Sshin		if (code != 0)
62453541Sshin			goto badcode;
62553541Sshin		if (icmp6len < sizeof(struct nd_neighbor_solicit))
62653541Sshin			goto badlen;
62753541Sshin		IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
62853541Sshin		nd6_ns_input(m, off, icmp6len);
62953541Sshin		/* m stays. */
63053541Sshin		break;
63153541Sshin
63253541Sshin	case ND_NEIGHBOR_ADVERT:
63353541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert);
63453541Sshin		if (code != 0)
63553541Sshin			goto badcode;
63653541Sshin		if (icmp6len < sizeof(struct nd_neighbor_advert))
63753541Sshin			goto badlen;
63853541Sshin		IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE);
63953541Sshin		nd6_na_input(m, off, icmp6len);
64053541Sshin		/* m stays. */
64153541Sshin		break;
64253541Sshin
64353541Sshin	case ND_REDIRECT:
64453541Sshin		icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect);
64553541Sshin		if (code != 0)
64653541Sshin			goto badcode;
64753541Sshin		if (icmp6len < sizeof(struct nd_redirect))
64853541Sshin			goto badlen;
64953541Sshin		icmp6_redirect_input(m, off);
65053541Sshin		/* m stays. */
65153541Sshin		break;
65253541Sshin
65353541Sshin	case ICMP6_ROUTER_RENUMBERING:
65453541Sshin		if (code != ICMP6_ROUTER_RENUMBERING_COMMAND &&
65553541Sshin		    code != ICMP6_ROUTER_RENUMBERING_RESULT)
65653541Sshin			goto badcode;
65753541Sshin		if (icmp6len < sizeof(struct icmp6_router_renum))
65853541Sshin			goto badlen;
65953541Sshin		break;
66053541Sshin
66153541Sshin	default:
66253541Sshin		printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n",
66353541Sshin		       icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src),
66453541Sshin		       ip6_sprintf(&ip6->ip6_dst),
66553541Sshin		       m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0);
66653541Sshin		if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) {
66753541Sshin			/* ICMPv6 error: MUST deliver it by spec... */
66853541Sshin			code = PRC_NCMDS;
66953541Sshin			/* deliver */
67053541Sshin		} else {
67153541Sshin			/* ICMPv6 informational: MUST not deliver */
67253541Sshin			break;
67353541Sshin		}
67453541Sshin	deliver:
67553541Sshin		if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) {
67653541Sshin			icmp6stat.icp6s_tooshort++;
67753541Sshin			goto freeit;
67853541Sshin		}
67953541Sshin		IP6_EXTHDR_CHECK(m, off,
68053541Sshin			sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr),
68153541Sshin			IPPROTO_DONE);
68253541Sshin		icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
68353541Sshin		bzero(&icmp6src, sizeof(icmp6src));
68453541Sshin		icmp6src.sin6_len = sizeof(struct sockaddr_in6);
68553541Sshin		icmp6src.sin6_family = AF_INET6;
68653541Sshin		icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst;
68753541Sshin
68853541Sshin		/* Detect the upper level protocol */
68953541Sshin	    {
69053541Sshin		void (*ctlfunc) __P((int, struct sockaddr *, void *));
69153541Sshin		struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1);
69253541Sshin		u_int8_t nxt = eip6->ip6_nxt;
69353541Sshin		int eoff = off + sizeof(struct icmp6_hdr) +
69453541Sshin			sizeof(struct ip6_hdr);
69553541Sshin		struct ip6ctlparam ip6cp;
69653541Sshin
69753541Sshin		while (1) { /* XXX: should avoid inf. loop explicitly? */
69853541Sshin			struct ip6_ext *eh;
69953541Sshin
70053541Sshin			switch(nxt) {
70153541Sshin			case IPPROTO_ESP:
70253541Sshin			case IPPROTO_NONE:
70353541Sshin				goto passit;
70453541Sshin			case IPPROTO_HOPOPTS:
70553541Sshin			case IPPROTO_DSTOPTS:
70653541Sshin			case IPPROTO_ROUTING:
70753541Sshin			case IPPROTO_AH:
70853541Sshin			case IPPROTO_FRAGMENT:
70953541Sshin				IP6_EXTHDR_CHECK(m, 0, eoff +
71053541Sshin						 sizeof(struct ip6_ext),
71153541Sshin						 IPPROTO_DONE);
71253541Sshin				eh = (struct ip6_ext *)(mtod(m, caddr_t)
71353541Sshin							+ eoff);
71453541Sshin				if (nxt == IPPROTO_AH)
71553541Sshin					eoff += (eh->ip6e_len + 2) << 2;
71653541Sshin				else if (nxt == IPPROTO_FRAGMENT)
71753541Sshin					eoff += sizeof(struct ip6_frag);
71853541Sshin				else
71953541Sshin					eoff += (eh->ip6e_len + 1) << 3;
72053541Sshin				nxt = eh->ip6e_nxt;
72153541Sshin				break;
72253541Sshin			default:
72353541Sshin				goto notify;
72453541Sshin			}
72553541Sshin		}
72653541Sshin	    notify:
72753541Sshin		icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
72853541Sshin		ctlfunc = (void (*) __P((int, struct sockaddr *, void *)))
72953541Sshin			(inet6sw[ip6_protox[nxt]].pr_ctlinput);
73053541Sshin		if (ctlfunc) {
73153541Sshin			ip6cp.ip6c_m = m;
73253541Sshin			ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1);
73353541Sshin			ip6cp.ip6c_off = eoff;
73453541Sshin			(*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp);
73553541Sshin		}
73653541Sshin	    }
73753541Sshin		break;
73853541Sshin
73953541Sshin	badcode:
74053541Sshin		icmp6stat.icp6s_badcode++;
74153541Sshin		break;
74253541Sshin
74353541Sshin	badlen:
74453541Sshin		icmp6stat.icp6s_badlen++;
74553541Sshin		break;
74653541Sshin	}
74753541Sshin
74853541Sshin passit:
74953541Sshin	icmp6_rip6_input(&m, *offp);
75053541Sshin	return IPPROTO_DONE;
75153541Sshin
75253541Sshin freeit:
75353541Sshin	m_freem(m);
75453541Sshin	return IPPROTO_DONE;
75553541Sshin}
75653541Sshin
75753541Sshin/*
75853541Sshin * Process a Node Information Query
75953541Sshin */
76053541Sshin#define	hostnamelen	strlen(hostname)
76153541Sshin#ifndef offsetof		/* XXX */
76253541Sshin#define	offsetof(type, member)	((size_t)(&((type *)0)->member))
76353541Sshin#endif
76453541Sshin
76553541Sshinstatic struct mbuf *
76653541Sshinni6_input(m, off)
76753541Sshin	struct mbuf *m;
76853541Sshin	int off;
76953541Sshin{
77053541Sshin	struct icmp6_nodeinfo *ni6 =
77153541Sshin		(struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off), *nni6;
77253541Sshin	struct mbuf *n = NULL;
77353541Sshin	u_int16_t qtype = ntohs(ni6->ni_qtype);
77453541Sshin	int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo);
77553541Sshin	struct ni_reply_fqdn *fqdn;
77653541Sshin	int addrs;		/* for NI_QTYPE_NODEADDR */
77753541Sshin	struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */
77853541Sshin
77953541Sshin	switch(qtype) {
78053541Sshin	 case NI_QTYPE_NOOP:
78153541Sshin		 break;		/* no reply data */
78253541Sshin	 case NI_QTYPE_SUPTYPES:
78353541Sshin		 goto bad;	/* xxx: to be implemented */
78453541Sshin		 break;
78553541Sshin	 case NI_QTYPE_FQDN:
78653541Sshin		 replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
78753541Sshin			 hostnamelen;
78853541Sshin		 break;
78953541Sshin	 case NI_QTYPE_NODEADDR:
79053541Sshin		 addrs = ni6_addrs(ni6, m, &ifp);
79153541Sshin		 if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES)
79253541Sshin			 replylen = MCLBYTES; /* XXX: we'll truncate later */
79353541Sshin
79453541Sshin		 break;
79553541Sshin	 default:
79653541Sshin		 /*
79753541Sshin		  * XXX: We must return a reply with the ICMP6 code
79853541Sshin		  * `unknown Qtype' in this case. However we regard the case
79953541Sshin		  * as an FQDN query for backward compatibility.
80053541Sshin		  * Older versions set a random value to this field,
80153541Sshin		  * so it rarely varies in the defined qtypes.
80253541Sshin		  * But the mechanism is not reliable...
80353541Sshin		  * maybe we should obsolete older versions.
80453541Sshin		  */
80553541Sshin		 qtype = NI_QTYPE_FQDN;
80653541Sshin		 replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) +
80753541Sshin			 hostnamelen;
80853541Sshin		 break;
80953541Sshin	}
81053541Sshin
81153541Sshin	/* allocate a mbuf to reply. */
81253541Sshin	MGETHDR(n, M_DONTWAIT, m->m_type);
81353541Sshin	if (n == NULL)
81453541Sshin		return(NULL);
81553541Sshin	M_COPY_PKTHDR(n, m); /* just for recvif */
81653541Sshin	if (replylen > MHLEN) {
81753541Sshin		if (replylen > MCLBYTES)
81853541Sshin			 /*
81953541Sshin			  * XXX: should we try to allocate more? But MCLBYTES is
82053541Sshin			  * probably much larger than IPV6_MMTU...
82153541Sshin			  */
82253541Sshin			goto bad;
82353541Sshin		MCLGET(n, M_DONTWAIT);
82453541Sshin		if ((n->m_flags & M_EXT) == 0) {
82553541Sshin			goto bad;
82653541Sshin		}
82753541Sshin	}
82853541Sshin	n->m_pkthdr.len = n->m_len = replylen;
82953541Sshin
83053541Sshin	/* copy mbuf header and IPv6 + Node Information base headers */
83153541Sshin	bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr));
83253541Sshin	nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1);
83353541Sshin	bcopy(mtod(m, caddr_t) + off, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo));
83453541Sshin
83553541Sshin	/* qtype dependent procedure */
83653541Sshin	switch (qtype) {
83753541Sshin	 case NI_QTYPE_NOOP:
83853541Sshin		 nni6->ni_flags = 0;
83953541Sshin		 break;
84053541Sshin	 case NI_QTYPE_SUPTYPES:
84153541Sshin		 goto bad;	/* xxx: to be implemented */
84253541Sshin		 break;
84353541Sshin	 case NI_QTYPE_FQDN:
84453541Sshin		 if (hostnamelen > 255) { /* XXX: rare case, but may happen */
84553541Sshin			 printf("ni6_input: "
84653541Sshin				"hostname length(%d) is too large for reply\n",
84753541Sshin				hostnamelen);
84853541Sshin			 goto bad;
84953541Sshin		 }
85053541Sshin		 fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) +
85153541Sshin						 sizeof(struct ip6_hdr) +
85253541Sshin						 sizeof(struct icmp6_nodeinfo));
85353541Sshin		 nni6->ni_flags = 0; /* XXX: meaningless TTL */
85453541Sshin		 fqdn->ni_fqdn_ttl = 0;	/* ditto. */
85553541Sshin		 fqdn->ni_fqdn_namelen = hostnamelen;
85653541Sshin		 bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen);
85753541Sshin		 break;
85853541Sshin	 case NI_QTYPE_NODEADDR:
85953541Sshin	 {
86053541Sshin		 int lenlim, copied;
86153541Sshin
86253541Sshin		 if (n->m_flags & M_EXT)
86353541Sshin			 lenlim = MCLBYTES - sizeof(struct ip6_hdr) -
86453541Sshin				 sizeof(struct icmp6_nodeinfo);
86553541Sshin		 else
86653541Sshin			 lenlim = MHLEN - sizeof(struct ip6_hdr) -
86753541Sshin				 sizeof(struct icmp6_nodeinfo);
86853541Sshin		 copied = ni6_store_addrs(ni6, nni6, ifp, lenlim);
86953541Sshin		 /* XXX: reset mbuf length */
87053541Sshin		 n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) +
87153541Sshin			 sizeof(struct icmp6_nodeinfo) + copied;
87253541Sshin		 break;
87353541Sshin	 }
87453541Sshin	 default:
87553541Sshin		 break;		/* XXX impossible! */
87653541Sshin	}
87753541Sshin
87853541Sshin	nni6->ni_type = ICMP6_NI_REPLY;
87953541Sshin	nni6->ni_code = ICMP6_NI_SUCESS;
88053541Sshin	return(n);
88153541Sshin
88253541Sshin  bad:
88353541Sshin	if (n)
88453541Sshin		m_freem(n);
88553541Sshin	return(NULL);
88653541Sshin}
88753541Sshin#undef hostnamelen
88853541Sshin
88953541Sshin/*
89053541Sshin * calculate the number of addresses to be returned in the node info reply.
89153541Sshin */
89253541Sshinstatic int
89353541Sshinni6_addrs(ni6, m, ifpp)
89453541Sshin	struct icmp6_nodeinfo *ni6;
89553541Sshin	struct mbuf *m;
89653541Sshin	struct ifnet **ifpp;
89753541Sshin{
89853541Sshin	register struct ifnet *ifp;
89953541Sshin	register struct in6_ifaddr *ifa6;
90053541Sshin	register struct ifaddr *ifa;
90153541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
90253541Sshin	int addrs = 0, addrsofif, iffound = 0;
90353541Sshin
90453541Sshin	for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list))
90553541Sshin	{
90653541Sshin		addrsofif = 0;
90754263Sshin		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
90853541Sshin		{
90953541Sshin			if (ifa->ifa_addr->sa_family != AF_INET6)
91053541Sshin				continue;
91153541Sshin			ifa6 = (struct in6_ifaddr *)ifa;
91253541Sshin
91353541Sshin			if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) &&
91453541Sshin			    IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst,
91553541Sshin					       &ifa6->ia_addr.sin6_addr))
91653541Sshin				iffound = 1;
91753541Sshin
91853541Sshin			if (ifa6->ia6_flags & IN6_IFF_ANYCAST)
91953541Sshin				continue; /* we need only unicast addresses */
92053541Sshin
92153541Sshin			if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL |
92253541Sshin					      NI_NODEADDR_FLAG_SITELOCAL |
92353541Sshin					      NI_NODEADDR_FLAG_GLOBAL)) == 0)
92453541Sshin				continue;
92553541Sshin
92653541Sshin			/* What do we have to do about ::1? */
92753541Sshin			switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
92853541Sshin			 case IPV6_ADDR_SCOPE_LINKLOCAL:
92953541Sshin				if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
93053541Sshin					addrsofif++;
93153541Sshin				break;
93253541Sshin			 case IPV6_ADDR_SCOPE_SITELOCAL:
93353541Sshin				if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
93453541Sshin					addrsofif++;
93553541Sshin				break;
93653541Sshin			 case IPV6_ADDR_SCOPE_GLOBAL:
93753541Sshin				 if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
93853541Sshin					 addrsofif++;
93953541Sshin				 break;
94053541Sshin			 default:
94153541Sshin				 continue;
94253541Sshin			}
94353541Sshin		}
94453541Sshin		if (iffound) {
94553541Sshin			*ifpp = ifp;
94653541Sshin			return(addrsofif);
94753541Sshin		}
94853541Sshin
94953541Sshin		addrs += addrsofif;
95053541Sshin	}
95153541Sshin
95253541Sshin	return(addrs);
95353541Sshin}
95453541Sshin
95553541Sshinstatic int
95653541Sshinni6_store_addrs(ni6, nni6, ifp0, resid)
95753541Sshin	struct icmp6_nodeinfo *ni6, *nni6;
95853541Sshin	struct ifnet *ifp0;
95953541Sshin	int resid;
96053541Sshin{
96153541Sshin	register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet);
96253541Sshin	register struct in6_ifaddr *ifa6;
96353541Sshin	register struct ifaddr *ifa;
96453541Sshin	int docopy, copied = 0;
96553541Sshin	u_char *cp = (u_char *)(nni6 + 1);
96653541Sshin
96753541Sshin	if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL))
96853541Sshin		return(0);	/* needless to copy */
96953541Sshin
97053541Sshin	for (; ifp; ifp = TAILQ_NEXT(ifp, if_list))
97153541Sshin	{
97254263Sshin		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
97353541Sshin		{
97453541Sshin			docopy = 0;
97553541Sshin
97653541Sshin			if (ifa->ifa_addr->sa_family != AF_INET6)
97753541Sshin				continue;
97853541Sshin			ifa6 = (struct in6_ifaddr *)ifa;
97953541Sshin
98053541Sshin			if (ifa6->ia6_flags & IN6_IFF_ANYCAST) {
98153541Sshin				/* just experimental. not in the spec. */
98253541Sshin				if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
98353541Sshin					docopy = 1;
98453541Sshin				else
98553541Sshin					continue;
98653541Sshin			} else {	/* unicast address */
98753541Sshin				if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST)
98853541Sshin					continue;
98953541Sshin				else
99053541Sshin					docopy = 1;
99153541Sshin			}
99253541Sshin
99353541Sshin			/* What do we have to do about ::1? */
99453541Sshin			switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) {
99553541Sshin			 case IPV6_ADDR_SCOPE_LINKLOCAL:
99653541Sshin				if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL)
99753541Sshin					docopy = 1;
99853541Sshin				break;
99953541Sshin			 case IPV6_ADDR_SCOPE_SITELOCAL:
100053541Sshin				if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL)
100153541Sshin					docopy = 1;
100253541Sshin				break;
100353541Sshin			 case IPV6_ADDR_SCOPE_GLOBAL:
100453541Sshin				 if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL)
100553541Sshin					 docopy = 1;
100653541Sshin				 break;
100753541Sshin			 default:
100853541Sshin				 continue;
100953541Sshin			}
101053541Sshin
101153541Sshin			if (docopy) {
101253541Sshin				if (resid < sizeof(struct in6_addr)) {
101353541Sshin					/*
101453541Sshin					 * We give up much more copy.
101553541Sshin					 * Set the truncate flag and return.
101653541Sshin					 */
101753541Sshin					nni6->ni_flags |=
101853541Sshin						NI_NODEADDR_FLAG_TRUNCATE;
101953541Sshin					return(copied);
102053541Sshin				}
102153541Sshin				bcopy(&ifa6->ia_addr.sin6_addr, cp,
102253541Sshin				      sizeof(struct in6_addr));
102353541Sshin				/* XXX: KAME link-local hack; remove ifindex */
102453541Sshin				if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr))
102553541Sshin					((struct in6_addr *)cp)->s6_addr16[1] = 0;
102653541Sshin				cp += sizeof(struct in6_addr);
102753541Sshin				resid -= sizeof(struct in6_addr);
102853541Sshin				copied += sizeof(struct in6_addr);
102953541Sshin			}
103053541Sshin		}
103153541Sshin		if (ifp0)	/* we need search only on the specified IF */
103253541Sshin			break;
103353541Sshin	}
103453541Sshin
103553541Sshin	return(copied);
103653541Sshin}
103753541Sshin
103853541Sshin/*
103953541Sshin * XXX almost dup'ed code with rip6_input.
104053541Sshin */
104153541Sshinstatic int
104253541Sshinicmp6_rip6_input(mp, off)
104353541Sshin	struct	mbuf **mp;
104453541Sshin	int	off;
104553541Sshin{
104653541Sshin	struct mbuf *m = *mp;
104753541Sshin	register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
104853541Sshin	register struct in6pcb *in6p;
104953541Sshin	struct in6pcb *last = NULL;
105053541Sshin	struct sockaddr_in6 rip6src;
105153541Sshin	struct icmp6_hdr *icmp6;
105253541Sshin	struct mbuf *opts = NULL;
105353541Sshin
105453541Sshin	/* this is assumed to be safe. */
105553541Sshin	icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off);
105653541Sshin
105753541Sshin	bzero(&rip6src, sizeof(rip6src));
105853541Sshin	rip6src.sin6_len = sizeof(struct sockaddr_in6);
105953541Sshin	rip6src.sin6_family = AF_INET6;
106053541Sshin	rip6src.sin6_addr = ip6->ip6_src;
106153541Sshin	if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
106253541Sshin		rip6src.sin6_addr.s6_addr16[1] = 0;
106353541Sshin	if (m->m_pkthdr.rcvif) {
106453541Sshin		if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr))
106553541Sshin			rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index;
106653541Sshin		else
106753541Sshin			rip6src.sin6_scope_id = 0;
106853541Sshin	} else
106953541Sshin		rip6src.sin6_scope_id = 0;
107053541Sshin
107153541Sshin	LIST_FOREACH(in6p, &ripcb, inp_list)
107253541Sshin	{
107354952Seivind		if ((in6p->inp_vflag & INP_IPV6) == 0)
107453541Sshin			continue;
107553541Sshin		if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6)
107653541Sshin			continue;
107753541Sshin		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) &&
107853541Sshin		   !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst))
107953541Sshin			continue;
108053541Sshin		if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) &&
108153541Sshin		   !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src))
108253541Sshin			continue;
108353541Sshin		if (in6p->in6p_icmp6filt
108453541Sshin		    && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type,
108553541Sshin				 in6p->in6p_icmp6filt))
108653541Sshin			continue;
108753541Sshin		if (last) {
108853541Sshin			struct	mbuf *n;
108953541Sshin			if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
109053541Sshin				if (last->in6p_flags & IN6P_CONTROLOPTS)
109153541Sshin					ip6_savecontrol(last, &opts, ip6, n);
109253541Sshin				/* strip intermediate headers */
109353541Sshin				m_adj(n, off);
109453541Sshin				if (sbappendaddr(&last->in6p_socket->so_rcv,
109553541Sshin						 (struct sockaddr *)&rip6src,
109653541Sshin						 n, opts) == 0) {
109753541Sshin					/* should notify about lost packet */
109853541Sshin					m_freem(n);
109953541Sshin					if (opts)
110053541Sshin						m_freem(opts);
110153541Sshin				} else
110253541Sshin					sorwakeup(last->in6p_socket);
110353541Sshin				opts = NULL;
110453541Sshin			}
110553541Sshin		}
110653541Sshin		last = in6p;
110753541Sshin	}
110853541Sshin	if (last) {
110953541Sshin		if (last->in6p_flags & IN6P_CONTROLOPTS)
111053541Sshin			ip6_savecontrol(last, &opts, ip6, m);
111153541Sshin		/* strip intermediate headers */
111253541Sshin		m_adj(m, off);
111353541Sshin		if (sbappendaddr(&last->in6p_socket->so_rcv,
111453541Sshin				(struct sockaddr *)&rip6src, m, opts) == 0) {
111553541Sshin			m_freem(m);
111653541Sshin			if (opts)
111753541Sshin				m_freem(opts);
111853541Sshin		} else
111953541Sshin			sorwakeup(last->in6p_socket);
112053541Sshin	} else {
112153541Sshin		m_freem(m);
112253541Sshin		ip6stat.ip6s_delivered--;
112353541Sshin	}
112453541Sshin	return IPPROTO_DONE;
112553541Sshin}
112653541Sshin
112753541Sshin/*
112853541Sshin * Reflect the ip6 packet back to the source.
112953541Sshin * The caller MUST check if the destination is multicast or not.
113053541Sshin * This function is usually called with a unicast destination which
113153541Sshin * can be safely the source of the reply packet. But some exceptions
113253541Sshin * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type).
113353541Sshin * ``off'' points to the icmp6 header, counted from the top of the mbuf.
113453541Sshin */
113553541Sshinvoid
113653541Sshinicmp6_reflect(m, off)
113753541Sshin	struct	mbuf *m;
113853541Sshin	size_t off;
113953541Sshin{
114053541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
114153541Sshin	struct icmp6_hdr *icmp6;
114253541Sshin	struct in6_ifaddr *ia;
114353541Sshin	struct in6_addr t, *src = 0;
114453541Sshin	int plen = m->m_pkthdr.len - sizeof(struct ip6_hdr);
114553541Sshin	int type, code;
114653541Sshin	struct ifnet *outif = NULL;
114753541Sshin#ifdef COMPAT_RFC1885
114853541Sshin	int mtu = IPV6_MMTU;
114953541Sshin	struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst;
115053541Sshin#endif
115153541Sshin
115253541Sshin	/*
115353541Sshin	 * If there are extra headers between IPv6 and ICMPv6, strip
115453541Sshin	 * off that header first.
115553541Sshin	 */
115653541Sshin	if (off != sizeof(struct ip6_hdr)) {
115753541Sshin		size_t siz;
115853541Sshin
115953541Sshin		/* sanity checks */
116053541Sshin		if (off < sizeof(struct ip6_hdr)) {
116153541Sshin			printf("sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n",
116253541Sshin			       (unsigned int)off,
116353541Sshin			       (unsigned int)sizeof(struct ip6_hdr),
116453541Sshin			       __FILE__, __LINE__);
116553541Sshin			goto bad;
116653541Sshin		}
116753541Sshin		siz = off - sizeof(struct ip6_hdr);
116853541Sshin		if (plen < siz) {
116953541Sshin			printf("sanity fail: siz=%x, payloadlen=%x in %s:%d\n",
117053541Sshin			       (unsigned int)siz, plen, __FILE__, __LINE__);
117153541Sshin			goto bad;
117253541Sshin		}
117353541Sshin		IP6_EXTHDR_CHECK(m, 0, off, /*nothing*/);
117453541Sshin		IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), /*nothing*/);
117553541Sshin
117653541Sshin		ovbcopy((caddr_t)ip6,
117753541Sshin			(caddr_t)(mtod(m, u_char *) + siz),
117853541Sshin			sizeof(struct ip6_hdr));
117953541Sshin		m->m_data += siz;
118053541Sshin		m->m_len -= siz;
118153541Sshin		m->m_pkthdr.len -= siz;
118253541Sshin		ip6 = mtod(m, struct ip6_hdr *);
118353541Sshin		ip6->ip6_nxt = IPPROTO_ICMPV6;
118453541Sshin		plen -= siz;
118553541Sshin	}
118653541Sshin
118753541Sshin	icmp6 = (struct icmp6_hdr *)(ip6 + 1);
118853541Sshin	type = icmp6->icmp6_type; /* keep type for statistics */
118953541Sshin	code = icmp6->icmp6_code; /* ditto. */
119053541Sshin
119153541Sshin	t = ip6->ip6_dst;
119253541Sshin	/*
119353541Sshin	 * ip6_input() drops a packet if its src is multicast.
119453541Sshin	 * So, the src is never multicast.
119553541Sshin	 */
119653541Sshin	ip6->ip6_dst = ip6->ip6_src;
119753541Sshin
119853541Sshin	/* XXX hack for link-local addresses */
119953541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst))
120053541Sshin		ip6->ip6_dst.s6_addr16[1] =
120153541Sshin			htons(m->m_pkthdr.rcvif->if_index);
120253541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&t))
120353541Sshin		t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index);
120453541Sshin
120553541Sshin#ifdef COMPAT_RFC1885
120653541Sshin	/*
120753541Sshin	 * xxx guess MTU
120853541Sshin	 * RFC 1885 requires that echo reply should be truncated if it
120953541Sshin	 * does not fit in with (return) path MTU, but the description was
121053541Sshin	 * removed in the new spec.
121153541Sshin	 */
121253541Sshin	if (icmp6_reflect_rt.ro_rt == 0 ||
121353541Sshin	    ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) {
121453541Sshin		if (icmp6_reflect_rt.ro_rt) {
121553541Sshin			RTFREE(icmp6_reflect_rt.ro_rt);
121653541Sshin			icmp6_reflect_rt.ro_rt = 0;
121753541Sshin		}
121853541Sshin		bzero(sin6, sizeof(*sin6));
121953541Sshin		sin6->sin6_family = PF_INET6;
122053541Sshin		sin6->sin6_len = sizeof(struct sockaddr_in6);
122153541Sshin		sin6->sin6_addr = ip6->ip6_dst;
122253541Sshin
122353541Sshin		rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt,
122453541Sshin			    RTF_PRCLONING);
122553541Sshin	}
122653541Sshin
122753541Sshin	if (icmp6_reflect_rt.ro_rt == 0)
122853541Sshin		goto bad;
122953541Sshin
123053541Sshin	if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST)
123153541Sshin	    && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu)
123253541Sshin		mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu;
123353541Sshin
123453541Sshin	if (mtu < m->m_pkthdr.len) {
123553541Sshin		plen -= (m->m_pkthdr.len - mtu);
123653541Sshin		m_adj(m, mtu - m->m_pkthdr.len);
123753541Sshin	}
123853541Sshin#endif
123953541Sshin	/*
124053541Sshin	 * If the incoming packet was addressed directly to us(i.e. unicast),
124153541Sshin	 * use dst as the src for the reply.
124253541Sshin	 */
124353541Sshin	for (ia = in6_ifaddr; ia; ia = ia->ia_next)
124453541Sshin		if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) &&
124553541Sshin		    (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) {
124653541Sshin			src = &t;
124753541Sshin			break;
124853541Sshin		}
124953541Sshin	if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) {
125053541Sshin		/*
125153541Sshin		 * This is the case if the dst is our link-local address
125253541Sshin		 * and the sender is also ourseleves.
125353541Sshin		 */
125453541Sshin		src = &t;
125553541Sshin	}
125653541Sshin
125753541Sshin	if (src == 0)
125853541Sshin		/*
125953541Sshin		 * We have not multicast routing yet. So this case matches
126053541Sshin		 * to our multicast, our anycast or not to our unicast.
126153541Sshin		 * Select a source address which has the same scope.
126253541Sshin		 */
126353541Sshin		if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0)
126453541Sshin			src = &IA6_SIN6(ia)->sin6_addr;
126553541Sshin
126653541Sshin	if (src == 0)
126753541Sshin		goto bad;
126853541Sshin
126953541Sshin	ip6->ip6_src = *src;
127053541Sshin
127153541Sshin	ip6->ip6_flow = 0;
127253541Sshin	ip6->ip6_vfc = IPV6_VERSION;
127353541Sshin	ip6->ip6_nxt = IPPROTO_ICMPV6;
127453541Sshin	if (m->m_pkthdr.rcvif) {
127553541Sshin		/* XXX: This may not be the outgoing interface */
127653541Sshin		ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim;
127753541Sshin	}
127853541Sshin
127953541Sshin	icmp6->icmp6_cksum = 0;
128053541Sshin	icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6,
128153541Sshin					sizeof(struct ip6_hdr), plen);
128253541Sshin
128353541Sshin	/*
128453541Sshin	 * xxx option handling
128553541Sshin	 */
128653541Sshin
128753541Sshin	m->m_flags &= ~(M_BCAST|M_MCAST);
128853541Sshin
128953541Sshin#ifdef COMPAT_RFC1885
129053541Sshin	ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif);
129153541Sshin#else
129253541Sshin	ip6_output(m, NULL, NULL, 0, NULL, &outif);
129353541Sshin#endif
129453541Sshin	if (outif)
129553541Sshin		icmp6_ifoutstat_inc(outif, type, code);
129653541Sshin
129753541Sshin	return;
129853541Sshin
129953541Sshin bad:
130053541Sshin	m_freem(m);
130153541Sshin	return;
130253541Sshin}
130353541Sshin
130453541Sshinvoid
130553541Sshinicmp6_fasttimo()
130653541Sshin{
130753541Sshin	mld6_fasttimeo();
130853541Sshin}
130953541Sshin
131053541Sshinstatic const char *
131153541Sshinicmp6_redirect_diag(src6, dst6, tgt6)
131253541Sshin	struct in6_addr *src6;
131353541Sshin	struct in6_addr *dst6;
131453541Sshin	struct in6_addr *tgt6;
131553541Sshin{
131653541Sshin	static char buf[1024];
131753541Sshin	snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)",
131853541Sshin		ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6));
131953541Sshin	return buf;
132053541Sshin}
132153541Sshin
132253541Sshinvoid
132353541Sshinicmp6_redirect_input(m, off)
132453541Sshin	register struct mbuf *m;
132553541Sshin	int off;
132653541Sshin{
132753541Sshin	struct ifnet *ifp = m->m_pkthdr.rcvif;
132853541Sshin	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
132953541Sshin	struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off);
133053541Sshin	int icmp6len = ntohs(ip6->ip6_plen);
133153541Sshin	char *lladdr = NULL;
133253541Sshin	int lladdrlen = 0;
133353541Sshin	u_char *redirhdr = NULL;
133453541Sshin	int redirhdrlen = 0;
133553541Sshin	struct rtentry *rt = NULL;
133653541Sshin	int is_router;
133753541Sshin	int is_onlink;
133853541Sshin	struct in6_addr src6 = ip6->ip6_src;
133953541Sshin	struct in6_addr redtgt6 = nd_rd->nd_rd_target;
134053541Sshin	struct in6_addr reddst6 = nd_rd->nd_rd_dst;
134153541Sshin	union nd_opts ndopts;
134253541Sshin
134353541Sshin	if (!m || !ifp)
134453541Sshin		return;
134553541Sshin
134653541Sshin	/* XXX if we are router, we don't update route by icmp6 redirect */
134753541Sshin	if (ip6_forwarding)
134853541Sshin		return;
134953541Sshin	if (!icmp6_rediraccept)
135053541Sshin		return;
135153541Sshin
135253541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
135353541Sshin		redtgt6.s6_addr16[1] = htons(ifp->if_index);
135453541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&reddst6))
135553541Sshin		reddst6.s6_addr16[1] = htons(ifp->if_index);
135653541Sshin
135753541Sshin	/* validation */
135853541Sshin	if (!IN6_IS_ADDR_LINKLOCAL(&src6)) {
135953541Sshin		log(LOG_ERR,
136053541Sshin			"ICMP6 redirect sent from %s rejected; "
136153541Sshin			"must be from linklocal\n", ip6_sprintf(&src6));
136253541Sshin		return;
136353541Sshin	}
136453541Sshin	if (ip6->ip6_hlim != 255) {
136553541Sshin		log(LOG_ERR,
136653541Sshin			"ICMP6 redirect sent from %s rejected; "
136753541Sshin			"hlim=%d (must be 255)\n",
136853541Sshin			ip6_sprintf(&src6), ip6->ip6_hlim);
136953541Sshin		return;
137053541Sshin	}
137153541Sshin    {
137253541Sshin	/* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */
137353541Sshin	struct sockaddr_in6 sin6;
137453541Sshin	struct in6_addr *gw6;
137553541Sshin
137653541Sshin	bzero(&sin6, sizeof(sin6));
137753541Sshin	sin6.sin6_family = AF_INET6;
137853541Sshin	sin6.sin6_len = sizeof(struct sockaddr_in6);
137953541Sshin	bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6));
138053541Sshin	rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
138153541Sshin	if (rt) {
138253541Sshin		gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr);
138353541Sshin		if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) {
138453541Sshin			log(LOG_ERR,
138553541Sshin				"ICMP6 redirect rejected; "
138653541Sshin				"not equal to gw-for-src=%s (must be same): "
138753541Sshin				"%s\n",
138853541Sshin				ip6_sprintf(gw6),
138953541Sshin				icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
139053541Sshin			RTFREE(rt);
139153541Sshin			return;
139253541Sshin		}
139353541Sshin	} else {
139453541Sshin		log(LOG_ERR,
139553541Sshin			"ICMP6 redirect rejected; "
139653541Sshin			"no route found for redirect dst: %s\n",
139753541Sshin			icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
139853541Sshin		return;
139953541Sshin	}
140053541Sshin	RTFREE(rt);
140153541Sshin	rt = NULL;
140253541Sshin    }
140353541Sshin	if (IN6_IS_ADDR_MULTICAST(&reddst6)) {
140453541Sshin		log(LOG_ERR,
140553541Sshin			"ICMP6 redirect rejected; "
140653541Sshin			"redirect dst must be unicast: %s\n",
140753541Sshin			icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
140853541Sshin		return;
140953541Sshin	}
141053541Sshin
141153541Sshin	is_router = is_onlink = 0;
141253541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&redtgt6))
141353541Sshin		is_router = 1;	/* router case */
141453541Sshin	if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0)
141553541Sshin		is_onlink = 1;	/* on-link destination case */
141653541Sshin	if (!is_router && !is_onlink) {
141753541Sshin		log(LOG_ERR,
141853541Sshin			"ICMP6 redirect rejected; "
141953541Sshin			"neither router case nor onlink case: %s\n",
142053541Sshin			icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
142153541Sshin		return;
142253541Sshin	}
142353541Sshin	/* validation passed */
142453541Sshin
142553541Sshin	icmp6len -= sizeof(*nd_rd);
142653541Sshin	nd6_option_init(nd_rd + 1, icmp6len, &ndopts);
142753541Sshin	if (nd6_options(&ndopts) < 0) {
142853541Sshin		log(LOG_INFO, "icmp6_redirect_input: "
142953541Sshin			"invalid ND option, rejected: %s\n",
143053541Sshin			icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
143153541Sshin		return;
143253541Sshin	}
143353541Sshin
143453541Sshin	if (ndopts.nd_opts_tgt_lladdr) {
143553541Sshin		lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);
143653541Sshin		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
143753541Sshin	}
143853541Sshin
143953541Sshin	if (ndopts.nd_opts_rh) {
144053541Sshin		redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len;
144153541Sshin		redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */
144253541Sshin	}
144353541Sshin
144453541Sshin	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {
144553541Sshin		log(LOG_INFO,
144653541Sshin			"icmp6_redirect_input: lladdrlen mismatch for %s "
144753541Sshin			"(if %d, icmp6 packet %d): %s\n",
144853541Sshin			ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2,
144953541Sshin			icmp6_redirect_diag(&src6, &reddst6, &redtgt6));
145053541Sshin	}
145153541Sshin
145253541Sshin	/* RFC 2461 8.3 */
145353541Sshin	nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
145453541Sshin			 is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
145553541Sshin
145653541Sshin	if (!is_onlink) {	/* better router case. perform rtredirect. */
145753541Sshin		/* perform rtredirect */
145853541Sshin		struct sockaddr_in6 sdst;
145953541Sshin		struct sockaddr_in6 sgw;
146053541Sshin		struct sockaddr_in6 ssrc;
146153541Sshin
146253541Sshin		bzero(&sdst, sizeof(sdst));
146353541Sshin		bzero(&sgw, sizeof(sgw));
146453541Sshin		bzero(&ssrc, sizeof(ssrc));
146553541Sshin		sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
146653541Sshin		sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
146753541Sshin			sizeof(struct sockaddr_in6);
146853541Sshin		bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
146953541Sshin		bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
147053541Sshin		bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
147153541Sshin		rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw,
147253541Sshin			   (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST,
147353541Sshin			   (struct sockaddr *)&ssrc,
147453541Sshin			   (struct rtentry **)NULL);
147553541Sshin	}
147653541Sshin	/* finally update cached route in each socket via pfctlinput */
147753541Sshin    {
147853541Sshin	struct sockaddr_in6 sdst;
147953541Sshin
148053541Sshin	bzero(&sdst, sizeof(sdst));
148153541Sshin	sdst.sin6_family = AF_INET6;
148253541Sshin	sdst.sin6_len = sizeof(struct sockaddr_in6);
148353541Sshin	bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
148453541Sshin	pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst);
148553541Sshin#ifdef IPSEC
148653541Sshin	key_sa_routechange((struct sockaddr *)&sdst);
148753541Sshin#endif
148853541Sshin    }
148953541Sshin}
149053541Sshin
149153541Sshinvoid
149253541Sshinicmp6_redirect_output(m0, rt)
149353541Sshin	struct mbuf *m0;
149453541Sshin	struct rtentry *rt;
149553541Sshin{
149653541Sshin	struct ifnet *ifp;	/* my outgoing interface */
149753541Sshin	struct in6_addr *ifp_ll6;
149853541Sshin	struct in6_addr *router_ll6;
149953541Sshin	struct ip6_hdr *sip6;	/* m0 as struct ip6_hdr */
150053541Sshin	struct mbuf *m = NULL;	/* newly allocated one */
150153541Sshin	struct ip6_hdr *ip6;	/* m as struct ip6_hdr */
150253541Sshin	struct nd_redirect *nd_rd;
150353541Sshin	size_t maxlen;
150453541Sshin	u_char *p;
150553541Sshin	struct ifnet *outif = NULL;
150653541Sshin
150753541Sshin	/* if we are not router, we don't send icmp6 redirect */
150853541Sshin	if (!ip6_forwarding || ip6_accept_rtadv)
150953541Sshin		goto fail;
151053541Sshin
151153541Sshin	/* sanity check */
151253541Sshin	if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp))
151353541Sshin		goto fail;
151453541Sshin
151553541Sshin	/*
151653541Sshin	 * Address check:
151753541Sshin	 *  the source address must identify a neighbor, and
151853541Sshin	 *  the destination address must not be a multicast address
151953541Sshin	 *  [RFC 2461, sec 8.2]
152053541Sshin	 */
152153541Sshin	sip6 = mtod(m0, struct ip6_hdr *);
152253541Sshin	if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0)
152353541Sshin		goto fail;
152453541Sshin	if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst))
152553541Sshin		goto fail;	/* what should we do here? */
152653541Sshin
152753541Sshin	/* rate limit */
152853541Sshin	if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0))
152953541Sshin		goto fail;
153053541Sshin
153153541Sshin	/*
153253541Sshin	 * Since we are going to append up to 1280 bytes (= IPV6_MMTU),
153353541Sshin	 * we almost always ask for an mbuf cluster for simplicity.
153453541Sshin	 * (MHLEN < IPV6_MMTU is almost always true)
153553541Sshin	 */
153653541Sshin	MGETHDR(m, M_DONTWAIT, MT_HEADER);
153753541Sshin	if (!m)
153853541Sshin		goto fail;
153953541Sshin	if (MHLEN < IPV6_MMTU)
154053541Sshin		MCLGET(m, M_DONTWAIT);
154153541Sshin	maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN;
154253541Sshin	maxlen = min(IPV6_MMTU, maxlen);
154353541Sshin	/* just for safety */
154453541Sshin	if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
154553541Sshin		goto fail;
154653541Sshin
154753541Sshin	{
154853541Sshin		/* get ip6 linklocal address for ifp(my outgoing interface). */
154953541Sshin		struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp);
155053541Sshin		if (ia == NULL)
155153541Sshin			goto fail;
155253541Sshin		ifp_ll6 = &ia->ia_addr.sin6_addr;
155353541Sshin	}
155453541Sshin
155553541Sshin	/* get ip6 linklocal address for the router. */
155653541Sshin	if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) {
155753541Sshin		struct sockaddr_in6 *sin6;
155853541Sshin		sin6 = (struct sockaddr_in6 *)rt->rt_gateway;
155953541Sshin		router_ll6 = &sin6->sin6_addr;
156053541Sshin		if (!IN6_IS_ADDR_LINKLOCAL(router_ll6))
156153541Sshin			router_ll6 = (struct in6_addr *)NULL;
156253541Sshin	} else
156353541Sshin		router_ll6 = (struct in6_addr *)NULL;
156453541Sshin
156553541Sshin	/* ip6 */
156653541Sshin	ip6 = mtod(m, struct ip6_hdr *);
156753541Sshin	ip6->ip6_flow = 0;
156853541Sshin	ip6->ip6_vfc = IPV6_VERSION;
156953541Sshin	/* ip6->ip6_plen will be set later */
157053541Sshin	ip6->ip6_nxt = IPPROTO_ICMPV6;
157153541Sshin	ip6->ip6_hlim = 255;
157253541Sshin	/* ip6->ip6_src must be linklocal addr for my outgoing if. */
157353541Sshin	bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr));
157453541Sshin	bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr));
157553541Sshin
157653541Sshin	/* ND Redirect */
157753541Sshin	nd_rd = (struct nd_redirect *)(ip6 + 1);
157853541Sshin	nd_rd->nd_rd_type = ND_REDIRECT;
157953541Sshin	nd_rd->nd_rd_code = 0;
158053541Sshin	nd_rd->nd_rd_reserved = 0;
158153541Sshin	if (rt->rt_flags & RTF_GATEWAY) {
158253541Sshin		/*
158353541Sshin		 * nd_rd->nd_rd_target must be a link-local address in
158453541Sshin		 * better router cases.
158553541Sshin		 */
158653541Sshin		if (!router_ll6)
158753541Sshin			goto fail;
158853541Sshin		bcopy(router_ll6, &nd_rd->nd_rd_target,
158953541Sshin		      sizeof(nd_rd->nd_rd_target));
159053541Sshin		bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
159153541Sshin		      sizeof(nd_rd->nd_rd_dst));
159253541Sshin	} else {
159353541Sshin		/* make sure redtgt == reddst */
159453541Sshin		bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target,
159553541Sshin		      sizeof(nd_rd->nd_rd_target));
159653541Sshin		bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst,
159753541Sshin		      sizeof(nd_rd->nd_rd_dst));
159853541Sshin	}
159953541Sshin
160053541Sshin	p = (u_char *)(nd_rd + 1);
160153541Sshin
160253541Sshin	if (!router_ll6)
160353541Sshin		goto nolladdropt;
160453541Sshin
160553541Sshin    {
160653541Sshin	/* target lladdr option */
160753541Sshin	struct rtentry *rt_router = NULL;
160853541Sshin	int len;
160953541Sshin	struct sockaddr_dl *sdl;
161053541Sshin	struct nd_opt_hdr *nd_opt;
161153541Sshin	char *lladdr;
161253541Sshin
161353541Sshin	rt_router = nd6_lookup(router_ll6, 0, ifp);
161453541Sshin	if (!rt_router)
161553541Sshin		goto nolladdropt;
161653541Sshin	if (!(rt_router->rt_flags & RTF_GATEWAY)
161753541Sshin	 && (rt_router->rt_flags & RTF_LLINFO)
161853541Sshin	 && (rt_router->rt_gateway->sa_family == AF_LINK)
161953541Sshin	 && (sdl = (struct sockaddr_dl *)rt_router->rt_gateway)) {
162053541Sshin		nd_opt = (struct nd_opt_hdr *)p;
162153541Sshin		nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
162253541Sshin		len = 2 + ifp->if_addrlen;
162353541Sshin		len = (len + 7) & ~7;	/*round by 8*/
162453541Sshin		nd_opt->nd_opt_len = len >> 3;
162553541Sshin		p += len;
162653541Sshin		lladdr = (char *)(nd_opt + 1);
162753541Sshin		bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
162853541Sshin	}
162953541Sshin    }
163053541Sshinnolladdropt:;
163153541Sshin
163253541Sshin	m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
163353541Sshin
163453541Sshin	/* just to be safe */
163553541Sshin	if (m0->m_flags & M_DECRYPTED)
163653541Sshin		goto noredhdropt;
163753541Sshin
163853541Sshin    {
163953541Sshin	/* redirected header option */
164053541Sshin	int len;
164153541Sshin	struct nd_opt_rd_hdr *nd_opt_rh;
164253541Sshin
164353541Sshin	/*
164453541Sshin	 * compute the maximum size for icmp6 redirect header option.
164553541Sshin	 * XXX room for auth header?
164653541Sshin	 */
164753541Sshin	len = maxlen - (p - (u_char *)ip6);
164853541Sshin	len &= ~7;
164953541Sshin
165053541Sshin	/* This is just for simplicity. */
165153541Sshin	if (m0->m_pkthdr.len != m0->m_len) {
165253541Sshin		if (m0->m_next) {
165353541Sshin			m_freem(m0->m_next);
165453541Sshin			m0->m_next = NULL;
165553541Sshin		}
165653541Sshin		m0->m_pkthdr.len = m0->m_len;
165753541Sshin	}
165853541Sshin
165953541Sshin	/*
166053541Sshin	 * Redirected header option spec (RFC2461 4.6.3) talks nothing
166153541Sshin	 * about padding/truncate rule for the original IP packet.
166253541Sshin	 * From the discussion on IPv6imp in Feb 1999, the consensus was:
166353541Sshin	 * - "attach as much as possible" is the goal
166453541Sshin	 * - pad if not aligned (original size can be guessed by original
166553541Sshin	 *   ip6 header)
166653541Sshin	 * Following code adds the padding if it is simple enough,
166753541Sshin	 * and truncates if not.
166853541Sshin	 */
166953541Sshin	if (m0->m_next || m0->m_pkthdr.len != m0->m_len)
167053541Sshin		panic("assumption failed in %s:%d\n", __FILE__, __LINE__);
167153541Sshin
167253541Sshin	if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) {
167353541Sshin		/* not enough room, truncate */
167453541Sshin		m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
167553541Sshin	} else {
167653541Sshin		/* enough room, pad or truncate */
167753541Sshin		size_t extra;
167853541Sshin
167953541Sshin		extra = m0->m_pkthdr.len % 8;
168053541Sshin		if (extra) {
168153541Sshin			/* pad if easy enough, truncate if not */
168253541Sshin			if (8 - extra <= M_TRAILINGSPACE(m0)) {
168353541Sshin				/* pad */
168453541Sshin				m0->m_len += (8 - extra);
168553541Sshin				m0->m_pkthdr.len += (8 - extra);
168653541Sshin			} else {
168753541Sshin				/* truncate */
168853541Sshin				m0->m_pkthdr.len -= extra;
168953541Sshin				m0->m_len -= extra;
169053541Sshin			}
169153541Sshin		}
169253541Sshin		len = m0->m_pkthdr.len + sizeof(*nd_opt_rh);
169353541Sshin		m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh);
169453541Sshin	}
169553541Sshin
169653541Sshin	nd_opt_rh = (struct nd_opt_rd_hdr *)p;
169753541Sshin	bzero(nd_opt_rh, sizeof(*nd_opt_rh));
169853541Sshin	nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER;
169953541Sshin	nd_opt_rh->nd_opt_rh_len = len >> 3;
170053541Sshin	p += sizeof(*nd_opt_rh);
170153541Sshin	m->m_pkthdr.len = m->m_len = p - (u_char *)ip6;
170253541Sshin
170353541Sshin	/* connect m0 to m */
170453541Sshin	m->m_next = m0;
170553541Sshin	m->m_pkthdr.len = m->m_len + m0->m_len;
170653541Sshin    }
170753541Sshinnoredhdropt:;
170853541Sshin
170953541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src))
171053541Sshin		sip6->ip6_src.s6_addr16[1] = 0;
171153541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst))
171253541Sshin		sip6->ip6_dst.s6_addr16[1] = 0;
171353541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target))
171453541Sshin		nd_rd->nd_rd_target.s6_addr16[1] = 0;
171553541Sshin	if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst))
171653541Sshin		nd_rd->nd_rd_dst.s6_addr16[1] = 0;
171753541Sshin
171853541Sshin	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
171953541Sshin
172053541Sshin	nd_rd->nd_rd_cksum = 0;
172153541Sshin	nd_rd->nd_rd_cksum
172253541Sshin		= in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen));
172353541Sshin
172453541Sshin	/* send the packet to outside... */
172553541Sshin	ip6_output(m, NULL, NULL, 0, NULL, &outif);
172653541Sshin	if (outif) {
172753541Sshin		icmp6_ifstat_inc(outif, ifs6_out_msg);
172853541Sshin		icmp6_ifstat_inc(outif, ifs6_out_redirect);
172953541Sshin	}
173053541Sshin	icmp6stat.icp6s_outhist[ND_REDIRECT]++;
173153541Sshin
173253541Sshin	return;
173353541Sshin
173453541Sshinfail:
173553541Sshin	if (m)
173653541Sshin		m_freem(m);
173753541Sshin	if (m0)
173853541Sshin		m_freem(m0);
173953541Sshin}
174053541Sshin
174153541Sshin/*
174253541Sshin * ICMPv6 socket option processing.
174353541Sshin */
174453541Sshinint
174553541Sshinicmp6_ctloutput(so, sopt)
174653541Sshin	struct socket *so;
174753541Sshin	struct sockopt *sopt;
174853541Sshin{
174953541Sshin	int error = 0;
175053541Sshin	int optlen;
175153541Sshin	register struct inpcb *inp = sotoinpcb(so);
175253541Sshin	int level, op, optname;
175353541Sshin
175453541Sshin	if (sopt) {
175553541Sshin		level = sopt->sopt_level;
175653541Sshin		op = sopt->sopt_dir;
175753541Sshin		optname = sopt->sopt_name;
175853541Sshin		optlen = sopt->sopt_valsize;
175953541Sshin	} else
176053541Sshin		level = op = optname = optlen = 0;
176153541Sshin	if (level != IPPROTO_ICMPV6) {
176253541Sshin		return EINVAL;
176353541Sshin	}
176453541Sshin
176553541Sshin	switch(op) {
176653541Sshin	case PRCO_SETOPT:
176753541Sshin		switch (optname) {
176853541Sshin		case ICMP6_FILTER:
176953541Sshin		    {
177053541Sshin			struct icmp6_filter *p;
177153541Sshin
177253541Sshin			if (optlen != sizeof(*p)) {
177353541Sshin				error = EMSGSIZE;
177453541Sshin				break;
177553541Sshin			}
177653541Sshin			if (inp->in6p_icmp6filt == NULL) {
177753541Sshin				error = EINVAL;
177853541Sshin				break;
177953541Sshin			}
178053541Sshin			error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen,
178153541Sshin				optlen);
178253541Sshin			break;
178353541Sshin		    }
178453541Sshin
178553541Sshin		default:
178653541Sshin			error = ENOPROTOOPT;
178753541Sshin			break;
178853541Sshin		}
178953541Sshin		break;
179053541Sshin
179153541Sshin	case PRCO_GETOPT:
179253541Sshin		switch (optname) {
179353541Sshin		case ICMP6_FILTER:
179453541Sshin		    {
179553541Sshin			if (inp->in6p_icmp6filt == NULL) {
179653541Sshin				error = EINVAL;
179753541Sshin				break;
179853541Sshin			}
179953541Sshin			error = sooptcopyout(sopt, inp->in6p_icmp6filt,
180053541Sshin				sizeof(struct icmp6_filter));
180153541Sshin			break;
180253541Sshin		    }
180353541Sshin
180453541Sshin		default:
180553541Sshin			error = ENOPROTOOPT;
180653541Sshin			break;
180753541Sshin		}
180853541Sshin		break;
180953541Sshin	}
181053541Sshin
181153541Sshin	return(error);
181253541Sshin}
181353541Sshin
181453541Sshin/*
181553541Sshin * Perform rate limit check.
181653541Sshin * Returns 0 if it is okay to send the icmp6 packet.
181753541Sshin * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate
181853541Sshin * limitation.
181953541Sshin *
182053541Sshin * XXX per-destination/type check necessary?
182153541Sshin */
182253541Sshinstatic int
182353541Sshinicmp6_ratelimit(dst, type, code)
182453541Sshin	const struct in6_addr *dst;	/* not used at this moment */
182553541Sshin	const int type;			/* not used at this moment */
182653541Sshin	const int code;			/* not used at this moment */
182753541Sshin{
182853541Sshin	struct timeval tp;
182953541Sshin	long sec_diff, usec_diff;
183053541Sshin
183153541Sshin	/* If we are not doing rate limitation, it is always okay to send */
183253541Sshin	if (!icmp6errratelim)
183353541Sshin		return 0;
183453541Sshin
183553541Sshin	microtime(&tp);
183653541Sshin	tp.tv_sec = time_second;
183753541Sshin	if (tp.tv_sec < icmp6_nextsend.tv_sec
183853541Sshin	 || (tp.tv_sec == icmp6_nextsend.tv_sec
183953541Sshin	  && tp.tv_usec < icmp6_nextsend.tv_usec)) {
184053541Sshin		/* The packet is subject to rate limit */
184153541Sshin		return 1;
184253541Sshin	}
184353541Sshin	sec_diff = icmp6errratelim / 1000000;
184453541Sshin	usec_diff = icmp6errratelim % 1000000;
184553541Sshin	icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff;
184653541Sshin	if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) {
184753541Sshin		icmp6_nextsend.tv_sec++;
184853541Sshin		icmp6_nextsend.tv_usec -= 1000000;
184953541Sshin	}
185053541Sshin
185153541Sshin	/* it is okay to send this */
185253541Sshin	return 0;
185353541Sshin}
1854