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