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