1686cdd19SJun-ichiro itojun Hagino /* $FreeBSD$ */ 2686cdd19SJun-ichiro itojun Hagino /* $KAME: icmp6.c,v 1.119 2000/07/03 14:16:46 itojun Exp $ */ 3686cdd19SJun-ichiro itojun Hagino 482cd038dSYoshinobu Inoue /* 582cd038dSYoshinobu Inoue * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 682cd038dSYoshinobu Inoue * All rights reserved. 782cd038dSYoshinobu Inoue * 882cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 982cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 1082cd038dSYoshinobu Inoue * are met: 1182cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 1282cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 1382cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 1482cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 1582cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 1682cd038dSYoshinobu Inoue * 3. Neither the name of the project nor the names of its contributors 1782cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 1882cd038dSYoshinobu Inoue * without specific prior written permission. 1982cd038dSYoshinobu Inoue * 2082cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2182cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2282cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2382cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2482cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2582cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2682cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2782cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2882cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2982cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3082cd038dSYoshinobu Inoue * SUCH DAMAGE. 3182cd038dSYoshinobu Inoue */ 3282cd038dSYoshinobu Inoue 3382cd038dSYoshinobu Inoue /* 3482cd038dSYoshinobu Inoue * Copyright (c) 1982, 1986, 1988, 1993 3582cd038dSYoshinobu Inoue * The Regents of the University of California. All rights reserved. 3682cd038dSYoshinobu Inoue * 3782cd038dSYoshinobu Inoue * Redistribution and use in source and binary forms, with or without 3882cd038dSYoshinobu Inoue * modification, are permitted provided that the following conditions 3982cd038dSYoshinobu Inoue * are met: 4082cd038dSYoshinobu Inoue * 1. Redistributions of source code must retain the above copyright 4182cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer. 4282cd038dSYoshinobu Inoue * 2. Redistributions in binary form must reproduce the above copyright 4382cd038dSYoshinobu Inoue * notice, this list of conditions and the following disclaimer in the 4482cd038dSYoshinobu Inoue * documentation and/or other materials provided with the distribution. 4582cd038dSYoshinobu Inoue * 3. All advertising materials mentioning features or use of this software 4682cd038dSYoshinobu Inoue * must display the following acknowledgement: 4782cd038dSYoshinobu Inoue * This product includes software developed by the University of 4882cd038dSYoshinobu Inoue * California, Berkeley and its contributors. 4982cd038dSYoshinobu Inoue * 4. Neither the name of the University nor the names of its contributors 5082cd038dSYoshinobu Inoue * may be used to endorse or promote products derived from this software 5182cd038dSYoshinobu Inoue * without specific prior written permission. 5282cd038dSYoshinobu Inoue * 5382cd038dSYoshinobu Inoue * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5482cd038dSYoshinobu Inoue * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5582cd038dSYoshinobu Inoue * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5682cd038dSYoshinobu Inoue * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5782cd038dSYoshinobu Inoue * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5882cd038dSYoshinobu Inoue * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5982cd038dSYoshinobu Inoue * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6082cd038dSYoshinobu Inoue * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6182cd038dSYoshinobu Inoue * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6282cd038dSYoshinobu Inoue * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6382cd038dSYoshinobu Inoue * SUCH DAMAGE. 6482cd038dSYoshinobu Inoue * 6582cd038dSYoshinobu Inoue * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 6682cd038dSYoshinobu Inoue */ 6782cd038dSYoshinobu Inoue 68686cdd19SJun-ichiro itojun Hagino #include "opt_inet.h" 69686cdd19SJun-ichiro itojun Hagino #include "opt_inet6.h" 706a800098SYoshinobu Inoue #include "opt_ipsec.h" 7182cd038dSYoshinobu Inoue 7282cd038dSYoshinobu Inoue #include <sys/param.h> 7382cd038dSYoshinobu Inoue #include <sys/systm.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> 90686cdd19SJun-ichiro itojun Hagino #include <netinet/ip6.h> 9182cd038dSYoshinobu Inoue #include <netinet6/ip6_var.h> 92686cdd19SJun-ichiro itojun Hagino #include <netinet/icmp6.h> 9382cd038dSYoshinobu Inoue #include <netinet6/mld6_var.h> 9482cd038dSYoshinobu Inoue #include <netinet/in_pcb.h> 95686cdd19SJun-ichiro itojun Hagino #include <netinet6/in6_pcb.h> 9682cd038dSYoshinobu Inoue #include <netinet6/nd6.h> 9782cd038dSYoshinobu Inoue #include <netinet6/in6_ifattach.h> 9882cd038dSYoshinobu Inoue #include <netinet6/ip6protosw.h> 9982cd038dSYoshinobu Inoue 10082cd038dSYoshinobu Inoue #ifdef IPSEC 10182cd038dSYoshinobu Inoue #include <netinet6/ipsec.h> 102686cdd19SJun-ichiro itojun Hagino #ifdef INET6 10382cd038dSYoshinobu Inoue #include <netinet6/ipsec6.h> 1046a800098SYoshinobu Inoue #endif 105686cdd19SJun-ichiro itojun Hagino #include <netkey/key.h> 106686cdd19SJun-ichiro itojun Hagino #endif 10782cd038dSYoshinobu Inoue 108cfa1ca9dSYoshinobu Inoue #include "faith.h" 10982cd038dSYoshinobu Inoue 11082cd038dSYoshinobu Inoue #include <net/net_osdep.h> 11182cd038dSYoshinobu Inoue 11282cd038dSYoshinobu Inoue extern struct domain inet6domain; 11382cd038dSYoshinobu Inoue extern struct ip6protosw inet6sw[]; 11482cd038dSYoshinobu Inoue extern u_char ip6_protox[]; 11582cd038dSYoshinobu Inoue 11682cd038dSYoshinobu Inoue struct icmp6stat icmp6stat; 11782cd038dSYoshinobu Inoue 11882cd038dSYoshinobu Inoue extern struct inpcbhead ripcb; 119686cdd19SJun-ichiro itojun Hagino extern struct timeval icmp6errratelim; 120686cdd19SJun-ichiro itojun Hagino static struct timeval icmp6errratelim_last; 121686cdd19SJun-ichiro itojun Hagino extern int icmp6errppslim; 122686cdd19SJun-ichiro itojun Hagino static int icmp6errpps_count = 0; 123686cdd19SJun-ichiro itojun Hagino extern int icmp6_nodeinfo; 12482cd038dSYoshinobu Inoue 125686cdd19SJun-ichiro itojun Hagino static void icmp6_errcount __P((struct icmp6errstat *, int, int)); 12682cd038dSYoshinobu Inoue static int icmp6_rip6_input __P((struct mbuf **, int)); 127686cdd19SJun-ichiro itojun Hagino static void icmp6_mtudisc_update __P((struct in6_addr *, struct icmp6_hdr *, 128686cdd19SJun-ichiro itojun Hagino struct mbuf *)); 12982cd038dSYoshinobu Inoue static int icmp6_ratelimit __P((const struct in6_addr *, const int, const int)); 13082cd038dSYoshinobu Inoue static const char *icmp6_redirect_diag __P((struct in6_addr *, 131686cdd19SJun-ichiro itojun Hagino struct in6_addr *, struct in6_addr *)); 132686cdd19SJun-ichiro itojun Hagino #ifndef HAVE_RATECHECK 133686cdd19SJun-ichiro itojun Hagino static int ratecheck __P((struct timeval *, struct timeval *)); 134686cdd19SJun-ichiro itojun Hagino #endif 13582cd038dSYoshinobu Inoue static struct mbuf *ni6_input __P((struct mbuf *, int)); 136686cdd19SJun-ichiro itojun Hagino static struct mbuf *ni6_nametodns __P((const char *, int, int)); 137686cdd19SJun-ichiro itojun Hagino static int ni6_dnsmatch __P((const char *, int, const char *, int)); 13882cd038dSYoshinobu Inoue static int ni6_addrs __P((struct icmp6_nodeinfo *, struct mbuf *, 13982cd038dSYoshinobu Inoue struct ifnet **)); 140686cdd19SJun-ichiro itojun Hagino static int ni6_store_addrs __P((struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, 14182cd038dSYoshinobu Inoue struct ifnet *, int)); 14282cd038dSYoshinobu Inoue 14382cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 14482cd038dSYoshinobu Inoue static struct route_in6 icmp6_reflect_rt; 14582cd038dSYoshinobu Inoue #endif 14682cd038dSYoshinobu Inoue 14782cd038dSYoshinobu Inoue void 14882cd038dSYoshinobu Inoue icmp6_init() 14982cd038dSYoshinobu Inoue { 15082cd038dSYoshinobu Inoue mld6_init(); 15182cd038dSYoshinobu Inoue } 15282cd038dSYoshinobu Inoue 153686cdd19SJun-ichiro itojun Hagino static void 154686cdd19SJun-ichiro itojun Hagino icmp6_errcount(stat, type, code) 155686cdd19SJun-ichiro itojun Hagino struct icmp6errstat *stat; 156686cdd19SJun-ichiro itojun Hagino int type, code; 157686cdd19SJun-ichiro itojun Hagino { 158686cdd19SJun-ichiro itojun Hagino switch(type) { 159686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH: 160686cdd19SJun-ichiro itojun Hagino switch (code) { 161686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_NOROUTE: 162686cdd19SJun-ichiro itojun Hagino stat->icp6errs_dst_unreach_noroute++; 163686cdd19SJun-ichiro itojun Hagino return; 164686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_ADMIN: 165686cdd19SJun-ichiro itojun Hagino stat->icp6errs_dst_unreach_admin++; 166686cdd19SJun-ichiro itojun Hagino return; 167686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_BEYONDSCOPE: 168686cdd19SJun-ichiro itojun Hagino stat->icp6errs_dst_unreach_beyondscope++; 169686cdd19SJun-ichiro itojun Hagino return; 170686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_ADDR: 171686cdd19SJun-ichiro itojun Hagino stat->icp6errs_dst_unreach_addr++; 172686cdd19SJun-ichiro itojun Hagino return; 173686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_NOPORT: 174686cdd19SJun-ichiro itojun Hagino stat->icp6errs_dst_unreach_noport++; 175686cdd19SJun-ichiro itojun Hagino return; 176686cdd19SJun-ichiro itojun Hagino } 177686cdd19SJun-ichiro itojun Hagino break; 178686cdd19SJun-ichiro itojun Hagino case ICMP6_PACKET_TOO_BIG: 179686cdd19SJun-ichiro itojun Hagino stat->icp6errs_packet_too_big++; 180686cdd19SJun-ichiro itojun Hagino return; 181686cdd19SJun-ichiro itojun Hagino case ICMP6_TIME_EXCEEDED: 182686cdd19SJun-ichiro itojun Hagino switch(code) { 183686cdd19SJun-ichiro itojun Hagino case ICMP6_TIME_EXCEED_TRANSIT: 184686cdd19SJun-ichiro itojun Hagino stat->icp6errs_time_exceed_transit++; 185686cdd19SJun-ichiro itojun Hagino return; 186686cdd19SJun-ichiro itojun Hagino case ICMP6_TIME_EXCEED_REASSEMBLY: 187686cdd19SJun-ichiro itojun Hagino stat->icp6errs_time_exceed_reassembly++; 188686cdd19SJun-ichiro itojun Hagino return; 189686cdd19SJun-ichiro itojun Hagino } 190686cdd19SJun-ichiro itojun Hagino break; 191686cdd19SJun-ichiro itojun Hagino case ICMP6_PARAM_PROB: 192686cdd19SJun-ichiro itojun Hagino switch(code) { 193686cdd19SJun-ichiro itojun Hagino case ICMP6_PARAMPROB_HEADER: 194686cdd19SJun-ichiro itojun Hagino stat->icp6errs_paramprob_header++; 195686cdd19SJun-ichiro itojun Hagino return; 196686cdd19SJun-ichiro itojun Hagino case ICMP6_PARAMPROB_NEXTHEADER: 197686cdd19SJun-ichiro itojun Hagino stat->icp6errs_paramprob_nextheader++; 198686cdd19SJun-ichiro itojun Hagino return; 199686cdd19SJun-ichiro itojun Hagino case ICMP6_PARAMPROB_OPTION: 200686cdd19SJun-ichiro itojun Hagino stat->icp6errs_paramprob_option++; 201686cdd19SJun-ichiro itojun Hagino return; 202686cdd19SJun-ichiro itojun Hagino } 203686cdd19SJun-ichiro itojun Hagino break; 204686cdd19SJun-ichiro itojun Hagino case ND_REDIRECT: 205686cdd19SJun-ichiro itojun Hagino stat->icp6errs_redirect++; 206686cdd19SJun-ichiro itojun Hagino return; 207686cdd19SJun-ichiro itojun Hagino } 208686cdd19SJun-ichiro itojun Hagino stat->icp6errs_unknown++; 209686cdd19SJun-ichiro itojun Hagino } 210686cdd19SJun-ichiro itojun Hagino 21182cd038dSYoshinobu Inoue /* 21282cd038dSYoshinobu Inoue * Generate an error packet of type error in response to bad IP6 packet. 21382cd038dSYoshinobu Inoue */ 21482cd038dSYoshinobu Inoue void 21582cd038dSYoshinobu Inoue icmp6_error(m, type, code, param) 21682cd038dSYoshinobu Inoue struct mbuf *m; 21782cd038dSYoshinobu Inoue int type, code, param; 21882cd038dSYoshinobu Inoue { 21982cd038dSYoshinobu Inoue struct ip6_hdr *oip6, *nip6; 22082cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6; 221686cdd19SJun-ichiro itojun Hagino u_int preplen; 22282cd038dSYoshinobu Inoue int off; 223686cdd19SJun-ichiro itojun Hagino int nxt; 22482cd038dSYoshinobu Inoue 22582cd038dSYoshinobu Inoue icmp6stat.icp6s_error++; 22682cd038dSYoshinobu Inoue 227686cdd19SJun-ichiro itojun Hagino /* count per-type-code statistics */ 228686cdd19SJun-ichiro itojun Hagino icmp6_errcount(&icmp6stat.icp6s_outerrhist, type, code); 22982cd038dSYoshinobu Inoue 230686cdd19SJun-ichiro itojun Hagino #ifdef M_DECRYPTED /*not openbsd*/ 231686cdd19SJun-ichiro itojun Hagino if (m->m_flags & M_DECRYPTED) { 232686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_canterror++; 233686cdd19SJun-ichiro itojun Hagino goto freeit; 234686cdd19SJun-ichiro itojun Hagino } 235686cdd19SJun-ichiro itojun Hagino #endif 236686cdd19SJun-ichiro itojun Hagino 237686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 238686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), ); 239686cdd19SJun-ichiro itojun Hagino #else 240686cdd19SJun-ichiro itojun Hagino if (m->m_len < sizeof(struct ip6_hdr)) { 241686cdd19SJun-ichiro itojun Hagino m = m_pullup(m, sizeof(struct ip6_hdr)); 242686cdd19SJun-ichiro itojun Hagino if (m == NULL) 243686cdd19SJun-ichiro itojun Hagino return; 244686cdd19SJun-ichiro itojun Hagino } 245686cdd19SJun-ichiro itojun Hagino #endif 24682cd038dSYoshinobu Inoue oip6 = mtod(m, struct ip6_hdr *); 24782cd038dSYoshinobu Inoue 24882cd038dSYoshinobu Inoue /* 24982cd038dSYoshinobu Inoue * Multicast destination check. For unrecognized option errors, 25082cd038dSYoshinobu Inoue * this check has already done in ip6_unknown_opt(), so we can 25182cd038dSYoshinobu Inoue * check only for other errors. 25282cd038dSYoshinobu Inoue */ 25382cd038dSYoshinobu Inoue if ((m->m_flags & (M_BCAST|M_MCAST) || 25482cd038dSYoshinobu Inoue IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && 25582cd038dSYoshinobu Inoue (type != ICMP6_PACKET_TOO_BIG && 25682cd038dSYoshinobu Inoue (type != ICMP6_PARAM_PROB || 25782cd038dSYoshinobu Inoue code != ICMP6_PARAMPROB_OPTION))) 25882cd038dSYoshinobu Inoue goto freeit; 25982cd038dSYoshinobu Inoue 26082cd038dSYoshinobu Inoue /* Source address check. XXX: the case of anycast source? */ 26182cd038dSYoshinobu Inoue if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || 26282cd038dSYoshinobu Inoue IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) 26382cd038dSYoshinobu Inoue goto freeit; 26482cd038dSYoshinobu Inoue 26582cd038dSYoshinobu Inoue /* 266686cdd19SJun-ichiro itojun Hagino * If we are about to send ICMPv6 against ICMPv6 error/redirect, 267686cdd19SJun-ichiro itojun Hagino * don't do it. 26882cd038dSYoshinobu Inoue */ 269686cdd19SJun-ichiro itojun Hagino nxt = -1; 270686cdd19SJun-ichiro itojun Hagino off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 271686cdd19SJun-ichiro itojun Hagino if (off >= 0 && nxt == IPPROTO_ICMPV6) { 27282cd038dSYoshinobu Inoue struct icmp6_hdr *icp; 27382cd038dSYoshinobu Inoue 274686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 27582cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), ); 27682cd038dSYoshinobu Inoue icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); 277686cdd19SJun-ichiro itojun Hagino #else 278686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off, 279686cdd19SJun-ichiro itojun Hagino sizeof(*icp)); 280686cdd19SJun-ichiro itojun Hagino if (icp == NULL) { 281686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 282686cdd19SJun-ichiro itojun Hagino return; 283686cdd19SJun-ichiro itojun Hagino } 284686cdd19SJun-ichiro itojun Hagino #endif 285686cdd19SJun-ichiro itojun Hagino if (icp->icmp6_type < ICMP6_ECHO_REQUEST || 286686cdd19SJun-ichiro itojun Hagino icp->icmp6_type == ND_REDIRECT) { 28782cd038dSYoshinobu Inoue /* 28882cd038dSYoshinobu Inoue * ICMPv6 error 28982cd038dSYoshinobu Inoue * Special case: for redirect (which is 29082cd038dSYoshinobu Inoue * informational) we must not send icmp6 error. 29182cd038dSYoshinobu Inoue */ 29282cd038dSYoshinobu Inoue icmp6stat.icp6s_canterror++; 29382cd038dSYoshinobu Inoue goto freeit; 29482cd038dSYoshinobu Inoue } else { 295686cdd19SJun-ichiro itojun Hagino /* ICMPv6 informational - send the error */ 29682cd038dSYoshinobu Inoue } 297686cdd19SJun-ichiro itojun Hagino } else { 298686cdd19SJun-ichiro itojun Hagino /* non-ICMPv6 - send the error */ 29982cd038dSYoshinobu Inoue } 30082cd038dSYoshinobu Inoue 30182cd038dSYoshinobu Inoue oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ 30282cd038dSYoshinobu Inoue 30382cd038dSYoshinobu Inoue /* Finally, do rate limitation check. */ 30482cd038dSYoshinobu Inoue if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { 30582cd038dSYoshinobu Inoue icmp6stat.icp6s_toofreq++; 30682cd038dSYoshinobu Inoue goto freeit; 30782cd038dSYoshinobu Inoue } 30882cd038dSYoshinobu Inoue 30982cd038dSYoshinobu Inoue /* 31082cd038dSYoshinobu Inoue * OK, ICMP6 can be generated. 31182cd038dSYoshinobu Inoue */ 31282cd038dSYoshinobu Inoue 31382cd038dSYoshinobu Inoue if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) 31482cd038dSYoshinobu Inoue m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); 31582cd038dSYoshinobu Inoue 316686cdd19SJun-ichiro itojun Hagino preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 317686cdd19SJun-ichiro itojun Hagino M_PREPEND(m, preplen, M_DONTWAIT); 318686cdd19SJun-ichiro itojun Hagino if (m && m->m_len < preplen) 319686cdd19SJun-ichiro itojun Hagino m = m_pullup(m, preplen); 32082cd038dSYoshinobu Inoue if (m == NULL) { 32182cd038dSYoshinobu Inoue printf("ENOBUFS in icmp6_error %d\n", __LINE__); 32282cd038dSYoshinobu Inoue return; 32382cd038dSYoshinobu Inoue } 32482cd038dSYoshinobu Inoue 32582cd038dSYoshinobu Inoue nip6 = mtod(m, struct ip6_hdr *); 32682cd038dSYoshinobu Inoue nip6->ip6_src = oip6->ip6_src; 32782cd038dSYoshinobu Inoue nip6->ip6_dst = oip6->ip6_dst; 32882cd038dSYoshinobu Inoue 32982cd038dSYoshinobu Inoue if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src)) 33082cd038dSYoshinobu Inoue oip6->ip6_src.s6_addr16[1] = 0; 33182cd038dSYoshinobu Inoue if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst)) 33282cd038dSYoshinobu Inoue oip6->ip6_dst.s6_addr16[1] = 0; 33382cd038dSYoshinobu Inoue 33482cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(nip6 + 1); 33582cd038dSYoshinobu Inoue icmp6->icmp6_type = type; 33682cd038dSYoshinobu Inoue icmp6->icmp6_code = code; 33782cd038dSYoshinobu Inoue icmp6->icmp6_pptr = htonl((u_int32_t)param); 33882cd038dSYoshinobu Inoue 33982cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[type]++; 34082cd038dSYoshinobu Inoue icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/ 341686cdd19SJun-ichiro itojun Hagino 342686cdd19SJun-ichiro itojun Hagino return; 343686cdd19SJun-ichiro itojun Hagino 344686cdd19SJun-ichiro itojun Hagino freeit: 345686cdd19SJun-ichiro itojun Hagino /* 346686cdd19SJun-ichiro itojun Hagino * If we can't tell wheter or not we can generate ICMP6, free it. 347686cdd19SJun-ichiro itojun Hagino */ 348686cdd19SJun-ichiro itojun Hagino m_freem(m); 34982cd038dSYoshinobu Inoue } 35082cd038dSYoshinobu Inoue 35182cd038dSYoshinobu Inoue /* 35282cd038dSYoshinobu Inoue * Process a received ICMP6 message. 35382cd038dSYoshinobu Inoue */ 35482cd038dSYoshinobu Inoue int 35582cd038dSYoshinobu Inoue icmp6_input(mp, offp, proto) 35682cd038dSYoshinobu Inoue struct mbuf **mp; 35782cd038dSYoshinobu Inoue int *offp, proto; 35882cd038dSYoshinobu Inoue { 35982cd038dSYoshinobu Inoue struct mbuf *m = *mp, *n; 36082cd038dSYoshinobu Inoue struct ip6_hdr *ip6, *nip6; 36182cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6, *nicmp6; 36282cd038dSYoshinobu Inoue int off = *offp; 36382cd038dSYoshinobu Inoue int icmp6len = m->m_pkthdr.len - *offp; 36482cd038dSYoshinobu Inoue int code, sum, noff; 36582cd038dSYoshinobu Inoue struct sockaddr_in6 icmp6src; 36682cd038dSYoshinobu Inoue 367686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 36882cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_hdr), IPPROTO_DONE); 36982cd038dSYoshinobu Inoue /* m might change if M_LOOP. So, call mtod after this */ 370686cdd19SJun-ichiro itojun Hagino #endif 37182cd038dSYoshinobu Inoue 37282cd038dSYoshinobu Inoue /* 37382cd038dSYoshinobu Inoue * Locate icmp6 structure in mbuf, and check 37482cd038dSYoshinobu Inoue * that not corrupted and of at least minimum length 37582cd038dSYoshinobu Inoue */ 37682cd038dSYoshinobu Inoue 37782cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 37882cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct icmp6_hdr)) { 37982cd038dSYoshinobu Inoue icmp6stat.icp6s_tooshort++; 38082cd038dSYoshinobu Inoue goto freeit; 38182cd038dSYoshinobu Inoue } 38282cd038dSYoshinobu Inoue 38382cd038dSYoshinobu Inoue /* 38482cd038dSYoshinobu Inoue * calculate the checksum 38582cd038dSYoshinobu Inoue */ 386686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 38782cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); 388686cdd19SJun-ichiro itojun Hagino #else 389686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); 390686cdd19SJun-ichiro itojun Hagino if (icmp6 == NULL) { 391686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 392686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 393686cdd19SJun-ichiro itojun Hagino } 394686cdd19SJun-ichiro itojun Hagino #endif 39582cd038dSYoshinobu Inoue code = icmp6->icmp6_code; 39682cd038dSYoshinobu Inoue 39782cd038dSYoshinobu Inoue if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { 39882cd038dSYoshinobu Inoue log(LOG_ERR, 39982cd038dSYoshinobu Inoue "ICMP6 checksum error(%d|%x) %s\n", 40082cd038dSYoshinobu Inoue icmp6->icmp6_type, 40182cd038dSYoshinobu Inoue sum, 40282cd038dSYoshinobu Inoue ip6_sprintf(&ip6->ip6_src)); 40382cd038dSYoshinobu Inoue icmp6stat.icp6s_checksum++; 40482cd038dSYoshinobu Inoue goto freeit; 40582cd038dSYoshinobu Inoue } 40682cd038dSYoshinobu Inoue 40782cd038dSYoshinobu Inoue #if defined(NFAITH) && 0 < NFAITH 40882cd038dSYoshinobu Inoue if (m->m_pkthdr.rcvif && m->m_pkthdr.rcvif->if_type == IFT_FAITH) { 40982cd038dSYoshinobu Inoue /* 41082cd038dSYoshinobu Inoue * Deliver very specific ICMP6 type only. 41182cd038dSYoshinobu Inoue * This is important to deilver TOOBIG. Otherwise PMTUD 41282cd038dSYoshinobu Inoue * will not work. 41382cd038dSYoshinobu Inoue */ 41482cd038dSYoshinobu Inoue switch (icmp6->icmp6_type) { 41582cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH: 41682cd038dSYoshinobu Inoue case ICMP6_PACKET_TOO_BIG: 41782cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEEDED: 41882cd038dSYoshinobu Inoue break; 41982cd038dSYoshinobu Inoue default: 42082cd038dSYoshinobu Inoue goto freeit; 42182cd038dSYoshinobu Inoue } 42282cd038dSYoshinobu Inoue } 42382cd038dSYoshinobu Inoue #endif 42482cd038dSYoshinobu Inoue 42582cd038dSYoshinobu Inoue #ifdef IPSEC 42682cd038dSYoshinobu Inoue /* drop it if it does not match the default policy */ 42782cd038dSYoshinobu Inoue if (ipsec6_in_reject(m, NULL)) { 42882cd038dSYoshinobu Inoue ipsecstat.in_polvio++; 42982cd038dSYoshinobu Inoue goto freeit; 43082cd038dSYoshinobu Inoue } 43182cd038dSYoshinobu Inoue #endif 43282cd038dSYoshinobu Inoue 43382cd038dSYoshinobu Inoue icmp6stat.icp6s_inhist[icmp6->icmp6_type]++; 43482cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_msg); 43582cd038dSYoshinobu Inoue if (icmp6->icmp6_type < ICMP6_INFOMSG_MASK) 43682cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_error); 43782cd038dSYoshinobu Inoue 43882cd038dSYoshinobu Inoue switch (icmp6->icmp6_type) { 43982cd038dSYoshinobu Inoue 44082cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH: 44182cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_dstunreach); 44282cd038dSYoshinobu Inoue switch (code) { 44382cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_NOROUTE: 44482cd038dSYoshinobu Inoue code = PRC_UNREACH_NET; 44582cd038dSYoshinobu Inoue break; 44682cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_ADMIN: 44782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_adminprohib); 448686cdd19SJun-ichiro itojun Hagino code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ 44982cd038dSYoshinobu Inoue break; 450686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_ADDR: 451686cdd19SJun-ichiro itojun Hagino code = PRC_HOSTDEAD; 452686cdd19SJun-ichiro itojun Hagino break; 453686cdd19SJun-ichiro itojun Hagino #ifdef COMPAT_RFC1885 45482cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_NOTNEIGHBOR: 45582cd038dSYoshinobu Inoue code = PRC_UNREACH_SRCFAIL; 45682cd038dSYoshinobu Inoue break; 457686cdd19SJun-ichiro itojun Hagino #else 458686cdd19SJun-ichiro itojun Hagino case ICMP6_DST_UNREACH_BEYONDSCOPE: 459686cdd19SJun-ichiro itojun Hagino /* I mean "source address was incorrect." */ 460686cdd19SJun-ichiro itojun Hagino code = PRC_PARAMPROB; 461686cdd19SJun-ichiro itojun Hagino break; 462686cdd19SJun-ichiro itojun Hagino #endif 46382cd038dSYoshinobu Inoue case ICMP6_DST_UNREACH_NOPORT: 46482cd038dSYoshinobu Inoue code = PRC_UNREACH_PORT; 46582cd038dSYoshinobu Inoue break; 46682cd038dSYoshinobu Inoue default: 46782cd038dSYoshinobu Inoue goto badcode; 46882cd038dSYoshinobu Inoue } 46982cd038dSYoshinobu Inoue goto deliver; 47082cd038dSYoshinobu Inoue break; 47182cd038dSYoshinobu Inoue 47282cd038dSYoshinobu Inoue case ICMP6_PACKET_TOO_BIG: 47382cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_pkttoobig); 47482cd038dSYoshinobu Inoue if (code != 0) 47582cd038dSYoshinobu Inoue goto badcode; 47682cd038dSYoshinobu Inoue 47782cd038dSYoshinobu Inoue code = PRC_MSGSIZE; 47882cd038dSYoshinobu Inoue 479686cdd19SJun-ichiro itojun Hagino /* 480686cdd19SJun-ichiro itojun Hagino * Updating the path MTU will be done after examining 481686cdd19SJun-ichiro itojun Hagino * intermediate extension headers. 482686cdd19SJun-ichiro itojun Hagino */ 48382cd038dSYoshinobu Inoue goto deliver; 48482cd038dSYoshinobu Inoue break; 48582cd038dSYoshinobu Inoue 48682cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEEDED: 48782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_timeexceed); 48882cd038dSYoshinobu Inoue switch (code) { 48982cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEED_TRANSIT: 49082cd038dSYoshinobu Inoue case ICMP6_TIME_EXCEED_REASSEMBLY: 49182cd038dSYoshinobu Inoue code += PRC_TIMXCEED_INTRANS; 49282cd038dSYoshinobu Inoue break; 49382cd038dSYoshinobu Inoue default: 49482cd038dSYoshinobu Inoue goto badcode; 49582cd038dSYoshinobu Inoue } 49682cd038dSYoshinobu Inoue goto deliver; 49782cd038dSYoshinobu Inoue break; 49882cd038dSYoshinobu Inoue 49982cd038dSYoshinobu Inoue case ICMP6_PARAM_PROB: 50082cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_paramprob); 50182cd038dSYoshinobu Inoue switch (code) { 50282cd038dSYoshinobu Inoue case ICMP6_PARAMPROB_NEXTHEADER: 50382cd038dSYoshinobu Inoue code = PRC_UNREACH_PROTOCOL; 50482cd038dSYoshinobu Inoue break; 50582cd038dSYoshinobu Inoue case ICMP6_PARAMPROB_HEADER: 50682cd038dSYoshinobu Inoue case ICMP6_PARAMPROB_OPTION: 50782cd038dSYoshinobu Inoue code = PRC_PARAMPROB; 50882cd038dSYoshinobu Inoue break; 50982cd038dSYoshinobu Inoue default: 51082cd038dSYoshinobu Inoue goto badcode; 51182cd038dSYoshinobu Inoue } 51282cd038dSYoshinobu Inoue goto deliver; 51382cd038dSYoshinobu Inoue break; 51482cd038dSYoshinobu Inoue 51582cd038dSYoshinobu Inoue case ICMP6_ECHO_REQUEST: 51682cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echo); 51782cd038dSYoshinobu Inoue if (code != 0) 51882cd038dSYoshinobu Inoue goto badcode; 51982cd038dSYoshinobu Inoue if ((n = m_copy(m, 0, M_COPYALL)) == NULL) { 52082cd038dSYoshinobu Inoue /* Give up remote */ 52182cd038dSYoshinobu Inoue break; 52282cd038dSYoshinobu Inoue } 523686cdd19SJun-ichiro itojun Hagino if ((n->m_flags & M_EXT) != 0 524686cdd19SJun-ichiro itojun Hagino || n->m_len < off + sizeof(struct icmp6_hdr)) { 52582cd038dSYoshinobu Inoue struct mbuf *n0 = n; 526686cdd19SJun-ichiro itojun Hagino const int maxlen = sizeof(*nip6) + sizeof(*nicmp6); 52782cd038dSYoshinobu Inoue 52882cd038dSYoshinobu Inoue /* 52982cd038dSYoshinobu Inoue * Prepare an internal mbuf. m_pullup() doesn't 53082cd038dSYoshinobu Inoue * always copy the length we specified. 53182cd038dSYoshinobu Inoue */ 532686cdd19SJun-ichiro itojun Hagino if (maxlen >= MCLBYTES) { 533686cdd19SJun-ichiro itojun Hagino #ifdef DIAGNOSTIC 534686cdd19SJun-ichiro itojun Hagino printf("MCLBYTES too small\n"); 535686cdd19SJun-ichiro itojun Hagino #endif 536686cdd19SJun-ichiro itojun Hagino /* Give up remote */ 537686cdd19SJun-ichiro itojun Hagino m_freem(n0); 538686cdd19SJun-ichiro itojun Hagino break; 539686cdd19SJun-ichiro itojun Hagino } 54082cd038dSYoshinobu Inoue MGETHDR(n, M_DONTWAIT, n0->m_type); 541686cdd19SJun-ichiro itojun Hagino if (n && maxlen >= MHLEN) { 542686cdd19SJun-ichiro itojun Hagino MCLGET(n, M_DONTWAIT); 543686cdd19SJun-ichiro itojun Hagino if ((n->m_flags & M_EXT) == 0) { 544686cdd19SJun-ichiro itojun Hagino m_free(n); 545686cdd19SJun-ichiro itojun Hagino n = NULL; 546686cdd19SJun-ichiro itojun Hagino } 547686cdd19SJun-ichiro itojun Hagino } 54882cd038dSYoshinobu Inoue if (n == NULL) { 54982cd038dSYoshinobu Inoue /* Give up remote */ 55082cd038dSYoshinobu Inoue m_freem(n0); 55182cd038dSYoshinobu Inoue break; 55282cd038dSYoshinobu Inoue } 55382cd038dSYoshinobu Inoue M_COPY_PKTHDR(n, n0); 55482cd038dSYoshinobu Inoue /* 55582cd038dSYoshinobu Inoue * Copy IPv6 and ICMPv6 only. 55682cd038dSYoshinobu Inoue */ 55782cd038dSYoshinobu Inoue nip6 = mtod(n, struct ip6_hdr *); 55882cd038dSYoshinobu Inoue bcopy(ip6, nip6, sizeof(struct ip6_hdr)); 55982cd038dSYoshinobu Inoue nicmp6 = (struct icmp6_hdr *)(nip6 + 1); 56082cd038dSYoshinobu Inoue bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); 56182cd038dSYoshinobu Inoue noff = sizeof(struct ip6_hdr); 562686cdd19SJun-ichiro itojun Hagino n->m_pkthdr.len = n->m_len = 563686cdd19SJun-ichiro itojun Hagino noff + sizeof(struct icmp6_hdr); 564686cdd19SJun-ichiro itojun Hagino /* 565686cdd19SJun-ichiro itojun Hagino * Adjust mbuf. ip6_plen will be adjusted in 566686cdd19SJun-ichiro itojun Hagino * ip6_output(). 567686cdd19SJun-ichiro itojun Hagino */ 568686cdd19SJun-ichiro itojun Hagino m_adj(n0, off + sizeof(struct icmp6_hdr)); 569686cdd19SJun-ichiro itojun Hagino n->m_pkthdr.len += n0->m_pkthdr.len; 570686cdd19SJun-ichiro itojun Hagino n->m_next = n0; 571686cdd19SJun-ichiro itojun Hagino n0->m_flags &= ~M_PKTHDR; 57282cd038dSYoshinobu Inoue } else { 57382cd038dSYoshinobu Inoue nip6 = mtod(n, struct ip6_hdr *); 57482cd038dSYoshinobu Inoue nicmp6 = (struct icmp6_hdr *)((caddr_t)nip6 + off); 57582cd038dSYoshinobu Inoue noff = off; 57682cd038dSYoshinobu Inoue } 57782cd038dSYoshinobu Inoue nicmp6->icmp6_type = ICMP6_ECHO_REPLY; 57882cd038dSYoshinobu Inoue nicmp6->icmp6_code = 0; 57982cd038dSYoshinobu Inoue if (n) { 58082cd038dSYoshinobu Inoue icmp6stat.icp6s_reflect++; 58182cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[ICMP6_ECHO_REPLY]++; 58282cd038dSYoshinobu Inoue icmp6_reflect(n, noff); 58382cd038dSYoshinobu Inoue } 58482cd038dSYoshinobu Inoue break; 58582cd038dSYoshinobu Inoue 58682cd038dSYoshinobu Inoue case ICMP6_ECHO_REPLY: 58782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_echoreply); 58882cd038dSYoshinobu Inoue if (code != 0) 58982cd038dSYoshinobu Inoue goto badcode; 59082cd038dSYoshinobu Inoue break; 59182cd038dSYoshinobu Inoue 59282cd038dSYoshinobu Inoue case MLD6_LISTENER_QUERY: 59382cd038dSYoshinobu Inoue case MLD6_LISTENER_REPORT: 59482cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct mld6_hdr)) 59582cd038dSYoshinobu Inoue goto badlen; 59682cd038dSYoshinobu Inoue if (icmp6->icmp6_type == MLD6_LISTENER_QUERY) /* XXX: ugly... */ 59782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldquery); 59882cd038dSYoshinobu Inoue else 59982cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mldreport); 600686cdd19SJun-ichiro itojun Hagino if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 601686cdd19SJun-ichiro itojun Hagino /* give up local */ 60282cd038dSYoshinobu Inoue mld6_input(m, off); 603686cdd19SJun-ichiro itojun Hagino m = NULL; 604686cdd19SJun-ichiro itojun Hagino goto freeit; 605686cdd19SJun-ichiro itojun Hagino } 606686cdd19SJun-ichiro itojun Hagino mld6_input(n, off); 60782cd038dSYoshinobu Inoue /* m stays. */ 60882cd038dSYoshinobu Inoue break; 60982cd038dSYoshinobu Inoue 61082cd038dSYoshinobu Inoue case MLD6_LISTENER_DONE: 61182cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mlddone); 61282cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct mld6_hdr)) /* necessary? */ 61382cd038dSYoshinobu Inoue goto badlen; 61482cd038dSYoshinobu Inoue break; /* nothing to be done in kernel */ 61582cd038dSYoshinobu Inoue 61682cd038dSYoshinobu Inoue case MLD6_MTRACE_RESP: 61782cd038dSYoshinobu Inoue case MLD6_MTRACE: 61882cd038dSYoshinobu Inoue /* XXX: these two are experimental. not officially defind. */ 61982cd038dSYoshinobu Inoue /* XXX: per-interface statistics? */ 620686cdd19SJun-ichiro itojun Hagino break; /* just pass it to applications */ 62182cd038dSYoshinobu Inoue 62282cd038dSYoshinobu Inoue case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ 62382cd038dSYoshinobu Inoue { 62482cd038dSYoshinobu Inoue enum { WRU, FQDN } mode; 62582cd038dSYoshinobu Inoue 626686cdd19SJun-ichiro itojun Hagino if (!icmp6_nodeinfo) 627686cdd19SJun-ichiro itojun Hagino break; 628686cdd19SJun-ichiro itojun Hagino 62982cd038dSYoshinobu Inoue if (icmp6len == sizeof(struct icmp6_hdr) + 4) 63082cd038dSYoshinobu Inoue mode = WRU; 631686cdd19SJun-ichiro itojun Hagino else if (icmp6len >= sizeof(struct icmp6_nodeinfo)) 63282cd038dSYoshinobu Inoue mode = FQDN; 63382cd038dSYoshinobu Inoue else 63482cd038dSYoshinobu Inoue goto badlen; 63582cd038dSYoshinobu Inoue 63682cd038dSYoshinobu Inoue #define hostnamelen strlen(hostname) 63782cd038dSYoshinobu Inoue if (mode == FQDN) { 638686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 63982cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, sizeof(struct icmp6_nodeinfo), 64082cd038dSYoshinobu Inoue IPPROTO_DONE); 641686cdd19SJun-ichiro itojun Hagino #endif 642686cdd19SJun-ichiro itojun Hagino n = m_copy(m, 0, M_COPYALL); 643686cdd19SJun-ichiro itojun Hagino if (n) 644686cdd19SJun-ichiro itojun Hagino n = ni6_input(n, off); 645686cdd19SJun-ichiro itojun Hagino /* XXX meaningless if n == NULL */ 64682cd038dSYoshinobu Inoue noff = sizeof(struct ip6_hdr); 64782cd038dSYoshinobu Inoue } else { 64882cd038dSYoshinobu Inoue u_char *p; 649686cdd19SJun-ichiro itojun Hagino int maxlen, maxhlen; 65082cd038dSYoshinobu Inoue 651686cdd19SJun-ichiro itojun Hagino if (code != 0) 652686cdd19SJun-ichiro itojun Hagino goto badcode; 653686cdd19SJun-ichiro itojun Hagino maxlen = sizeof(*nip6) + sizeof(*nicmp6) + 4; 654686cdd19SJun-ichiro itojun Hagino if (maxlen >= MCLBYTES) { 655686cdd19SJun-ichiro itojun Hagino #ifdef DIAGNOSTIC 656686cdd19SJun-ichiro itojun Hagino printf("MCLBYTES too small\n"); 657686cdd19SJun-ichiro itojun Hagino #endif 658686cdd19SJun-ichiro itojun Hagino /* Give up remote */ 659686cdd19SJun-ichiro itojun Hagino break; 660686cdd19SJun-ichiro itojun Hagino } 66182cd038dSYoshinobu Inoue MGETHDR(n, M_DONTWAIT, m->m_type); 662686cdd19SJun-ichiro itojun Hagino if (n && maxlen > MHLEN) { 663686cdd19SJun-ichiro itojun Hagino MCLGET(n, M_DONTWAIT); 664686cdd19SJun-ichiro itojun Hagino if ((n->m_flags & M_EXT) == 0) { 665686cdd19SJun-ichiro itojun Hagino m_free(n); 666686cdd19SJun-ichiro itojun Hagino n = NULL; 667686cdd19SJun-ichiro itojun Hagino } 668686cdd19SJun-ichiro itojun Hagino } 66982cd038dSYoshinobu Inoue if (n == NULL) { 67082cd038dSYoshinobu Inoue /* Give up remote */ 67182cd038dSYoshinobu Inoue break; 67282cd038dSYoshinobu Inoue } 673686cdd19SJun-ichiro itojun Hagino n->m_len = 0; 674686cdd19SJun-ichiro itojun Hagino maxhlen = M_TRAILINGSPACE(n) - maxlen; 675686cdd19SJun-ichiro itojun Hagino if (maxhlen > hostnamelen) 676686cdd19SJun-ichiro itojun Hagino maxhlen = hostnamelen; 67782cd038dSYoshinobu Inoue /* 67882cd038dSYoshinobu Inoue * Copy IPv6 and ICMPv6 only. 67982cd038dSYoshinobu Inoue */ 68082cd038dSYoshinobu Inoue nip6 = mtod(n, struct ip6_hdr *); 68182cd038dSYoshinobu Inoue bcopy(ip6, nip6, sizeof(struct ip6_hdr)); 68282cd038dSYoshinobu Inoue nicmp6 = (struct icmp6_hdr *)(nip6 + 1); 68382cd038dSYoshinobu Inoue bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); 68482cd038dSYoshinobu Inoue p = (u_char *)(nicmp6 + 1); 68582cd038dSYoshinobu Inoue bzero(p, 4); 686686cdd19SJun-ichiro itojun Hagino bcopy(hostname, p + 4, maxhlen); /*meaningless TTL*/ 68782cd038dSYoshinobu Inoue noff = sizeof(struct ip6_hdr); 68882cd038dSYoshinobu Inoue M_COPY_PKTHDR(n, m); /* just for recvif */ 68982cd038dSYoshinobu Inoue n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + 690686cdd19SJun-ichiro itojun Hagino sizeof(struct icmp6_hdr) + 4 + maxhlen; 69182cd038dSYoshinobu Inoue nicmp6->icmp6_type = ICMP6_WRUREPLY; 69282cd038dSYoshinobu Inoue nicmp6->icmp6_code = 0; 69382cd038dSYoshinobu Inoue } 69482cd038dSYoshinobu Inoue #undef hostnamelen 69582cd038dSYoshinobu Inoue if (n) { 69682cd038dSYoshinobu Inoue icmp6stat.icp6s_reflect++; 69782cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[ICMP6_WRUREPLY]++; 69882cd038dSYoshinobu Inoue icmp6_reflect(n, noff); 69982cd038dSYoshinobu Inoue } 70082cd038dSYoshinobu Inoue break; 70182cd038dSYoshinobu Inoue } 70282cd038dSYoshinobu Inoue 70382cd038dSYoshinobu Inoue case ICMP6_WRUREPLY: 70482cd038dSYoshinobu Inoue if (code != 0) 70582cd038dSYoshinobu Inoue goto badcode; 70682cd038dSYoshinobu Inoue break; 70782cd038dSYoshinobu Inoue 70882cd038dSYoshinobu Inoue case ND_ROUTER_SOLICIT: 70982cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routersolicit); 71082cd038dSYoshinobu Inoue if (code != 0) 71182cd038dSYoshinobu Inoue goto badcode; 71282cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_router_solicit)) 71382cd038dSYoshinobu Inoue goto badlen; 714686cdd19SJun-ichiro itojun Hagino if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 715686cdd19SJun-ichiro itojun Hagino /* give up local */ 71682cd038dSYoshinobu Inoue nd6_rs_input(m, off, icmp6len); 717686cdd19SJun-ichiro itojun Hagino m = NULL; 718686cdd19SJun-ichiro itojun Hagino goto freeit; 719686cdd19SJun-ichiro itojun Hagino } 720686cdd19SJun-ichiro itojun Hagino nd6_rs_input(n, off, icmp6len); 72182cd038dSYoshinobu Inoue /* m stays. */ 72282cd038dSYoshinobu Inoue break; 72382cd038dSYoshinobu Inoue 72482cd038dSYoshinobu Inoue case ND_ROUTER_ADVERT: 72582cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_routeradvert); 72682cd038dSYoshinobu Inoue if (code != 0) 72782cd038dSYoshinobu Inoue goto badcode; 72882cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_router_advert)) 72982cd038dSYoshinobu Inoue goto badlen; 730686cdd19SJun-ichiro itojun Hagino if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 731686cdd19SJun-ichiro itojun Hagino /* give up local */ 73282cd038dSYoshinobu Inoue nd6_ra_input(m, off, icmp6len); 733686cdd19SJun-ichiro itojun Hagino m = NULL; 734686cdd19SJun-ichiro itojun Hagino goto freeit; 735686cdd19SJun-ichiro itojun Hagino } 736686cdd19SJun-ichiro itojun Hagino nd6_ra_input(n, off, icmp6len); 73782cd038dSYoshinobu Inoue /* m stays. */ 73882cd038dSYoshinobu Inoue break; 73982cd038dSYoshinobu Inoue 74082cd038dSYoshinobu Inoue case ND_NEIGHBOR_SOLICIT: 74182cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighborsolicit); 74282cd038dSYoshinobu Inoue if (code != 0) 74382cd038dSYoshinobu Inoue goto badcode; 74482cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_neighbor_solicit)) 74582cd038dSYoshinobu Inoue goto badlen; 746686cdd19SJun-ichiro itojun Hagino if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 747686cdd19SJun-ichiro itojun Hagino /* give up local */ 74882cd038dSYoshinobu Inoue nd6_ns_input(m, off, icmp6len); 749686cdd19SJun-ichiro itojun Hagino m = NULL; 750686cdd19SJun-ichiro itojun Hagino goto freeit; 751686cdd19SJun-ichiro itojun Hagino } 752686cdd19SJun-ichiro itojun Hagino nd6_ns_input(n, off, icmp6len); 75382cd038dSYoshinobu Inoue /* m stays. */ 75482cd038dSYoshinobu Inoue break; 75582cd038dSYoshinobu Inoue 75682cd038dSYoshinobu Inoue case ND_NEIGHBOR_ADVERT: 75782cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_neighboradvert); 75882cd038dSYoshinobu Inoue if (code != 0) 75982cd038dSYoshinobu Inoue goto badcode; 76082cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_neighbor_advert)) 76182cd038dSYoshinobu Inoue goto badlen; 762686cdd19SJun-ichiro itojun Hagino if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 763686cdd19SJun-ichiro itojun Hagino /* give up local */ 76482cd038dSYoshinobu Inoue nd6_na_input(m, off, icmp6len); 765686cdd19SJun-ichiro itojun Hagino m = NULL; 766686cdd19SJun-ichiro itojun Hagino goto freeit; 767686cdd19SJun-ichiro itojun Hagino } 768686cdd19SJun-ichiro itojun Hagino nd6_na_input(n, off, icmp6len); 76982cd038dSYoshinobu Inoue /* m stays. */ 77082cd038dSYoshinobu Inoue break; 77182cd038dSYoshinobu Inoue 77282cd038dSYoshinobu Inoue case ND_REDIRECT: 77382cd038dSYoshinobu Inoue icmp6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_redirect); 77482cd038dSYoshinobu Inoue if (code != 0) 77582cd038dSYoshinobu Inoue goto badcode; 77682cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct nd_redirect)) 77782cd038dSYoshinobu Inoue goto badlen; 778686cdd19SJun-ichiro itojun Hagino if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 779686cdd19SJun-ichiro itojun Hagino /* give up local */ 78082cd038dSYoshinobu Inoue icmp6_redirect_input(m, off); 781686cdd19SJun-ichiro itojun Hagino m = NULL; 782686cdd19SJun-ichiro itojun Hagino goto freeit; 783686cdd19SJun-ichiro itojun Hagino } 784686cdd19SJun-ichiro itojun Hagino icmp6_redirect_input(n, off); 78582cd038dSYoshinobu Inoue /* m stays. */ 78682cd038dSYoshinobu Inoue break; 78782cd038dSYoshinobu Inoue 78882cd038dSYoshinobu Inoue case ICMP6_ROUTER_RENUMBERING: 78982cd038dSYoshinobu Inoue if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && 79082cd038dSYoshinobu Inoue code != ICMP6_ROUTER_RENUMBERING_RESULT) 79182cd038dSYoshinobu Inoue goto badcode; 79282cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct icmp6_router_renum)) 79382cd038dSYoshinobu Inoue goto badlen; 79482cd038dSYoshinobu Inoue break; 79582cd038dSYoshinobu Inoue 79682cd038dSYoshinobu Inoue default: 79782cd038dSYoshinobu Inoue printf("icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%d)\n", 79882cd038dSYoshinobu Inoue icmp6->icmp6_type, ip6_sprintf(&ip6->ip6_src), 79982cd038dSYoshinobu Inoue ip6_sprintf(&ip6->ip6_dst), 80082cd038dSYoshinobu Inoue m->m_pkthdr.rcvif ? m->m_pkthdr.rcvif->if_index : 0); 80182cd038dSYoshinobu Inoue if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { 80282cd038dSYoshinobu Inoue /* ICMPv6 error: MUST deliver it by spec... */ 80382cd038dSYoshinobu Inoue code = PRC_NCMDS; 80482cd038dSYoshinobu Inoue /* deliver */ 80582cd038dSYoshinobu Inoue } else { 80682cd038dSYoshinobu Inoue /* ICMPv6 informational: MUST not deliver */ 80782cd038dSYoshinobu Inoue break; 80882cd038dSYoshinobu Inoue } 80982cd038dSYoshinobu Inoue deliver: 81082cd038dSYoshinobu Inoue if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { 81182cd038dSYoshinobu Inoue icmp6stat.icp6s_tooshort++; 81282cd038dSYoshinobu Inoue goto freeit; 81382cd038dSYoshinobu Inoue } 814686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 81582cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, off, 81682cd038dSYoshinobu Inoue sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr), 81782cd038dSYoshinobu Inoue IPPROTO_DONE); 81882cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); 819686cdd19SJun-ichiro itojun Hagino #else 820686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, 821686cdd19SJun-ichiro itojun Hagino sizeof(*icmp6) + sizeof(struct ip6_hdr)); 822686cdd19SJun-ichiro itojun Hagino if (icmp6 == NULL) { 823686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 824686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 825686cdd19SJun-ichiro itojun Hagino } 826686cdd19SJun-ichiro itojun Hagino #endif 82782cd038dSYoshinobu Inoue bzero(&icmp6src, sizeof(icmp6src)); 82882cd038dSYoshinobu Inoue icmp6src.sin6_len = sizeof(struct sockaddr_in6); 82982cd038dSYoshinobu Inoue icmp6src.sin6_family = AF_INET6; 83082cd038dSYoshinobu Inoue icmp6src.sin6_addr = ((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; 83182cd038dSYoshinobu Inoue 83282cd038dSYoshinobu Inoue /* Detect the upper level protocol */ 83382cd038dSYoshinobu Inoue { 83482cd038dSYoshinobu Inoue void (*ctlfunc) __P((int, struct sockaddr *, void *)); 83582cd038dSYoshinobu Inoue struct ip6_hdr *eip6 = (struct ip6_hdr *)(icmp6 + 1); 83682cd038dSYoshinobu Inoue u_int8_t nxt = eip6->ip6_nxt; 83782cd038dSYoshinobu Inoue int eoff = off + sizeof(struct icmp6_hdr) + 83882cd038dSYoshinobu Inoue sizeof(struct ip6_hdr); 83982cd038dSYoshinobu Inoue struct ip6ctlparam ip6cp; 840686cdd19SJun-ichiro itojun Hagino struct in6_addr *finaldst = NULL; 841686cdd19SJun-ichiro itojun Hagino int icmp6type = icmp6->icmp6_type; 842686cdd19SJun-ichiro itojun Hagino struct ip6_frag *fh; 843686cdd19SJun-ichiro itojun Hagino struct ip6_rthdr *rth; 844686cdd19SJun-ichiro itojun Hagino struct ip6_rthdr0 *rth0; 845686cdd19SJun-ichiro itojun Hagino int rthlen; 84682cd038dSYoshinobu Inoue 84782cd038dSYoshinobu Inoue while (1) { /* XXX: should avoid inf. loop explicitly? */ 84882cd038dSYoshinobu Inoue struct ip6_ext *eh; 84982cd038dSYoshinobu Inoue 85082cd038dSYoshinobu Inoue switch(nxt) { 85182cd038dSYoshinobu Inoue case IPPROTO_HOPOPTS: 85282cd038dSYoshinobu Inoue case IPPROTO_DSTOPTS: 85382cd038dSYoshinobu Inoue case IPPROTO_AH: 854686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 85582cd038dSYoshinobu Inoue IP6_EXTHDR_CHECK(m, 0, eoff + 85682cd038dSYoshinobu Inoue sizeof(struct ip6_ext), 85782cd038dSYoshinobu Inoue IPPROTO_DONE); 85882cd038dSYoshinobu Inoue eh = (struct ip6_ext *)(mtod(m, caddr_t) 85982cd038dSYoshinobu Inoue + eoff); 860686cdd19SJun-ichiro itojun Hagino #else 861686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(eh, struct ip6_ext *, m, 862686cdd19SJun-ichiro itojun Hagino eoff, sizeof(*eh)); 863686cdd19SJun-ichiro itojun Hagino if (eh == NULL) { 864686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 865686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 866686cdd19SJun-ichiro itojun Hagino } 867686cdd19SJun-ichiro itojun Hagino #endif 868686cdd19SJun-ichiro itojun Hagino 86982cd038dSYoshinobu Inoue if (nxt == IPPROTO_AH) 87082cd038dSYoshinobu Inoue eoff += (eh->ip6e_len + 2) << 2; 87182cd038dSYoshinobu Inoue else 87282cd038dSYoshinobu Inoue eoff += (eh->ip6e_len + 1) << 3; 87382cd038dSYoshinobu Inoue nxt = eh->ip6e_nxt; 87482cd038dSYoshinobu Inoue break; 875686cdd19SJun-ichiro itojun Hagino case IPPROTO_ROUTING: 876686cdd19SJun-ichiro itojun Hagino /* 877686cdd19SJun-ichiro itojun Hagino * When the erroneous packet contains a 878686cdd19SJun-ichiro itojun Hagino * routing header, we should examine the 879686cdd19SJun-ichiro itojun Hagino * header to determine the final destination. 880686cdd19SJun-ichiro itojun Hagino * Otherwise, we can't properly update 881686cdd19SJun-ichiro itojun Hagino * information that depends on the final 882686cdd19SJun-ichiro itojun Hagino * destination (e.g. path MTU). 883686cdd19SJun-ichiro itojun Hagino */ 884686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 885686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_CHECK(m, 0, eoff + sizeof(*rth), 886686cdd19SJun-ichiro itojun Hagino IPPROTO_DONE); 887686cdd19SJun-ichiro itojun Hagino rth = (struct ip6_rthdr *)(mtod(m, caddr_t) 888686cdd19SJun-ichiro itojun Hagino + eoff); 889686cdd19SJun-ichiro itojun Hagino #else 890686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, 891686cdd19SJun-ichiro itojun Hagino eoff, sizeof(*rth)); 892686cdd19SJun-ichiro itojun Hagino if (rth == NULL) { 893686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 894686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 895686cdd19SJun-ichiro itojun Hagino } 896686cdd19SJun-ichiro itojun Hagino #endif 897686cdd19SJun-ichiro itojun Hagino rthlen = (rth->ip6r_len + 1) << 3; 898686cdd19SJun-ichiro itojun Hagino /* 899686cdd19SJun-ichiro itojun Hagino * XXX: currently there is no 900686cdd19SJun-ichiro itojun Hagino * officially defined type other 901686cdd19SJun-ichiro itojun Hagino * than type-0. 902686cdd19SJun-ichiro itojun Hagino * Note that if the segment left field 903686cdd19SJun-ichiro itojun Hagino * is 0, all intermediate hops must 904686cdd19SJun-ichiro itojun Hagino * have been passed. 905686cdd19SJun-ichiro itojun Hagino */ 906686cdd19SJun-ichiro itojun Hagino if (rth->ip6r_segleft && 907686cdd19SJun-ichiro itojun Hagino rth->ip6r_type == IPV6_RTHDR_TYPE_0) { 908686cdd19SJun-ichiro itojun Hagino int hops; 909686cdd19SJun-ichiro itojun Hagino 910686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 911686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_CHECK(m, 0, eoff + rthlen, 912686cdd19SJun-ichiro itojun Hagino IPPROTO_DONE); 913686cdd19SJun-ichiro itojun Hagino rth0 = (struct ip6_rthdr0 *)(mtod(m, caddr_t) + eoff); 914686cdd19SJun-ichiro itojun Hagino #else 915686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(rth0, 916686cdd19SJun-ichiro itojun Hagino struct ip6_rthdr0 *, m, 917686cdd19SJun-ichiro itojun Hagino eoff, rthlen); 918686cdd19SJun-ichiro itojun Hagino if (rth0 == NULL) { 919686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 920686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 921686cdd19SJun-ichiro itojun Hagino } 922686cdd19SJun-ichiro itojun Hagino #endif 923686cdd19SJun-ichiro itojun Hagino /* just ignore a bogus header */ 924686cdd19SJun-ichiro itojun Hagino if ((rth0->ip6r0_len % 2) == 0 && 925686cdd19SJun-ichiro itojun Hagino (hops = rth0->ip6r0_len/2)) 926686cdd19SJun-ichiro itojun Hagino finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); 927686cdd19SJun-ichiro itojun Hagino } 928686cdd19SJun-ichiro itojun Hagino eoff += rthlen; 929686cdd19SJun-ichiro itojun Hagino nxt = rth->ip6r_nxt; 930686cdd19SJun-ichiro itojun Hagino break; 931686cdd19SJun-ichiro itojun Hagino case IPPROTO_FRAGMENT: 932686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 933686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_CHECK(m, 0, eoff + 934686cdd19SJun-ichiro itojun Hagino sizeof(struct ip6_frag), 935686cdd19SJun-ichiro itojun Hagino IPPROTO_DONE); 936686cdd19SJun-ichiro itojun Hagino fh = (struct ip6_frag *)(mtod(m, caddr_t) 937686cdd19SJun-ichiro itojun Hagino + eoff); 938686cdd19SJun-ichiro itojun Hagino #else 939686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(fh, struct ip6_frag *, m, 940686cdd19SJun-ichiro itojun Hagino eoff, sizeof(*fh)); 941686cdd19SJun-ichiro itojun Hagino if (fh == NULL) { 942686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 943686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 944686cdd19SJun-ichiro itojun Hagino } 945686cdd19SJun-ichiro itojun Hagino #endif 946686cdd19SJun-ichiro itojun Hagino /* 947686cdd19SJun-ichiro itojun Hagino * Data after a fragment header is meaningless 948686cdd19SJun-ichiro itojun Hagino * unless it is the first fragment, but 949686cdd19SJun-ichiro itojun Hagino * we'll go to the notify label for path MTU 950686cdd19SJun-ichiro itojun Hagino * discovery. 951686cdd19SJun-ichiro itojun Hagino */ 952686cdd19SJun-ichiro itojun Hagino if (fh->ip6f_offlg & IP6F_OFF_MASK) 953686cdd19SJun-ichiro itojun Hagino goto notify; 954686cdd19SJun-ichiro itojun Hagino 955686cdd19SJun-ichiro itojun Hagino eoff += sizeof(struct ip6_frag); 956686cdd19SJun-ichiro itojun Hagino nxt = fh->ip6f_nxt; 957686cdd19SJun-ichiro itojun Hagino break; 95882cd038dSYoshinobu Inoue default: 959686cdd19SJun-ichiro itojun Hagino /* 960686cdd19SJun-ichiro itojun Hagino * This case includes ESP and the No Next 961686cdd19SJun-ichiro itojun Hagino * Header. In such cases going to the notify 962686cdd19SJun-ichiro itojun Hagino * label does not have any meaning 963686cdd19SJun-ichiro itojun Hagino * (i.e. ctlfunc will be NULL), but we go 964686cdd19SJun-ichiro itojun Hagino * anyway since we might have to update 965686cdd19SJun-ichiro itojun Hagino * path MTU information. 966686cdd19SJun-ichiro itojun Hagino */ 96782cd038dSYoshinobu Inoue goto notify; 96882cd038dSYoshinobu Inoue } 96982cd038dSYoshinobu Inoue } 97082cd038dSYoshinobu Inoue notify: 971686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 97282cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(mtod(m, caddr_t) + off); 973686cdd19SJun-ichiro itojun Hagino #else 974686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, 975686cdd19SJun-ichiro itojun Hagino sizeof(*icmp6) + sizeof(struct ip6_hdr)); 976686cdd19SJun-ichiro itojun Hagino if (icmp6 == NULL) { 977686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 978686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 979686cdd19SJun-ichiro itojun Hagino } 980686cdd19SJun-ichiro itojun Hagino #endif 981686cdd19SJun-ichiro itojun Hagino if (icmp6type == ICMP6_PACKET_TOO_BIG) { 982686cdd19SJun-ichiro itojun Hagino if (finaldst == NULL) 983686cdd19SJun-ichiro itojun Hagino finaldst = &((struct ip6_hdr *)(icmp6 + 1))->ip6_dst; 984686cdd19SJun-ichiro itojun Hagino icmp6_mtudisc_update(finaldst, icmp6, m); 985686cdd19SJun-ichiro itojun Hagino } 986686cdd19SJun-ichiro itojun Hagino 98782cd038dSYoshinobu Inoue ctlfunc = (void (*) __P((int, struct sockaddr *, void *))) 98882cd038dSYoshinobu Inoue (inet6sw[ip6_protox[nxt]].pr_ctlinput); 98982cd038dSYoshinobu Inoue if (ctlfunc) { 99082cd038dSYoshinobu Inoue ip6cp.ip6c_m = m; 99182cd038dSYoshinobu Inoue ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); 99282cd038dSYoshinobu Inoue ip6cp.ip6c_off = eoff; 99382cd038dSYoshinobu Inoue (*ctlfunc)(code, (struct sockaddr *)&icmp6src, &ip6cp); 99482cd038dSYoshinobu Inoue } 99582cd038dSYoshinobu Inoue } 99682cd038dSYoshinobu Inoue break; 99782cd038dSYoshinobu Inoue 99882cd038dSYoshinobu Inoue badcode: 99982cd038dSYoshinobu Inoue icmp6stat.icp6s_badcode++; 100082cd038dSYoshinobu Inoue break; 100182cd038dSYoshinobu Inoue 100282cd038dSYoshinobu Inoue badlen: 100382cd038dSYoshinobu Inoue icmp6stat.icp6s_badlen++; 100482cd038dSYoshinobu Inoue break; 100582cd038dSYoshinobu Inoue } 100682cd038dSYoshinobu Inoue 1007686cdd19SJun-ichiro itojun Hagino #ifdef HAVE_NRL_INPCB 1008686cdd19SJun-ichiro itojun Hagino rip6_input(&m, offp, IPPROTO_ICMPV6); 1009686cdd19SJun-ichiro itojun Hagino #else 101082cd038dSYoshinobu Inoue icmp6_rip6_input(&m, *offp); 1011686cdd19SJun-ichiro itojun Hagino #endif 101282cd038dSYoshinobu Inoue return IPPROTO_DONE; 101382cd038dSYoshinobu Inoue 101482cd038dSYoshinobu Inoue freeit: 101582cd038dSYoshinobu Inoue m_freem(m); 101682cd038dSYoshinobu Inoue return IPPROTO_DONE; 101782cd038dSYoshinobu Inoue } 101882cd038dSYoshinobu Inoue 1019686cdd19SJun-ichiro itojun Hagino static void 1020686cdd19SJun-ichiro itojun Hagino icmp6_mtudisc_update(dst, icmp6, m) 1021686cdd19SJun-ichiro itojun Hagino struct in6_addr *dst; 1022686cdd19SJun-ichiro itojun Hagino struct icmp6_hdr *icmp6;/* we can assume the validity of the pointer */ 1023686cdd19SJun-ichiro itojun Hagino struct mbuf *m; /* currently unused but added for scoped addrs */ 1024686cdd19SJun-ichiro itojun Hagino { 1025686cdd19SJun-ichiro itojun Hagino u_int mtu = ntohl(icmp6->icmp6_mtu); 1026686cdd19SJun-ichiro itojun Hagino struct rtentry *rt = NULL; 1027686cdd19SJun-ichiro itojun Hagino struct sockaddr_in6 sin6; 1028686cdd19SJun-ichiro itojun Hagino 1029686cdd19SJun-ichiro itojun Hagino bzero(&sin6, sizeof(sin6)); 1030686cdd19SJun-ichiro itojun Hagino sin6.sin6_family = PF_INET6; 1031686cdd19SJun-ichiro itojun Hagino sin6.sin6_len = sizeof(struct sockaddr_in6); 1032686cdd19SJun-ichiro itojun Hagino sin6.sin6_addr = *dst; 1033686cdd19SJun-ichiro itojun Hagino /* sin6.sin6_scope_id = XXX: should be set if DST is a scoped addr */ 1034686cdd19SJun-ichiro itojun Hagino rt = rtalloc1((struct sockaddr *)&sin6, 0, 1035686cdd19SJun-ichiro itojun Hagino RTF_CLONING | RTF_PRCLONING); 1036686cdd19SJun-ichiro itojun Hagino 1037686cdd19SJun-ichiro itojun Hagino if (rt && (rt->rt_flags & RTF_HOST) 1038686cdd19SJun-ichiro itojun Hagino && !(rt->rt_rmx.rmx_locks & RTV_MTU)) { 1039686cdd19SJun-ichiro itojun Hagino if (mtu < IPV6_MMTU) { 1040686cdd19SJun-ichiro itojun Hagino /* xxx */ 1041686cdd19SJun-ichiro itojun Hagino rt->rt_rmx.rmx_locks |= RTV_MTU; 1042686cdd19SJun-ichiro itojun Hagino } else if (mtu < rt->rt_ifp->if_mtu && 1043686cdd19SJun-ichiro itojun Hagino rt->rt_rmx.rmx_mtu > mtu) { 1044686cdd19SJun-ichiro itojun Hagino rt->rt_rmx.rmx_mtu = mtu; 1045686cdd19SJun-ichiro itojun Hagino } 1046686cdd19SJun-ichiro itojun Hagino } 1047686cdd19SJun-ichiro itojun Hagino if (rt) 1048686cdd19SJun-ichiro itojun Hagino RTFREE(rt); 1049686cdd19SJun-ichiro itojun Hagino } 1050686cdd19SJun-ichiro itojun Hagino 105182cd038dSYoshinobu Inoue /* 1052686cdd19SJun-ichiro itojun Hagino * Process a Node Information Query packet, (roughly) based on 1053686cdd19SJun-ichiro itojun Hagino * draft-ietf-ipngwg-icmp-name-lookups-05. 1054686cdd19SJun-ichiro itojun Hagino * 1055686cdd19SJun-ichiro itojun Hagino * Spec incompatibilities: 1056686cdd19SJun-ichiro itojun Hagino * - IPv6 Subject address handling 1057686cdd19SJun-ichiro itojun Hagino * - IPv4 Subject address handling support missing 1058686cdd19SJun-ichiro itojun Hagino * - Proxy reply (answer even if it's not for me) 1059686cdd19SJun-ichiro itojun Hagino * - "Supported Qtypes" support missing 1060686cdd19SJun-ichiro itojun Hagino * - joins NI group address at in6_ifattach() time only, does not cope 1061686cdd19SJun-ichiro itojun Hagino * with hostname changes by sethostname(3) 106282cd038dSYoshinobu Inoue */ 106382cd038dSYoshinobu Inoue #define hostnamelen strlen(hostname) 106482cd038dSYoshinobu Inoue #ifndef offsetof /* XXX */ 106582cd038dSYoshinobu Inoue #define offsetof(type, member) ((size_t)(&((type *)0)->member)) 106682cd038dSYoshinobu Inoue #endif 106782cd038dSYoshinobu Inoue static struct mbuf * 106882cd038dSYoshinobu Inoue ni6_input(m, off) 106982cd038dSYoshinobu Inoue struct mbuf *m; 107082cd038dSYoshinobu Inoue int off; 107182cd038dSYoshinobu Inoue { 1072686cdd19SJun-ichiro itojun Hagino struct icmp6_nodeinfo *ni6, *nni6; 107382cd038dSYoshinobu Inoue struct mbuf *n = NULL; 1074686cdd19SJun-ichiro itojun Hagino u_int16_t qtype; 1075686cdd19SJun-ichiro itojun Hagino int subjlen; 107682cd038dSYoshinobu Inoue int replylen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); 107782cd038dSYoshinobu Inoue struct ni_reply_fqdn *fqdn; 107882cd038dSYoshinobu Inoue int addrs; /* for NI_QTYPE_NODEADDR */ 107982cd038dSYoshinobu Inoue struct ifnet *ifp = NULL; /* for NI_QTYPE_NODEADDR */ 1080686cdd19SJun-ichiro itojun Hagino struct sockaddr_in6 sin6; 1081686cdd19SJun-ichiro itojun Hagino struct ip6_hdr *ip6; 1082686cdd19SJun-ichiro itojun Hagino int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ 1083686cdd19SJun-ichiro itojun Hagino char *subj; 108482cd038dSYoshinobu Inoue 1085686cdd19SJun-ichiro itojun Hagino ip6 = mtod(m, struct ip6_hdr *); 1086686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 1087686cdd19SJun-ichiro itojun Hagino ni6 = (struct icmp6_nodeinfo *)(mtod(m, caddr_t) + off); 1088686cdd19SJun-ichiro itojun Hagino #else 1089686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6)); 1090686cdd19SJun-ichiro itojun Hagino if (ni6 == NULL) { 1091686cdd19SJun-ichiro itojun Hagino /* m is already reclaimed */ 1092686cdd19SJun-ichiro itojun Hagino return NULL; 1093686cdd19SJun-ichiro itojun Hagino } 1094686cdd19SJun-ichiro itojun Hagino #endif 1095686cdd19SJun-ichiro itojun Hagino 1096686cdd19SJun-ichiro itojun Hagino /* 1097686cdd19SJun-ichiro itojun Hagino * Validate IPv6 destination address. 1098686cdd19SJun-ichiro itojun Hagino * 1099686cdd19SJun-ichiro itojun Hagino * We accept packets with the following IPv6 destination address: 1100686cdd19SJun-ichiro itojun Hagino * - Responder's unicast/anycast address, 1101686cdd19SJun-ichiro itojun Hagino * - link-local multicast address 1102686cdd19SJun-ichiro itojun Hagino * This is a violation to last paragraph in icmp-name-lookups-05 1103686cdd19SJun-ichiro itojun Hagino * page 4, which restricts IPv6 destination address of a query to: 1104686cdd19SJun-ichiro itojun Hagino * - Responder's unicast/anycast address, 1105686cdd19SJun-ichiro itojun Hagino * - NI group address for a name belongs to the Responder, or 1106686cdd19SJun-ichiro itojun Hagino * - NI group address for a name for which the Responder is providing 1107686cdd19SJun-ichiro itojun Hagino * proxy service. 1108686cdd19SJun-ichiro itojun Hagino * (note: NI group address is a link-local multicast address) 1109686cdd19SJun-ichiro itojun Hagino * 1110686cdd19SJun-ichiro itojun Hagino * We allow any link-local multicast address, since "ping6 -w ff02::1" 1111686cdd19SJun-ichiro itojun Hagino * has been really useful for us debugging our network. Also this is 1112686cdd19SJun-ichiro itojun Hagino * still questionable if the restriction in spec buy us security at all, 1113686cdd19SJun-ichiro itojun Hagino * since RFC2463 permits echo packet to multicast destination. 1114686cdd19SJun-ichiro itojun Hagino * Even if we forbid NI query to ff02::1, we can effectively get the 1115686cdd19SJun-ichiro itojun Hagino * same result as "ping6 -w ff02::1" by the following steps: 1116686cdd19SJun-ichiro itojun Hagino * - run "ping6 ff02::1", then 1117686cdd19SJun-ichiro itojun Hagino * - run "ping6 -w" for all addresses replied. 1118686cdd19SJun-ichiro itojun Hagino */ 1119686cdd19SJun-ichiro itojun Hagino bzero(&sin6, sizeof(sin6)); 1120686cdd19SJun-ichiro itojun Hagino sin6.sin6_family = AF_INET6; 1121686cdd19SJun-ichiro itojun Hagino sin6.sin6_len = sizeof(struct sockaddr_in6); 1122686cdd19SJun-ichiro itojun Hagino bcopy(&ip6->ip6_dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr)); 1123686cdd19SJun-ichiro itojun Hagino /* XXX scopeid */ 1124686cdd19SJun-ichiro itojun Hagino if (ifa_ifwithaddr((struct sockaddr *)&sin6)) 1125686cdd19SJun-ichiro itojun Hagino ; /*unicast/anycast, fine*/ 1126686cdd19SJun-ichiro itojun Hagino else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) 1127686cdd19SJun-ichiro itojun Hagino ; /*violates spec slightly, see above*/ 1128686cdd19SJun-ichiro itojun Hagino else 1129686cdd19SJun-ichiro itojun Hagino goto bad; 1130686cdd19SJun-ichiro itojun Hagino 1131686cdd19SJun-ichiro itojun Hagino /* guess reply length */ 1132686cdd19SJun-ichiro itojun Hagino qtype = ntohs(ni6->ni_qtype); 113382cd038dSYoshinobu Inoue switch (qtype) { 113482cd038dSYoshinobu Inoue case NI_QTYPE_NOOP: 113582cd038dSYoshinobu Inoue break; /* no reply data */ 113682cd038dSYoshinobu Inoue case NI_QTYPE_SUPTYPES: 113782cd038dSYoshinobu Inoue goto bad; /* xxx: to be implemented */ 113882cd038dSYoshinobu Inoue break; 113982cd038dSYoshinobu Inoue case NI_QTYPE_FQDN: 1140686cdd19SJun-ichiro itojun Hagino /* XXX will append a mbuf */ 1141686cdd19SJun-ichiro itojun Hagino replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); 114282cd038dSYoshinobu Inoue break; 114382cd038dSYoshinobu Inoue case NI_QTYPE_NODEADDR: 114482cd038dSYoshinobu Inoue addrs = ni6_addrs(ni6, m, &ifp); 114582cd038dSYoshinobu Inoue if ((replylen += addrs * sizeof(struct in6_addr)) > MCLBYTES) 114682cd038dSYoshinobu Inoue replylen = MCLBYTES; /* XXX: we'll truncate later */ 114782cd038dSYoshinobu Inoue break; 114882cd038dSYoshinobu Inoue default: 114982cd038dSYoshinobu Inoue /* 115082cd038dSYoshinobu Inoue * XXX: We must return a reply with the ICMP6 code 115182cd038dSYoshinobu Inoue * `unknown Qtype' in this case. However we regard the case 115282cd038dSYoshinobu Inoue * as an FQDN query for backward compatibility. 115382cd038dSYoshinobu Inoue * Older versions set a random value to this field, 115482cd038dSYoshinobu Inoue * so it rarely varies in the defined qtypes. 115582cd038dSYoshinobu Inoue * But the mechanism is not reliable... 115682cd038dSYoshinobu Inoue * maybe we should obsolete older versions. 115782cd038dSYoshinobu Inoue */ 115882cd038dSYoshinobu Inoue qtype = NI_QTYPE_FQDN; 1159686cdd19SJun-ichiro itojun Hagino /* XXX will append a mbuf */ 1160686cdd19SJun-ichiro itojun Hagino replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); 1161686cdd19SJun-ichiro itojun Hagino oldfqdn++; 116282cd038dSYoshinobu Inoue break; 116382cd038dSYoshinobu Inoue } 116482cd038dSYoshinobu Inoue 1165686cdd19SJun-ichiro itojun Hagino /* validate query Subject field. */ 1166686cdd19SJun-ichiro itojun Hagino subjlen = m->m_pkthdr.len - off - sizeof(struct icmp6_nodeinfo); 1167686cdd19SJun-ichiro itojun Hagino switch (qtype) { 1168686cdd19SJun-ichiro itojun Hagino case NI_QTYPE_NOOP: 1169686cdd19SJun-ichiro itojun Hagino case NI_QTYPE_SUPTYPES: 1170686cdd19SJun-ichiro itojun Hagino if (subjlen != 0) 1171686cdd19SJun-ichiro itojun Hagino goto bad; 1172686cdd19SJun-ichiro itojun Hagino break; 1173686cdd19SJun-ichiro itojun Hagino 1174686cdd19SJun-ichiro itojun Hagino case NI_QTYPE_FQDN: 1175686cdd19SJun-ichiro itojun Hagino case NI_QTYPE_NODEADDR: 1176686cdd19SJun-ichiro itojun Hagino switch (ni6->ni_code) { 1177686cdd19SJun-ichiro itojun Hagino case ICMP6_NI_SUBJ_IPV6: 1178686cdd19SJun-ichiro itojun Hagino #if ICMP6_NI_SUBJ_IPV6 != 0 1179686cdd19SJun-ichiro itojun Hagino case 0: 1180686cdd19SJun-ichiro itojun Hagino #endif 1181686cdd19SJun-ichiro itojun Hagino /* 1182686cdd19SJun-ichiro itojun Hagino * backward compatibility - try to accept 03 draft 1183686cdd19SJun-ichiro itojun Hagino * format, where no Subject is present. 1184686cdd19SJun-ichiro itojun Hagino */ 1185686cdd19SJun-ichiro itojun Hagino if (subjlen == 0) { 1186686cdd19SJun-ichiro itojun Hagino oldfqdn++; 1187686cdd19SJun-ichiro itojun Hagino break; 1188686cdd19SJun-ichiro itojun Hagino } 1189686cdd19SJun-ichiro itojun Hagino 1190686cdd19SJun-ichiro itojun Hagino if (subjlen != sizeof(sin6.sin6_addr)) 1191686cdd19SJun-ichiro itojun Hagino goto bad; 1192686cdd19SJun-ichiro itojun Hagino 1193686cdd19SJun-ichiro itojun Hagino /* 1194686cdd19SJun-ichiro itojun Hagino * Validate Subject address. 1195686cdd19SJun-ichiro itojun Hagino * 1196686cdd19SJun-ichiro itojun Hagino * Not sure what exactly does "address belongs to the 1197686cdd19SJun-ichiro itojun Hagino * node" mean in the spec, is it just unicast, or what? 1198686cdd19SJun-ichiro itojun Hagino * 1199686cdd19SJun-ichiro itojun Hagino * At this moment we consider Subject address as 1200686cdd19SJun-ichiro itojun Hagino * "belong to the node" if the Subject address equals 1201686cdd19SJun-ichiro itojun Hagino * to the IPv6 destination address; validation for 1202686cdd19SJun-ichiro itojun Hagino * IPv6 destination address should have done enough 1203686cdd19SJun-ichiro itojun Hagino * check for us. 1204686cdd19SJun-ichiro itojun Hagino * 1205686cdd19SJun-ichiro itojun Hagino * We do not do proxy at this moment. 1206686cdd19SJun-ichiro itojun Hagino */ 1207686cdd19SJun-ichiro itojun Hagino /* m_pulldown instead of copy? */ 1208686cdd19SJun-ichiro itojun Hagino m_copydata(m, off + sizeof(struct icmp6_nodeinfo), 1209686cdd19SJun-ichiro itojun Hagino subjlen, (caddr_t)&sin6.sin6_addr); 1210686cdd19SJun-ichiro itojun Hagino /* XXX kame scope hack */ 1211686cdd19SJun-ichiro itojun Hagino if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) { 1212686cdd19SJun-ichiro itojun Hagino #ifdef FAKE_LOOPBACK_IF 1213686cdd19SJun-ichiro itojun Hagino if ((m->m_flags & M_PKTHDR) != 0 && 1214686cdd19SJun-ichiro itojun Hagino m->m_pkthdr.rcvif) { 1215686cdd19SJun-ichiro itojun Hagino sin6.sin6_addr.s6_addr16[1] = 1216686cdd19SJun-ichiro itojun Hagino htons(m->m_pkthdr.rcvif->if_index); 1217686cdd19SJun-ichiro itojun Hagino } 1218686cdd19SJun-ichiro itojun Hagino #else 1219686cdd19SJun-ichiro itojun Hagino if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 1220686cdd19SJun-ichiro itojun Hagino sin6.sin6_addr.s6_addr16[1] = 1221686cdd19SJun-ichiro itojun Hagino ip6->ip6_dst.s6_addr16[1]; 1222686cdd19SJun-ichiro itojun Hagino } 1223686cdd19SJun-ichiro itojun Hagino #endif 1224686cdd19SJun-ichiro itojun Hagino } 1225686cdd19SJun-ichiro itojun Hagino if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &sin6.sin6_addr)) 1226686cdd19SJun-ichiro itojun Hagino break; 1227686cdd19SJun-ichiro itojun Hagino /* 1228686cdd19SJun-ichiro itojun Hagino * XXX if we are to allow other cases, we should really 1229686cdd19SJun-ichiro itojun Hagino * be careful about scope here. 1230686cdd19SJun-ichiro itojun Hagino * basically, we should disallow queries toward IPv6 1231686cdd19SJun-ichiro itojun Hagino * destination X with subject Y, if scope(X) > scope(Y). 1232686cdd19SJun-ichiro itojun Hagino * if we allow scope(X) > scope(Y), it will result in 1233686cdd19SJun-ichiro itojun Hagino * information leakage across scope boundary. 1234686cdd19SJun-ichiro itojun Hagino */ 1235686cdd19SJun-ichiro itojun Hagino goto bad; 1236686cdd19SJun-ichiro itojun Hagino 1237686cdd19SJun-ichiro itojun Hagino case ICMP6_NI_SUBJ_FQDN: 1238686cdd19SJun-ichiro itojun Hagino /* 1239686cdd19SJun-ichiro itojun Hagino * Validate Subject name with gethostname(3). 1240686cdd19SJun-ichiro itojun Hagino * 1241686cdd19SJun-ichiro itojun Hagino * The behavior may need some debate, since: 1242686cdd19SJun-ichiro itojun Hagino * - we are not sure if the node has FQDN as 1243686cdd19SJun-ichiro itojun Hagino * hostname (returned by gethostname(3)). 1244686cdd19SJun-ichiro itojun Hagino * - the code does wildcard match for truncated names. 1245686cdd19SJun-ichiro itojun Hagino * however, we are not sure if we want to perform 1246686cdd19SJun-ichiro itojun Hagino * wildcard match, if gethostname(3) side has 1247686cdd19SJun-ichiro itojun Hagino * truncated hostname. 1248686cdd19SJun-ichiro itojun Hagino */ 1249686cdd19SJun-ichiro itojun Hagino n = ni6_nametodns(hostname, hostnamelen, 0); 1250686cdd19SJun-ichiro itojun Hagino if (!n || n->m_next || n->m_len == 0) 1251686cdd19SJun-ichiro itojun Hagino goto bad; 1252686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(subj, char *, m, 1253686cdd19SJun-ichiro itojun Hagino off + sizeof(struct icmp6_nodeinfo), subjlen); 1254686cdd19SJun-ichiro itojun Hagino if (subj == NULL) 1255686cdd19SJun-ichiro itojun Hagino goto bad; 1256686cdd19SJun-ichiro itojun Hagino if (!ni6_dnsmatch(subj, subjlen, mtod(n, const char *), 1257686cdd19SJun-ichiro itojun Hagino n->m_len)) { 1258686cdd19SJun-ichiro itojun Hagino goto bad; 1259686cdd19SJun-ichiro itojun Hagino } 1260686cdd19SJun-ichiro itojun Hagino m_freem(n); 1261686cdd19SJun-ichiro itojun Hagino n = NULL; 1262686cdd19SJun-ichiro itojun Hagino break; 1263686cdd19SJun-ichiro itojun Hagino 1264686cdd19SJun-ichiro itojun Hagino case ICMP6_NI_SUBJ_IPV4: /* xxx: to be implemented? */ 1265686cdd19SJun-ichiro itojun Hagino default: 1266686cdd19SJun-ichiro itojun Hagino goto bad; 1267686cdd19SJun-ichiro itojun Hagino } 1268686cdd19SJun-ichiro itojun Hagino break; 1269686cdd19SJun-ichiro itojun Hagino 1270686cdd19SJun-ichiro itojun Hagino default: 1271686cdd19SJun-ichiro itojun Hagino /* should never be here due to "switch (qtype)" above */ 1272686cdd19SJun-ichiro itojun Hagino goto bad; 1273686cdd19SJun-ichiro itojun Hagino } 1274686cdd19SJun-ichiro itojun Hagino 127582cd038dSYoshinobu Inoue /* allocate a mbuf to reply. */ 127682cd038dSYoshinobu Inoue MGETHDR(n, M_DONTWAIT, m->m_type); 1277686cdd19SJun-ichiro itojun Hagino if (n == NULL) { 1278686cdd19SJun-ichiro itojun Hagino m_freem(m); 127982cd038dSYoshinobu Inoue return(NULL); 1280686cdd19SJun-ichiro itojun Hagino } 128182cd038dSYoshinobu Inoue M_COPY_PKTHDR(n, m); /* just for recvif */ 128282cd038dSYoshinobu Inoue if (replylen > MHLEN) { 1283686cdd19SJun-ichiro itojun Hagino if (replylen > MCLBYTES) { 128482cd038dSYoshinobu Inoue /* 128582cd038dSYoshinobu Inoue * XXX: should we try to allocate more? But MCLBYTES is 128682cd038dSYoshinobu Inoue * probably much larger than IPV6_MMTU... 128782cd038dSYoshinobu Inoue */ 128882cd038dSYoshinobu Inoue goto bad; 1289686cdd19SJun-ichiro itojun Hagino } 129082cd038dSYoshinobu Inoue MCLGET(n, M_DONTWAIT); 129182cd038dSYoshinobu Inoue if ((n->m_flags & M_EXT) == 0) { 129282cd038dSYoshinobu Inoue goto bad; 129382cd038dSYoshinobu Inoue } 129482cd038dSYoshinobu Inoue } 129582cd038dSYoshinobu Inoue n->m_pkthdr.len = n->m_len = replylen; 129682cd038dSYoshinobu Inoue 129782cd038dSYoshinobu Inoue /* copy mbuf header and IPv6 + Node Information base headers */ 129882cd038dSYoshinobu Inoue bcopy(mtod(m, caddr_t), mtod(n, caddr_t), sizeof(struct ip6_hdr)); 129982cd038dSYoshinobu Inoue nni6 = (struct icmp6_nodeinfo *)(mtod(n, struct ip6_hdr *) + 1); 1300686cdd19SJun-ichiro itojun Hagino bcopy((caddr_t)ni6, (caddr_t)nni6, sizeof(struct icmp6_nodeinfo)); 130182cd038dSYoshinobu Inoue 130282cd038dSYoshinobu Inoue /* qtype dependent procedure */ 130382cd038dSYoshinobu Inoue switch (qtype) { 130482cd038dSYoshinobu Inoue case NI_QTYPE_NOOP: 130582cd038dSYoshinobu Inoue nni6->ni_flags = 0; 130682cd038dSYoshinobu Inoue break; 130782cd038dSYoshinobu Inoue case NI_QTYPE_SUPTYPES: 130882cd038dSYoshinobu Inoue goto bad; /* xxx: to be implemented */ 130982cd038dSYoshinobu Inoue break; 131082cd038dSYoshinobu Inoue case NI_QTYPE_FQDN: 131182cd038dSYoshinobu Inoue fqdn = (struct ni_reply_fqdn *)(mtod(n, caddr_t) + 131282cd038dSYoshinobu Inoue sizeof(struct ip6_hdr) + 131382cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo)); 131482cd038dSYoshinobu Inoue nni6->ni_flags = 0; /* XXX: meaningless TTL */ 131582cd038dSYoshinobu Inoue fqdn->ni_fqdn_ttl = 0; /* ditto. */ 1316686cdd19SJun-ichiro itojun Hagino /* 1317686cdd19SJun-ichiro itojun Hagino * XXX do we really have FQDN in variable "hostname"? 1318686cdd19SJun-ichiro itojun Hagino */ 1319686cdd19SJun-ichiro itojun Hagino n->m_next = ni6_nametodns(hostname, hostnamelen, oldfqdn); 1320686cdd19SJun-ichiro itojun Hagino if (n->m_next == NULL) 1321686cdd19SJun-ichiro itojun Hagino goto bad; 1322686cdd19SJun-ichiro itojun Hagino /* XXX we assume that n->m_next is not a chain */ 1323686cdd19SJun-ichiro itojun Hagino if (n->m_next->m_next != NULL) 1324686cdd19SJun-ichiro itojun Hagino goto bad; 1325686cdd19SJun-ichiro itojun Hagino n->m_pkthdr.len += n->m_next->m_len; 132682cd038dSYoshinobu Inoue break; 132782cd038dSYoshinobu Inoue case NI_QTYPE_NODEADDR: 132882cd038dSYoshinobu Inoue { 132982cd038dSYoshinobu Inoue int lenlim, copied; 133082cd038dSYoshinobu Inoue 133182cd038dSYoshinobu Inoue if (n->m_flags & M_EXT) 133282cd038dSYoshinobu Inoue lenlim = MCLBYTES - sizeof(struct ip6_hdr) - 133382cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo); 133482cd038dSYoshinobu Inoue else 133582cd038dSYoshinobu Inoue lenlim = MHLEN - sizeof(struct ip6_hdr) - 133682cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo); 133782cd038dSYoshinobu Inoue copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); 133882cd038dSYoshinobu Inoue /* XXX: reset mbuf length */ 133982cd038dSYoshinobu Inoue n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + 134082cd038dSYoshinobu Inoue sizeof(struct icmp6_nodeinfo) + copied; 134182cd038dSYoshinobu Inoue break; 134282cd038dSYoshinobu Inoue } 134382cd038dSYoshinobu Inoue default: 134482cd038dSYoshinobu Inoue break; /* XXX impossible! */ 134582cd038dSYoshinobu Inoue } 134682cd038dSYoshinobu Inoue 134782cd038dSYoshinobu Inoue nni6->ni_type = ICMP6_NI_REPLY; 134882cd038dSYoshinobu Inoue nni6->ni_code = ICMP6_NI_SUCESS; 1349686cdd19SJun-ichiro itojun Hagino m_freem(m); 135082cd038dSYoshinobu Inoue return(n); 135182cd038dSYoshinobu Inoue 135282cd038dSYoshinobu Inoue bad: 1353686cdd19SJun-ichiro itojun Hagino m_freem(m); 135482cd038dSYoshinobu Inoue if (n) 135582cd038dSYoshinobu Inoue m_freem(n); 135682cd038dSYoshinobu Inoue return(NULL); 135782cd038dSYoshinobu Inoue } 135882cd038dSYoshinobu Inoue #undef hostnamelen 135982cd038dSYoshinobu Inoue 136082cd038dSYoshinobu Inoue /* 1361686cdd19SJun-ichiro itojun Hagino * make a mbuf with DNS-encoded string. no compression support. 1362686cdd19SJun-ichiro itojun Hagino * 1363686cdd19SJun-ichiro itojun Hagino * XXX names with less than 2 dots (like "foo" or "foo.section") will be 1364686cdd19SJun-ichiro itojun Hagino * treated as truncated name (two \0 at the end). this is a wild guess. 1365686cdd19SJun-ichiro itojun Hagino */ 1366686cdd19SJun-ichiro itojun Hagino static struct mbuf * 1367686cdd19SJun-ichiro itojun Hagino ni6_nametodns(name, namelen, old) 1368686cdd19SJun-ichiro itojun Hagino const char *name; 1369686cdd19SJun-ichiro itojun Hagino int namelen; 1370686cdd19SJun-ichiro itojun Hagino int old; /* return pascal string if non-zero */ 1371686cdd19SJun-ichiro itojun Hagino { 1372686cdd19SJun-ichiro itojun Hagino struct mbuf *m; 1373686cdd19SJun-ichiro itojun Hagino char *cp, *ep; 1374686cdd19SJun-ichiro itojun Hagino const char *p, *q; 1375686cdd19SJun-ichiro itojun Hagino int i, len, nterm; 1376686cdd19SJun-ichiro itojun Hagino 1377686cdd19SJun-ichiro itojun Hagino if (old) 1378686cdd19SJun-ichiro itojun Hagino len = namelen + 1; 1379686cdd19SJun-ichiro itojun Hagino else 1380686cdd19SJun-ichiro itojun Hagino len = MCLBYTES; 1381686cdd19SJun-ichiro itojun Hagino 1382686cdd19SJun-ichiro itojun Hagino /* because MAXHOSTNAMELEN is usually 256, we use cluster mbuf */ 1383686cdd19SJun-ichiro itojun Hagino MGET(m, M_DONTWAIT, MT_DATA); 1384686cdd19SJun-ichiro itojun Hagino if (m && len > MLEN) { 1385686cdd19SJun-ichiro itojun Hagino MCLGET(m, M_DONTWAIT); 1386686cdd19SJun-ichiro itojun Hagino if ((m->m_flags & M_EXT) == 0) 1387686cdd19SJun-ichiro itojun Hagino goto fail; 1388686cdd19SJun-ichiro itojun Hagino } 1389686cdd19SJun-ichiro itojun Hagino if (!m) 1390686cdd19SJun-ichiro itojun Hagino goto fail; 1391686cdd19SJun-ichiro itojun Hagino m->m_next = NULL; 1392686cdd19SJun-ichiro itojun Hagino 1393686cdd19SJun-ichiro itojun Hagino if (old) { 1394686cdd19SJun-ichiro itojun Hagino m->m_len = len; 1395686cdd19SJun-ichiro itojun Hagino *mtod(m, char *) = namelen; 1396686cdd19SJun-ichiro itojun Hagino bcopy(name, mtod(m, char *) + 1, namelen); 1397686cdd19SJun-ichiro itojun Hagino return m; 1398686cdd19SJun-ichiro itojun Hagino } else { 1399686cdd19SJun-ichiro itojun Hagino m->m_len = 0; 1400686cdd19SJun-ichiro itojun Hagino cp = mtod(m, char *); 1401686cdd19SJun-ichiro itojun Hagino ep = mtod(m, char *) + M_TRAILINGSPACE(m); 1402686cdd19SJun-ichiro itojun Hagino 1403686cdd19SJun-ichiro itojun Hagino /* if not certain about my name, return empty buffer */ 1404686cdd19SJun-ichiro itojun Hagino if (namelen == 0) 1405686cdd19SJun-ichiro itojun Hagino return m; 1406686cdd19SJun-ichiro itojun Hagino 1407686cdd19SJun-ichiro itojun Hagino /* 1408686cdd19SJun-ichiro itojun Hagino * guess if it looks like shortened hostname, or FQDN. 1409686cdd19SJun-ichiro itojun Hagino * shortened hostname needs two trailing "\0". 1410686cdd19SJun-ichiro itojun Hagino */ 1411686cdd19SJun-ichiro itojun Hagino i = 0; 1412686cdd19SJun-ichiro itojun Hagino for (p = name; p < name + namelen; p++) { 1413686cdd19SJun-ichiro itojun Hagino if (*p && *p == '.') 1414686cdd19SJun-ichiro itojun Hagino i++; 1415686cdd19SJun-ichiro itojun Hagino } 1416686cdd19SJun-ichiro itojun Hagino if (i < 2) 1417686cdd19SJun-ichiro itojun Hagino nterm = 2; 1418686cdd19SJun-ichiro itojun Hagino else 1419686cdd19SJun-ichiro itojun Hagino nterm = 1; 1420686cdd19SJun-ichiro itojun Hagino 1421686cdd19SJun-ichiro itojun Hagino p = name; 1422686cdd19SJun-ichiro itojun Hagino while (cp < ep && p < name + namelen) { 1423686cdd19SJun-ichiro itojun Hagino i = 0; 1424686cdd19SJun-ichiro itojun Hagino for (q = p; q < name + namelen && *q && *q != '.'; q++) 1425686cdd19SJun-ichiro itojun Hagino i++; 1426686cdd19SJun-ichiro itojun Hagino /* result does not fit into mbuf */ 1427686cdd19SJun-ichiro itojun Hagino if (cp + i + 1 >= ep) 1428686cdd19SJun-ichiro itojun Hagino goto fail; 1429686cdd19SJun-ichiro itojun Hagino /* DNS label length restriction, RFC1035 page 8 */ 1430686cdd19SJun-ichiro itojun Hagino if (i >= 64) 1431686cdd19SJun-ichiro itojun Hagino goto fail; 1432686cdd19SJun-ichiro itojun Hagino *cp++ = i; 1433686cdd19SJun-ichiro itojun Hagino bcopy(p, cp, i); 1434686cdd19SJun-ichiro itojun Hagino cp += i; 1435686cdd19SJun-ichiro itojun Hagino p = q; 1436686cdd19SJun-ichiro itojun Hagino if (p < name + namelen && *p == '.') 1437686cdd19SJun-ichiro itojun Hagino p++; 1438686cdd19SJun-ichiro itojun Hagino } 1439686cdd19SJun-ichiro itojun Hagino /* termination */ 1440686cdd19SJun-ichiro itojun Hagino if (cp + nterm >= ep) 1441686cdd19SJun-ichiro itojun Hagino goto fail; 1442686cdd19SJun-ichiro itojun Hagino while (nterm-- > 0) 1443686cdd19SJun-ichiro itojun Hagino *cp++ = '\0'; 1444686cdd19SJun-ichiro itojun Hagino m->m_len = cp - mtod(m, char *); 1445686cdd19SJun-ichiro itojun Hagino return m; 1446686cdd19SJun-ichiro itojun Hagino } 1447686cdd19SJun-ichiro itojun Hagino 1448686cdd19SJun-ichiro itojun Hagino panic("should not reach here"); 1449686cdd19SJun-ichiro itojun Hagino /*NOTREACHED*/ 1450686cdd19SJun-ichiro itojun Hagino 1451686cdd19SJun-ichiro itojun Hagino fail: 1452686cdd19SJun-ichiro itojun Hagino if (m) 1453686cdd19SJun-ichiro itojun Hagino m_freem(m); 1454686cdd19SJun-ichiro itojun Hagino return NULL; 1455686cdd19SJun-ichiro itojun Hagino } 1456686cdd19SJun-ichiro itojun Hagino 1457686cdd19SJun-ichiro itojun Hagino /* 1458686cdd19SJun-ichiro itojun Hagino * check if two DNS-encoded string matches. takes care of truncated 1459686cdd19SJun-ichiro itojun Hagino * form (with \0\0 at the end). no compression support. 1460686cdd19SJun-ichiro itojun Hagino */ 1461686cdd19SJun-ichiro itojun Hagino static int 1462686cdd19SJun-ichiro itojun Hagino ni6_dnsmatch(a, alen, b, blen) 1463686cdd19SJun-ichiro itojun Hagino const char *a; 1464686cdd19SJun-ichiro itojun Hagino int alen; 1465686cdd19SJun-ichiro itojun Hagino const char *b; 1466686cdd19SJun-ichiro itojun Hagino int blen; 1467686cdd19SJun-ichiro itojun Hagino { 1468686cdd19SJun-ichiro itojun Hagino const char *a0, *b0; 1469686cdd19SJun-ichiro itojun Hagino int l; 1470686cdd19SJun-ichiro itojun Hagino 1471686cdd19SJun-ichiro itojun Hagino /* simplest case - need validation? */ 1472686cdd19SJun-ichiro itojun Hagino if (alen == blen && bcmp(a, b, alen) == 0) 1473686cdd19SJun-ichiro itojun Hagino return 1; 1474686cdd19SJun-ichiro itojun Hagino 1475686cdd19SJun-ichiro itojun Hagino a0 = a; 1476686cdd19SJun-ichiro itojun Hagino b0 = b; 1477686cdd19SJun-ichiro itojun Hagino 1478686cdd19SJun-ichiro itojun Hagino /* termination is mandatory */ 1479686cdd19SJun-ichiro itojun Hagino if (alen < 2 || blen < 2) 1480686cdd19SJun-ichiro itojun Hagino return 0; 1481686cdd19SJun-ichiro itojun Hagino if (a0[alen - 1] != '\0' || b0[blen - 1] != '\0') 1482686cdd19SJun-ichiro itojun Hagino return 0; 1483686cdd19SJun-ichiro itojun Hagino alen--; 1484686cdd19SJun-ichiro itojun Hagino blen--; 1485686cdd19SJun-ichiro itojun Hagino 1486686cdd19SJun-ichiro itojun Hagino while (a - a0 < alen && b - b0 < blen) { 1487686cdd19SJun-ichiro itojun Hagino if (a - a0 + 1 > alen || b - b0 + 1 > blen) 1488686cdd19SJun-ichiro itojun Hagino return 0; 1489686cdd19SJun-ichiro itojun Hagino 1490686cdd19SJun-ichiro itojun Hagino if ((signed char)a[0] < 0 || (signed char)b[0] < 0) 1491686cdd19SJun-ichiro itojun Hagino return 0; 1492686cdd19SJun-ichiro itojun Hagino /* we don't support compression yet */ 1493686cdd19SJun-ichiro itojun Hagino if (a[0] >= 64 || b[0] >= 64) 1494686cdd19SJun-ichiro itojun Hagino return 0; 1495686cdd19SJun-ichiro itojun Hagino 1496686cdd19SJun-ichiro itojun Hagino /* truncated case */ 1497686cdd19SJun-ichiro itojun Hagino if (a[0] == 0 && a - a0 == alen - 1) 1498686cdd19SJun-ichiro itojun Hagino return 1; 1499686cdd19SJun-ichiro itojun Hagino if (b[0] == 0 && b - b0 == blen - 1) 1500686cdd19SJun-ichiro itojun Hagino return 1; 1501686cdd19SJun-ichiro itojun Hagino if (a[0] == 0 || b[0] == 0) 1502686cdd19SJun-ichiro itojun Hagino return 0; 1503686cdd19SJun-ichiro itojun Hagino 1504686cdd19SJun-ichiro itojun Hagino if (a[0] != b[0]) 1505686cdd19SJun-ichiro itojun Hagino return 0; 1506686cdd19SJun-ichiro itojun Hagino l = a[0]; 1507686cdd19SJun-ichiro itojun Hagino if (a - a0 + 1 + l > alen || b - b0 + 1 + l > blen) 1508686cdd19SJun-ichiro itojun Hagino return 0; 1509686cdd19SJun-ichiro itojun Hagino if (bcmp(a + 1, b + 1, l) != 0) 1510686cdd19SJun-ichiro itojun Hagino return 0; 1511686cdd19SJun-ichiro itojun Hagino 1512686cdd19SJun-ichiro itojun Hagino a += 1 + l; 1513686cdd19SJun-ichiro itojun Hagino b += 1 + l; 1514686cdd19SJun-ichiro itojun Hagino } 1515686cdd19SJun-ichiro itojun Hagino 1516686cdd19SJun-ichiro itojun Hagino if (a - a0 == alen && b - b0 == blen) 1517686cdd19SJun-ichiro itojun Hagino return 1; 1518686cdd19SJun-ichiro itojun Hagino else 1519686cdd19SJun-ichiro itojun Hagino return 0; 1520686cdd19SJun-ichiro itojun Hagino } 1521686cdd19SJun-ichiro itojun Hagino 1522686cdd19SJun-ichiro itojun Hagino /* 152382cd038dSYoshinobu Inoue * calculate the number of addresses to be returned in the node info reply. 152482cd038dSYoshinobu Inoue */ 152582cd038dSYoshinobu Inoue static int 152682cd038dSYoshinobu Inoue ni6_addrs(ni6, m, ifpp) 152782cd038dSYoshinobu Inoue struct icmp6_nodeinfo *ni6; 152882cd038dSYoshinobu Inoue struct mbuf *m; 152982cd038dSYoshinobu Inoue struct ifnet **ifpp; 153082cd038dSYoshinobu Inoue { 153182cd038dSYoshinobu Inoue register struct ifnet *ifp; 153282cd038dSYoshinobu Inoue register struct in6_ifaddr *ifa6; 153382cd038dSYoshinobu Inoue register struct ifaddr *ifa; 153482cd038dSYoshinobu Inoue struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 153582cd038dSYoshinobu Inoue int addrs = 0, addrsofif, iffound = 0; 153682cd038dSYoshinobu Inoue 153782cd038dSYoshinobu Inoue for (ifp = TAILQ_FIRST(&ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) 153882cd038dSYoshinobu Inoue { 153982cd038dSYoshinobu Inoue addrsofif = 0; 1540686cdd19SJun-ichiro itojun Hagino for (ifa = ifp->if_addrlist.tqh_first; ifa; 1541686cdd19SJun-ichiro itojun Hagino ifa = ifa->ifa_list.tqe_next) 154282cd038dSYoshinobu Inoue { 154382cd038dSYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 154482cd038dSYoshinobu Inoue continue; 154582cd038dSYoshinobu Inoue ifa6 = (struct in6_ifaddr *)ifa; 154682cd038dSYoshinobu Inoue 154782cd038dSYoshinobu Inoue if (!(ni6->ni_flags & NI_NODEADDR_FLAG_ALL) && 154882cd038dSYoshinobu Inoue IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, 154982cd038dSYoshinobu Inoue &ifa6->ia_addr.sin6_addr)) 155082cd038dSYoshinobu Inoue iffound = 1; 155182cd038dSYoshinobu Inoue 1552686cdd19SJun-ichiro itojun Hagino /* 1553686cdd19SJun-ichiro itojun Hagino * IPv4-mapped addresses can only be returned by a 1554686cdd19SJun-ichiro itojun Hagino * Node Information proxy, since they represent 1555686cdd19SJun-ichiro itojun Hagino * addresses of IPv4-only nodes, which perforce do 1556686cdd19SJun-ichiro itojun Hagino * not implement this protocol. 1557686cdd19SJun-ichiro itojun Hagino * [icmp-name-lookups-05] 1558686cdd19SJun-ichiro itojun Hagino * So we don't support NI_NODEADDR_FLAG_COMPAT in 1559686cdd19SJun-ichiro itojun Hagino * this function at this moment. 1560686cdd19SJun-ichiro itojun Hagino */ 1561686cdd19SJun-ichiro itojun Hagino 1562686cdd19SJun-ichiro itojun Hagino if (ifa6->ia6_flags & IN6_IFF_ANYCAST) 156382cd038dSYoshinobu Inoue continue; /* we need only unicast addresses */ 156482cd038dSYoshinobu Inoue 156582cd038dSYoshinobu Inoue if ((ni6->ni_flags & (NI_NODEADDR_FLAG_LINKLOCAL | 156682cd038dSYoshinobu Inoue NI_NODEADDR_FLAG_SITELOCAL | 156782cd038dSYoshinobu Inoue NI_NODEADDR_FLAG_GLOBAL)) == 0) 156882cd038dSYoshinobu Inoue continue; 156982cd038dSYoshinobu Inoue 157082cd038dSYoshinobu Inoue /* What do we have to do about ::1? */ 157182cd038dSYoshinobu Inoue switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { 157282cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_LINKLOCAL: 157382cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) 157482cd038dSYoshinobu Inoue addrsofif++; 157582cd038dSYoshinobu Inoue break; 157682cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_SITELOCAL: 157782cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) 157882cd038dSYoshinobu Inoue addrsofif++; 157982cd038dSYoshinobu Inoue break; 158082cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_GLOBAL: 158182cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) 158282cd038dSYoshinobu Inoue addrsofif++; 158382cd038dSYoshinobu Inoue break; 158482cd038dSYoshinobu Inoue default: 158582cd038dSYoshinobu Inoue continue; 158682cd038dSYoshinobu Inoue } 158782cd038dSYoshinobu Inoue } 158882cd038dSYoshinobu Inoue if (iffound) { 158982cd038dSYoshinobu Inoue *ifpp = ifp; 159082cd038dSYoshinobu Inoue return(addrsofif); 159182cd038dSYoshinobu Inoue } 159282cd038dSYoshinobu Inoue 159382cd038dSYoshinobu Inoue addrs += addrsofif; 159482cd038dSYoshinobu Inoue } 159582cd038dSYoshinobu Inoue 159682cd038dSYoshinobu Inoue return(addrs); 159782cd038dSYoshinobu Inoue } 159882cd038dSYoshinobu Inoue 159982cd038dSYoshinobu Inoue static int 160082cd038dSYoshinobu Inoue ni6_store_addrs(ni6, nni6, ifp0, resid) 160182cd038dSYoshinobu Inoue struct icmp6_nodeinfo *ni6, *nni6; 160282cd038dSYoshinobu Inoue struct ifnet *ifp0; 160382cd038dSYoshinobu Inoue int resid; 160482cd038dSYoshinobu Inoue { 160582cd038dSYoshinobu Inoue register struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&ifnet); 160682cd038dSYoshinobu Inoue register struct in6_ifaddr *ifa6; 160782cd038dSYoshinobu Inoue register struct ifaddr *ifa; 160882cd038dSYoshinobu Inoue int docopy, copied = 0; 160982cd038dSYoshinobu Inoue u_char *cp = (u_char *)(nni6 + 1); 161082cd038dSYoshinobu Inoue 161182cd038dSYoshinobu Inoue if (ifp0 == NULL && !(ni6->ni_flags & NI_NODEADDR_FLAG_ALL)) 161282cd038dSYoshinobu Inoue return(0); /* needless to copy */ 161382cd038dSYoshinobu Inoue 161482cd038dSYoshinobu Inoue for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) 161582cd038dSYoshinobu Inoue { 1616686cdd19SJun-ichiro itojun Hagino for (ifa = ifp->if_addrlist.tqh_first; ifa; 1617686cdd19SJun-ichiro itojun Hagino ifa = ifa->ifa_list.tqe_next) 161882cd038dSYoshinobu Inoue { 161982cd038dSYoshinobu Inoue docopy = 0; 162082cd038dSYoshinobu Inoue 162182cd038dSYoshinobu Inoue if (ifa->ifa_addr->sa_family != AF_INET6) 162282cd038dSYoshinobu Inoue continue; 162382cd038dSYoshinobu Inoue ifa6 = (struct in6_ifaddr *)ifa; 162482cd038dSYoshinobu Inoue 162582cd038dSYoshinobu Inoue if (ifa6->ia6_flags & IN6_IFF_ANYCAST) { 162682cd038dSYoshinobu Inoue /* just experimental. not in the spec. */ 162782cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) 162882cd038dSYoshinobu Inoue docopy = 1; 162982cd038dSYoshinobu Inoue else 163082cd038dSYoshinobu Inoue continue; 1631686cdd19SJun-ichiro itojun Hagino } 1632686cdd19SJun-ichiro itojun Hagino else { /* unicast address */ 163382cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_ANYCAST) 163482cd038dSYoshinobu Inoue continue; 1635686cdd19SJun-ichiro itojun Hagino else 1636686cdd19SJun-ichiro itojun Hagino docopy = 1; 163782cd038dSYoshinobu Inoue } 163882cd038dSYoshinobu Inoue 163982cd038dSYoshinobu Inoue /* What do we have to do about ::1? */ 164082cd038dSYoshinobu Inoue switch(in6_addrscope(&ifa6->ia_addr.sin6_addr)) { 164182cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_LINKLOCAL: 164282cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_LINKLOCAL) 164382cd038dSYoshinobu Inoue docopy = 1; 164482cd038dSYoshinobu Inoue break; 164582cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_SITELOCAL: 164682cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_SITELOCAL) 164782cd038dSYoshinobu Inoue docopy = 1; 164882cd038dSYoshinobu Inoue break; 164982cd038dSYoshinobu Inoue case IPV6_ADDR_SCOPE_GLOBAL: 165082cd038dSYoshinobu Inoue if (ni6->ni_flags & NI_NODEADDR_FLAG_GLOBAL) 165182cd038dSYoshinobu Inoue docopy = 1; 165282cd038dSYoshinobu Inoue break; 165382cd038dSYoshinobu Inoue default: 165482cd038dSYoshinobu Inoue continue; 165582cd038dSYoshinobu Inoue } 165682cd038dSYoshinobu Inoue 165782cd038dSYoshinobu Inoue if (docopy) { 165882cd038dSYoshinobu Inoue if (resid < sizeof(struct in6_addr)) { 165982cd038dSYoshinobu Inoue /* 166082cd038dSYoshinobu Inoue * We give up much more copy. 166182cd038dSYoshinobu Inoue * Set the truncate flag and return. 166282cd038dSYoshinobu Inoue */ 166382cd038dSYoshinobu Inoue nni6->ni_flags |= 166482cd038dSYoshinobu Inoue NI_NODEADDR_FLAG_TRUNCATE; 166582cd038dSYoshinobu Inoue return(copied); 166682cd038dSYoshinobu Inoue } 166782cd038dSYoshinobu Inoue bcopy(&ifa6->ia_addr.sin6_addr, cp, 166882cd038dSYoshinobu Inoue sizeof(struct in6_addr)); 166982cd038dSYoshinobu Inoue /* XXX: KAME link-local hack; remove ifindex */ 167082cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&ifa6->ia_addr.sin6_addr)) 167182cd038dSYoshinobu Inoue ((struct in6_addr *)cp)->s6_addr16[1] = 0; 167282cd038dSYoshinobu Inoue cp += sizeof(struct in6_addr); 167382cd038dSYoshinobu Inoue resid -= sizeof(struct in6_addr); 167482cd038dSYoshinobu Inoue copied += sizeof(struct in6_addr); 167582cd038dSYoshinobu Inoue } 167682cd038dSYoshinobu Inoue } 167782cd038dSYoshinobu Inoue if (ifp0) /* we need search only on the specified IF */ 167882cd038dSYoshinobu Inoue break; 167982cd038dSYoshinobu Inoue } 168082cd038dSYoshinobu Inoue 168182cd038dSYoshinobu Inoue return(copied); 168282cd038dSYoshinobu Inoue } 168382cd038dSYoshinobu Inoue 168482cd038dSYoshinobu Inoue /* 168582cd038dSYoshinobu Inoue * XXX almost dup'ed code with rip6_input. 168682cd038dSYoshinobu Inoue */ 168782cd038dSYoshinobu Inoue static int 168882cd038dSYoshinobu Inoue icmp6_rip6_input(mp, off) 168982cd038dSYoshinobu Inoue struct mbuf **mp; 169082cd038dSYoshinobu Inoue int off; 169182cd038dSYoshinobu Inoue { 169282cd038dSYoshinobu Inoue struct mbuf *m = *mp; 169382cd038dSYoshinobu Inoue register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 169482cd038dSYoshinobu Inoue register struct in6pcb *in6p; 169582cd038dSYoshinobu Inoue struct in6pcb *last = NULL; 169682cd038dSYoshinobu Inoue struct sockaddr_in6 rip6src; 169782cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6; 169882cd038dSYoshinobu Inoue struct mbuf *opts = NULL; 169982cd038dSYoshinobu Inoue 1700686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 170182cd038dSYoshinobu Inoue /* this is assumed to be safe. */ 170282cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); 1703686cdd19SJun-ichiro itojun Hagino #else 1704686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); 1705686cdd19SJun-ichiro itojun Hagino if (icmp6 == NULL) { 1706686cdd19SJun-ichiro itojun Hagino /* m is already reclaimed */ 1707686cdd19SJun-ichiro itojun Hagino return IPPROTO_DONE; 1708686cdd19SJun-ichiro itojun Hagino } 1709686cdd19SJun-ichiro itojun Hagino #endif 171082cd038dSYoshinobu Inoue 171182cd038dSYoshinobu Inoue bzero(&rip6src, sizeof(rip6src)); 171282cd038dSYoshinobu Inoue rip6src.sin6_len = sizeof(struct sockaddr_in6); 171382cd038dSYoshinobu Inoue rip6src.sin6_family = AF_INET6; 1714686cdd19SJun-ichiro itojun Hagino /* KAME hack: recover scopeid */ 1715686cdd19SJun-ichiro itojun Hagino (void)in6_recoverscope(&rip6src, &ip6->ip6_src, m->m_pkthdr.rcvif); 171682cd038dSYoshinobu Inoue 171782cd038dSYoshinobu Inoue LIST_FOREACH(in6p, &ripcb, inp_list) 171882cd038dSYoshinobu Inoue { 1719686cdd19SJun-ichiro itojun Hagino if ((in6p->inp_vflag & INP_IPV6) == NULL) 172082cd038dSYoshinobu Inoue continue; 172182cd038dSYoshinobu Inoue if (in6p->in6p_ip6_nxt != IPPROTO_ICMPV6) 172282cd038dSYoshinobu Inoue continue; 172382cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) && 172482cd038dSYoshinobu Inoue !IN6_ARE_ADDR_EQUAL(&in6p->in6p_laddr, &ip6->ip6_dst)) 172582cd038dSYoshinobu Inoue continue; 172682cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && 172782cd038dSYoshinobu Inoue !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) 172882cd038dSYoshinobu Inoue continue; 172982cd038dSYoshinobu Inoue if (in6p->in6p_icmp6filt 173082cd038dSYoshinobu Inoue && ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, 173182cd038dSYoshinobu Inoue in6p->in6p_icmp6filt)) 173282cd038dSYoshinobu Inoue continue; 173382cd038dSYoshinobu Inoue if (last) { 173482cd038dSYoshinobu Inoue struct mbuf *n; 173582cd038dSYoshinobu Inoue if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { 173682cd038dSYoshinobu Inoue if (last->in6p_flags & IN6P_CONTROLOPTS) 173782cd038dSYoshinobu Inoue ip6_savecontrol(last, &opts, ip6, n); 173882cd038dSYoshinobu Inoue /* strip intermediate headers */ 173982cd038dSYoshinobu Inoue m_adj(n, off); 174082cd038dSYoshinobu Inoue if (sbappendaddr(&last->in6p_socket->so_rcv, 174182cd038dSYoshinobu Inoue (struct sockaddr *)&rip6src, 174282cd038dSYoshinobu Inoue n, opts) == 0) { 174382cd038dSYoshinobu Inoue /* should notify about lost packet */ 174482cd038dSYoshinobu Inoue m_freem(n); 174582cd038dSYoshinobu Inoue if (opts) 174682cd038dSYoshinobu Inoue m_freem(opts); 174782cd038dSYoshinobu Inoue } else 174882cd038dSYoshinobu Inoue sorwakeup(last->in6p_socket); 174982cd038dSYoshinobu Inoue opts = NULL; 175082cd038dSYoshinobu Inoue } 175182cd038dSYoshinobu Inoue } 175282cd038dSYoshinobu Inoue last = in6p; 175382cd038dSYoshinobu Inoue } 175482cd038dSYoshinobu Inoue if (last) { 175582cd038dSYoshinobu Inoue if (last->in6p_flags & IN6P_CONTROLOPTS) 175682cd038dSYoshinobu Inoue ip6_savecontrol(last, &opts, ip6, m); 175782cd038dSYoshinobu Inoue /* strip intermediate headers */ 175882cd038dSYoshinobu Inoue m_adj(m, off); 175982cd038dSYoshinobu Inoue if (sbappendaddr(&last->in6p_socket->so_rcv, 176082cd038dSYoshinobu Inoue (struct sockaddr *)&rip6src, m, opts) == 0) { 176182cd038dSYoshinobu Inoue m_freem(m); 176282cd038dSYoshinobu Inoue if (opts) 176382cd038dSYoshinobu Inoue m_freem(opts); 176482cd038dSYoshinobu Inoue } else 176582cd038dSYoshinobu Inoue sorwakeup(last->in6p_socket); 176682cd038dSYoshinobu Inoue } else { 176782cd038dSYoshinobu Inoue m_freem(m); 176882cd038dSYoshinobu Inoue ip6stat.ip6s_delivered--; 176982cd038dSYoshinobu Inoue } 177082cd038dSYoshinobu Inoue return IPPROTO_DONE; 177182cd038dSYoshinobu Inoue } 177282cd038dSYoshinobu Inoue 177382cd038dSYoshinobu Inoue /* 177482cd038dSYoshinobu Inoue * Reflect the ip6 packet back to the source. 1775686cdd19SJun-ichiro itojun Hagino * OFF points to the icmp6 header, counted from the top of the mbuf. 177682cd038dSYoshinobu Inoue */ 177782cd038dSYoshinobu Inoue void 177882cd038dSYoshinobu Inoue icmp6_reflect(m, off) 177982cd038dSYoshinobu Inoue struct mbuf *m; 178082cd038dSYoshinobu Inoue size_t off; 178182cd038dSYoshinobu Inoue { 1782686cdd19SJun-ichiro itojun Hagino struct ip6_hdr *ip6; 178382cd038dSYoshinobu Inoue struct icmp6_hdr *icmp6; 178482cd038dSYoshinobu Inoue struct in6_ifaddr *ia; 178582cd038dSYoshinobu Inoue struct in6_addr t, *src = 0; 1786686cdd19SJun-ichiro itojun Hagino int plen; 178782cd038dSYoshinobu Inoue int type, code; 178882cd038dSYoshinobu Inoue struct ifnet *outif = NULL; 178982cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 179082cd038dSYoshinobu Inoue int mtu = IPV6_MMTU; 179182cd038dSYoshinobu Inoue struct sockaddr_in6 *sin6 = &icmp6_reflect_rt.ro_dst; 179282cd038dSYoshinobu Inoue #endif 179382cd038dSYoshinobu Inoue 1794686cdd19SJun-ichiro itojun Hagino /* too short to reflect */ 1795686cdd19SJun-ichiro itojun Hagino if (off < sizeof(struct ip6_hdr)) { 1796686cdd19SJun-ichiro itojun Hagino printf("sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", 1797686cdd19SJun-ichiro itojun Hagino (u_long)off, (u_long)sizeof(struct ip6_hdr), 1798686cdd19SJun-ichiro itojun Hagino __FILE__, __LINE__); 1799686cdd19SJun-ichiro itojun Hagino goto bad; 1800686cdd19SJun-ichiro itojun Hagino } 1801686cdd19SJun-ichiro itojun Hagino 180282cd038dSYoshinobu Inoue /* 180382cd038dSYoshinobu Inoue * If there are extra headers between IPv6 and ICMPv6, strip 180482cd038dSYoshinobu Inoue * off that header first. 180582cd038dSYoshinobu Inoue */ 1806686cdd19SJun-ichiro itojun Hagino #ifdef DIAGNOSTIC 1807686cdd19SJun-ichiro itojun Hagino if (sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) > MHLEN) 1808686cdd19SJun-ichiro itojun Hagino panic("assumption failed in icmp6_reflect"); 1809686cdd19SJun-ichiro itojun Hagino #endif 1810686cdd19SJun-ichiro itojun Hagino if (off > sizeof(struct ip6_hdr)) { 1811686cdd19SJun-ichiro itojun Hagino size_t l; 1812686cdd19SJun-ichiro itojun Hagino struct ip6_hdr nip6; 181382cd038dSYoshinobu Inoue 1814686cdd19SJun-ichiro itojun Hagino l = off - sizeof(struct ip6_hdr); 1815686cdd19SJun-ichiro itojun Hagino m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6); 1816686cdd19SJun-ichiro itojun Hagino m_adj(m, l); 1817686cdd19SJun-ichiro itojun Hagino l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 1818686cdd19SJun-ichiro itojun Hagino if (m->m_len < l) { 1819686cdd19SJun-ichiro itojun Hagino if ((m = m_pullup(m, l)) == NULL) 1820686cdd19SJun-ichiro itojun Hagino return; 182182cd038dSYoshinobu Inoue } 1822686cdd19SJun-ichiro itojun Hagino bcopy((caddr_t)&nip6, mtod(m, caddr_t), sizeof(nip6)); 1823686cdd19SJun-ichiro itojun Hagino } else /* off == sizeof(struct ip6_hdr) */ { 1824686cdd19SJun-ichiro itojun Hagino size_t l; 1825686cdd19SJun-ichiro itojun Hagino l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 1826686cdd19SJun-ichiro itojun Hagino if (m->m_len < l) { 1827686cdd19SJun-ichiro itojun Hagino if ((m = m_pullup(m, l)) == NULL) 1828686cdd19SJun-ichiro itojun Hagino return; 182982cd038dSYoshinobu Inoue } 1830686cdd19SJun-ichiro itojun Hagino } 1831686cdd19SJun-ichiro itojun Hagino plen = m->m_pkthdr.len - sizeof(struct ip6_hdr); 183282cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 183382cd038dSYoshinobu Inoue ip6->ip6_nxt = IPPROTO_ICMPV6; 183482cd038dSYoshinobu Inoue icmp6 = (struct icmp6_hdr *)(ip6 + 1); 183582cd038dSYoshinobu Inoue type = icmp6->icmp6_type; /* keep type for statistics */ 183682cd038dSYoshinobu Inoue code = icmp6->icmp6_code; /* ditto. */ 183782cd038dSYoshinobu Inoue 183882cd038dSYoshinobu Inoue t = ip6->ip6_dst; 183982cd038dSYoshinobu Inoue /* 184082cd038dSYoshinobu Inoue * ip6_input() drops a packet if its src is multicast. 184182cd038dSYoshinobu Inoue * So, the src is never multicast. 184282cd038dSYoshinobu Inoue */ 184382cd038dSYoshinobu Inoue ip6->ip6_dst = ip6->ip6_src; 184482cd038dSYoshinobu Inoue 184582cd038dSYoshinobu Inoue /* XXX hack for link-local addresses */ 184682cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) 184782cd038dSYoshinobu Inoue ip6->ip6_dst.s6_addr16[1] = 184882cd038dSYoshinobu Inoue htons(m->m_pkthdr.rcvif->if_index); 184982cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&t)) 185082cd038dSYoshinobu Inoue t.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); 185182cd038dSYoshinobu Inoue 185282cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 185382cd038dSYoshinobu Inoue /* 185482cd038dSYoshinobu Inoue * xxx guess MTU 185582cd038dSYoshinobu Inoue * RFC 1885 requires that echo reply should be truncated if it 185682cd038dSYoshinobu Inoue * does not fit in with (return) path MTU, but the description was 185782cd038dSYoshinobu Inoue * removed in the new spec. 185882cd038dSYoshinobu Inoue */ 185982cd038dSYoshinobu Inoue if (icmp6_reflect_rt.ro_rt == 0 || 186082cd038dSYoshinobu Inoue ! (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_dst))) { 186182cd038dSYoshinobu Inoue if (icmp6_reflect_rt.ro_rt) { 186282cd038dSYoshinobu Inoue RTFREE(icmp6_reflect_rt.ro_rt); 186382cd038dSYoshinobu Inoue icmp6_reflect_rt.ro_rt = 0; 186482cd038dSYoshinobu Inoue } 186582cd038dSYoshinobu Inoue bzero(sin6, sizeof(*sin6)); 186682cd038dSYoshinobu Inoue sin6->sin6_family = PF_INET6; 186782cd038dSYoshinobu Inoue sin6->sin6_len = sizeof(struct sockaddr_in6); 186882cd038dSYoshinobu Inoue sin6->sin6_addr = ip6->ip6_dst; 186982cd038dSYoshinobu Inoue 187082cd038dSYoshinobu Inoue rtalloc_ign((struct route *)&icmp6_reflect_rt.ro_rt, 187182cd038dSYoshinobu Inoue RTF_PRCLONING); 187282cd038dSYoshinobu Inoue } 187382cd038dSYoshinobu Inoue 187482cd038dSYoshinobu Inoue if (icmp6_reflect_rt.ro_rt == 0) 187582cd038dSYoshinobu Inoue goto bad; 187682cd038dSYoshinobu Inoue 187782cd038dSYoshinobu Inoue if ((icmp6_reflect_rt.ro_rt->rt_flags & RTF_HOST) 187882cd038dSYoshinobu Inoue && mtu < icmp6_reflect_rt.ro_rt->rt_ifp->if_mtu) 187982cd038dSYoshinobu Inoue mtu = icmp6_reflect_rt.ro_rt->rt_rmx.rmx_mtu; 188082cd038dSYoshinobu Inoue 188182cd038dSYoshinobu Inoue if (mtu < m->m_pkthdr.len) { 188282cd038dSYoshinobu Inoue plen -= (m->m_pkthdr.len - mtu); 188382cd038dSYoshinobu Inoue m_adj(m, mtu - m->m_pkthdr.len); 188482cd038dSYoshinobu Inoue } 188582cd038dSYoshinobu Inoue #endif 188682cd038dSYoshinobu Inoue /* 188782cd038dSYoshinobu Inoue * If the incoming packet was addressed directly to us(i.e. unicast), 188882cd038dSYoshinobu Inoue * use dst as the src for the reply. 1889686cdd19SJun-ichiro itojun Hagino * The IN6_IFF_NOTREADY case would be VERY rare, but is possible 1890686cdd19SJun-ichiro itojun Hagino * (for example) when we encounter an error while forwarding procedure 1891686cdd19SJun-ichiro itojun Hagino * destined to a duplicated address of ours. 189282cd038dSYoshinobu Inoue */ 189382cd038dSYoshinobu Inoue for (ia = in6_ifaddr; ia; ia = ia->ia_next) 189482cd038dSYoshinobu Inoue if (IN6_ARE_ADDR_EQUAL(&t, &ia->ia_addr.sin6_addr) && 1895686cdd19SJun-ichiro itojun Hagino (ia->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) { 189682cd038dSYoshinobu Inoue src = &t; 189782cd038dSYoshinobu Inoue break; 189882cd038dSYoshinobu Inoue } 189982cd038dSYoshinobu Inoue if (ia == NULL && IN6_IS_ADDR_LINKLOCAL(&t) && (m->m_flags & M_LOOP)) { 190082cd038dSYoshinobu Inoue /* 190182cd038dSYoshinobu Inoue * This is the case if the dst is our link-local address 190282cd038dSYoshinobu Inoue * and the sender is also ourseleves. 190382cd038dSYoshinobu Inoue */ 190482cd038dSYoshinobu Inoue src = &t; 190582cd038dSYoshinobu Inoue } 190682cd038dSYoshinobu Inoue 190782cd038dSYoshinobu Inoue if (src == 0) 190882cd038dSYoshinobu Inoue /* 1909686cdd19SJun-ichiro itojun Hagino * This case matches to multicasts, our anycast, or unicasts 1910686cdd19SJun-ichiro itojun Hagino * that we do not own. Select a source address which has the 1911686cdd19SJun-ichiro itojun Hagino * same scope. 1912686cdd19SJun-ichiro itojun Hagino * XXX: for (non link-local) multicast addresses, this might 1913686cdd19SJun-ichiro itojun Hagino * not be a good choice. 191482cd038dSYoshinobu Inoue */ 191582cd038dSYoshinobu Inoue if ((ia = in6_ifawithscope(m->m_pkthdr.rcvif, &t)) != 0) 191682cd038dSYoshinobu Inoue src = &IA6_SIN6(ia)->sin6_addr; 191782cd038dSYoshinobu Inoue 191882cd038dSYoshinobu Inoue if (src == 0) 191982cd038dSYoshinobu Inoue goto bad; 192082cd038dSYoshinobu Inoue 192182cd038dSYoshinobu Inoue ip6->ip6_src = *src; 192282cd038dSYoshinobu Inoue 192382cd038dSYoshinobu Inoue ip6->ip6_flow = 0; 1924686cdd19SJun-ichiro itojun Hagino ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 1925686cdd19SJun-ichiro itojun Hagino ip6->ip6_vfc |= IPV6_VERSION; 192682cd038dSYoshinobu Inoue ip6->ip6_nxt = IPPROTO_ICMPV6; 192782cd038dSYoshinobu Inoue if (m->m_pkthdr.rcvif) { 192882cd038dSYoshinobu Inoue /* XXX: This may not be the outgoing interface */ 192982cd038dSYoshinobu Inoue ip6->ip6_hlim = nd_ifinfo[m->m_pkthdr.rcvif->if_index].chlim; 193082cd038dSYoshinobu Inoue } 193182cd038dSYoshinobu Inoue 193282cd038dSYoshinobu Inoue icmp6->icmp6_cksum = 0; 193382cd038dSYoshinobu Inoue icmp6->icmp6_cksum = in6_cksum(m, IPPROTO_ICMPV6, 193482cd038dSYoshinobu Inoue sizeof(struct ip6_hdr), plen); 193582cd038dSYoshinobu Inoue 193682cd038dSYoshinobu Inoue /* 193782cd038dSYoshinobu Inoue * xxx option handling 193882cd038dSYoshinobu Inoue */ 193982cd038dSYoshinobu Inoue 194082cd038dSYoshinobu Inoue m->m_flags &= ~(M_BCAST|M_MCAST); 1941686cdd19SJun-ichiro itojun Hagino #ifdef IPSEC 1942686cdd19SJun-ichiro itojun Hagino /* Don't lookup socket */ 1943686cdd19SJun-ichiro itojun Hagino ipsec_setsocket(m, NULL); 1944686cdd19SJun-ichiro itojun Hagino #endif /*IPSEC*/ 194582cd038dSYoshinobu Inoue 194682cd038dSYoshinobu Inoue #ifdef COMPAT_RFC1885 194782cd038dSYoshinobu Inoue ip6_output(m, NULL, &icmp6_reflect_rt, 0, NULL, &outif); 194882cd038dSYoshinobu Inoue #else 194982cd038dSYoshinobu Inoue ip6_output(m, NULL, NULL, 0, NULL, &outif); 195082cd038dSYoshinobu Inoue #endif 195182cd038dSYoshinobu Inoue if (outif) 195282cd038dSYoshinobu Inoue icmp6_ifoutstat_inc(outif, type, code); 195382cd038dSYoshinobu Inoue 195482cd038dSYoshinobu Inoue return; 195582cd038dSYoshinobu Inoue 195682cd038dSYoshinobu Inoue bad: 195782cd038dSYoshinobu Inoue m_freem(m); 195882cd038dSYoshinobu Inoue return; 195982cd038dSYoshinobu Inoue } 196082cd038dSYoshinobu Inoue 196182cd038dSYoshinobu Inoue void 196282cd038dSYoshinobu Inoue icmp6_fasttimo() 196382cd038dSYoshinobu Inoue { 1964686cdd19SJun-ichiro itojun Hagino 196582cd038dSYoshinobu Inoue mld6_fasttimeo(); 1966686cdd19SJun-ichiro itojun Hagino 1967686cdd19SJun-ichiro itojun Hagino /* reset ICMPv6 pps limit */ 1968686cdd19SJun-ichiro itojun Hagino icmp6errpps_count = 0; 196982cd038dSYoshinobu Inoue } 197082cd038dSYoshinobu Inoue 197182cd038dSYoshinobu Inoue static const char * 197282cd038dSYoshinobu Inoue icmp6_redirect_diag(src6, dst6, tgt6) 197382cd038dSYoshinobu Inoue struct in6_addr *src6; 197482cd038dSYoshinobu Inoue struct in6_addr *dst6; 197582cd038dSYoshinobu Inoue struct in6_addr *tgt6; 197682cd038dSYoshinobu Inoue { 197782cd038dSYoshinobu Inoue static char buf[1024]; 197882cd038dSYoshinobu Inoue snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", 197982cd038dSYoshinobu Inoue ip6_sprintf(src6), ip6_sprintf(dst6), ip6_sprintf(tgt6)); 198082cd038dSYoshinobu Inoue return buf; 198182cd038dSYoshinobu Inoue } 198282cd038dSYoshinobu Inoue 198382cd038dSYoshinobu Inoue void 198482cd038dSYoshinobu Inoue icmp6_redirect_input(m, off) 198582cd038dSYoshinobu Inoue register struct mbuf *m; 198682cd038dSYoshinobu Inoue int off; 198782cd038dSYoshinobu Inoue { 198882cd038dSYoshinobu Inoue struct ifnet *ifp = m->m_pkthdr.rcvif; 198982cd038dSYoshinobu Inoue struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1990686cdd19SJun-ichiro itojun Hagino struct nd_redirect *nd_rd; 199182cd038dSYoshinobu Inoue int icmp6len = ntohs(ip6->ip6_plen); 199282cd038dSYoshinobu Inoue char *lladdr = NULL; 199382cd038dSYoshinobu Inoue int lladdrlen = 0; 199482cd038dSYoshinobu Inoue u_char *redirhdr = NULL; 199582cd038dSYoshinobu Inoue int redirhdrlen = 0; 199682cd038dSYoshinobu Inoue struct rtentry *rt = NULL; 199782cd038dSYoshinobu Inoue int is_router; 199882cd038dSYoshinobu Inoue int is_onlink; 199982cd038dSYoshinobu Inoue struct in6_addr src6 = ip6->ip6_src; 2000686cdd19SJun-ichiro itojun Hagino struct in6_addr redtgt6; 2001686cdd19SJun-ichiro itojun Hagino struct in6_addr reddst6; 200282cd038dSYoshinobu Inoue union nd_opts ndopts; 200382cd038dSYoshinobu Inoue 200482cd038dSYoshinobu Inoue if (!m || !ifp) 200582cd038dSYoshinobu Inoue return; 200682cd038dSYoshinobu Inoue 200782cd038dSYoshinobu Inoue /* XXX if we are router, we don't update route by icmp6 redirect */ 200882cd038dSYoshinobu Inoue if (ip6_forwarding) 2009686cdd19SJun-ichiro itojun Hagino goto freeit; 201082cd038dSYoshinobu Inoue if (!icmp6_rediraccept) 2011686cdd19SJun-ichiro itojun Hagino goto freeit; 2012686cdd19SJun-ichiro itojun Hagino 2013686cdd19SJun-ichiro itojun Hagino #ifndef PULLDOWN_TEST 2014686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_CHECK(m, off, icmp6len,); 2015686cdd19SJun-ichiro itojun Hagino nd_rd = (struct nd_redirect *)((caddr_t)ip6 + off); 2016686cdd19SJun-ichiro itojun Hagino #else 2017686cdd19SJun-ichiro itojun Hagino IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); 2018686cdd19SJun-ichiro itojun Hagino if (nd_rd == NULL) { 2019686cdd19SJun-ichiro itojun Hagino icmp6stat.icp6s_tooshort++; 202082cd038dSYoshinobu Inoue return; 2021686cdd19SJun-ichiro itojun Hagino } 2022686cdd19SJun-ichiro itojun Hagino #endif 2023686cdd19SJun-ichiro itojun Hagino redtgt6 = nd_rd->nd_rd_target; 2024686cdd19SJun-ichiro itojun Hagino reddst6 = nd_rd->nd_rd_dst; 202582cd038dSYoshinobu Inoue 202682cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) 202782cd038dSYoshinobu Inoue redtgt6.s6_addr16[1] = htons(ifp->if_index); 202882cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) 202982cd038dSYoshinobu Inoue reddst6.s6_addr16[1] = htons(ifp->if_index); 203082cd038dSYoshinobu Inoue 203182cd038dSYoshinobu Inoue /* validation */ 203282cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { 203382cd038dSYoshinobu Inoue log(LOG_ERR, 203482cd038dSYoshinobu Inoue "ICMP6 redirect sent from %s rejected; " 203582cd038dSYoshinobu Inoue "must be from linklocal\n", ip6_sprintf(&src6)); 2036686cdd19SJun-ichiro itojun Hagino goto freeit; 203782cd038dSYoshinobu Inoue } 203882cd038dSYoshinobu Inoue if (ip6->ip6_hlim != 255) { 203982cd038dSYoshinobu Inoue log(LOG_ERR, 204082cd038dSYoshinobu Inoue "ICMP6 redirect sent from %s rejected; " 204182cd038dSYoshinobu Inoue "hlim=%d (must be 255)\n", 204282cd038dSYoshinobu Inoue ip6_sprintf(&src6), ip6->ip6_hlim); 2043686cdd19SJun-ichiro itojun Hagino goto freeit; 204482cd038dSYoshinobu Inoue } 204582cd038dSYoshinobu Inoue { 204682cd038dSYoshinobu Inoue /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ 204782cd038dSYoshinobu Inoue struct sockaddr_in6 sin6; 204882cd038dSYoshinobu Inoue struct in6_addr *gw6; 204982cd038dSYoshinobu Inoue 205082cd038dSYoshinobu Inoue bzero(&sin6, sizeof(sin6)); 205182cd038dSYoshinobu Inoue sin6.sin6_family = AF_INET6; 205282cd038dSYoshinobu Inoue sin6.sin6_len = sizeof(struct sockaddr_in6); 205382cd038dSYoshinobu Inoue bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); 205482cd038dSYoshinobu Inoue rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); 205582cd038dSYoshinobu Inoue if (rt) { 205682cd038dSYoshinobu Inoue gw6 = &(((struct sockaddr_in6 *)rt->rt_gateway)->sin6_addr); 205782cd038dSYoshinobu Inoue if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { 205882cd038dSYoshinobu Inoue log(LOG_ERR, 205982cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 206082cd038dSYoshinobu Inoue "not equal to gw-for-src=%s (must be same): " 206182cd038dSYoshinobu Inoue "%s\n", 206282cd038dSYoshinobu Inoue ip6_sprintf(gw6), 206382cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 206482cd038dSYoshinobu Inoue RTFREE(rt); 2065686cdd19SJun-ichiro itojun Hagino goto freeit; 206682cd038dSYoshinobu Inoue } 206782cd038dSYoshinobu Inoue } else { 206882cd038dSYoshinobu Inoue log(LOG_ERR, 206982cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 207082cd038dSYoshinobu Inoue "no route found for redirect dst: %s\n", 207182cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 2072686cdd19SJun-ichiro itojun Hagino goto freeit; 207382cd038dSYoshinobu Inoue } 207482cd038dSYoshinobu Inoue RTFREE(rt); 207582cd038dSYoshinobu Inoue rt = NULL; 207682cd038dSYoshinobu Inoue } 207782cd038dSYoshinobu Inoue if (IN6_IS_ADDR_MULTICAST(&reddst6)) { 207882cd038dSYoshinobu Inoue log(LOG_ERR, 207982cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 208082cd038dSYoshinobu Inoue "redirect dst must be unicast: %s\n", 208182cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 2082686cdd19SJun-ichiro itojun Hagino goto freeit; 208382cd038dSYoshinobu Inoue } 208482cd038dSYoshinobu Inoue 208582cd038dSYoshinobu Inoue is_router = is_onlink = 0; 208682cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) 208782cd038dSYoshinobu Inoue is_router = 1; /* router case */ 208882cd038dSYoshinobu Inoue if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) 208982cd038dSYoshinobu Inoue is_onlink = 1; /* on-link destination case */ 209082cd038dSYoshinobu Inoue if (!is_router && !is_onlink) { 209182cd038dSYoshinobu Inoue log(LOG_ERR, 209282cd038dSYoshinobu Inoue "ICMP6 redirect rejected; " 209382cd038dSYoshinobu Inoue "neither router case nor onlink case: %s\n", 209482cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 2095686cdd19SJun-ichiro itojun Hagino goto freeit; 209682cd038dSYoshinobu Inoue } 209782cd038dSYoshinobu Inoue /* validation passed */ 209882cd038dSYoshinobu Inoue 209982cd038dSYoshinobu Inoue icmp6len -= sizeof(*nd_rd); 210082cd038dSYoshinobu Inoue nd6_option_init(nd_rd + 1, icmp6len, &ndopts); 210182cd038dSYoshinobu Inoue if (nd6_options(&ndopts) < 0) { 210282cd038dSYoshinobu Inoue log(LOG_INFO, "icmp6_redirect_input: " 210382cd038dSYoshinobu Inoue "invalid ND option, rejected: %s\n", 210482cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 2105686cdd19SJun-ichiro itojun Hagino goto freeit; 210682cd038dSYoshinobu Inoue } 210782cd038dSYoshinobu Inoue 210882cd038dSYoshinobu Inoue if (ndopts.nd_opts_tgt_lladdr) { 210982cd038dSYoshinobu Inoue lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); 211082cd038dSYoshinobu Inoue lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; 211182cd038dSYoshinobu Inoue } 211282cd038dSYoshinobu Inoue 211382cd038dSYoshinobu Inoue if (ndopts.nd_opts_rh) { 211482cd038dSYoshinobu Inoue redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len; 211582cd038dSYoshinobu Inoue redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */ 211682cd038dSYoshinobu Inoue } 211782cd038dSYoshinobu Inoue 211882cd038dSYoshinobu Inoue if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { 211982cd038dSYoshinobu Inoue log(LOG_INFO, 212082cd038dSYoshinobu Inoue "icmp6_redirect_input: lladdrlen mismatch for %s " 212182cd038dSYoshinobu Inoue "(if %d, icmp6 packet %d): %s\n", 212282cd038dSYoshinobu Inoue ip6_sprintf(&redtgt6), ifp->if_addrlen, lladdrlen - 2, 212382cd038dSYoshinobu Inoue icmp6_redirect_diag(&src6, &reddst6, &redtgt6)); 212482cd038dSYoshinobu Inoue } 212582cd038dSYoshinobu Inoue 212682cd038dSYoshinobu Inoue /* RFC 2461 8.3 */ 212782cd038dSYoshinobu Inoue nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, 212882cd038dSYoshinobu Inoue is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER); 212982cd038dSYoshinobu Inoue 213082cd038dSYoshinobu Inoue if (!is_onlink) { /* better router case. perform rtredirect. */ 213182cd038dSYoshinobu Inoue /* perform rtredirect */ 213282cd038dSYoshinobu Inoue struct sockaddr_in6 sdst; 213382cd038dSYoshinobu Inoue struct sockaddr_in6 sgw; 213482cd038dSYoshinobu Inoue struct sockaddr_in6 ssrc; 213582cd038dSYoshinobu Inoue 213682cd038dSYoshinobu Inoue bzero(&sdst, sizeof(sdst)); 213782cd038dSYoshinobu Inoue bzero(&sgw, sizeof(sgw)); 213882cd038dSYoshinobu Inoue bzero(&ssrc, sizeof(ssrc)); 213982cd038dSYoshinobu Inoue sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6; 214082cd038dSYoshinobu Inoue sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len = 214182cd038dSYoshinobu Inoue sizeof(struct sockaddr_in6); 214282cd038dSYoshinobu Inoue bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); 214382cd038dSYoshinobu Inoue bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); 214482cd038dSYoshinobu Inoue bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); 214582cd038dSYoshinobu Inoue rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw, 214682cd038dSYoshinobu Inoue (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST, 214782cd038dSYoshinobu Inoue (struct sockaddr *)&ssrc, 214882cd038dSYoshinobu Inoue (struct rtentry **)NULL); 214982cd038dSYoshinobu Inoue } 215082cd038dSYoshinobu Inoue /* finally update cached route in each socket via pfctlinput */ 215182cd038dSYoshinobu Inoue { 215282cd038dSYoshinobu Inoue struct sockaddr_in6 sdst; 215382cd038dSYoshinobu Inoue 215482cd038dSYoshinobu Inoue bzero(&sdst, sizeof(sdst)); 215582cd038dSYoshinobu Inoue sdst.sin6_family = AF_INET6; 215682cd038dSYoshinobu Inoue sdst.sin6_len = sizeof(struct sockaddr_in6); 215782cd038dSYoshinobu Inoue bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); 215882cd038dSYoshinobu Inoue pfctlinput(PRC_REDIRECT_HOST, (struct sockaddr *)&sdst); 215982cd038dSYoshinobu Inoue #ifdef IPSEC 216082cd038dSYoshinobu Inoue key_sa_routechange((struct sockaddr *)&sdst); 216182cd038dSYoshinobu Inoue #endif 216282cd038dSYoshinobu Inoue } 2163686cdd19SJun-ichiro itojun Hagino 2164686cdd19SJun-ichiro itojun Hagino freeit: 2165686cdd19SJun-ichiro itojun Hagino m_freem(m); 216682cd038dSYoshinobu Inoue } 216782cd038dSYoshinobu Inoue 216882cd038dSYoshinobu Inoue void 216982cd038dSYoshinobu Inoue icmp6_redirect_output(m0, rt) 217082cd038dSYoshinobu Inoue struct mbuf *m0; 217182cd038dSYoshinobu Inoue struct rtentry *rt; 217282cd038dSYoshinobu Inoue { 217382cd038dSYoshinobu Inoue struct ifnet *ifp; /* my outgoing interface */ 217482cd038dSYoshinobu Inoue struct in6_addr *ifp_ll6; 217582cd038dSYoshinobu Inoue struct in6_addr *router_ll6; 217682cd038dSYoshinobu Inoue struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ 217782cd038dSYoshinobu Inoue struct mbuf *m = NULL; /* newly allocated one */ 217882cd038dSYoshinobu Inoue struct ip6_hdr *ip6; /* m as struct ip6_hdr */ 217982cd038dSYoshinobu Inoue struct nd_redirect *nd_rd; 218082cd038dSYoshinobu Inoue size_t maxlen; 218182cd038dSYoshinobu Inoue u_char *p; 218282cd038dSYoshinobu Inoue struct ifnet *outif = NULL; 2183686cdd19SJun-ichiro itojun Hagino struct sockaddr_in6 src_sa; 2184686cdd19SJun-ichiro itojun Hagino 2185686cdd19SJun-ichiro itojun Hagino icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0); 218682cd038dSYoshinobu Inoue 218782cd038dSYoshinobu Inoue /* if we are not router, we don't send icmp6 redirect */ 218882cd038dSYoshinobu Inoue if (!ip6_forwarding || ip6_accept_rtadv) 218982cd038dSYoshinobu Inoue goto fail; 219082cd038dSYoshinobu Inoue 219182cd038dSYoshinobu Inoue /* sanity check */ 219282cd038dSYoshinobu Inoue if (!m0 || !rt || !(rt->rt_flags & RTF_UP) || !(ifp = rt->rt_ifp)) 219382cd038dSYoshinobu Inoue goto fail; 219482cd038dSYoshinobu Inoue 219582cd038dSYoshinobu Inoue /* 219682cd038dSYoshinobu Inoue * Address check: 219782cd038dSYoshinobu Inoue * the source address must identify a neighbor, and 219882cd038dSYoshinobu Inoue * the destination address must not be a multicast address 219982cd038dSYoshinobu Inoue * [RFC 2461, sec 8.2] 220082cd038dSYoshinobu Inoue */ 220182cd038dSYoshinobu Inoue sip6 = mtod(m0, struct ip6_hdr *); 2202686cdd19SJun-ichiro itojun Hagino bzero(&src_sa, sizeof(src_sa)); 2203686cdd19SJun-ichiro itojun Hagino src_sa.sin6_family = AF_INET6; 2204686cdd19SJun-ichiro itojun Hagino src_sa.sin6_len = sizeof(src_sa); 2205686cdd19SJun-ichiro itojun Hagino src_sa.sin6_addr = sip6->ip6_src; 2206686cdd19SJun-ichiro itojun Hagino /* we don't currently use sin6_scope_id, but eventually use it */ 2207686cdd19SJun-ichiro itojun Hagino src_sa.sin6_scope_id = in6_addr2scopeid(ifp, &sip6->ip6_src); 2208686cdd19SJun-ichiro itojun Hagino if (nd6_is_addr_neighbor(&src_sa, ifp) == 0) 220982cd038dSYoshinobu Inoue goto fail; 221082cd038dSYoshinobu Inoue if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) 221182cd038dSYoshinobu Inoue goto fail; /* what should we do here? */ 221282cd038dSYoshinobu Inoue 221382cd038dSYoshinobu Inoue /* rate limit */ 221482cd038dSYoshinobu Inoue if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) 221582cd038dSYoshinobu Inoue goto fail; 221682cd038dSYoshinobu Inoue 221782cd038dSYoshinobu Inoue /* 221882cd038dSYoshinobu Inoue * Since we are going to append up to 1280 bytes (= IPV6_MMTU), 221982cd038dSYoshinobu Inoue * we almost always ask for an mbuf cluster for simplicity. 222082cd038dSYoshinobu Inoue * (MHLEN < IPV6_MMTU is almost always true) 222182cd038dSYoshinobu Inoue */ 2222686cdd19SJun-ichiro itojun Hagino #if IPV6_MMTU >= MCLBYTES 2223686cdd19SJun-ichiro itojun Hagino # error assumption failed about IPV6_MMTU and MCLBYTES 2224686cdd19SJun-ichiro itojun Hagino #endif 222582cd038dSYoshinobu Inoue MGETHDR(m, M_DONTWAIT, MT_HEADER); 2226686cdd19SJun-ichiro itojun Hagino if (m && IPV6_MMTU >= MHLEN) 2227686cdd19SJun-ichiro itojun Hagino MCLGET(m, M_DONTWAIT); 222882cd038dSYoshinobu Inoue if (!m) 222982cd038dSYoshinobu Inoue goto fail; 223082cd038dSYoshinobu Inoue maxlen = (m->m_flags & M_EXT) ? MCLBYTES : MHLEN; 223182cd038dSYoshinobu Inoue maxlen = min(IPV6_MMTU, maxlen); 223282cd038dSYoshinobu Inoue /* just for safety */ 2233686cdd19SJun-ichiro itojun Hagino if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 2234686cdd19SJun-ichiro itojun Hagino ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { 223582cd038dSYoshinobu Inoue goto fail; 2236686cdd19SJun-ichiro itojun Hagino } 223782cd038dSYoshinobu Inoue 223882cd038dSYoshinobu Inoue { 223982cd038dSYoshinobu Inoue /* get ip6 linklocal address for ifp(my outgoing interface). */ 2240686cdd19SJun-ichiro itojun Hagino struct in6_ifaddr *ia; 2241686cdd19SJun-ichiro itojun Hagino if ((ia = in6ifa_ifpforlinklocal(ifp, 2242686cdd19SJun-ichiro itojun Hagino IN6_IFF_NOTREADY| 2243686cdd19SJun-ichiro itojun Hagino IN6_IFF_ANYCAST)) == NULL) 224482cd038dSYoshinobu Inoue goto fail; 224582cd038dSYoshinobu Inoue ifp_ll6 = &ia->ia_addr.sin6_addr; 224682cd038dSYoshinobu Inoue } 224782cd038dSYoshinobu Inoue 224882cd038dSYoshinobu Inoue /* get ip6 linklocal address for the router. */ 224982cd038dSYoshinobu Inoue if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { 225082cd038dSYoshinobu Inoue struct sockaddr_in6 *sin6; 225182cd038dSYoshinobu Inoue sin6 = (struct sockaddr_in6 *)rt->rt_gateway; 225282cd038dSYoshinobu Inoue router_ll6 = &sin6->sin6_addr; 225382cd038dSYoshinobu Inoue if (!IN6_IS_ADDR_LINKLOCAL(router_ll6)) 225482cd038dSYoshinobu Inoue router_ll6 = (struct in6_addr *)NULL; 225582cd038dSYoshinobu Inoue } else 225682cd038dSYoshinobu Inoue router_ll6 = (struct in6_addr *)NULL; 225782cd038dSYoshinobu Inoue 225882cd038dSYoshinobu Inoue /* ip6 */ 225982cd038dSYoshinobu Inoue ip6 = mtod(m, struct ip6_hdr *); 226082cd038dSYoshinobu Inoue ip6->ip6_flow = 0; 2261686cdd19SJun-ichiro itojun Hagino ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 2262686cdd19SJun-ichiro itojun Hagino ip6->ip6_vfc |= IPV6_VERSION; 226382cd038dSYoshinobu Inoue /* ip6->ip6_plen will be set later */ 226482cd038dSYoshinobu Inoue ip6->ip6_nxt = IPPROTO_ICMPV6; 226582cd038dSYoshinobu Inoue ip6->ip6_hlim = 255; 226682cd038dSYoshinobu Inoue /* ip6->ip6_src must be linklocal addr for my outgoing if. */ 226782cd038dSYoshinobu Inoue bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); 226882cd038dSYoshinobu Inoue bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); 226982cd038dSYoshinobu Inoue 227082cd038dSYoshinobu Inoue /* ND Redirect */ 227182cd038dSYoshinobu Inoue nd_rd = (struct nd_redirect *)(ip6 + 1); 227282cd038dSYoshinobu Inoue nd_rd->nd_rd_type = ND_REDIRECT; 227382cd038dSYoshinobu Inoue nd_rd->nd_rd_code = 0; 227482cd038dSYoshinobu Inoue nd_rd->nd_rd_reserved = 0; 227582cd038dSYoshinobu Inoue if (rt->rt_flags & RTF_GATEWAY) { 227682cd038dSYoshinobu Inoue /* 227782cd038dSYoshinobu Inoue * nd_rd->nd_rd_target must be a link-local address in 227882cd038dSYoshinobu Inoue * better router cases. 227982cd038dSYoshinobu Inoue */ 228082cd038dSYoshinobu Inoue if (!router_ll6) 228182cd038dSYoshinobu Inoue goto fail; 228282cd038dSYoshinobu Inoue bcopy(router_ll6, &nd_rd->nd_rd_target, 228382cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_target)); 228482cd038dSYoshinobu Inoue bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, 228582cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_dst)); 228682cd038dSYoshinobu Inoue } else { 228782cd038dSYoshinobu Inoue /* make sure redtgt == reddst */ 228882cd038dSYoshinobu Inoue bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, 228982cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_target)); 229082cd038dSYoshinobu Inoue bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, 229182cd038dSYoshinobu Inoue sizeof(nd_rd->nd_rd_dst)); 229282cd038dSYoshinobu Inoue } 229382cd038dSYoshinobu Inoue 229482cd038dSYoshinobu Inoue p = (u_char *)(nd_rd + 1); 229582cd038dSYoshinobu Inoue 229682cd038dSYoshinobu Inoue if (!router_ll6) 229782cd038dSYoshinobu Inoue goto nolladdropt; 229882cd038dSYoshinobu Inoue 229982cd038dSYoshinobu Inoue { 230082cd038dSYoshinobu Inoue /* target lladdr option */ 230182cd038dSYoshinobu Inoue struct rtentry *rt_router = NULL; 230282cd038dSYoshinobu Inoue int len; 230382cd038dSYoshinobu Inoue struct sockaddr_dl *sdl; 230482cd038dSYoshinobu Inoue struct nd_opt_hdr *nd_opt; 230582cd038dSYoshinobu Inoue char *lladdr; 230682cd038dSYoshinobu Inoue 230782cd038dSYoshinobu Inoue rt_router = nd6_lookup(router_ll6, 0, ifp); 230882cd038dSYoshinobu Inoue if (!rt_router) 230982cd038dSYoshinobu Inoue goto nolladdropt; 2310686cdd19SJun-ichiro itojun Hagino len = sizeof(*nd_opt) + ifp->if_addrlen; 2311686cdd19SJun-ichiro itojun Hagino len = (len + 7) & ~7; /*round by 8*/ 2312686cdd19SJun-ichiro itojun Hagino /* safety check */ 2313686cdd19SJun-ichiro itojun Hagino if (len + (p - (u_char *)ip6) > maxlen) 2314686cdd19SJun-ichiro itojun Hagino goto nolladdropt; 2315686cdd19SJun-ichiro itojun Hagino if (!(rt_router->rt_flags & RTF_GATEWAY) && 2316686cdd19SJun-ichiro itojun Hagino (rt_router->rt_flags & RTF_LLINFO) && 2317686cdd19SJun-ichiro itojun Hagino (rt_router->rt_gateway->sa_family == AF_LINK) && 2318686cdd19SJun-ichiro itojun Hagino (sdl = (struct sockaddr_dl *)rt_router->rt_gateway) && 2319686cdd19SJun-ichiro itojun Hagino sdl->sdl_alen) { 232082cd038dSYoshinobu Inoue nd_opt = (struct nd_opt_hdr *)p; 232182cd038dSYoshinobu Inoue nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 232282cd038dSYoshinobu Inoue nd_opt->nd_opt_len = len >> 3; 232382cd038dSYoshinobu Inoue lladdr = (char *)(nd_opt + 1); 232482cd038dSYoshinobu Inoue bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); 2325686cdd19SJun-ichiro itojun Hagino p += len; 232682cd038dSYoshinobu Inoue } 232782cd038dSYoshinobu Inoue } 232882cd038dSYoshinobu Inoue nolladdropt:; 232982cd038dSYoshinobu Inoue 233082cd038dSYoshinobu Inoue m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; 233182cd038dSYoshinobu Inoue 233282cd038dSYoshinobu Inoue /* just to be safe */ 2333686cdd19SJun-ichiro itojun Hagino #ifdef M_DECRYPTED /*not openbsd*/ 233482cd038dSYoshinobu Inoue if (m0->m_flags & M_DECRYPTED) 233582cd038dSYoshinobu Inoue goto noredhdropt; 2336686cdd19SJun-ichiro itojun Hagino #endif 2337686cdd19SJun-ichiro itojun Hagino if (p - (u_char *)ip6 > maxlen) 2338686cdd19SJun-ichiro itojun Hagino goto noredhdropt; 233982cd038dSYoshinobu Inoue 234082cd038dSYoshinobu Inoue { 234182cd038dSYoshinobu Inoue /* redirected header option */ 234282cd038dSYoshinobu Inoue int len; 234382cd038dSYoshinobu Inoue struct nd_opt_rd_hdr *nd_opt_rh; 234482cd038dSYoshinobu Inoue 234582cd038dSYoshinobu Inoue /* 234682cd038dSYoshinobu Inoue * compute the maximum size for icmp6 redirect header option. 234782cd038dSYoshinobu Inoue * XXX room for auth header? 234882cd038dSYoshinobu Inoue */ 234982cd038dSYoshinobu Inoue len = maxlen - (p - (u_char *)ip6); 235082cd038dSYoshinobu Inoue len &= ~7; 235182cd038dSYoshinobu Inoue 235282cd038dSYoshinobu Inoue /* This is just for simplicity. */ 235382cd038dSYoshinobu Inoue if (m0->m_pkthdr.len != m0->m_len) { 235482cd038dSYoshinobu Inoue if (m0->m_next) { 235582cd038dSYoshinobu Inoue m_freem(m0->m_next); 235682cd038dSYoshinobu Inoue m0->m_next = NULL; 235782cd038dSYoshinobu Inoue } 235882cd038dSYoshinobu Inoue m0->m_pkthdr.len = m0->m_len; 235982cd038dSYoshinobu Inoue } 236082cd038dSYoshinobu Inoue 236182cd038dSYoshinobu Inoue /* 236282cd038dSYoshinobu Inoue * Redirected header option spec (RFC2461 4.6.3) talks nothing 236382cd038dSYoshinobu Inoue * about padding/truncate rule for the original IP packet. 236482cd038dSYoshinobu Inoue * From the discussion on IPv6imp in Feb 1999, the consensus was: 236582cd038dSYoshinobu Inoue * - "attach as much as possible" is the goal 236682cd038dSYoshinobu Inoue * - pad if not aligned (original size can be guessed by original 236782cd038dSYoshinobu Inoue * ip6 header) 236882cd038dSYoshinobu Inoue * Following code adds the padding if it is simple enough, 236982cd038dSYoshinobu Inoue * and truncates if not. 237082cd038dSYoshinobu Inoue */ 237182cd038dSYoshinobu Inoue if (m0->m_next || m0->m_pkthdr.len != m0->m_len) 237282cd038dSYoshinobu Inoue panic("assumption failed in %s:%d\n", __FILE__, __LINE__); 237382cd038dSYoshinobu Inoue 237482cd038dSYoshinobu Inoue if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { 237582cd038dSYoshinobu Inoue /* not enough room, truncate */ 237682cd038dSYoshinobu Inoue m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); 237782cd038dSYoshinobu Inoue } else { 237882cd038dSYoshinobu Inoue /* enough room, pad or truncate */ 237982cd038dSYoshinobu Inoue size_t extra; 238082cd038dSYoshinobu Inoue 238182cd038dSYoshinobu Inoue extra = m0->m_pkthdr.len % 8; 238282cd038dSYoshinobu Inoue if (extra) { 238382cd038dSYoshinobu Inoue /* pad if easy enough, truncate if not */ 238482cd038dSYoshinobu Inoue if (8 - extra <= M_TRAILINGSPACE(m0)) { 238582cd038dSYoshinobu Inoue /* pad */ 238682cd038dSYoshinobu Inoue m0->m_len += (8 - extra); 238782cd038dSYoshinobu Inoue m0->m_pkthdr.len += (8 - extra); 238882cd038dSYoshinobu Inoue } else { 238982cd038dSYoshinobu Inoue /* truncate */ 239082cd038dSYoshinobu Inoue m0->m_pkthdr.len -= extra; 239182cd038dSYoshinobu Inoue m0->m_len -= extra; 239282cd038dSYoshinobu Inoue } 239382cd038dSYoshinobu Inoue } 239482cd038dSYoshinobu Inoue len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); 239582cd038dSYoshinobu Inoue m0->m_pkthdr.len = m0->m_len = len - sizeof(*nd_opt_rh); 239682cd038dSYoshinobu Inoue } 239782cd038dSYoshinobu Inoue 239882cd038dSYoshinobu Inoue nd_opt_rh = (struct nd_opt_rd_hdr *)p; 239982cd038dSYoshinobu Inoue bzero(nd_opt_rh, sizeof(*nd_opt_rh)); 240082cd038dSYoshinobu Inoue nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; 240182cd038dSYoshinobu Inoue nd_opt_rh->nd_opt_rh_len = len >> 3; 240282cd038dSYoshinobu Inoue p += sizeof(*nd_opt_rh); 240382cd038dSYoshinobu Inoue m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; 240482cd038dSYoshinobu Inoue 240582cd038dSYoshinobu Inoue /* connect m0 to m */ 240682cd038dSYoshinobu Inoue m->m_next = m0; 240782cd038dSYoshinobu Inoue m->m_pkthdr.len = m->m_len + m0->m_len; 240882cd038dSYoshinobu Inoue } 240982cd038dSYoshinobu Inoue noredhdropt:; 241082cd038dSYoshinobu Inoue 241182cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src)) 241282cd038dSYoshinobu Inoue sip6->ip6_src.s6_addr16[1] = 0; 241382cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst)) 241482cd038dSYoshinobu Inoue sip6->ip6_dst.s6_addr16[1] = 0; 2415686cdd19SJun-ichiro itojun Hagino #if 0 2416686cdd19SJun-ichiro itojun Hagino if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) 2417686cdd19SJun-ichiro itojun Hagino ip6->ip6_src.s6_addr16[1] = 0; 2418686cdd19SJun-ichiro itojun Hagino if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) 2419686cdd19SJun-ichiro itojun Hagino ip6->ip6_dst.s6_addr16[1] = 0; 2420686cdd19SJun-ichiro itojun Hagino #endif 242182cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target)) 242282cd038dSYoshinobu Inoue nd_rd->nd_rd_target.s6_addr16[1] = 0; 242382cd038dSYoshinobu Inoue if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst)) 242482cd038dSYoshinobu Inoue nd_rd->nd_rd_dst.s6_addr16[1] = 0; 242582cd038dSYoshinobu Inoue 242682cd038dSYoshinobu Inoue ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 242782cd038dSYoshinobu Inoue 242882cd038dSYoshinobu Inoue nd_rd->nd_rd_cksum = 0; 242982cd038dSYoshinobu Inoue nd_rd->nd_rd_cksum 243082cd038dSYoshinobu Inoue = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), ntohs(ip6->ip6_plen)); 243182cd038dSYoshinobu Inoue 243282cd038dSYoshinobu Inoue /* send the packet to outside... */ 2433686cdd19SJun-ichiro itojun Hagino #ifdef IPSEC 2434686cdd19SJun-ichiro itojun Hagino /* Don't lookup socket */ 2435686cdd19SJun-ichiro itojun Hagino ipsec_setsocket(m, NULL); 2436686cdd19SJun-ichiro itojun Hagino #endif /*IPSEC*/ 243782cd038dSYoshinobu Inoue ip6_output(m, NULL, NULL, 0, NULL, &outif); 243882cd038dSYoshinobu Inoue if (outif) { 243982cd038dSYoshinobu Inoue icmp6_ifstat_inc(outif, ifs6_out_msg); 244082cd038dSYoshinobu Inoue icmp6_ifstat_inc(outif, ifs6_out_redirect); 244182cd038dSYoshinobu Inoue } 244282cd038dSYoshinobu Inoue icmp6stat.icp6s_outhist[ND_REDIRECT]++; 244382cd038dSYoshinobu Inoue 244482cd038dSYoshinobu Inoue return; 244582cd038dSYoshinobu Inoue 244682cd038dSYoshinobu Inoue fail: 244782cd038dSYoshinobu Inoue if (m) 244882cd038dSYoshinobu Inoue m_freem(m); 244982cd038dSYoshinobu Inoue if (m0) 245082cd038dSYoshinobu Inoue m_freem(m0); 245182cd038dSYoshinobu Inoue } 245282cd038dSYoshinobu Inoue 245382cd038dSYoshinobu Inoue /* 245482cd038dSYoshinobu Inoue * ICMPv6 socket option processing. 2455686cdd19SJun-ichiro itojun Hagino * 2456686cdd19SJun-ichiro itojun Hagino * NOTE: for OSes that use NRL inpcb (bsdi4/openbsd), do not forget to modify 2457686cdd19SJun-ichiro itojun Hagino * sys/netinet6/raw_ipv6.c:rip6_ctloutput(). 245882cd038dSYoshinobu Inoue */ 245982cd038dSYoshinobu Inoue int 246082cd038dSYoshinobu Inoue icmp6_ctloutput(so, sopt) 246182cd038dSYoshinobu Inoue struct socket *so; 246282cd038dSYoshinobu Inoue struct sockopt *sopt; 246382cd038dSYoshinobu Inoue { 246482cd038dSYoshinobu Inoue int error = 0; 246582cd038dSYoshinobu Inoue int optlen; 246682cd038dSYoshinobu Inoue register struct inpcb *inp = sotoinpcb(so); 246782cd038dSYoshinobu Inoue int level, op, optname; 246882cd038dSYoshinobu Inoue 246982cd038dSYoshinobu Inoue if (sopt) { 247082cd038dSYoshinobu Inoue level = sopt->sopt_level; 247182cd038dSYoshinobu Inoue op = sopt->sopt_dir; 247282cd038dSYoshinobu Inoue optname = sopt->sopt_name; 247382cd038dSYoshinobu Inoue optlen = sopt->sopt_valsize; 247482cd038dSYoshinobu Inoue } else 247582cd038dSYoshinobu Inoue level = op = optname = optlen = 0; 247682cd038dSYoshinobu Inoue if (level != IPPROTO_ICMPV6) { 247782cd038dSYoshinobu Inoue return EINVAL; 247882cd038dSYoshinobu Inoue } 247982cd038dSYoshinobu Inoue 248082cd038dSYoshinobu Inoue switch(op) { 248182cd038dSYoshinobu Inoue case PRCO_SETOPT: 248282cd038dSYoshinobu Inoue switch (optname) { 248382cd038dSYoshinobu Inoue case ICMP6_FILTER: 248482cd038dSYoshinobu Inoue { 248582cd038dSYoshinobu Inoue struct icmp6_filter *p; 248682cd038dSYoshinobu Inoue 248782cd038dSYoshinobu Inoue if (optlen != sizeof(*p)) { 248882cd038dSYoshinobu Inoue error = EMSGSIZE; 248982cd038dSYoshinobu Inoue break; 249082cd038dSYoshinobu Inoue } 249182cd038dSYoshinobu Inoue if (inp->in6p_icmp6filt == NULL) { 249282cd038dSYoshinobu Inoue error = EINVAL; 249382cd038dSYoshinobu Inoue break; 249482cd038dSYoshinobu Inoue } 249582cd038dSYoshinobu Inoue error = sooptcopyin(sopt, inp->in6p_icmp6filt, optlen, 249682cd038dSYoshinobu Inoue optlen); 249782cd038dSYoshinobu Inoue break; 249882cd038dSYoshinobu Inoue } 249982cd038dSYoshinobu Inoue 250082cd038dSYoshinobu Inoue default: 250182cd038dSYoshinobu Inoue error = ENOPROTOOPT; 250282cd038dSYoshinobu Inoue break; 250382cd038dSYoshinobu Inoue } 250482cd038dSYoshinobu Inoue break; 250582cd038dSYoshinobu Inoue 250682cd038dSYoshinobu Inoue case PRCO_GETOPT: 250782cd038dSYoshinobu Inoue switch (optname) { 250882cd038dSYoshinobu Inoue case ICMP6_FILTER: 250982cd038dSYoshinobu Inoue { 251082cd038dSYoshinobu Inoue if (inp->in6p_icmp6filt == NULL) { 251182cd038dSYoshinobu Inoue error = EINVAL; 251282cd038dSYoshinobu Inoue break; 251382cd038dSYoshinobu Inoue } 251482cd038dSYoshinobu Inoue error = sooptcopyout(sopt, inp->in6p_icmp6filt, 251582cd038dSYoshinobu Inoue sizeof(struct icmp6_filter)); 251682cd038dSYoshinobu Inoue break; 251782cd038dSYoshinobu Inoue } 251882cd038dSYoshinobu Inoue 251982cd038dSYoshinobu Inoue default: 252082cd038dSYoshinobu Inoue error = ENOPROTOOPT; 252182cd038dSYoshinobu Inoue break; 252282cd038dSYoshinobu Inoue } 252382cd038dSYoshinobu Inoue break; 252482cd038dSYoshinobu Inoue } 252582cd038dSYoshinobu Inoue 252682cd038dSYoshinobu Inoue return(error); 252782cd038dSYoshinobu Inoue } 252882cd038dSYoshinobu Inoue 2529686cdd19SJun-ichiro itojun Hagino #ifndef HAVE_RATECHECK 2530686cdd19SJun-ichiro itojun Hagino /* 2531686cdd19SJun-ichiro itojun Hagino * ratecheck() returns true if it is okay to send. We return 2532686cdd19SJun-ichiro itojun Hagino * true if it is not okay to send. 2533686cdd19SJun-ichiro itojun Hagino */ 2534686cdd19SJun-ichiro itojun Hagino static int 2535686cdd19SJun-ichiro itojun Hagino ratecheck(last, limit) 2536686cdd19SJun-ichiro itojun Hagino struct timeval *last; 2537686cdd19SJun-ichiro itojun Hagino struct timeval *limit; 2538686cdd19SJun-ichiro itojun Hagino { 2539686cdd19SJun-ichiro itojun Hagino struct timeval tp; 2540686cdd19SJun-ichiro itojun Hagino struct timeval nextsend; 2541686cdd19SJun-ichiro itojun Hagino 2542686cdd19SJun-ichiro itojun Hagino microtime(&tp); 2543686cdd19SJun-ichiro itojun Hagino tp.tv_sec = time_second; 2544686cdd19SJun-ichiro itojun Hagino 2545686cdd19SJun-ichiro itojun Hagino /* rate limit */ 2546686cdd19SJun-ichiro itojun Hagino if (last->tv_sec != 0 || last->tv_usec != 0) { 2547686cdd19SJun-ichiro itojun Hagino nextsend.tv_sec = last->tv_sec + limit->tv_sec; 2548686cdd19SJun-ichiro itojun Hagino nextsend.tv_usec = last->tv_usec + limit->tv_usec; 2549686cdd19SJun-ichiro itojun Hagino nextsend.tv_sec += (nextsend.tv_usec / 1000000); 2550686cdd19SJun-ichiro itojun Hagino nextsend.tv_usec %= 1000000; 2551686cdd19SJun-ichiro itojun Hagino 2552686cdd19SJun-ichiro itojun Hagino if (nextsend.tv_sec == tp.tv_sec && nextsend.tv_usec <= tp.tv_usec) 2553686cdd19SJun-ichiro itojun Hagino ; 2554686cdd19SJun-ichiro itojun Hagino else if (nextsend.tv_sec <= tp.tv_sec) 2555686cdd19SJun-ichiro itojun Hagino ; 2556686cdd19SJun-ichiro itojun Hagino else { 2557686cdd19SJun-ichiro itojun Hagino /* The packet is subject to rate limit */ 2558686cdd19SJun-ichiro itojun Hagino return 0; 2559686cdd19SJun-ichiro itojun Hagino } 2560686cdd19SJun-ichiro itojun Hagino } 2561686cdd19SJun-ichiro itojun Hagino 2562686cdd19SJun-ichiro itojun Hagino *last = tp; 2563686cdd19SJun-ichiro itojun Hagino return 1; 2564686cdd19SJun-ichiro itojun Hagino } 2565686cdd19SJun-ichiro itojun Hagino #endif 2566686cdd19SJun-ichiro itojun Hagino 256782cd038dSYoshinobu Inoue /* 256882cd038dSYoshinobu Inoue * Perform rate limit check. 256982cd038dSYoshinobu Inoue * Returns 0 if it is okay to send the icmp6 packet. 257082cd038dSYoshinobu Inoue * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate 257182cd038dSYoshinobu Inoue * limitation. 257282cd038dSYoshinobu Inoue * 2573686cdd19SJun-ichiro itojun Hagino * There are two limitations defined: 2574686cdd19SJun-ichiro itojun Hagino * - pps limit: ICMPv6 error packet cannot exceed defined packet-per-second. 2575686cdd19SJun-ichiro itojun Hagino * we measure it every 0.2 second, since fasttimo works every 0.2 second. 2576686cdd19SJun-ichiro itojun Hagino * - rate limit: ICMPv6 error packet cannot appear more than once per 2577686cdd19SJun-ichiro itojun Hagino * defined interval. 2578686cdd19SJun-ichiro itojun Hagino * In any case, if we perform rate limitation, we'll see jitter in the ICMPv6 2579686cdd19SJun-ichiro itojun Hagino * error packets. 2580686cdd19SJun-ichiro itojun Hagino * 258182cd038dSYoshinobu Inoue * XXX per-destination/type check necessary? 258282cd038dSYoshinobu Inoue */ 258382cd038dSYoshinobu Inoue static int 258482cd038dSYoshinobu Inoue icmp6_ratelimit(dst, type, code) 258582cd038dSYoshinobu Inoue const struct in6_addr *dst; /* not used at this moment */ 258682cd038dSYoshinobu Inoue const int type; /* not used at this moment */ 258782cd038dSYoshinobu Inoue const int code; /* not used at this moment */ 258882cd038dSYoshinobu Inoue { 2589686cdd19SJun-ichiro itojun Hagino int ret; 259082cd038dSYoshinobu Inoue 2591686cdd19SJun-ichiro itojun Hagino ret = 0; /*okay to send*/ 259282cd038dSYoshinobu Inoue 2593686cdd19SJun-ichiro itojun Hagino /* PPS limit */ 2594686cdd19SJun-ichiro itojun Hagino icmp6errpps_count++; 2595686cdd19SJun-ichiro itojun Hagino if (icmp6errppslim && icmp6errpps_count > icmp6errppslim / 5) { 2596686cdd19SJun-ichiro itojun Hagino /* The packet is subject to pps limit */ 2597686cdd19SJun-ichiro itojun Hagino ret++; 2598686cdd19SJun-ichiro itojun Hagino } 2599686cdd19SJun-ichiro itojun Hagino 2600686cdd19SJun-ichiro itojun Hagino if (!ratecheck(&icmp6errratelim_last, &icmp6errratelim)) { 260182cd038dSYoshinobu Inoue /* The packet is subject to rate limit */ 2602686cdd19SJun-ichiro itojun Hagino ret++; 260382cd038dSYoshinobu Inoue } 260482cd038dSYoshinobu Inoue 2605686cdd19SJun-ichiro itojun Hagino return ret; 260682cd038dSYoshinobu Inoue } 2607