182cd038dSYoshinobu Inoue /* 282cd038dSYoshinobu Inoue * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 382cd038dSYoshinobu Inoue * All rights reserved. 482cd038dSYoshinobu Inoue * 582cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 682cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 782cd038dSYoshinobu Inoue * are met: 882cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 982cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 1082cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 1182cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 1282cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 1382cd038dSYoshinobu Inoue * 3. Neither the name of the project nor the names of its contributors 1482cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 1582cd038dSYoshinobu Inoue * without specific prior written permission. 1682cd038dSYoshinobu Inoue * 1782cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1882cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1982cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2082cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2182cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2282cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2382cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2482cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2582cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2682cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2782cd038dSYoshinobu Inoue * SUCH DAMAGE. 2882cd038dSYoshinobu Inoue * 2982cd038dSYoshinobu Inoue * $FreeBSD$ 3082cd038dSYoshinobu Inoue */ 3182cd038dSYoshinobu Inoue 3282cd038dSYoshinobu Inoue /* 3382cd038dSYoshinobu Inoue * Copyright (c) 1982, 1986, 1988, 1993 3482cd038dSYoshinobu Inoue * The Regents of the University of California. All rights reserved. 3582cd038dSYoshinobu Inoue * 3682cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 3782cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 3882cd038dSYoshinobu Inoue * are met: 3982cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 4082cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 4182cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 4282cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 4382cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 4482cd038dSYoshinobu Inoue * 3. All advertising materials mentioning features or use of this software 4582cd038dSYoshinobu Inoue * must display the following acknowledgement: 4682cd038dSYoshinobu Inoue * This product includes software developed by the University of 4782cd038dSYoshinobu Inoue * California, Berkeley and its contributors. 4882cd038dSYoshinobu Inoue * 4. Neither the name of the University nor the names of its contributors 4982cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 5082cd038dSYoshinobu Inoue * without specific prior written permission. 5182cd038dSYoshinobu Inoue * 5282cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5382cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5482cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5582cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5682cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5782cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5882cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5982cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6082cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6182cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6282cd038dSYoshinobu Inoue * SUCH DAMAGE. 6382cd038dSYoshinobu Inoue * 6482cd038dSYoshinobu Inoue * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 6582cd038dSYoshinobu Inoue */ 6682cd038dSYoshinobu Inoue 6782cd038dSYoshinobu Inoue #include "opt_inet.h" 6882cd038dSYoshinobu Inoue #include "opt_ipsec.h" 6982cd038dSYoshinobu Inoue #include "opt_key.h" 7082cd038dSYoshinobu Inoue 7182cd038dSYoshinobu Inoue #include <sys/param.h> 7282cd038dSYoshinobu Inoue #include <sys/systm.h> 7382cd038dSYoshinobu Inoue #include <sys/malloc.h> 7482cd038dSYoshinobu Inoue #include <sys/mbuf.h> 7582cd038dSYoshinobu Inoue #include <sys/protosw.h> 7682cd038dSYoshinobu Inoue #include <sys/socket.h> 7782cd038dSYoshinobu Inoue #include <sys/socketvar.h> 7882cd038dSYoshinobu Inoue #include <sys/time.h> 7982cd038dSYoshinobu Inoue #include <sys/kernel.h> 8082cd038dSYoshinobu Inoue #include <sys/syslog.h> 8182cd038dSYoshinobu Inoue #include <sys/domain.h> 8282cd038dSYoshinobu Inoue 8382cd038dSYoshinobu Inoue #include <net/if.h> 8482cd038dSYoshinobu Inoue #include <net/route.h> 8582cd038dSYoshinobu Inoue #include <net/if_dl.h> 8682cd038dSYoshinobu Inoue #include <net/if_types.h> 8782cd038dSYoshinobu Inoue 8882cd038dSYoshinobu Inoue #include <netinet/in.h> 8982cd038dSYoshinobu Inoue #include <netinet/in_var.h> 9082cd038dSYoshinobu Inoue #include <netinet6/ip6.h> 9182cd038dSYoshinobu Inoue #include <netinet6/ip6_var.h> 9282cd038dSYoshinobu Inoue #include <netinet6/icmp6.h> 9382cd038dSYoshinobu Inoue #include <netinet6/mld6_var.h> 9482cd038dSYoshinobu Inoue #include <netinet/in_pcb.h> 9582cd038dSYoshinobu Inoue #include <netinet6/nd6.h> 9682cd038dSYoshinobu Inoue #include <netinet6/in6_ifattach.h> 9782cd038dSYoshinobu Inoue #include <netinet6/ip6protosw.h> 9882cd038dSYoshinobu Inoue 9982cd038dSYoshinobu Inoue #ifdef IPSEC 10082cd038dSYoshinobu Inoue #include <netinet6/ipsec.h> 10182cd038dSYoshinobu Inoue #ifdef INET6 10282cd038dSYoshinobu Inoue #include <netinet6/ipsec6.h> 10382cd038dSYoshinobu Inoue #endif /* INET6 */ 10482cd038dSYoshinobu Inoue #include <netkey/key.h> 10582cd038dSYoshinobu Inoue #ifdef KEY_DEBUG 10682cd038dSYoshinobu Inoue #include <netkey/key_debug.h> 10782cd038dSYoshinobu Inoue #ifdef INET6 10882cd038dSYoshinobu Inoue #include <netkey/key_debug6.h> 10982cd038dSYoshinobu Inoue #endif /* INET6 */ 11082cd038dSYoshinobu Inoue #else 11182cd038dSYoshinobu Inoue #define DPRINTF(lev,arg) 11282cd038dSYoshinobu Inoue #define DDO(lev, stmt) 11382cd038dSYoshinobu Inoue #define DP(x, y, z) 11482cd038dSYoshinobu Inoue #endif /* KEY_DEBUG */ 11582cd038dSYoshinobu Inoue #endif /* IPSEC */ 11682cd038dSYoshinobu Inoue 11782cd038dSYoshinobu Inoue /* #include "faith.h" */ 11882cd038dSYoshinobu Inoue 11982cd038dSYoshinobu Inoue #include <net/net_osdep.h> 12082cd038dSYoshinobu Inoue 12182cd038dSYoshinobu Inoue extern struct domain inet6domain; 12282cd038dSYoshinobu Inoue extern struct ip6protosw inet6sw[]; 12382cd038dSYoshinobu Inoue extern u_char ip6_protox[]; 12482cd038dSYoshinobu Inoue 12582cd038dSYoshinobu Inoue struct icmp6stat icmp6stat; 12682cd038dSYoshinobu Inoue 12782cd038dSYoshinobu Inoue extern struct inpcbhead ripcb; 12882cd038dSYoshinobu Inoue extern u_int icmp6errratelim; 12982cd038dSYoshinobu Inoue 13082cd038dSYoshinobu Inoue static int icmp6_rip6_input __P((struct mbuf **, int)); 13182cd038dSYoshinobu Inoue static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); 13282cd038dSYoshinobu Inoue static const char *icmp6_redirect_diag __P((struct in6_addr *, 13382cd038dSYoshinobu Inoue struct in6_addr *, 13482cd038dSYoshinobu Inoue struct in6_addr *)); 13582cd038dSYoshinobu Inoue static struct mbuf *ni6_input __P((struct mbuf *, int)); 13682cd038dSYoshinobu Inoue static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, 13782cd038dSYoshinobu Inoue struct ifnet **)); 13882cd038dSYoshinobu Inoue static int ni6_store_addrs __P((struct icmp6_nodeinfo *, 13982cd038dSYoshinobu Inoue struct icmp6_nodeinfo *, 14082cd038dSYoshinobu Inoue struct ifnet *, int)); 14182cd038dSYoshinobu Inoue 14282cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 14382cd038dSYoshinobu Inoue static struct route_in6 icmp6_reflect_rt; 14482cd038dSYoshinobu Inoue #endif 14582cd038dSYoshinobu Inoue static struct timeval icmp6_nextsend = {0, 0}; 14682cd038dSYoshinobu Inoue 14782cd038dSYoshinobu Inoue void 14882cd038dSYoshinobu Inoue icmp6_init() 14982cd038dSYoshinobu Inoue { 15082cd038dSYoshinobu Inoue mld6_init(); 15182cd038dSYoshinobu Inoue } 15282cd038dSYoshinobu Inoue 15382cd038dSYoshinobu Inoue /* 15482cd038dSYoshinobu Inoue * Generate an error packet of type error in response to bad IP6 packet. 15582cd038dSYoshinobu Inoue */ 15682cd038dSYoshinobu Inoue void 15782cd038dSYoshinobu Inoue icmp6_error(m, type, code, param) 15882cd038dSYoshinobu Inoue struct mbuf *m; 15982cd038dSYoshinobu Inoue int type, code, param; 16082cd038dSYoshinobu Inoue { 16182cd038dSYoshinobu Inoue struct ip6_hdr *oip6, *nip6; 16282cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6; 16382cd038dSYoshinobu Inoue u_int prep; 16482cd038dSYoshinobu Inoue int off; 16582cd038dSYoshinobu Inoue u_char nxt; 16682cd038dSYoshinobu Inoue 16782cd038dSYoshinobu Inoue icmp6stat.icp6s_error++; 16882cd038dSYoshinobu Inoue 16982cd038dSYoshinobu Inoue if (m->m_flags & M_DECRYPTED) 17082cd038dSYoshinobu Inoue goto freeit; 17182cd038dSYoshinobu Inoue 17282cd038dSYoshinobu Inoue oip6 = mtod(m, struct ip6_hdr *); 17382cd038dSYoshinobu Inoue 17482cd038dSYoshinobu Inoue /* 17582cd038dSYoshinobu Inoue * Multicast destination check. For unrecognized option errors, 17682cd038dSYoshinobu Inoue * this check has already done in ip6_unknown_opt(), so we can 17782cd038dSYoshinobu Inoue * check only for other errors. 17882cd038dSYoshinobu Inoue */ 17982cd038dSYoshinobu Inoue if ((m->m_flags & (M_BCAST|M_MCAST) || 18082cd038dSYoshinobu Inoue IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && 18182cd038dSYoshinobu Inoue (type != ICMP6_PACKET_TOO_BIG && 18282cd038dSYoshinobu Inoue (type != ICMP6_PARAM_PROB || 18382cd038dSYoshinobu Inoue code != ICMP6_PARAMPROB_OPTION))) 18482cd038dSYoshinobu Inoue goto freeit; 18582cd038dSYoshinobu Inoue 18682cd038dSYoshinobu Inoue /* Source address check. XXX: the case of anycast source? */ 18782cd038dSYoshinobu Inoue if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || 18882cd038dSYoshinobu Inoue IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) 18982cd038dSYoshinobu Inoue goto freeit; 19082cd038dSYoshinobu Inoue 19182cd038dSYoshinobu Inoue /* 19282cd038dSYoshinobu Inoue * If the erroneous packet is also an ICMP error, discard it. 19382cd038dSYoshinobu Inoue */ 19482cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); 19582cd038dSYoshinobu Inoue off = sizeof(struct ip6_hdr); 19682cd038dSYoshinobu Inoue nxt = oip6->ip6_nxt; 19782cd038dSYoshinobu Inoue while(1) { /* XXX: should avoid inf. loop explicitly? */ 19882cd038dSYoshinobu Inoue struct ip6_ext *ip6e; 19982cd038dSYoshinobu Inoue struct icmp6_hdr *icp; 20082cd038dSYoshinobu Inoue 20182cd038dSYoshinobu Inoue switch(nxt) { 20282cd038dSYoshinobu Inoue case IPPROTO_IPV6: 20382cd038dSYoshinobu Inoue case IPPROTO_IPV4: 20482cd038dSYoshinobu Inoue case IPPROTO_UDP: 20582cd038dSYoshinobu Inoue case IPPROTO_TCP: 20682cd038dSYoshinobu Inoue case IPPROTO_ESP: 20782cd038dSYoshinobu Inoue case IPPROTO_FRAGMENT: 20882cd038dSYoshinobu Inoue /* 20982cd038dSYoshinobu Inoue * ICMPv6 error must not be fragmented. 21082cd038dSYoshinobu Inoue * XXX: but can we trust the sender? 21182cd038dSYoshinobu Inoue */ 21282cd038dSYoshinobu Inoue default: 21382cd038dSYoshinobu Inoue /* What if unknown header followed by ICMP error? */ 21482cd038dSYoshinobu Inoue goto generate; 21582cd038dSYoshinobu Inoue case IPPROTO_ICMPV6: 21682cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); 21782cd038dSYoshinobu Inoue icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); 21882cd038dSYoshinobu Inoue if (icp->icmp6_type < ICMP6_ECHO_REQUEST 21982cd038dSYoshinobu Inoue || icp->icmp6_type == ND_REDIRECT) { 22082cd038dSYoshinobu Inoue /* 22182cd038dSYoshinobu Inoue * ICMPv6 error 22282cd038dSYoshinobu Inoue * Special case: for redirect (which is 22382cd038dSYoshinobu Inoue * informational) we must not send icmp6 error. 22482cd038dSYoshinobu Inoue */ 22582cd038dSYoshinobu Inoue icmp6stat.icp6s_canterror++; 22682cd038dSYoshinobu Inoue goto freeit; 22782cd038dSYoshinobu Inoue } else { 22882cd038dSYoshinobu Inoue /* ICMPv6 informational */ 22982cd038dSYoshinobu Inoue goto generate; 23082cd038dSYoshinobu Inoue } 23182cd038dSYoshinobu Inoue case IPPROTO_HOPOPTS: 23282cd038dSYoshinobu Inoue case IPPROTO_DSTOPTS: 23382cd038dSYoshinobu Inoue case IPPROTO_ROUTING: 23482cd038dSYoshinobu Inoue case IPPROTO_AH: 23582cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), ); 23682cd038dSYoshinobu Inoue ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); 23782cd038dSYoshinobu Inoue if (nxt == IPPROTO_AH) 23882cd038dSYoshinobu Inoue off += (ip6e->ip6e_len + 2) << 2; 23982cd038dSYoshinobu Inoue else 24082cd038dSYoshinobu Inoue off += (ip6e->ip6e_len + 1) << 3; 24182cd038dSYoshinobu Inoue nxt = ip6e->ip6e_nxt; 24282cd038dSYoshinobu Inoue break; 24382cd038dSYoshinobu Inoue } 24482cd038dSYoshinobu Inoue } 24582cd038dSYoshinobu Inoue 24682cd038dSYoshinobu Inoue freeit: 24782cd038dSYoshinobu Inoue /* 24882cd038dSYoshinobu Inoue * If we can't tell wheter or not we can generate ICMP6, free it. 24982cd038dSYoshinobu Inoue */ 25082cd038dSYoshinobu Inoue m_freem(m); 25182cd038dSYoshinobu Inoue return; 25282cd038dSYoshinobu Inoue 25382cd038dSYoshinobu Inoue generate: 25482cd038dSYoshinobu Inoue oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ 25582cd038dSYoshinobu Inoue 25682cd038dSYoshinobu Inoue /* Finally, do rate limitation check. */ 25782cd038dSYoshinobu Inoue if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { 25882cd038dSYoshinobu Inoue icmp6stat.icp6s_toofreq++; 25982cd038dSYoshinobu Inoue goto freeit; 26082cd038dSYoshinobu Inoue } 26182cd038dSYoshinobu Inoue 26282cd038dSYoshinobu Inoue /* 26382cd038dSYoshinobu Inoue * OK, ICMP6 can be generated. 26482cd038dSYoshinobu Inoue */ 26582cd038dSYoshinobu Inoue 26682cd038dSYoshinobu Inoue if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) 26782cd038dSYoshinobu Inoue m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); 26882cd038dSYoshinobu Inoue 26982cd038dSYoshinobu Inoue prep = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 27082cd038dSYoshinobu Inoue M_PREPEND(m, prep, M_DONTWAIT); 27182cd038dSYoshinobu Inoue if (m && m->m_len < prep) 27282cd038dSYoshinobu Inoue m = m_pullup(m, prep); 27382cd038dSYoshinobu Inoue if (m == NULL) { 27482cd038dSYoshinobu Inoue printf("ENOBUFS in icmp6_error %d\n", __LINE__); 27582cd038dSYoshinobu Inoue return; 27682cd038dSYoshinobu Inoue } 27782cd038dSYoshinobu Inoue 27882cd038dSYoshinobu Inoue nip6 = mtod(m, struct ip6_hdr *); 27982cd038dSYoshinobu Inoue nip6->ip6_src = oip6->ip6_src; 28082cd038dSYoshinobu Inoue nip6->ip6_dst = oip6->ip6_dst; 28182cd038dSYoshinobu Inoue 28282cd038dSYoshinobu Inoue if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 28382cd038dSYoshinobu Inoue oip6->ip6_src.s6_addr16[1] = 0; 28482cd038dSYoshinobu Inoue if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 28582cd038dSYoshinobu Inoue oip6->ip6_dst.s6_addr16[1] = 0; 28682cd038dSYoshinobu Inoue 28782cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(nip6 + 1); 28882cd038dSYoshinobu Inoue icmp6->icmp6_type = type; 28982cd038dSYoshinobu Inoue icmp6->icmp6_code = code; 29082cd038dSYoshinobu Inoue icmp6->icmp6_pptr = htonl((u_int32_t)param); 29182cd038dSYoshinobu Inoue 29282cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[type]++; 29382cd038dSYoshinobu Inoue icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ 29482cd038dSYoshinobu Inoue } 29582cd038dSYoshinobu Inoue 29682cd038dSYoshinobu Inoue /* 29782cd038dSYoshinobu Inoue * Process a received ICMP6 message. 29882cd038dSYoshinobu Inoue */ 29982cd038dSYoshinobu Inoue int 30082cd038dSYoshinobu Inoue icmp6_input(mp, offp, proto) 30182cd038dSYoshinobu Inoue struct mbuf **mp; 30282cd038dSYoshinobu Inoue int *offp, proto; 30382cd038dSYoshinobu Inoue { 30482cd038dSYoshinobu Inoue struct mbuf *m = *mp, *n; 30582cd038dSYoshinobu Inoue struct ip6_hdr *ip6, *nip6; 30682cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6, *nicmp6; 30782cd038dSYoshinobu Inoue int off = *offp; 30882cd038dSYoshinobu Inoue int icmp6len = m->m_pkthdr.len - *offp; 30982cd038dSYoshinobu Inoue int code, sum, noff; 31082cd038dSYoshinobu Inoue struct sockaddr_in6 icmp6src; 31182cd038dSYoshinobu Inoue 31282cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); 31382cd038dSYoshinobu Inoue /* m might change if M_LOOP. So, call mtod after this */ 31482cd038dSYoshinobu Inoue 31582cd038dSYoshinobu Inoue /* 31682cd038dSYoshinobu Inoue * Locate icmp6 structure in mbuf, and check 31782cd038dSYoshinobu Inoue * that not corrupted and of at least minimum length 31882cd038dSYoshinobu Inoue */ 31982cd038dSYoshinobu Inoue 32082cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 32182cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct icmp6_hdr)) { 32282cd038dSYoshinobu Inoue icmp6stat.icp6s_tooshort++; 32382cd038dSYoshinobu Inoue goto freeit; 32482cd038dSYoshinobu Inoue } 32582cd038dSYoshinobu Inoue 32682cd038dSYoshinobu Inoue /* 32782cd038dSYoshinobu Inoue * calculate the checksum 32882cd038dSYoshinobu Inoue */ 32982cd038dSYoshinobu Inoue 33082cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); 33182cd038dSYoshinobu Inoue code = icmp6->icmp6_code; 33282cd038dSYoshinobu Inoue 33382cd038dSYoshinobu Inoue if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { 33482cd038dSYoshinobu Inoue log(LOG_ERR, 33582cd038dSYoshinobu Inoue "ICMP6 checksum error(%d|%x) %s\n", 33682cd038dSYoshinobu Inoue icmp6->icmp6_type, 33782cd038dSYoshinobu Inoue sum, 33882cd038dSYoshinobu Inoue ip6_sprintf(&ip6->ip6_src)); 33982cd038dSYoshinobu Inoue icmp6stat.icp6s_checksum++; 34082cd038dSYoshinobu Inoue goto freeit; 34182cd038dSYoshinobu Inoue } 34282cd038dSYoshinobu Inoue 34382cd038dSYoshinobu Inoue #if defined(NFAITH) && 0 < NFAITH 34482cd038dSYoshinobu Inoue if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { 34582cd038dSYoshinobu Inoue /* 34682cd038dSYoshinobu Inoue * Deliver very specific ICMP6 type only. 34782cd038dSYoshinobu Inoue * This is important to deilver TOOBIG. Otherwise PMTUD 34882cd038dSYoshinobu Inoue * will not work. 34982cd038dSYoshinobu Inoue */ 35082cd038dSYoshinobu Inoue switch (icmp6->icmp6_type) { 35182cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH: 35282cd038dSYoshinobu Inoue case ICMP6_PACKET_TOO_BIG: 35382cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEEDED: 35482cd038dSYoshinobu Inoue break; 35582cd038dSYoshinobu Inoue default: 35682cd038dSYoshinobu Inoue goto freeit; 35782cd038dSYoshinobu Inoue } 35882cd038dSYoshinobu Inoue } 35982cd038dSYoshinobu Inoue #endif 36082cd038dSYoshinobu Inoue 36182cd038dSYoshinobu Inoue #ifdef IPSEC 36282cd038dSYoshinobu Inoue /* drop it if it does not match the default policy */ 36382cd038dSYoshinobu Inoue if (ipsec6_in_reject(m, NULL)) { 36482cd038dSYoshinobu Inoue ipsecstat.in_polvio++; 36582cd038dSYoshinobu Inoue goto freeit; 36682cd038dSYoshinobu Inoue } 36782cd038dSYoshinobu Inoue #endif 36882cd038dSYoshinobu Inoue 36982cd038dSYoshinobu Inoue icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; 37082cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); 37182cd038dSYoshinobu Inoue if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) 37282cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); 37382cd038dSYoshinobu Inoue 37482cd038dSYoshinobu Inoue switch (icmp6->icmp6_type) { 37582cd038dSYoshinobu Inoue 37682cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH: 37782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); 37882cd038dSYoshinobu Inoue switch (code) { 37982cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_NOROUTE: 38082cd038dSYoshinobu Inoue code = PRC_UNREACH_NET; 38182cd038dSYoshinobu Inoue break; 38282cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_ADMIN: 38382cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib); 38482cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_ADDR: 38582cd038dSYoshinobu Inoue code = PRC_UNREACH_HOST; 38682cd038dSYoshinobu Inoue break; 38782cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_NOTNEIGHBOR: 38882cd038dSYoshinobu Inoue code = PRC_UNREACH_SRCFAIL; 38982cd038dSYoshinobu Inoue break; 39082cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_NOPORT: 39182cd038dSYoshinobu Inoue code = PRC_UNREACH_PORT; 39282cd038dSYoshinobu Inoue break; 39382cd038dSYoshinobu Inoue default: 39482cd038dSYoshinobu Inoue goto badcode; 39582cd038dSYoshinobu Inoue } 39682cd038dSYoshinobu Inoue goto deliver; 39782cd038dSYoshinobu Inoue break; 39882cd038dSYoshinobu Inoue 39982cd038dSYoshinobu Inoue case ICMP6_PACKET_TOO_BIG: 40082cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); 40182cd038dSYoshinobu Inoue if (code != 0) 40282cd038dSYoshinobu Inoue goto badcode; 40382cd038dSYoshinobu Inoue { 40482cd038dSYoshinobu Inoue u_int mtu = ntohl(icmp6->icmp6_mtu); 40582cd038dSYoshinobu Inoue struct rtentry *rt = NULL; 40682cd038dSYoshinobu Inoue struct sockaddr_in6 sin6; 40782cd038dSYoshinobu Inoue 40882cd038dSYoshinobu Inoue code = PRC_MSGSIZE; 40982cd038dSYoshinobu Inoue bzero(&sin6, sizeof(sin6)); 41082cd038dSYoshinobu Inoue sin6.sin6_family = PF_INET6; 41182cd038dSYoshinobu Inoue sin6.sin6_len = sizeof(struct sockaddr_in6); 41282cd038dSYoshinobu Inoue sin6.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; 41382cd038dSYoshinobu Inoue rt = rtalloc1((struct sockaddr *)&sin6, 0, 41482cd038dSYoshinobu Inoue RTF_CLONING | RTF_PRCLONING); 41582cd038dSYoshinobu Inoue if (rt && (rt->rt_flags & RTF_HOST) 41682cd038dSYoshinobu Inoue && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { 41782cd038dSYoshinobu Inoue if (mtu < IPV6_MMTU) { 41882cd038dSYoshinobu Inoue /* xxx */ 41982cd038dSYoshinobu Inoue rt->rt_rmx.rmx_locks |= RTV_MTU; 42082cd038dSYoshinobu Inoue } else if (mtu < rt->rt_ifp->if_mtu && 42182cd038dSYoshinobu Inoue rt->rt_rmx.rmx_mtu > mtu) { 42282cd038dSYoshinobu Inoue rt->rt_rmx.rmx_mtu = mtu; 42382cd038dSYoshinobu Inoue } 42482cd038dSYoshinobu Inoue } 42582cd038dSYoshinobu Inoue if (rt) 42682cd038dSYoshinobu Inoue RTFREE(rt); 42782cd038dSYoshinobu Inoue 42882cd038dSYoshinobu Inoue goto deliver; 42982cd038dSYoshinobu Inoue } 43082cd038dSYoshinobu Inoue break; 43182cd038dSYoshinobu Inoue 43282cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEEDED: 43382cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed); 43482cd038dSYoshinobu Inoue switch (code) { 43582cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEED_TRANSIT: 43682cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEED_REASSEMBLY: 43782cd038dSYoshinobu Inoue code += PRC_TIMXCEED_INTRANS; 43882cd038dSYoshinobu Inoue break; 43982cd038dSYoshinobu Inoue default: 44082cd038dSYoshinobu Inoue goto badcode; 44182cd038dSYoshinobu Inoue } 44282cd038dSYoshinobu Inoue goto deliver; 44382cd038dSYoshinobu Inoue break; 44482cd038dSYoshinobu Inoue 44582cd038dSYoshinobu Inoue case ICMP6_PARAM_PROB: 44682cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob); 44782cd038dSYoshinobu Inoue switch (code) { 44882cd038dSYoshinobu Inoue case ICMP6_PARAMPROB_NEXTHEADER: 44982cd038dSYoshinobu Inoue code = PRC_UNREACH_PROTOCOL; 45082cd038dSYoshinobu Inoue break; 45182cd038dSYoshinobu Inoue case ICMP6_PARAMPROB_HEADER: 45282cd038dSYoshinobu Inoue case ICMP6_PARAMPROB_OPTION: 45382cd038dSYoshinobu Inoue code = PRC_PARAMPROB; 45482cd038dSYoshinobu Inoue break; 45582cd038dSYoshinobu Inoue default: 45682cd038dSYoshinobu Inoue goto badcode; 45782cd038dSYoshinobu Inoue } 45882cd038dSYoshinobu Inoue goto deliver; 45982cd038dSYoshinobu Inoue break; 46082cd038dSYoshinobu Inoue 46182cd038dSYoshinobu Inoue case ICMP6_ECHO_REQUEST: 46282cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo); 46382cd038dSYoshinobu Inoue if (code != 0) 46482cd038dSYoshinobu Inoue goto badcode; 46582cd038dSYoshinobu Inoue if ((n = m_copy(m, 0, M_COPYALL)) == NULL) { 46682cd038dSYoshinobu Inoue /* Give up remote */ 46782cd038dSYoshinobu Inoue break; 46882cd038dSYoshinobu Inoue } 46982cd038dSYoshinobu Inoue if (n->m_flags & M_EXT) { 47082cd038dSYoshinobu Inoue int gap, move; 47182cd038dSYoshinobu Inoue struct mbuf *n0 = n; 47282cd038dSYoshinobu Inoue 47382cd038dSYoshinobu Inoue /* 47482cd038dSYoshinobu Inoue * Prepare an internal mbuf. m_pullup() doesn't 47582cd038dSYoshinobu Inoue * always copy the length we specified. 47682cd038dSYoshinobu Inoue */ 47782cd038dSYoshinobu Inoue MGETHDR(n, M_DONTWAIT, n0->m_type); 47882cd038dSYoshinobu Inoue if (n == NULL) { 47982cd038dSYoshinobu Inoue /* Give up remote */ 48082cd038dSYoshinobu Inoue m_freem(n0); 48182cd038dSYoshinobu Inoue break; 48282cd038dSYoshinobu Inoue } 48382cd038dSYoshinobu Inoue M_COPY_PKTHDR(n, n0); 48482cd038dSYoshinobu Inoue n0->m_flags &= ~M_PKTHDR; 48582cd038dSYoshinobu Inoue n->m_next = n0; 48682cd038dSYoshinobu Inoue /* 48782cd038dSYoshinobu Inoue * Copy IPv6 and ICMPv6 only. 48882cd038dSYoshinobu Inoue */ 48982cd038dSYoshinobu Inoue nip6 = mtod(n, struct ip6_hdr *); 49082cd038dSYoshinobu Inoue bcopy(ip6, nip6, sizeof(struct ip6_hdr)); 49182cd038dSYoshinobu Inoue nicmp6 = (struct icmp6_hdr *)(nip6 + 1); 49282cd038dSYoshinobu Inoue bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); 49382cd038dSYoshinobu Inoue /* 49482cd038dSYoshinobu Inoue * Adjust mbuf. ip6_plen will be adjusted. 49582cd038dSYoshinobu Inoue */ 49682cd038dSYoshinobu Inoue noff = sizeof(struct ip6_hdr); 49782cd038dSYoshinobu Inoue n->m_len = noff + sizeof(struct icmp6_hdr); 49882cd038dSYoshinobu Inoue move = off + sizeof(struct icmp6_hdr); 49982cd038dSYoshinobu Inoue n0->m_len -= move; 50082cd038dSYoshinobu Inoue n0->m_data += move; 50182cd038dSYoshinobu Inoue gap = off - noff; 50282cd038dSYoshinobu Inoue n->m_pkthdr.len -= gap; 50382cd038dSYoshinobu Inoue } else { 50482cd038dSYoshinobu Inoue nip6 = mtod(n, struct ip6_hdr *); 50582cd038dSYoshinobu Inoue nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off); 50682cd038dSYoshinobu Inoue noff = off; 50782cd038dSYoshinobu Inoue } 50882cd038dSYoshinobu Inoue nicmp6->icmp6_type = ICMP6_ECHO_REPLY; 50982cd038dSYoshinobu Inoue nicmp6->icmp6_code = 0; 51082cd038dSYoshinobu Inoue if (n) { 51182cd038dSYoshinobu Inoue icmp6stat.icp6s_reflect++; 51282cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++; 51382cd038dSYoshinobu Inoue icmp6_reflect(n, noff); 51482cd038dSYoshinobu Inoue } 51582cd038dSYoshinobu Inoue break; 51682cd038dSYoshinobu Inoue 51782cd038dSYoshinobu Inoue case ICMP6_ECHO_REPLY: 51882cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply); 51982cd038dSYoshinobu Inoue if (code != 0) 52082cd038dSYoshinobu Inoue goto badcode; 52182cd038dSYoshinobu Inoue break; 52282cd038dSYoshinobu Inoue 52382cd038dSYoshinobu Inoue case MLD6_LISTENER_QUERY: 52482cd038dSYoshinobu Inoue case MLD6_LISTENER_REPORT: 52582cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct mld6_hdr)) 52682cd038dSYoshinobu Inoue goto badlen; 52782cd038dSYoshinobu Inoue if (icmp6->icmp6_type == MLD6_LISTENER_QUERY) /* XXX: ugly... */ 52882cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); 52982cd038dSYoshinobu Inoue else 53082cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); 53182cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); 53282cd038dSYoshinobu Inoue mld6_input(m, off); 53382cd038dSYoshinobu Inoue /* m stays. */ 53482cd038dSYoshinobu Inoue break; 53582cd038dSYoshinobu Inoue 53682cd038dSYoshinobu Inoue case MLD6_LISTENER_DONE: 53782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone); 53882cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct mld6_hdr)) /* necessary? */ 53982cd038dSYoshinobu Inoue goto badlen; 54082cd038dSYoshinobu Inoue break; /* nothing to be done in kernel */ 54182cd038dSYoshinobu Inoue 54282cd038dSYoshinobu Inoue case MLD6_MTRACE_RESP: 54382cd038dSYoshinobu Inoue case MLD6_MTRACE: 54482cd038dSYoshinobu Inoue /* XXX: these two are experimental. not officially defind. */ 54582cd038dSYoshinobu Inoue /* XXX: per-interface statistics? */ 54682cd038dSYoshinobu Inoue break; /* just pass it to the userland daemon */ 54782cd038dSYoshinobu Inoue 54882cd038dSYoshinobu Inoue case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ 54982cd038dSYoshinobu Inoue { 55082cd038dSYoshinobu Inoue enum { WRU, FQDN } mode; 55182cd038dSYoshinobu Inoue 55282cd038dSYoshinobu Inoue if (code != 0) 55382cd038dSYoshinobu Inoue goto badcode; 55482cd038dSYoshinobu Inoue if (icmp6len == sizeof(struct icmp6_hdr) + 4) 55582cd038dSYoshinobu Inoue mode = WRU; 55682cd038dSYoshinobu Inoue else if (icmp6len >= sizeof(struct icmp6_hdr) + 8) /* XXX */ 55782cd038dSYoshinobu Inoue mode = FQDN; 55882cd038dSYoshinobu Inoue else 55982cd038dSYoshinobu Inoue goto badlen; 56082cd038dSYoshinobu Inoue 56182cd038dSYoshinobu Inoue #define hostnamelen strlen(hostname) 56282cd038dSYoshinobu Inoue if (mode == FQDN) { 56382cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), 56482cd038dSYoshinobu Inoue IPPROTO_DONE); 56582cd038dSYoshinobu Inoue n = ni6_input(m, off); 56682cd038dSYoshinobu Inoue noff = sizeof(struct ip6_hdr); 56782cd038dSYoshinobu Inoue } else { 56882cd038dSYoshinobu Inoue u_char *p; 56982cd038dSYoshinobu Inoue 57082cd038dSYoshinobu Inoue MGETHDR(n, M_DONTWAIT, m->m_type); 57182cd038dSYoshinobu Inoue if (n == NULL) { 57282cd038dSYoshinobu Inoue /* Give up remote */ 57382cd038dSYoshinobu Inoue break; 57482cd038dSYoshinobu Inoue } 57582cd038dSYoshinobu Inoue /* 57682cd038dSYoshinobu Inoue * Copy IPv6 and ICMPv6 only. 57782cd038dSYoshinobu Inoue */ 57882cd038dSYoshinobu Inoue nip6 = mtod(n, struct ip6_hdr *); 57982cd038dSYoshinobu Inoue bcopy(ip6, nip6, sizeof(struct ip6_hdr)); 58082cd038dSYoshinobu Inoue nicmp6 = (struct icmp6_hdr *)(nip6 + 1); 58182cd038dSYoshinobu Inoue bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); 58282cd038dSYoshinobu Inoue p = (u_char *)(nicmp6 + 1); 58382cd038dSYoshinobu Inoue bzero(p, 4); 58482cd038dSYoshinobu Inoue bcopy(hostname, p + 4, hostnamelen); 58582cd038dSYoshinobu Inoue noff = sizeof(struct ip6_hdr); 58682cd038dSYoshinobu Inoue M_COPY_PKTHDR(n, m); /* just for recvif */ 58782cd038dSYoshinobu Inoue n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + 58882cd038dSYoshinobu Inoue sizeof(struct icmp6_hdr) + 4 + hostnamelen; 58982cd038dSYoshinobu Inoue nicmp6->icmp6_type = ICMP6_WRUREPLY; 59082cd038dSYoshinobu Inoue nicmp6->icmp6_code = 0; 59182cd038dSYoshinobu Inoue } 59282cd038dSYoshinobu Inoue #undef hostnamelen 59382cd038dSYoshinobu Inoue if (n) { 59482cd038dSYoshinobu Inoue icmp6stat.icp6s_reflect++; 59582cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++; 59682cd038dSYoshinobu Inoue icmp6_reflect(n, noff); 59782cd038dSYoshinobu Inoue } 59882cd038dSYoshinobu Inoue break; 59982cd038dSYoshinobu Inoue } 60082cd038dSYoshinobu Inoue 60182cd038dSYoshinobu Inoue case ICMP6_WRUREPLY: 60282cd038dSYoshinobu Inoue if (code != 0) 60382cd038dSYoshinobu Inoue goto badcode; 60482cd038dSYoshinobu Inoue break; 60582cd038dSYoshinobu Inoue 60682cd038dSYoshinobu Inoue case ND_ROUTER_SOLICIT: 60782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit); 60882cd038dSYoshinobu Inoue if (code != 0) 60982cd038dSYoshinobu Inoue goto badcode; 61082cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_router_solicit)) 61182cd038dSYoshinobu Inoue goto badlen; 61282cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); 61382cd038dSYoshinobu Inoue nd6_rs_input(m, off, icmp6len); 61482cd038dSYoshinobu Inoue /* m stays. */ 61582cd038dSYoshinobu Inoue break; 61682cd038dSYoshinobu Inoue 61782cd038dSYoshinobu Inoue case ND_ROUTER_ADVERT: 61882cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert); 61982cd038dSYoshinobu Inoue if (code != 0) 62082cd038dSYoshinobu Inoue goto badcode; 62182cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_router_advert)) 62282cd038dSYoshinobu Inoue goto badlen; 62382cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); 62482cd038dSYoshinobu Inoue nd6_ra_input(m, off, icmp6len); 62582cd038dSYoshinobu Inoue /* m stays. */ 62682cd038dSYoshinobu Inoue break; 62782cd038dSYoshinobu Inoue 62882cd038dSYoshinobu Inoue case ND_NEIGHBOR_SOLICIT: 62982cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit); 63082cd038dSYoshinobu Inoue if (code != 0) 63182cd038dSYoshinobu Inoue goto badcode; 63282cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_neighbor_solicit)) 63382cd038dSYoshinobu Inoue goto badlen; 63482cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); 63582cd038dSYoshinobu Inoue nd6_ns_input(m, off, icmp6len); 63682cd038dSYoshinobu Inoue /* m stays. */ 63782cd038dSYoshinobu Inoue break; 63882cd038dSYoshinobu Inoue 63982cd038dSYoshinobu Inoue case ND_NEIGHBOR_ADVERT: 64082cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert); 64182cd038dSYoshinobu Inoue if (code != 0) 64282cd038dSYoshinobu Inoue goto badcode; 64382cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_neighbor_advert)) 64482cd038dSYoshinobu Inoue goto badlen; 64582cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, icmp6len, IPPROTO_DONE); 64682cd038dSYoshinobu Inoue nd6_na_input(m, off, icmp6len); 64782cd038dSYoshinobu Inoue /* m stays. */ 64882cd038dSYoshinobu Inoue break; 64982cd038dSYoshinobu Inoue 65082cd038dSYoshinobu Inoue case ND_REDIRECT: 65182cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect); 65282cd038dSYoshinobu Inoue if (code != 0) 65382cd038dSYoshinobu Inoue goto badcode; 65482cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_redirect)) 65582cd038dSYoshinobu Inoue goto badlen; 65682cd038dSYoshinobu Inoue icmp6_redirect_input(m, off); 65782cd038dSYoshinobu Inoue /* m stays. */ 65882cd038dSYoshinobu Inoue break; 65982cd038dSYoshinobu Inoue 66082cd038dSYoshinobu Inoue case ICMP6_ROUTER_RENUMBERING: 66182cd038dSYoshinobu Inoue if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && 66282cd038dSYoshinobu Inoue code != ICMP6_ROUTER_RENUMBERING_RESULT) 66382cd038dSYoshinobu Inoue goto badcode; 66482cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct icmp6_router_renum)) 66582cd038dSYoshinobu Inoue goto badlen; 66682cd038dSYoshinobu Inoue break; 66782cd038dSYoshinobu Inoue 66882cd038dSYoshinobu Inoue default: 66982cd038dSYoshinobu Inoue printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", 67082cd038dSYoshinobu Inoue icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), 67182cd038dSYoshinobu Inoue ip6_sprintf(&ip6->ip6_dst), 67282cd038dSYoshinobu Inoue m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0); 67382cd038dSYoshinobu Inoue if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { 67482cd038dSYoshinobu Inoue /* ICMPv6 error: MUST deliver it by spec... */ 67582cd038dSYoshinobu Inoue code = PRC_NCMDS; 67682cd038dSYoshinobu Inoue /* deliver */ 67782cd038dSYoshinobu Inoue } else { 67882cd038dSYoshinobu Inoue /* ICMPv6 informational: MUST not deliver */ 67982cd038dSYoshinobu Inoue break; 68082cd038dSYoshinobu Inoue } 68182cd038dSYoshinobu Inoue deliver: 68282cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { 68382cd038dSYoshinobu Inoue icmp6stat.icp6s_tooshort++; 68482cd038dSYoshinobu Inoue goto freeit; 68582cd038dSYoshinobu Inoue } 68682cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, 68782cd038dSYoshinobu Inoue sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), 68882cd038dSYoshinobu Inoue IPPROTO_DONE); 68982cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); 69082cd038dSYoshinobu Inoue bzero(&icmp6src, sizeof(icmp6src)); 69182cd038dSYoshinobu Inoue icmp6src.sin6_len = sizeof(struct sockaddr_in6); 69282cd038dSYoshinobu Inoue icmp6src.sin6_family = AF_INET6; 69382cd038dSYoshinobu Inoue icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; 69482cd038dSYoshinobu Inoue 69582cd038dSYoshinobu Inoue /* Detect the upper level protocol */ 69682cd038dSYoshinobu Inoue { 69782cd038dSYoshinobu Inoue void (*ctlfunc) __P((int, struct sockaddr *, void *)); 69882cd038dSYoshinobu Inoue struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); 69982cd038dSYoshinobu Inoue u_int8_t nxt = eip6->ip6_nxt; 70082cd038dSYoshinobu Inoue int eoff = off + sizeof(struct icmp6_hdr) + 70182cd038dSYoshinobu Inoue sizeof(struct ip6_hdr); 70282cd038dSYoshinobu Inoue struct ip6ctlparam ip6cp; 70382cd038dSYoshinobu Inoue 70482cd038dSYoshinobu Inoue while (1) { /* XXX: should avoid inf. loop explicitly? */ 70582cd038dSYoshinobu Inoue struct ip6_ext *eh; 70682cd038dSYoshinobu Inoue 70782cd038dSYoshinobu Inoue switch(nxt) { 70882cd038dSYoshinobu Inoue case IPPROTO_ESP: 70982cd038dSYoshinobu Inoue case IPPROTO_NONE: 71082cd038dSYoshinobu Inoue goto passit; 71182cd038dSYoshinobu Inoue case IPPROTO_HOPOPTS: 71282cd038dSYoshinobu Inoue case IPPROTO_DSTOPTS: 71382cd038dSYoshinobu Inoue case IPPROTO_ROUTING: 71482cd038dSYoshinobu Inoue case IPPROTO_AH: 71582cd038dSYoshinobu Inoue case IPPROTO_FRAGMENT: 71682cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, eoff + 71782cd038dSYoshinobu Inoue sizeof(struct ip6_ext), 71882cd038dSYoshinobu Inoue IPPROTO_DONE); 71982cd038dSYoshinobu Inoue eh = (struct ip6_ext *)(mtod(m, caddr_t) 72082cd038dSYoshinobu Inoue + eoff); 72182cd038dSYoshinobu Inoue if (nxt == IPPROTO_AH) 72282cd038dSYoshinobu Inoue eoff += (eh->ip6e_len + 2) << 2; 72382cd038dSYoshinobu Inoue else if (nxt == IPPROTO_FRAGMENT) 72482cd038dSYoshinobu Inoue eoff += sizeof(struct ip6_frag); 72582cd038dSYoshinobu Inoue else 72682cd038dSYoshinobu Inoue eoff += (eh->ip6e_len + 1) << 3; 72782cd038dSYoshinobu Inoue nxt = eh->ip6e_nxt; 72882cd038dSYoshinobu Inoue break; 72982cd038dSYoshinobu Inoue default: 73082cd038dSYoshinobu Inoue goto notify; 73182cd038dSYoshinobu Inoue } 73282cd038dSYoshinobu Inoue } 73382cd038dSYoshinobu Inoue notify: 73482cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); 73582cd038dSYoshinobu Inoue ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) 73682cd038dSYoshinobu Inoue (inet6sw[ip6_protox[nxt]].pr_ctlinput); 73782cd038dSYoshinobu Inoue if (ctlfunc) { 73882cd038dSYoshinobu Inoue ip6cp.ip6c_m = m; 73982cd038dSYoshinobu Inoue ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); 74082cd038dSYoshinobu Inoue ip6cp.ip6c_off = eoff; 74182cd038dSYoshinobu Inoue (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp); 74282cd038dSYoshinobu Inoue } 74382cd038dSYoshinobu Inoue } 74482cd038dSYoshinobu Inoue break; 74582cd038dSYoshinobu Inoue 74682cd038dSYoshinobu Inoue badcode: 74782cd038dSYoshinobu Inoue icmp6stat.icp6s_badcode++; 74882cd038dSYoshinobu Inoue break; 74982cd038dSYoshinobu Inoue 75082cd038dSYoshinobu Inoue badlen: 75182cd038dSYoshinobu Inoue icmp6stat.icp6s_badlen++; 75282cd038dSYoshinobu Inoue break; 75382cd038dSYoshinobu Inoue } 75482cd038dSYoshinobu Inoue 75582cd038dSYoshinobu Inoue passit: 75682cd038dSYoshinobu Inoue icmp6_rip6_input(&m, *offp); 75782cd038dSYoshinobu Inoue return IPPROTO_DONE; 75882cd038dSYoshinobu Inoue 75982cd038dSYoshinobu Inoue freeit: 76082cd038dSYoshinobu Inoue m_freem(m); 76182cd038dSYoshinobu Inoue return IPPROTO_DONE; 76282cd038dSYoshinobu Inoue } 76382cd038dSYoshinobu Inoue 76482cd038dSYoshinobu Inoue /* 76582cd038dSYoshinobu Inoue * Process a Node Information Query 76682cd038dSYoshinobu Inoue */ 76782cd038dSYoshinobu Inoue #define hostnamelen strlen(hostname) 76882cd038dSYoshinobu Inoue #ifndef offsetof /* XXX */ 76982cd038dSYoshinobu Inoue #define offsetof(type, member) ((size_t)(&((type *)0)->member)) 77082cd038dSYoshinobu Inoue #endif 77182cd038dSYoshinobu Inoue 77282cd038dSYoshinobu Inoue static struct mbuf * 77382cd038dSYoshinobu Inoue ni6_input(m, off) 77482cd038dSYoshinobu Inoue struct mbuf *m; 77582cd038dSYoshinobu Inoue int off; 77682cd038dSYoshinobu Inoue { 77782cd038dSYoshinobu Inoue struct icmp6_nodeinfo *ni6 = 77882cd038dSYoshinobu Inoue (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off), *nni6; 77982cd038dSYoshinobu Inoue struct mbuf *n = NULL; 78082cd038dSYoshinobu Inoue u_int16_t qtype = ntohs(ni6->ni_qtype); 78182cd038dSYoshinobu Inoue int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); 78282cd038dSYoshinobu Inoue struct ni_reply_fqdn *fqdn; 78382cd038dSYoshinobu Inoue int addrs; /* for NI_QTYPE_NODEADDR */ 78482cd038dSYoshinobu Inoue struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ 78582cd038dSYoshinobu Inoue 78682cd038dSYoshinobu Inoue switch(qtype) { 78782cd038dSYoshinobu Inoue case NI_QTYPE_NOOP: 78882cd038dSYoshinobu Inoue break; /* no reply data */ 78982cd038dSYoshinobu Inoue case NI_QTYPE_SUPTYPES: 79082cd038dSYoshinobu Inoue goto bad; /* xxx: to be implemented */ 79182cd038dSYoshinobu Inoue break; 79282cd038dSYoshinobu Inoue case NI_QTYPE_FQDN: 79382cd038dSYoshinobu Inoue replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + 79482cd038dSYoshinobu Inoue hostnamelen; 79582cd038dSYoshinobu Inoue break; 79682cd038dSYoshinobu Inoue case NI_QTYPE_NODEADDR: 79782cd038dSYoshinobu Inoue addrs = ni6_addrs(ni6, m, &ifp); 79882cd038dSYoshinobu Inoue if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) 79982cd038dSYoshinobu Inoue replylen = MCLBYTES; /* XXX: we'll truncate later */ 80082cd038dSYoshinobu Inoue 80182cd038dSYoshinobu Inoue break; 80282cd038dSYoshinobu Inoue default: 80382cd038dSYoshinobu Inoue /* 80482cd038dSYoshinobu Inoue * XXX: We must return a reply with the ICMP6 code 80582cd038dSYoshinobu Inoue * `unknown Qtype' in this case. However we regard the case 80682cd038dSYoshinobu Inoue * as an FQDN query for backward compatibility. 80782cd038dSYoshinobu Inoue * Older versions set a random value to this field, 80882cd038dSYoshinobu Inoue * so it rarely varies in the defined qtypes. 80982cd038dSYoshinobu Inoue * But the mechanism is not reliable... 81082cd038dSYoshinobu Inoue * maybe we should obsolete older versions. 81182cd038dSYoshinobu Inoue */ 81282cd038dSYoshinobu Inoue qtype = NI_QTYPE_FQDN; 81382cd038dSYoshinobu Inoue replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_name) + 81482cd038dSYoshinobu Inoue hostnamelen; 81582cd038dSYoshinobu Inoue break; 81682cd038dSYoshinobu Inoue } 81782cd038dSYoshinobu Inoue 81882cd038dSYoshinobu Inoue /* allocate a mbuf to reply. */ 81982cd038dSYoshinobu Inoue MGETHDR(n, M_DONTWAIT, m->m_type); 82082cd038dSYoshinobu Inoue if (n == NULL) 82182cd038dSYoshinobu Inoue return(NULL); 82282cd038dSYoshinobu Inoue M_COPY_PKTHDR(n, m); /* just for recvif */ 82382cd038dSYoshinobu Inoue if (replylen > MHLEN) { 82482cd038dSYoshinobu Inoue if (replylen > MCLBYTES) 82582cd038dSYoshinobu Inoue /* 82682cd038dSYoshinobu Inoue * XXX: should we try to allocate more? But MCLBYTES is 82782cd038dSYoshinobu Inoue * probably much larger than IPV6_MMTU... 82882cd038dSYoshinobu Inoue */ 82982cd038dSYoshinobu Inoue goto bad; 83082cd038dSYoshinobu Inoue MCLGET(n, M_DONTWAIT); 83182cd038dSYoshinobu Inoue if ((n->m_flags & M_EXT) == 0) { 83282cd038dSYoshinobu Inoue goto bad; 83382cd038dSYoshinobu Inoue } 83482cd038dSYoshinobu Inoue } 83582cd038dSYoshinobu Inoue n->m_pkthdr.len = n->m_len = replylen; 83682cd038dSYoshinobu Inoue 83782cd038dSYoshinobu Inoue /* copy mbuf header and IPv6 + Node Information base headers */ 83882cd038dSYoshinobu Inoue bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr)); 83982cd038dSYoshinobu Inoue nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); 84082cd038dSYoshinobu Inoue bcopy(mtod(m, caddr_t) + off, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); 84182cd038dSYoshinobu Inoue 84282cd038dSYoshinobu Inoue /* qtype dependent procedure */ 84382cd038dSYoshinobu Inoue switch (qtype) { 84482cd038dSYoshinobu Inoue case NI_QTYPE_NOOP: 84582cd038dSYoshinobu Inoue nni6->ni_flags = 0; 84682cd038dSYoshinobu Inoue break; 84782cd038dSYoshinobu Inoue case NI_QTYPE_SUPTYPES: 84882cd038dSYoshinobu Inoue goto bad; /* xxx: to be implemented */ 84982cd038dSYoshinobu Inoue break; 85082cd038dSYoshinobu Inoue case NI_QTYPE_FQDN: 85182cd038dSYoshinobu Inoue if (hostnamelen > 255) { /* XXX: rare case, but may happen */ 85282cd038dSYoshinobu Inoue printf("ni6_input: " 85382cd038dSYoshinobu Inoue "hostname length(%d) is too large for reply\n", 85482cd038dSYoshinobu Inoue hostnamelen); 85582cd038dSYoshinobu Inoue goto bad; 85682cd038dSYoshinobu Inoue } 85782cd038dSYoshinobu Inoue fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + 85882cd038dSYoshinobu Inoue sizeof(struct ip6_hdr) + 85982cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo)); 86082cd038dSYoshinobu Inoue nni6->ni_flags = 0; /* XXX: meaningless TTL */ 86182cd038dSYoshinobu Inoue fqdn->ni_fqdn_ttl = 0; /* ditto. */ 86282cd038dSYoshinobu Inoue fqdn->ni_fqdn_namelen = hostnamelen; 86382cd038dSYoshinobu Inoue bcopy(hostname, &fqdn->ni_fqdn_name[0], hostnamelen); 86482cd038dSYoshinobu Inoue break; 86582cd038dSYoshinobu Inoue case NI_QTYPE_NODEADDR: 86682cd038dSYoshinobu Inoue { 86782cd038dSYoshinobu Inoue int lenlim, copied; 86882cd038dSYoshinobu Inoue 86982cd038dSYoshinobu Inoue if (n->m_flags & M_EXT) 87082cd038dSYoshinobu Inoue lenlim = MCLBYTES - sizeof(struct ip6_hdr) - 87182cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo); 87282cd038dSYoshinobu Inoue else 87382cd038dSYoshinobu Inoue lenlim = MHLEN - sizeof(struct ip6_hdr) - 87482cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo); 87582cd038dSYoshinobu Inoue copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); 87682cd038dSYoshinobu Inoue /* XXX: reset mbuf length */ 87782cd038dSYoshinobu Inoue n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + 87882cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo) + copied; 87982cd038dSYoshinobu Inoue break; 88082cd038dSYoshinobu Inoue } 88182cd038dSYoshinobu Inoue default: 88282cd038dSYoshinobu Inoue break; /* XXX impossible! */ 88382cd038dSYoshinobu Inoue } 88482cd038dSYoshinobu Inoue 88582cd038dSYoshinobu Inoue nni6->ni_type = ICMP6_NI_REPLY; 88682cd038dSYoshinobu Inoue nni6->ni_code = ICMP6_NI_SUCESS; 88782cd038dSYoshinobu Inoue return(n); 88882cd038dSYoshinobu Inoue 88982cd038dSYoshinobu Inoue bad: 89082cd038dSYoshinobu Inoue if (n) 89182cd038dSYoshinobu Inoue m_freem(n); 89282cd038dSYoshinobu Inoue return(NULL); 89382cd038dSYoshinobu Inoue } 89482cd038dSYoshinobu Inoue #undef hostnamelen 89582cd038dSYoshinobu Inoue 89682cd038dSYoshinobu Inoue /* 89782cd038dSYoshinobu Inoue * calculate the number of addresses to be returned in the node info reply. 89882cd038dSYoshinobu Inoue */ 89982cd038dSYoshinobu Inoue static int 90082cd038dSYoshinobu Inoue ni6_addrs(ni6, m, ifpp) 90182cd038dSYoshinobu Inoue struct icmp6_nodeinfo *ni6; 90282cd038dSYoshinobu Inoue struct mbuf *m; 90382cd038dSYoshinobu Inoue struct ifnet **ifpp; 90482cd038dSYoshinobu Inoue { 90582cd038dSYoshinobu Inoue register struct ifnet *ifp; 90682cd038dSYoshinobu Inoue register struct in6_ifaddr *ifa6; 90782cd038dSYoshinobu Inoue register struct ifaddr *ifa; 90882cd038dSYoshinobu Inoue struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 90982cd038dSYoshinobu Inoue int addrs = 0, addrsofif, iffound = 0; 91082cd038dSYoshinobu Inoue 91182cd038dSYoshinobu Inoue for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) 91282cd038dSYoshinobu Inoue { 91382cd038dSYoshinobu Inoue addrsofif = 0; 91482cd038dSYoshinobu Inoue for (ifa = ifp->if_addrlist.tqh_first; ifa; 91582cd038dSYoshinobu Inoue ifa = ifa->ifa_list.tqe_next) 91682cd038dSYoshinobu Inoue { 91782cd038dSYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 91882cd038dSYoshinobu Inoue continue; 91982cd038dSYoshinobu Inoue ifa6 = (struct in6_ifaddr *)ifa; 92082cd038dSYoshinobu Inoue 92182cd038dSYoshinobu Inoue if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) && 92282cd038dSYoshinobu Inoue IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, 92382cd038dSYoshinobu Inoue &ifa6->ia_addr.sin6_addr)) 92482cd038dSYoshinobu Inoue iffound = 1; 92582cd038dSYoshinobu Inoue 92682cd038dSYoshinobu Inoue if (ifa6->ia6_flags & IN6_IFF_ANYCAST) 92782cd038dSYoshinobu Inoue continue; /* we need only unicast addresses */ 92882cd038dSYoshinobu Inoue 92982cd038dSYoshinobu Inoue if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL | 93082cd038dSYoshinobu Inoue NI_NODEADDR_FLAG_SITELOCAL | 93182cd038dSYoshinobu Inoue NI_NODEADDR_FLAG_GLOBAL)) == 0) 93282cd038dSYoshinobu Inoue continue; 93382cd038dSYoshinobu Inoue 93482cd038dSYoshinobu Inoue /* What do we have to do about ::1? */ 93582cd038dSYoshinobu Inoue switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { 93682cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_LINKLOCAL: 93782cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) 93882cd038dSYoshinobu Inoue addrsofif++; 93982cd038dSYoshinobu Inoue break; 94082cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_SITELOCAL: 94182cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) 94282cd038dSYoshinobu Inoue addrsofif++; 94382cd038dSYoshinobu Inoue break; 94482cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_GLOBAL: 94582cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) 94682cd038dSYoshinobu Inoue addrsofif++; 94782cd038dSYoshinobu Inoue break; 94882cd038dSYoshinobu Inoue default: 94982cd038dSYoshinobu Inoue continue; 95082cd038dSYoshinobu Inoue } 95182cd038dSYoshinobu Inoue } 95282cd038dSYoshinobu Inoue if (iffound) { 95382cd038dSYoshinobu Inoue *ifpp = ifp; 95482cd038dSYoshinobu Inoue return(addrsofif); 95582cd038dSYoshinobu Inoue } 95682cd038dSYoshinobu Inoue 95782cd038dSYoshinobu Inoue addrs += addrsofif; 95882cd038dSYoshinobu Inoue } 95982cd038dSYoshinobu Inoue 96082cd038dSYoshinobu Inoue return(addrs); 96182cd038dSYoshinobu Inoue } 96282cd038dSYoshinobu Inoue 96382cd038dSYoshinobu Inoue static int 96482cd038dSYoshinobu Inoue ni6_store_addrs(ni6, nni6, ifp0, resid) 96582cd038dSYoshinobu Inoue struct icmp6_nodeinfo *ni6, *nni6; 96682cd038dSYoshinobu Inoue struct ifnet *ifp0; 96782cd038dSYoshinobu Inoue int resid; 96882cd038dSYoshinobu Inoue { 96982cd038dSYoshinobu Inoue register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); 97082cd038dSYoshinobu Inoue register struct in6_ifaddr *ifa6; 97182cd038dSYoshinobu Inoue register struct ifaddr *ifa; 97282cd038dSYoshinobu Inoue int docopy, copied = 0; 97382cd038dSYoshinobu Inoue u_char *cp = (u_char *)(nni6 + 1); 97482cd038dSYoshinobu Inoue 97582cd038dSYoshinobu Inoue if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL)) 97682cd038dSYoshinobu Inoue return(0); /* needless to copy */ 97782cd038dSYoshinobu Inoue 97882cd038dSYoshinobu Inoue for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) 97982cd038dSYoshinobu Inoue { 98082cd038dSYoshinobu Inoue for (ifa = ifp->if_addrlist.tqh_first; ifa; 98182cd038dSYoshinobu Inoue ifa = ifa->ifa_list.tqe_next) 98282cd038dSYoshinobu Inoue { 98382cd038dSYoshinobu Inoue docopy = 0; 98482cd038dSYoshinobu Inoue 98582cd038dSYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 98682cd038dSYoshinobu Inoue continue; 98782cd038dSYoshinobu Inoue ifa6 = (struct in6_ifaddr *)ifa; 98882cd038dSYoshinobu Inoue 98982cd038dSYoshinobu Inoue if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { 99082cd038dSYoshinobu Inoue /* just experimental. not in the spec. */ 99182cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) 99282cd038dSYoshinobu Inoue docopy = 1; 99382cd038dSYoshinobu Inoue else 99482cd038dSYoshinobu Inoue continue; 99582cd038dSYoshinobu Inoue } else { /* unicast address */ 99682cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) 99782cd038dSYoshinobu Inoue continue; 99882cd038dSYoshinobu Inoue else 99982cd038dSYoshinobu Inoue docopy = 1; 100082cd038dSYoshinobu Inoue } 100182cd038dSYoshinobu Inoue 100282cd038dSYoshinobu Inoue /* What do we have to do about ::1? */ 100382cd038dSYoshinobu Inoue switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { 100482cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_LINKLOCAL: 100582cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) 100682cd038dSYoshinobu Inoue docopy = 1; 100782cd038dSYoshinobu Inoue break; 100882cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_SITELOCAL: 100982cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) 101082cd038dSYoshinobu Inoue docopy = 1; 101182cd038dSYoshinobu Inoue break; 101282cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_GLOBAL: 101382cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) 101482cd038dSYoshinobu Inoue docopy = 1; 101582cd038dSYoshinobu Inoue break; 101682cd038dSYoshinobu Inoue default: 101782cd038dSYoshinobu Inoue continue; 101882cd038dSYoshinobu Inoue } 101982cd038dSYoshinobu Inoue 102082cd038dSYoshinobu Inoue if (docopy) { 102182cd038dSYoshinobu Inoue if (resid < sizeof(struct in6_addr)) { 102282cd038dSYoshinobu Inoue /* 102382cd038dSYoshinobu Inoue * We give up much more copy. 102482cd038dSYoshinobu Inoue * Set the truncate flag and return. 102582cd038dSYoshinobu Inoue */ 102682cd038dSYoshinobu Inoue nni6->ni_flags |= 102782cd038dSYoshinobu Inoue NI_NODEADDR_FLAG_TRUNCATE; 102882cd038dSYoshinobu Inoue return(copied); 102982cd038dSYoshinobu Inoue } 103082cd038dSYoshinobu Inoue bcopy(&ifa6->ia_addr.sin6_addr, cp, 103182cd038dSYoshinobu Inoue sizeof(struct in6_addr)); 103282cd038dSYoshinobu Inoue /* XXX: KAME link-local hack; remove ifindex */ 103382cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) 103482cd038dSYoshinobu Inoue ((struct in6_addr *)cp)->s6_addr16[1] = 0; 103582cd038dSYoshinobu Inoue cp += sizeof(struct in6_addr); 103682cd038dSYoshinobu Inoue resid -= sizeof(struct in6_addr); 103782cd038dSYoshinobu Inoue copied += sizeof(struct in6_addr); 103882cd038dSYoshinobu Inoue } 103982cd038dSYoshinobu Inoue } 104082cd038dSYoshinobu Inoue if (ifp0) /* we need search only on the specified IF */ 104182cd038dSYoshinobu Inoue break; 104282cd038dSYoshinobu Inoue } 104382cd038dSYoshinobu Inoue 104482cd038dSYoshinobu Inoue return(copied); 104582cd038dSYoshinobu Inoue } 104682cd038dSYoshinobu Inoue 104782cd038dSYoshinobu Inoue /* 104882cd038dSYoshinobu Inoue * XXX almost dup'ed code with rip6_input. 104982cd038dSYoshinobu Inoue */ 105082cd038dSYoshinobu Inoue static int 105182cd038dSYoshinobu Inoue icmp6_rip6_input(mp, off) 105282cd038dSYoshinobu Inoue struct mbuf **mp; 105382cd038dSYoshinobu Inoue int off; 105482cd038dSYoshinobu Inoue { 105582cd038dSYoshinobu Inoue struct mbuf *m = *mp; 105682cd038dSYoshinobu Inoue register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 105782cd038dSYoshinobu Inoue register struct in6pcb *in6p; 105882cd038dSYoshinobu Inoue struct in6pcb *last = NULL; 105982cd038dSYoshinobu Inoue struct sockaddr_in6 rip6src; 106082cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6; 106182cd038dSYoshinobu Inoue struct mbuf *opts = NULL; 106282cd038dSYoshinobu Inoue 106382cd038dSYoshinobu Inoue /* this is assumed to be safe. */ 106482cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); 106582cd038dSYoshinobu Inoue 106682cd038dSYoshinobu Inoue bzero(&rip6src, sizeof(rip6src)); 106782cd038dSYoshinobu Inoue rip6src.sin6_len = sizeof(struct sockaddr_in6); 106882cd038dSYoshinobu Inoue rip6src.sin6_family = AF_INET6; 106982cd038dSYoshinobu Inoue rip6src.sin6_addr = ip6->ip6_src; 107082cd038dSYoshinobu Inoue if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) 107182cd038dSYoshinobu Inoue rip6src.sin6_addr.s6_addr16[1] = 0; 107282cd038dSYoshinobu Inoue if (m->m_pkthdr.rcvif) { 107382cd038dSYoshinobu Inoue if (IN6_IS_SCOPE_LINKLOCAL(&rip6src.sin6_addr)) 107482cd038dSYoshinobu Inoue rip6src.sin6_scope_id = m->m_pkthdr.rcvif->if_index; 107582cd038dSYoshinobu Inoue else 107682cd038dSYoshinobu Inoue rip6src.sin6_scope_id = 0; 107782cd038dSYoshinobu Inoue } else 107882cd038dSYoshinobu Inoue rip6src.sin6_scope_id = 0; 107982cd038dSYoshinobu Inoue 108082cd038dSYoshinobu Inoue LIST_FOREACH(in6p, &ripcb, inp_list) 108182cd038dSYoshinobu Inoue { 108282cd038dSYoshinobu Inoue if ((in6p->inp_vflag & INP_IPV6) == NULL) 108382cd038dSYoshinobu Inoue continue; 108482cd038dSYoshinobu Inoue if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) 108582cd038dSYoshinobu Inoue continue; 108682cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 108782cd038dSYoshinobu Inoue !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 108882cd038dSYoshinobu Inoue continue; 108982cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 109082cd038dSYoshinobu Inoue !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 109182cd038dSYoshinobu Inoue continue; 109282cd038dSYoshinobu Inoue if (in6p->in6p_icmp6filt 109382cd038dSYoshinobu Inoue && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, 109482cd038dSYoshinobu Inoue in6p->in6p_icmp6filt)) 109582cd038dSYoshinobu Inoue continue; 109682cd038dSYoshinobu Inoue if (last) { 109782cd038dSYoshinobu Inoue struct mbuf *n; 109882cd038dSYoshinobu Inoue if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { 109982cd038dSYoshinobu Inoue if (last->in6p_flags & IN6P_CONTROLOPTS) 110082cd038dSYoshinobu Inoue ip6_savecontrol(last, &opts, ip6, n); 110182cd038dSYoshinobu Inoue /* strip intermediate headers */ 110282cd038dSYoshinobu Inoue m_adj(n, off); 110382cd038dSYoshinobu Inoue if (sbappendaddr(&last->in6p_socket->so_rcv, 110482cd038dSYoshinobu Inoue (struct sockaddr *)&rip6src, 110582cd038dSYoshinobu Inoue n, opts) == 0) { 110682cd038dSYoshinobu Inoue /* should notify about lost packet */ 110782cd038dSYoshinobu Inoue m_freem(n); 110882cd038dSYoshinobu Inoue if (opts) 110982cd038dSYoshinobu Inoue m_freem(opts); 111082cd038dSYoshinobu Inoue } else 111182cd038dSYoshinobu Inoue sorwakeup(last->in6p_socket); 111282cd038dSYoshinobu Inoue opts = NULL; 111382cd038dSYoshinobu Inoue } 111482cd038dSYoshinobu Inoue } 111582cd038dSYoshinobu Inoue last = in6p; 111682cd038dSYoshinobu Inoue } 111782cd038dSYoshinobu Inoue if (last) { 111882cd038dSYoshinobu Inoue if (last->in6p_flags & IN6P_CONTROLOPTS) 111982cd038dSYoshinobu Inoue ip6_savecontrol(last, &opts, ip6, m); 112082cd038dSYoshinobu Inoue /* strip intermediate headers */ 112182cd038dSYoshinobu Inoue m_adj(m, off); 112282cd038dSYoshinobu Inoue if (sbappendaddr(&last->in6p_socket->so_rcv, 112382cd038dSYoshinobu Inoue (struct sockaddr *)&rip6src, m, opts) == 0) { 112482cd038dSYoshinobu Inoue m_freem(m); 112582cd038dSYoshinobu Inoue if (opts) 112682cd038dSYoshinobu Inoue m_freem(opts); 112782cd038dSYoshinobu Inoue } else 112882cd038dSYoshinobu Inoue sorwakeup(last->in6p_socket); 112982cd038dSYoshinobu Inoue } else { 113082cd038dSYoshinobu Inoue m_freem(m); 113182cd038dSYoshinobu Inoue ip6stat.ip6s_delivered--; 113282cd038dSYoshinobu Inoue } 113382cd038dSYoshinobu Inoue return IPPROTO_DONE; 113482cd038dSYoshinobu Inoue } 113582cd038dSYoshinobu Inoue 113682cd038dSYoshinobu Inoue /* 113782cd038dSYoshinobu Inoue * Reflect the ip6 packet back to the source. 113882cd038dSYoshinobu Inoue * The caller MUST check if the destination is multicast or not. 113982cd038dSYoshinobu Inoue * This function is usually called with a unicast destination which 114082cd038dSYoshinobu Inoue * can be safely the source of the reply packet. But some exceptions 114182cd038dSYoshinobu Inoue * exist(e.g. ECHOREPLY, PATCKET_TOOBIG, "10" in OPTION type). 114282cd038dSYoshinobu Inoue * ``off'' points to the icmp6 header, counted from the top of the mbuf. 114382cd038dSYoshinobu Inoue */ 114482cd038dSYoshinobu Inoue void 114582cd038dSYoshinobu Inoue icmp6_reflect(m, off) 114682cd038dSYoshinobu Inoue struct mbuf *m; 114782cd038dSYoshinobu Inoue size_t off; 114882cd038dSYoshinobu Inoue { 114982cd038dSYoshinobu Inoue struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 115082cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6; 115182cd038dSYoshinobu Inoue struct in6_ifaddr *ia; 115282cd038dSYoshinobu Inoue struct in6_addr t, *src = 0; 115382cd038dSYoshinobu Inoue int plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); 115482cd038dSYoshinobu Inoue int type, code; 115582cd038dSYoshinobu Inoue struct ifnet *outif = NULL; 115682cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 115782cd038dSYoshinobu Inoue int mtu = IPV6_MMTU; 115882cd038dSYoshinobu Inoue struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; 115982cd038dSYoshinobu Inoue #endif 116082cd038dSYoshinobu Inoue 116182cd038dSYoshinobu Inoue /* 116282cd038dSYoshinobu Inoue * If there are extra headers between IPv6 and ICMPv6, strip 116382cd038dSYoshinobu Inoue * off that header first. 116482cd038dSYoshinobu Inoue */ 116582cd038dSYoshinobu Inoue if (off != sizeof(struct ip6_hdr)) { 116682cd038dSYoshinobu Inoue size_t siz; 116782cd038dSYoshinobu Inoue 116882cd038dSYoshinobu Inoue /* sanity checks */ 116982cd038dSYoshinobu Inoue if (off < sizeof(struct ip6_hdr)) { 117082cd038dSYoshinobu Inoue printf("sanity fail: off=%x, sizeof(ip6)=%x in %s:%d\n", 117182cd038dSYoshinobu Inoue (unsigned int)off, 117282cd038dSYoshinobu Inoue (unsigned int)sizeof(struct ip6_hdr), 117382cd038dSYoshinobu Inoue __FILE__, __LINE__); 117482cd038dSYoshinobu Inoue goto bad; 117582cd038dSYoshinobu Inoue } 117682cd038dSYoshinobu Inoue siz = off - sizeof(struct ip6_hdr); 117782cd038dSYoshinobu Inoue if (plen < siz) { 117882cd038dSYoshinobu Inoue printf("sanity fail: siz=%x, payloadlen=%x in %s:%d\n", 117982cd038dSYoshinobu Inoue (unsigned int)siz, plen, __FILE__, __LINE__); 118082cd038dSYoshinobu Inoue goto bad; 118182cd038dSYoshinobu Inoue } 118282cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, off, /*nothing*/); 118382cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), /*nothing*/); 118482cd038dSYoshinobu Inoue 118582cd038dSYoshinobu Inoue ovbcopy((caddr_t)ip6, 118682cd038dSYoshinobu Inoue (caddr_t)(mtod(m, u_char *) + siz), 118782cd038dSYoshinobu Inoue sizeof(struct ip6_hdr)); 118882cd038dSYoshinobu Inoue m->m_data += siz; 118982cd038dSYoshinobu Inoue m->m_len -= siz; 119082cd038dSYoshinobu Inoue m->m_pkthdr.len -= siz; 119182cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 119282cd038dSYoshinobu Inoue ip6->ip6_nxt = IPPROTO_ICMPV6; 119382cd038dSYoshinobu Inoue plen -= siz; 119482cd038dSYoshinobu Inoue } 119582cd038dSYoshinobu Inoue 119682cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(ip6 + 1); 119782cd038dSYoshinobu Inoue type = icmp6->icmp6_type; /* keep type for statistics */ 119882cd038dSYoshinobu Inoue code = icmp6->icmp6_code; /* ditto. */ 119982cd038dSYoshinobu Inoue 120082cd038dSYoshinobu Inoue t = ip6->ip6_dst; 120182cd038dSYoshinobu Inoue /* 120282cd038dSYoshinobu Inoue * ip6_input() drops a packet if its src is multicast. 120382cd038dSYoshinobu Inoue * So, the src is never multicast. 120482cd038dSYoshinobu Inoue */ 120582cd038dSYoshinobu Inoue ip6->ip6_dst = ip6->ip6_src; 120682cd038dSYoshinobu Inoue 120782cd038dSYoshinobu Inoue /* XXX hack for link-local addresses */ 120882cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) 120982cd038dSYoshinobu Inoue ip6->ip6_dst.s6_addr16[1] = 121082cd038dSYoshinobu Inoue htons(m->m_pkthdr.rcvif->if_index); 121182cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&t)) 121282cd038dSYoshinobu Inoue t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); 121382cd038dSYoshinobu Inoue 121482cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 121582cd038dSYoshinobu Inoue /* 121682cd038dSYoshinobu Inoue * xxx guess MTU 121782cd038dSYoshinobu Inoue * RFC 1885 requires that echo reply should be truncated if it 121882cd038dSYoshinobu Inoue * does not fit in with (return) path MTU, but the description was 121982cd038dSYoshinobu Inoue * removed in the new spec. 122082cd038dSYoshinobu Inoue */ 122182cd038dSYoshinobu Inoue if (icmp6_reflect_rt.ro_rt == 0 || 122282cd038dSYoshinobu Inoue ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) { 122382cd038dSYoshinobu Inoue if (icmp6_reflect_rt.ro_rt) { 122482cd038dSYoshinobu Inoue RTFREE(icmp6_reflect_rt.ro_rt); 122582cd038dSYoshinobu Inoue icmp6_reflect_rt.ro_rt = 0; 122682cd038dSYoshinobu Inoue } 122782cd038dSYoshinobu Inoue bzero(sin6, sizeof(*sin6)); 122882cd038dSYoshinobu Inoue sin6->sin6_family = PF_INET6; 122982cd038dSYoshinobu Inoue sin6->sin6_len = sizeof(struct sockaddr_in6); 123082cd038dSYoshinobu Inoue sin6->sin6_addr = ip6->ip6_dst; 123182cd038dSYoshinobu Inoue 123282cd038dSYoshinobu Inoue rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt, 123382cd038dSYoshinobu Inoue RTF_PRCLONING); 123482cd038dSYoshinobu Inoue } 123582cd038dSYoshinobu Inoue 123682cd038dSYoshinobu Inoue if (icmp6_reflect_rt.ro_rt == 0) 123782cd038dSYoshinobu Inoue goto bad; 123882cd038dSYoshinobu Inoue 123982cd038dSYoshinobu Inoue if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST) 124082cd038dSYoshinobu Inoue && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu) 124182cd038dSYoshinobu Inoue mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu; 124282cd038dSYoshinobu Inoue 124382cd038dSYoshinobu Inoue if (mtu < m->m_pkthdr.len) { 124482cd038dSYoshinobu Inoue plen -= (m->m_pkthdr.len - mtu); 124582cd038dSYoshinobu Inoue m_adj(m, mtu - m->m_pkthdr.len); 124682cd038dSYoshinobu Inoue } 124782cd038dSYoshinobu Inoue #endif 124882cd038dSYoshinobu Inoue /* 124982cd038dSYoshinobu Inoue * If the incoming packet was addressed directly to us(i.e. unicast), 125082cd038dSYoshinobu Inoue * use dst as the src for the reply. 125182cd038dSYoshinobu Inoue */ 125282cd038dSYoshinobu Inoue for (ia = in6_ifaddr; ia; ia = ia->ia_next) 125382cd038dSYoshinobu Inoue if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && 125482cd038dSYoshinobu Inoue (ia->ia6_flags & IN6_IFF_ANYCAST) == 0) { 125582cd038dSYoshinobu Inoue src = &t; 125682cd038dSYoshinobu Inoue break; 125782cd038dSYoshinobu Inoue } 125882cd038dSYoshinobu Inoue if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) { 125982cd038dSYoshinobu Inoue /* 126082cd038dSYoshinobu Inoue * This is the case if the dst is our link-local address 126182cd038dSYoshinobu Inoue * and the sender is also ourseleves. 126282cd038dSYoshinobu Inoue */ 126382cd038dSYoshinobu Inoue src = &t; 126482cd038dSYoshinobu Inoue } 126582cd038dSYoshinobu Inoue 126682cd038dSYoshinobu Inoue if (src == 0) 126782cd038dSYoshinobu Inoue /* 126882cd038dSYoshinobu Inoue * We have not multicast routing yet. So this case matches 126982cd038dSYoshinobu Inoue * to our multicast, our anycast or not to our unicast. 127082cd038dSYoshinobu Inoue * Select a source address which has the same scope. 127182cd038dSYoshinobu Inoue */ 127282cd038dSYoshinobu Inoue if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) 127382cd038dSYoshinobu Inoue src = &IA6_SIN6(ia)->sin6_addr; 127482cd038dSYoshinobu Inoue 127582cd038dSYoshinobu Inoue if (src == 0) 127682cd038dSYoshinobu Inoue goto bad; 127782cd038dSYoshinobu Inoue 127882cd038dSYoshinobu Inoue ip6->ip6_src = *src; 127982cd038dSYoshinobu Inoue 128082cd038dSYoshinobu Inoue ip6->ip6_flow = 0; 128182cd038dSYoshinobu Inoue ip6->ip6_vfc = IPV6_VERSION; 128282cd038dSYoshinobu Inoue ip6->ip6_nxt = IPPROTO_ICMPV6; 128382cd038dSYoshinobu Inoue if (m->m_pkthdr.rcvif) { 128482cd038dSYoshinobu Inoue /* XXX: This may not be the outgoing interface */ 128582cd038dSYoshinobu Inoue ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; 128682cd038dSYoshinobu Inoue } 128782cd038dSYoshinobu Inoue 128882cd038dSYoshinobu Inoue icmp6->icmp6_cksum = 0; 128982cd038dSYoshinobu Inoue icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, 129082cd038dSYoshinobu Inoue sizeof(struct ip6_hdr), plen); 129182cd038dSYoshinobu Inoue 129282cd038dSYoshinobu Inoue /* 129382cd038dSYoshinobu Inoue * xxx option handling 129482cd038dSYoshinobu Inoue */ 129582cd038dSYoshinobu Inoue 129682cd038dSYoshinobu Inoue m->m_flags &= ~(M_BCAST|M_MCAST); 129782cd038dSYoshinobu Inoue #ifdef IPSEC 129882cd038dSYoshinobu Inoue m->m_pkthdr.rcvif = NULL; 129982cd038dSYoshinobu Inoue #endif /*IPSEC*/ 130082cd038dSYoshinobu Inoue 130182cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 130282cd038dSYoshinobu Inoue ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif); 130382cd038dSYoshinobu Inoue #else 130482cd038dSYoshinobu Inoue ip6_output(m, NULL, NULL, 0, NULL, &outif); 130582cd038dSYoshinobu Inoue #endif 130682cd038dSYoshinobu Inoue if (outif) 130782cd038dSYoshinobu Inoue icmp6_ifoutstat_inc(outif, type, code); 130882cd038dSYoshinobu Inoue 130982cd038dSYoshinobu Inoue return; 131082cd038dSYoshinobu Inoue 131182cd038dSYoshinobu Inoue bad: 131282cd038dSYoshinobu Inoue m_freem(m); 131382cd038dSYoshinobu Inoue return; 131482cd038dSYoshinobu Inoue } 131582cd038dSYoshinobu Inoue 131682cd038dSYoshinobu Inoue void 131782cd038dSYoshinobu Inoue icmp6_fasttimo() 131882cd038dSYoshinobu Inoue { 131982cd038dSYoshinobu Inoue mld6_fasttimeo(); 132082cd038dSYoshinobu Inoue } 132182cd038dSYoshinobu Inoue 132282cd038dSYoshinobu Inoue static const char * 132382cd038dSYoshinobu Inoue icmp6_redirect_diag(src6, dst6, tgt6) 132482cd038dSYoshinobu Inoue struct in6_addr *src6; 132582cd038dSYoshinobu Inoue struct in6_addr *dst6; 132682cd038dSYoshinobu Inoue struct in6_addr *tgt6; 132782cd038dSYoshinobu Inoue { 132882cd038dSYoshinobu Inoue static char buf[1024]; 132982cd038dSYoshinobu Inoue snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", 133082cd038dSYoshinobu Inoue ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6)); 133182cd038dSYoshinobu Inoue return buf; 133282cd038dSYoshinobu Inoue } 133382cd038dSYoshinobu Inoue 133482cd038dSYoshinobu Inoue void 133582cd038dSYoshinobu Inoue icmp6_redirect_input(m, off) 133682cd038dSYoshinobu Inoue register struct mbuf *m; 133782cd038dSYoshinobu Inoue int off; 133882cd038dSYoshinobu Inoue { 133982cd038dSYoshinobu Inoue struct ifnet *ifp = m->m_pkthdr.rcvif; 134082cd038dSYoshinobu Inoue struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 134182cd038dSYoshinobu Inoue struct nd_redirect *nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); 134282cd038dSYoshinobu Inoue int icmp6len = ntohs(ip6->ip6_plen); 134382cd038dSYoshinobu Inoue char *lladdr = NULL; 134482cd038dSYoshinobu Inoue int lladdrlen = 0; 134582cd038dSYoshinobu Inoue u_char *redirhdr = NULL; 134682cd038dSYoshinobu Inoue int redirhdrlen = 0; 134782cd038dSYoshinobu Inoue struct rtentry *rt = NULL; 134882cd038dSYoshinobu Inoue int is_router; 134982cd038dSYoshinobu Inoue int is_onlink; 135082cd038dSYoshinobu Inoue struct in6_addr src6 = ip6->ip6_src; 135182cd038dSYoshinobu Inoue struct in6_addr redtgt6 = nd_rd->nd_rd_target; 135282cd038dSYoshinobu Inoue struct in6_addr reddst6 = nd_rd->nd_rd_dst; 135382cd038dSYoshinobu Inoue union nd_opts ndopts; 135482cd038dSYoshinobu Inoue 135582cd038dSYoshinobu Inoue if (!m || !ifp) 135682cd038dSYoshinobu Inoue return; 135782cd038dSYoshinobu Inoue 135882cd038dSYoshinobu Inoue /* XXX if we are router, we don't update route by icmp6 redirect */ 135982cd038dSYoshinobu Inoue if (ip6_forwarding) 136082cd038dSYoshinobu Inoue return; 136182cd038dSYoshinobu Inoue if (!icmp6_rediraccept) 136282cd038dSYoshinobu Inoue return; 136382cd038dSYoshinobu Inoue 136482cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) 136582cd038dSYoshinobu Inoue redtgt6.s6_addr16[1] = htons(ifp->if_index); 136682cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) 136782cd038dSYoshinobu Inoue reddst6.s6_addr16[1] = htons(ifp->if_index); 136882cd038dSYoshinobu Inoue 136982cd038dSYoshinobu Inoue /* validation */ 137082cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { 137182cd038dSYoshinobu Inoue log(LOG_ERR, 137282cd038dSYoshinobu Inoue "ICMP6 redirect sent from %s rejected; " 137382cd038dSYoshinobu Inoue "must be from linklocal\n", ip6_sprintf(&src6)); 137482cd038dSYoshinobu Inoue return; 137582cd038dSYoshinobu Inoue } 137682cd038dSYoshinobu Inoue if (ip6->ip6_hlim != 255) { 137782cd038dSYoshinobu Inoue log(LOG_ERR, 137882cd038dSYoshinobu Inoue "ICMP6 redirect sent from %s rejected; " 137982cd038dSYoshinobu Inoue "hlim=%d (must be 255)\n", 138082cd038dSYoshinobu Inoue ip6_sprintf(&src6), ip6->ip6_hlim); 138182cd038dSYoshinobu Inoue return; 138282cd038dSYoshinobu Inoue } 138382cd038dSYoshinobu Inoue { 138482cd038dSYoshinobu Inoue /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ 138582cd038dSYoshinobu Inoue struct sockaddr_in6 sin6; 138682cd038dSYoshinobu Inoue struct in6_addr *gw6; 138782cd038dSYoshinobu Inoue 138882cd038dSYoshinobu Inoue bzero(&sin6, sizeof(sin6)); 138982cd038dSYoshinobu Inoue sin6.sin6_family = AF_INET6; 139082cd038dSYoshinobu Inoue sin6.sin6_len = sizeof(struct sockaddr_in6); 139182cd038dSYoshinobu Inoue bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); 139282cd038dSYoshinobu Inoue rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); 139382cd038dSYoshinobu Inoue if (rt) { 139482cd038dSYoshinobu Inoue gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); 139582cd038dSYoshinobu Inoue if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { 139682cd038dSYoshinobu Inoue log(LOG_ERR, 139782cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 139882cd038dSYoshinobu Inoue "not equal to gw-for-src=%s (must be same): " 139982cd038dSYoshinobu Inoue "%s\n", 140082cd038dSYoshinobu Inoue ip6_sprintf(gw6), 140182cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 140282cd038dSYoshinobu Inoue RTFREE(rt); 140382cd038dSYoshinobu Inoue return; 140482cd038dSYoshinobu Inoue } 140582cd038dSYoshinobu Inoue } else { 140682cd038dSYoshinobu Inoue log(LOG_ERR, 140782cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 140882cd038dSYoshinobu Inoue "no route found for redirect dst: %s\n", 140982cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 141082cd038dSYoshinobu Inoue return; 141182cd038dSYoshinobu Inoue } 141282cd038dSYoshinobu Inoue RTFREE(rt); 141382cd038dSYoshinobu Inoue rt = NULL; 141482cd038dSYoshinobu Inoue } 141582cd038dSYoshinobu Inoue if (IN6_IS_ADDR_MULTICAST(&reddst6)) { 141682cd038dSYoshinobu Inoue log(LOG_ERR, 141782cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 141882cd038dSYoshinobu Inoue "redirect dst must be unicast: %s\n", 141982cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 142082cd038dSYoshinobu Inoue return; 142182cd038dSYoshinobu Inoue } 142282cd038dSYoshinobu Inoue 142382cd038dSYoshinobu Inoue is_router = is_onlink = 0; 142482cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) 142582cd038dSYoshinobu Inoue is_router = 1; /* router case */ 142682cd038dSYoshinobu Inoue if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) 142782cd038dSYoshinobu Inoue is_onlink = 1; /* on-link destination case */ 142882cd038dSYoshinobu Inoue if (!is_router && !is_onlink) { 142982cd038dSYoshinobu Inoue log(LOG_ERR, 143082cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 143182cd038dSYoshinobu Inoue "neither router case nor onlink case: %s\n", 143282cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 143382cd038dSYoshinobu Inoue return; 143482cd038dSYoshinobu Inoue } 143582cd038dSYoshinobu Inoue /* validation passed */ 143682cd038dSYoshinobu Inoue 143782cd038dSYoshinobu Inoue icmp6len -= sizeof(*nd_rd); 143882cd038dSYoshinobu Inoue nd6_option_init(nd_rd + 1, icmp6len, &ndopts); 143982cd038dSYoshinobu Inoue if (nd6_options(&ndopts) < 0) { 144082cd038dSYoshinobu Inoue log(LOG_INFO, "icmp6_redirect_input: " 144182cd038dSYoshinobu Inoue "invalid ND option, rejected: %s\n", 144282cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 144382cd038dSYoshinobu Inoue return; 144482cd038dSYoshinobu Inoue } 144582cd038dSYoshinobu Inoue 144682cd038dSYoshinobu Inoue if (ndopts.nd_opts_tgt_lladdr) { 144782cd038dSYoshinobu Inoue lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); 144882cd038dSYoshinobu Inoue lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; 144982cd038dSYoshinobu Inoue } 145082cd038dSYoshinobu Inoue 145182cd038dSYoshinobu Inoue if (ndopts.nd_opts_rh) { 145282cd038dSYoshinobu Inoue redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len; 145382cd038dSYoshinobu Inoue redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */ 145482cd038dSYoshinobu Inoue } 145582cd038dSYoshinobu Inoue 145682cd038dSYoshinobu Inoue if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { 145782cd038dSYoshinobu Inoue log(LOG_INFO, 145882cd038dSYoshinobu Inoue "icmp6_redirect_input: lladdrlen mismatch for %s " 145982cd038dSYoshinobu Inoue "(if %d, icmp6 packet %d): %s\n", 146082cd038dSYoshinobu Inoue ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, 146182cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 146282cd038dSYoshinobu Inoue } 146382cd038dSYoshinobu Inoue 146482cd038dSYoshinobu Inoue /* RFC 2461 8.3 */ 146582cd038dSYoshinobu Inoue nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, 146682cd038dSYoshinobu Inoue is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER); 146782cd038dSYoshinobu Inoue 146882cd038dSYoshinobu Inoue if (!is_onlink) { /* better router case. perform rtredirect. */ 146982cd038dSYoshinobu Inoue /* perform rtredirect */ 147082cd038dSYoshinobu Inoue struct sockaddr_in6 sdst; 147182cd038dSYoshinobu Inoue struct sockaddr_in6 sgw; 147282cd038dSYoshinobu Inoue struct sockaddr_in6 ssrc; 147382cd038dSYoshinobu Inoue 147482cd038dSYoshinobu Inoue bzero(&sdst, sizeof(sdst)); 147582cd038dSYoshinobu Inoue bzero(&sgw, sizeof(sgw)); 147682cd038dSYoshinobu Inoue bzero(&ssrc, sizeof(ssrc)); 147782cd038dSYoshinobu Inoue sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6; 147882cd038dSYoshinobu Inoue sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len = 147982cd038dSYoshinobu Inoue sizeof(struct sockaddr_in6); 148082cd038dSYoshinobu Inoue bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); 148182cd038dSYoshinobu Inoue bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); 148282cd038dSYoshinobu Inoue bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); 148382cd038dSYoshinobu Inoue rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw, 148482cd038dSYoshinobu Inoue (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST, 148582cd038dSYoshinobu Inoue (struct sockaddr *)&ssrc, 148682cd038dSYoshinobu Inoue (struct rtentry **)NULL); 148782cd038dSYoshinobu Inoue } 148882cd038dSYoshinobu Inoue /* finally update cached route in each socket via pfctlinput */ 148982cd038dSYoshinobu Inoue { 149082cd038dSYoshinobu Inoue struct sockaddr_in6 sdst; 149182cd038dSYoshinobu Inoue 149282cd038dSYoshinobu Inoue bzero(&sdst, sizeof(sdst)); 149382cd038dSYoshinobu Inoue sdst.sin6_family = AF_INET6; 149482cd038dSYoshinobu Inoue sdst.sin6_len = sizeof(struct sockaddr_in6); 149582cd038dSYoshinobu Inoue bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); 149682cd038dSYoshinobu Inoue pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); 149782cd038dSYoshinobu Inoue #ifdef IPSEC 149882cd038dSYoshinobu Inoue key_sa_routechange((struct sockaddr *)&sdst); 149982cd038dSYoshinobu Inoue #endif 150082cd038dSYoshinobu Inoue } 150182cd038dSYoshinobu Inoue } 150282cd038dSYoshinobu Inoue 150382cd038dSYoshinobu Inoue void 150482cd038dSYoshinobu Inoue icmp6_redirect_output(m0, rt) 150582cd038dSYoshinobu Inoue struct mbuf *m0; 150682cd038dSYoshinobu Inoue struct rtentry *rt; 150782cd038dSYoshinobu Inoue { 150882cd038dSYoshinobu Inoue struct ifnet *ifp; /* my outgoing interface */ 150982cd038dSYoshinobu Inoue struct in6_addr *ifp_ll6; 151082cd038dSYoshinobu Inoue struct in6_addr *router_ll6; 151182cd038dSYoshinobu Inoue struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ 151282cd038dSYoshinobu Inoue struct mbuf *m = NULL; /* newly allocated one */ 151382cd038dSYoshinobu Inoue struct ip6_hdr *ip6; /* m as struct ip6_hdr */ 151482cd038dSYoshinobu Inoue struct nd_redirect *nd_rd; 151582cd038dSYoshinobu Inoue size_t maxlen; 151682cd038dSYoshinobu Inoue u_char *p; 151782cd038dSYoshinobu Inoue struct ifnet *outif = NULL; 151882cd038dSYoshinobu Inoue 151982cd038dSYoshinobu Inoue /* if we are not router, we don't send icmp6 redirect */ 152082cd038dSYoshinobu Inoue if (!ip6_forwarding || ip6_accept_rtadv) 152182cd038dSYoshinobu Inoue goto fail; 152282cd038dSYoshinobu Inoue 152382cd038dSYoshinobu Inoue /* sanity check */ 152482cd038dSYoshinobu Inoue if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) 152582cd038dSYoshinobu Inoue goto fail; 152682cd038dSYoshinobu Inoue 152782cd038dSYoshinobu Inoue /* 152882cd038dSYoshinobu Inoue * Address check: 152982cd038dSYoshinobu Inoue * the source address must identify a neighbor, and 153082cd038dSYoshinobu Inoue * the destination address must not be a multicast address 153182cd038dSYoshinobu Inoue * [RFC 2461, sec 8.2] 153282cd038dSYoshinobu Inoue */ 153382cd038dSYoshinobu Inoue sip6 = mtod(m0, struct ip6_hdr *); 153482cd038dSYoshinobu Inoue if (nd6_is_addr_neighbor(&sip6->ip6_src, ifp) == 0) 153582cd038dSYoshinobu Inoue goto fail; 153682cd038dSYoshinobu Inoue if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) 153782cd038dSYoshinobu Inoue goto fail; /* what should we do here? */ 153882cd038dSYoshinobu Inoue 153982cd038dSYoshinobu Inoue /* rate limit */ 154082cd038dSYoshinobu Inoue if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) 154182cd038dSYoshinobu Inoue goto fail; 154282cd038dSYoshinobu Inoue 154382cd038dSYoshinobu Inoue /* 154482cd038dSYoshinobu Inoue * Since we are going to append up to 1280 bytes (= IPV6_MMTU), 154582cd038dSYoshinobu Inoue * we almost always ask for an mbuf cluster for simplicity. 154682cd038dSYoshinobu Inoue * (MHLEN < IPV6_MMTU is almost always true) 154782cd038dSYoshinobu Inoue */ 154882cd038dSYoshinobu Inoue MGETHDR(m, M_DONTWAIT, MT_HEADER); 154982cd038dSYoshinobu Inoue if (!m) 155082cd038dSYoshinobu Inoue goto fail; 155182cd038dSYoshinobu Inoue if (MHLEN < IPV6_MMTU) 155282cd038dSYoshinobu Inoue MCLGET(m, M_DONTWAIT); 155382cd038dSYoshinobu Inoue maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; 155482cd038dSYoshinobu Inoue maxlen = min(IPV6_MMTU, maxlen); 155582cd038dSYoshinobu Inoue /* just for safety */ 155682cd038dSYoshinobu Inoue if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) 155782cd038dSYoshinobu Inoue goto fail; 155882cd038dSYoshinobu Inoue 155982cd038dSYoshinobu Inoue { 156082cd038dSYoshinobu Inoue /* get ip6 linklocal address for ifp(my outgoing interface). */ 156182cd038dSYoshinobu Inoue struct in6_ifaddr *ia = in6ifa_ifpforlinklocal(ifp); 156282cd038dSYoshinobu Inoue if (ia == NULL) 156382cd038dSYoshinobu Inoue goto fail; 156482cd038dSYoshinobu Inoue ifp_ll6 = &ia->ia_addr.sin6_addr; 156582cd038dSYoshinobu Inoue } 156682cd038dSYoshinobu Inoue 156782cd038dSYoshinobu Inoue /* get ip6 linklocal address for the router. */ 156882cd038dSYoshinobu Inoue if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { 156982cd038dSYoshinobu Inoue struct sockaddr_in6 *sin6; 157082cd038dSYoshinobu Inoue sin6 = (struct sockaddr_in6 *)rt->rt_gateway; 157182cd038dSYoshinobu Inoue router_ll6 = &sin6->sin6_addr; 157282cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_LINKLOCAL(router_ll6)) 157382cd038dSYoshinobu Inoue router_ll6 = (struct in6_addr *)NULL; 157482cd038dSYoshinobu Inoue } else 157582cd038dSYoshinobu Inoue router_ll6 = (struct in6_addr *)NULL; 157682cd038dSYoshinobu Inoue 157782cd038dSYoshinobu Inoue /* ip6 */ 157882cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 157982cd038dSYoshinobu Inoue ip6->ip6_flow = 0; 158082cd038dSYoshinobu Inoue ip6->ip6_vfc = IPV6_VERSION; 158182cd038dSYoshinobu Inoue /* ip6->ip6_plen will be set later */ 158282cd038dSYoshinobu Inoue ip6->ip6_nxt = IPPROTO_ICMPV6; 158382cd038dSYoshinobu Inoue ip6->ip6_hlim = 255; 158482cd038dSYoshinobu Inoue /* ip6->ip6_src must be linklocal addr for my outgoing if. */ 158582cd038dSYoshinobu Inoue bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); 158682cd038dSYoshinobu Inoue bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); 158782cd038dSYoshinobu Inoue 158882cd038dSYoshinobu Inoue /* ND Redirect */ 158982cd038dSYoshinobu Inoue nd_rd = (struct nd_redirect *)(ip6 + 1); 159082cd038dSYoshinobu Inoue nd_rd->nd_rd_type = ND_REDIRECT; 159182cd038dSYoshinobu Inoue nd_rd->nd_rd_code = 0; 159282cd038dSYoshinobu Inoue nd_rd->nd_rd_reserved = 0; 159382cd038dSYoshinobu Inoue if (rt->rt_flags & RTF_GATEWAY) { 159482cd038dSYoshinobu Inoue /* 159582cd038dSYoshinobu Inoue * nd_rd->nd_rd_target must be a link-local address in 159682cd038dSYoshinobu Inoue * better router cases. 159782cd038dSYoshinobu Inoue */ 159882cd038dSYoshinobu Inoue if (!router_ll6) 159982cd038dSYoshinobu Inoue goto fail; 160082cd038dSYoshinobu Inoue bcopy(router_ll6, &nd_rd->nd_rd_target, 160182cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_target)); 160282cd038dSYoshinobu Inoue bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, 160382cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_dst)); 160482cd038dSYoshinobu Inoue } else { 160582cd038dSYoshinobu Inoue /* make sure redtgt == reddst */ 160682cd038dSYoshinobu Inoue bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, 160782cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_target)); 160882cd038dSYoshinobu Inoue bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, 160982cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_dst)); 161082cd038dSYoshinobu Inoue } 161182cd038dSYoshinobu Inoue 161282cd038dSYoshinobu Inoue p = (u_char *)(nd_rd + 1); 161382cd038dSYoshinobu Inoue 161482cd038dSYoshinobu Inoue if (!router_ll6) 161582cd038dSYoshinobu Inoue goto nolladdropt; 161682cd038dSYoshinobu Inoue 161782cd038dSYoshinobu Inoue { 161882cd038dSYoshinobu Inoue /* target lladdr option */ 161982cd038dSYoshinobu Inoue struct rtentry *rt_router = NULL; 162082cd038dSYoshinobu Inoue int len; 162182cd038dSYoshinobu Inoue struct sockaddr_dl *sdl; 162282cd038dSYoshinobu Inoue struct nd_opt_hdr *nd_opt; 162382cd038dSYoshinobu Inoue char *lladdr; 162482cd038dSYoshinobu Inoue 162582cd038dSYoshinobu Inoue rt_router = nd6_lookup(router_ll6, 0, ifp); 162682cd038dSYoshinobu Inoue if (!rt_router) 162782cd038dSYoshinobu Inoue goto nolladdropt; 162882cd038dSYoshinobu Inoue if (!(rt_router->rt_flags & RTF_GATEWAY) 162982cd038dSYoshinobu Inoue && (rt_router->rt_flags & RTF_LLINFO) 163082cd038dSYoshinobu Inoue && (rt_router->rt_gateway->sa_family == AF_LINK) 163182cd038dSYoshinobu Inoue && (sdl = (struct sockaddr_dl *)rt_router->rt_gateway)) { 163282cd038dSYoshinobu Inoue nd_opt = (struct nd_opt_hdr *)p; 163382cd038dSYoshinobu Inoue nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 163482cd038dSYoshinobu Inoue len = 2 + ifp->if_addrlen; 163582cd038dSYoshinobu Inoue len = (len + 7) & ~7; /*round by 8*/ 163682cd038dSYoshinobu Inoue nd_opt->nd_opt_len = len >> 3; 163782cd038dSYoshinobu Inoue p += len; 163882cd038dSYoshinobu Inoue lladdr = (char *)(nd_opt + 1); 163982cd038dSYoshinobu Inoue bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); 164082cd038dSYoshinobu Inoue } 164182cd038dSYoshinobu Inoue } 164282cd038dSYoshinobu Inoue nolladdropt:; 164382cd038dSYoshinobu Inoue 164482cd038dSYoshinobu Inoue m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; 164582cd038dSYoshinobu Inoue 164682cd038dSYoshinobu Inoue /* just to be safe */ 164782cd038dSYoshinobu Inoue if (m0->m_flags & M_DECRYPTED) 164882cd038dSYoshinobu Inoue goto noredhdropt; 164982cd038dSYoshinobu Inoue 165082cd038dSYoshinobu Inoue { 165182cd038dSYoshinobu Inoue /* redirected header option */ 165282cd038dSYoshinobu Inoue int len; 165382cd038dSYoshinobu Inoue struct nd_opt_rd_hdr *nd_opt_rh; 165482cd038dSYoshinobu Inoue 165582cd038dSYoshinobu Inoue /* 165682cd038dSYoshinobu Inoue * compute the maximum size for icmp6 redirect header option. 165782cd038dSYoshinobu Inoue * XXX room for auth header? 165882cd038dSYoshinobu Inoue */ 165982cd038dSYoshinobu Inoue len = maxlen - (p - (u_char *)ip6); 166082cd038dSYoshinobu Inoue len &= ~7; 166182cd038dSYoshinobu Inoue 166282cd038dSYoshinobu Inoue /* This is just for simplicity. */ 166382cd038dSYoshinobu Inoue if (m0->m_pkthdr.len != m0->m_len) { 166482cd038dSYoshinobu Inoue if (m0->m_next) { 166582cd038dSYoshinobu Inoue m_freem(m0->m_next); 166682cd038dSYoshinobu Inoue m0->m_next = NULL; 166782cd038dSYoshinobu Inoue } 166882cd038dSYoshinobu Inoue m0->m_pkthdr.len = m0->m_len; 166982cd038dSYoshinobu Inoue } 167082cd038dSYoshinobu Inoue 167182cd038dSYoshinobu Inoue /* 167282cd038dSYoshinobu Inoue * Redirected header option spec (RFC2461 4.6.3) talks nothing 167382cd038dSYoshinobu Inoue * about padding/truncate rule for the original IP packet. 167482cd038dSYoshinobu Inoue * From the discussion on IPv6imp in Feb 1999, the consensus was: 167582cd038dSYoshinobu Inoue * - "attach as much as possible" is the goal 167682cd038dSYoshinobu Inoue * - pad if not aligned (original size can be guessed by original 167782cd038dSYoshinobu Inoue * ip6 header) 167882cd038dSYoshinobu Inoue * Following code adds the padding if it is simple enough, 167982cd038dSYoshinobu Inoue * and truncates if not. 168082cd038dSYoshinobu Inoue */ 168182cd038dSYoshinobu Inoue if (m0->m_next || m0->m_pkthdr.len != m0->m_len) 168282cd038dSYoshinobu Inoue panic("assumption failed in %s:%d\n", __FILE__, __LINE__); 168382cd038dSYoshinobu Inoue 168482cd038dSYoshinobu Inoue if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { 168582cd038dSYoshinobu Inoue /* not enough room, truncate */ 168682cd038dSYoshinobu Inoue m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); 168782cd038dSYoshinobu Inoue } else { 168882cd038dSYoshinobu Inoue /* enough room, pad or truncate */ 168982cd038dSYoshinobu Inoue size_t extra; 169082cd038dSYoshinobu Inoue 169182cd038dSYoshinobu Inoue extra = m0->m_pkthdr.len % 8; 169282cd038dSYoshinobu Inoue if (extra) { 169382cd038dSYoshinobu Inoue /* pad if easy enough, truncate if not */ 169482cd038dSYoshinobu Inoue if (8 - extra <= M_TRAILINGSPACE(m0)) { 169582cd038dSYoshinobu Inoue /* pad */ 169682cd038dSYoshinobu Inoue m0->m_len += (8 - extra); 169782cd038dSYoshinobu Inoue m0->m_pkthdr.len += (8 - extra); 169882cd038dSYoshinobu Inoue } else { 169982cd038dSYoshinobu Inoue /* truncate */ 170082cd038dSYoshinobu Inoue m0->m_pkthdr.len -= extra; 170182cd038dSYoshinobu Inoue m0->m_len -= extra; 170282cd038dSYoshinobu Inoue } 170382cd038dSYoshinobu Inoue } 170482cd038dSYoshinobu Inoue len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); 170582cd038dSYoshinobu Inoue m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); 170682cd038dSYoshinobu Inoue } 170782cd038dSYoshinobu Inoue 170882cd038dSYoshinobu Inoue nd_opt_rh = (struct nd_opt_rd_hdr *)p; 170982cd038dSYoshinobu Inoue bzero(nd_opt_rh, sizeof(*nd_opt_rh)); 171082cd038dSYoshinobu Inoue nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; 171182cd038dSYoshinobu Inoue nd_opt_rh->nd_opt_rh_len = len >> 3; 171282cd038dSYoshinobu Inoue p += sizeof(*nd_opt_rh); 171382cd038dSYoshinobu Inoue m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; 171482cd038dSYoshinobu Inoue 171582cd038dSYoshinobu Inoue /* connect m0 to m */ 171682cd038dSYoshinobu Inoue m->m_next = m0; 171782cd038dSYoshinobu Inoue m->m_pkthdr.len = m->m_len + m0->m_len; 171882cd038dSYoshinobu Inoue } 171982cd038dSYoshinobu Inoue noredhdropt:; 172082cd038dSYoshinobu Inoue 172182cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src)) 172282cd038dSYoshinobu Inoue sip6->ip6_src.s6_addr16[1] = 0; 172382cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst)) 172482cd038dSYoshinobu Inoue sip6->ip6_dst.s6_addr16[1] = 0; 172582cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target)) 172682cd038dSYoshinobu Inoue nd_rd->nd_rd_target.s6_addr16[1] = 0; 172782cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst)) 172882cd038dSYoshinobu Inoue nd_rd->nd_rd_dst.s6_addr16[1] = 0; 172982cd038dSYoshinobu Inoue 173082cd038dSYoshinobu Inoue ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 173182cd038dSYoshinobu Inoue 173282cd038dSYoshinobu Inoue nd_rd->nd_rd_cksum = 0; 173382cd038dSYoshinobu Inoue nd_rd->nd_rd_cksum 173482cd038dSYoshinobu Inoue = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); 173582cd038dSYoshinobu Inoue 173682cd038dSYoshinobu Inoue /* send the packet to outside... */ 173782cd038dSYoshinobu Inoue #ifdef IPSEC 173882cd038dSYoshinobu Inoue m->m_pkthdr.rcvif = NULL; 173982cd038dSYoshinobu Inoue #endif /*IPSEC*/ 174082cd038dSYoshinobu Inoue ip6_output(m, NULL, NULL, 0, NULL, &outif); 174182cd038dSYoshinobu Inoue if (outif) { 174282cd038dSYoshinobu Inoue icmp6_ifstat_inc(outif, ifs6_out_msg); 174382cd038dSYoshinobu Inoue icmp6_ifstat_inc(outif, ifs6_out_redirect); 174482cd038dSYoshinobu Inoue } 174582cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[ND_REDIRECT]++; 174682cd038dSYoshinobu Inoue 174782cd038dSYoshinobu Inoue return; 174882cd038dSYoshinobu Inoue 174982cd038dSYoshinobu Inoue fail: 175082cd038dSYoshinobu Inoue if (m) 175182cd038dSYoshinobu Inoue m_freem(m); 175282cd038dSYoshinobu Inoue if (m0) 175382cd038dSYoshinobu Inoue m_freem(m0); 175482cd038dSYoshinobu Inoue } 175582cd038dSYoshinobu Inoue 175682cd038dSYoshinobu Inoue /* 175782cd038dSYoshinobu Inoue * ICMPv6 socket option processing. 175882cd038dSYoshinobu Inoue */ 175982cd038dSYoshinobu Inoue int 176082cd038dSYoshinobu Inoue icmp6_ctloutput(so, sopt) 176182cd038dSYoshinobu Inoue struct socket *so; 176282cd038dSYoshinobu Inoue struct sockopt *sopt; 176382cd038dSYoshinobu Inoue { 176482cd038dSYoshinobu Inoue int error = 0; 176582cd038dSYoshinobu Inoue int optlen; 176682cd038dSYoshinobu Inoue register struct inpcb *inp = sotoinpcb(so); 176782cd038dSYoshinobu Inoue int level, op, optname; 176882cd038dSYoshinobu Inoue 176982cd038dSYoshinobu Inoue if (sopt) { 177082cd038dSYoshinobu Inoue level = sopt->sopt_level; 177182cd038dSYoshinobu Inoue op = sopt->sopt_dir; 177282cd038dSYoshinobu Inoue optname = sopt->sopt_name; 177382cd038dSYoshinobu Inoue optlen = sopt->sopt_valsize; 177482cd038dSYoshinobu Inoue } else 177582cd038dSYoshinobu Inoue level = op = optname = optlen = 0; 177682cd038dSYoshinobu Inoue if (level != IPPROTO_ICMPV6) { 177782cd038dSYoshinobu Inoue return EINVAL; 177882cd038dSYoshinobu Inoue } 177982cd038dSYoshinobu Inoue 178082cd038dSYoshinobu Inoue switch(op) { 178182cd038dSYoshinobu Inoue case PRCO_SETOPT: 178282cd038dSYoshinobu Inoue switch (optname) { 178382cd038dSYoshinobu Inoue case ICMP6_FILTER: 178482cd038dSYoshinobu Inoue { 178582cd038dSYoshinobu Inoue struct icmp6_filter *p; 178682cd038dSYoshinobu Inoue 178782cd038dSYoshinobu Inoue if (optlen != sizeof(*p)) { 178882cd038dSYoshinobu Inoue error = EMSGSIZE; 178982cd038dSYoshinobu Inoue break; 179082cd038dSYoshinobu Inoue } 179182cd038dSYoshinobu Inoue if (inp->in6p_icmp6filt == NULL) { 179282cd038dSYoshinobu Inoue error = EINVAL; 179382cd038dSYoshinobu Inoue break; 179482cd038dSYoshinobu Inoue } 179582cd038dSYoshinobu Inoue error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen, 179682cd038dSYoshinobu Inoue optlen); 179782cd038dSYoshinobu Inoue break; 179882cd038dSYoshinobu Inoue } 179982cd038dSYoshinobu Inoue 180082cd038dSYoshinobu Inoue default: 180182cd038dSYoshinobu Inoue error = ENOPROTOOPT; 180282cd038dSYoshinobu Inoue break; 180382cd038dSYoshinobu Inoue } 180482cd038dSYoshinobu Inoue break; 180582cd038dSYoshinobu Inoue 180682cd038dSYoshinobu Inoue case PRCO_GETOPT: 180782cd038dSYoshinobu Inoue switch (optname) { 180882cd038dSYoshinobu Inoue case ICMP6_FILTER: 180982cd038dSYoshinobu Inoue { 181082cd038dSYoshinobu Inoue if (inp->in6p_icmp6filt == NULL) { 181182cd038dSYoshinobu Inoue error = EINVAL; 181282cd038dSYoshinobu Inoue break; 181382cd038dSYoshinobu Inoue } 181482cd038dSYoshinobu Inoue error = sooptcopyout(sopt, inp->in6p_icmp6filt, 181582cd038dSYoshinobu Inoue sizeof(struct icmp6_filter)); 181682cd038dSYoshinobu Inoue break; 181782cd038dSYoshinobu Inoue } 181882cd038dSYoshinobu Inoue 181982cd038dSYoshinobu Inoue default: 182082cd038dSYoshinobu Inoue error = ENOPROTOOPT; 182182cd038dSYoshinobu Inoue break; 182282cd038dSYoshinobu Inoue } 182382cd038dSYoshinobu Inoue break; 182482cd038dSYoshinobu Inoue } 182582cd038dSYoshinobu Inoue 182682cd038dSYoshinobu Inoue return(error); 182782cd038dSYoshinobu Inoue } 182882cd038dSYoshinobu Inoue 182982cd038dSYoshinobu Inoue /* 183082cd038dSYoshinobu Inoue * Perform rate limit check. 183182cd038dSYoshinobu Inoue * Returns 0 if it is okay to send the icmp6 packet. 183282cd038dSYoshinobu Inoue * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate 183382cd038dSYoshinobu Inoue * limitation. 183482cd038dSYoshinobu Inoue * 183582cd038dSYoshinobu Inoue * XXX per-destination/type check necessary? 183682cd038dSYoshinobu Inoue */ 183782cd038dSYoshinobu Inoue static int 183882cd038dSYoshinobu Inoue icmp6_ratelimit(dst, type, code) 183982cd038dSYoshinobu Inoue const struct in6_addr *dst; /* not used at this moment */ 184082cd038dSYoshinobu Inoue const int type; /* not used at this moment */ 184182cd038dSYoshinobu Inoue const int code; /* not used at this moment */ 184282cd038dSYoshinobu Inoue { 184382cd038dSYoshinobu Inoue struct timeval tp; 184482cd038dSYoshinobu Inoue long sec_diff, usec_diff; 184582cd038dSYoshinobu Inoue 184682cd038dSYoshinobu Inoue /* If we are not doing rate limitation, it is always okay to send */ 184782cd038dSYoshinobu Inoue if (!icmp6errratelim) 184882cd038dSYoshinobu Inoue return 0; 184982cd038dSYoshinobu Inoue 185082cd038dSYoshinobu Inoue microtime(&tp); 185182cd038dSYoshinobu Inoue tp.tv_sec = time_second; 185282cd038dSYoshinobu Inoue if (tp.tv_sec < icmp6_nextsend.tv_sec 185382cd038dSYoshinobu Inoue || (tp.tv_sec == icmp6_nextsend.tv_sec 185482cd038dSYoshinobu Inoue && tp.tv_usec < icmp6_nextsend.tv_usec)) { 185582cd038dSYoshinobu Inoue /* The packet is subject to rate limit */ 185682cd038dSYoshinobu Inoue return 1; 185782cd038dSYoshinobu Inoue } 185882cd038dSYoshinobu Inoue sec_diff = icmp6errratelim / 1000000; 185982cd038dSYoshinobu Inoue usec_diff = icmp6errratelim % 1000000; 186082cd038dSYoshinobu Inoue icmp6_nextsend.tv_sec = tp.tv_sec + sec_diff; 186182cd038dSYoshinobu Inoue if ((tp.tv_usec = tp.tv_usec + usec_diff) >= 1000000) { 186282cd038dSYoshinobu Inoue icmp6_nextsend.tv_sec++; 186382cd038dSYoshinobu Inoue icmp6_nextsend.tv_usec -= 1000000; 186482cd038dSYoshinobu Inoue } 186582cd038dSYoshinobu Inoue 186682cd038dSYoshinobu Inoue /* it is okay to send this */ 186782cd038dSYoshinobu Inoue return 0; 186882cd038dSYoshinobu Inoue } 1869