1*84d9c64aSbluhm /* $OpenBSD: icmp6.c,v 1.256 2025/01/03 21:27:40 bluhm Exp $ */ 2a53c329fSitojun /* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */ 3287546eaSitojun 4287546eaSitojun /* 5287546eaSitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6287546eaSitojun * All rights reserved. 7287546eaSitojun * 8287546eaSitojun * Redistribution and use in source and binary forms, with or without 9287546eaSitojun * modification, are permitted provided that the following conditions 10287546eaSitojun * are met: 11287546eaSitojun * 1. Redistributions of source code must retain the above copyright 12287546eaSitojun * notice, this list of conditions and the following disclaimer. 13287546eaSitojun * 2. Redistributions in binary form must reproduce the above copyright 14287546eaSitojun * notice, this list of conditions and the following disclaimer in the 15287546eaSitojun * documentation and/or other materials provided with the distribution. 16287546eaSitojun * 3. Neither the name of the project nor the names of its contributors 17287546eaSitojun * may be used to endorse or promote products derived from this software 18287546eaSitojun * without specific prior written permission. 19287546eaSitojun * 20287546eaSitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21287546eaSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22287546eaSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23287546eaSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24287546eaSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25287546eaSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26287546eaSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27287546eaSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28287546eaSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29287546eaSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30287546eaSitojun * SUCH DAMAGE. 31287546eaSitojun */ 32287546eaSitojun 33287546eaSitojun /* 34287546eaSitojun * Copyright (c) 1982, 1986, 1988, 1993 35287546eaSitojun * The Regents of the University of California. All rights reserved. 36287546eaSitojun * 37287546eaSitojun * Redistribution and use in source and binary forms, with or without 38287546eaSitojun * modification, are permitted provided that the following conditions 39287546eaSitojun * are met: 40287546eaSitojun * 1. Redistributions of source code must retain the above copyright 41287546eaSitojun * notice, this list of conditions and the following disclaimer. 42287546eaSitojun * 2. Redistributions in binary form must reproduce the above copyright 43287546eaSitojun * notice, this list of conditions and the following disclaimer in the 44287546eaSitojun * documentation and/or other materials provided with the distribution. 4529295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 46287546eaSitojun * may be used to endorse or promote products derived from this software 47287546eaSitojun * without specific prior written permission. 48287546eaSitojun * 49287546eaSitojun * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50287546eaSitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51287546eaSitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52287546eaSitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53287546eaSitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54287546eaSitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55287546eaSitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56287546eaSitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57287546eaSitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58287546eaSitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59287546eaSitojun * SUCH DAMAGE. 60287546eaSitojun * 61287546eaSitojun * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 62287546eaSitojun */ 63287546eaSitojun 64ca90c36bSmpf #include "carp.h" 65ba0d80aaShenning #include "pf.h" 66ca90c36bSmpf 67287546eaSitojun #include <sys/param.h> 68287546eaSitojun #include <sys/systm.h> 69287546eaSitojun #include <sys/malloc.h> 70287546eaSitojun #include <sys/mbuf.h> 71e6c6495dSderaadt #include <sys/sysctl.h> 72287546eaSitojun #include <sys/protosw.h> 73287546eaSitojun #include <sys/socket.h> 74287546eaSitojun #include <sys/socketvar.h> 75287546eaSitojun #include <sys/time.h> 76287546eaSitojun #include <sys/kernel.h> 77287546eaSitojun #include <sys/syslog.h> 78287546eaSitojun 79287546eaSitojun #include <net/if.h> 800deb6685Smpi #include <net/if_var.h> 81287546eaSitojun #include <net/route.h> 82287546eaSitojun #include <net/if_dl.h> 83287546eaSitojun #include <net/if_types.h> 84287546eaSitojun 85287546eaSitojun #include <netinet/in.h> 86287546eaSitojun #include <netinet/ip.h> 87dc572864Sbluhm #include <netinet6/in6_var.h> 88fa86ee14Sitojun #include <netinet/ip6.h> 89287546eaSitojun #include <netinet6/ip6_var.h> 90fa86ee14Sitojun #include <netinet/icmp6.h> 91287546eaSitojun #include <netinet6/mld6_var.h> 92287546eaSitojun #include <netinet/in_pcb.h> 93287546eaSitojun #include <netinet6/nd6.h> 94287546eaSitojun #include <netinet6/ip6protosw.h> 95287546eaSitojun 96ca90c36bSmpf #if NCARP > 0 97ca90c36bSmpf #include <netinet/ip_carp.h> 98ca90c36bSmpf #endif 99287546eaSitojun 100ba0d80aaShenning #if NPF > 0 101ba0d80aaShenning #include <net/pfvar.h> 102ba0d80aaShenning #endif 103ba0d80aaShenning 10407ba5f7cSjca struct cpumem *icmp6counters; 105287546eaSitojun 106f6e55599Sitojun extern int icmp6errppslim; 107f6e55599Sitojun static int icmp6errpps_count = 0; 1081ad7b35bSitojun static struct timeval icmp6errppslim_last; 1094d113176Sitojun 1104d113176Sitojun /* 1114d113176Sitojun * List of callbacks to notify when Path MTU changes are made. 1124d113176Sitojun */ 1134d113176Sitojun struct icmp6_mtudisc_callback { 1144d113176Sitojun LIST_ENTRY(icmp6_mtudisc_callback) mc_list; 115a8cd54d7Sphessler void (*mc_func)(struct sockaddr_in6 *, u_int); 1164d113176Sitojun }; 1174d113176Sitojun 1184d113176Sitojun LIST_HEAD(, icmp6_mtudisc_callback) icmp6_mtudisc_callbacks = 1197887b06bSguenther LIST_HEAD_INITIALIZER(icmp6_mtudisc_callbacks); 1204d113176Sitojun 1212028c273Sclaudio struct rttimer_queue icmp6_mtudisc_timeout_q; 122287546eaSitojun 1234d113176Sitojun /* XXX do these values make any sense? */ 12452166b8fSitojun static int icmp6_mtudisc_hiwat = 1280; 12552166b8fSitojun static int icmp6_mtudisc_lowat = 256; 12652166b8fSitojun 12752166b8fSitojun /* 12852166b8fSitojun * keep track of # of redirect routes. 12952166b8fSitojun */ 1302028c273Sclaudio struct rttimer_queue icmp6_redirect_timeout_q; 13152166b8fSitojun 13207ba5f7cSjca void icmp6_errcount(int, int); 133454f069bSjsing int icmp6_ratelimit(const struct in6_addr *, const int, const int); 134454f069bSjsing const char *icmp6_redirect_diag(struct in6_addr *, struct in6_addr *, 135454f069bSjsing struct in6_addr *); 136454f069bSjsing int icmp6_notify_error(struct mbuf *, int, int, int); 13780eefb13Sclaudio void icmp6_mtudisc_timeout(struct rtentry *, u_int); 138287546eaSitojun 139287546eaSitojun void 140454f069bSjsing icmp6_init(void) 141287546eaSitojun { 142287546eaSitojun mld6_init(); 1432028c273Sclaudio rt_timer_queue_init(&icmp6_mtudisc_timeout_q, ip6_mtudisc_timeout, 14438f7d33dSclaudio &icmp6_mtudisc_timeout); 1452028c273Sclaudio rt_timer_queue_init(&icmp6_redirect_timeout_q, icmp6_redirtimeout, 14638f7d33dSclaudio NULL); 14707ba5f7cSjca icmp6counters = counters_alloc(icp6s_ncounters); 148287546eaSitojun } 149287546eaSitojun 150454f069bSjsing void 15107ba5f7cSjca icmp6_errcount(int type, int code) 152f6e55599Sitojun { 153d7cc7a51Sjca enum icmp6stat_counters c = icp6s_ounknown; 154d7cc7a51Sjca 155f6e55599Sitojun switch (type) { 156f6e55599Sitojun case ICMP6_DST_UNREACH: 157f6e55599Sitojun switch (code) { 158f6e55599Sitojun case ICMP6_DST_UNREACH_NOROUTE: 159d7cc7a51Sjca c = icp6s_odst_unreach_noroute; 160d7cc7a51Sjca break; 161f6e55599Sitojun case ICMP6_DST_UNREACH_ADMIN: 162d7cc7a51Sjca c = icp6s_odst_unreach_admin; 163d7cc7a51Sjca break; 164f6e55599Sitojun case ICMP6_DST_UNREACH_BEYONDSCOPE: 165d7cc7a51Sjca c = icp6s_odst_unreach_beyondscope; 166d7cc7a51Sjca break; 167f6e55599Sitojun case ICMP6_DST_UNREACH_ADDR: 168d7cc7a51Sjca c = icp6s_odst_unreach_addr; 169d7cc7a51Sjca break; 170f6e55599Sitojun case ICMP6_DST_UNREACH_NOPORT: 171d7cc7a51Sjca c = icp6s_odst_unreach_noport; 172d7cc7a51Sjca break; 173f6e55599Sitojun } 174f6e55599Sitojun break; 175f6e55599Sitojun case ICMP6_PACKET_TOO_BIG: 176d7cc7a51Sjca c = icp6s_opacket_too_big; 177d7cc7a51Sjca break; 178f6e55599Sitojun case ICMP6_TIME_EXCEEDED: 179f6e55599Sitojun switch (code) { 180f6e55599Sitojun case ICMP6_TIME_EXCEED_TRANSIT: 181d7cc7a51Sjca c = icp6s_otime_exceed_transit; 182d7cc7a51Sjca break; 183f6e55599Sitojun case ICMP6_TIME_EXCEED_REASSEMBLY: 184d7cc7a51Sjca c = icp6s_otime_exceed_reassembly; 185d7cc7a51Sjca break; 186f6e55599Sitojun } 187f6e55599Sitojun break; 188f6e55599Sitojun case ICMP6_PARAM_PROB: 189f6e55599Sitojun switch (code) { 190f6e55599Sitojun case ICMP6_PARAMPROB_HEADER: 191d7cc7a51Sjca c = icp6s_oparamprob_header; 192d7cc7a51Sjca break; 193f6e55599Sitojun case ICMP6_PARAMPROB_NEXTHEADER: 194d7cc7a51Sjca c = icp6s_oparamprob_nextheader; 195d7cc7a51Sjca break; 196f6e55599Sitojun case ICMP6_PARAMPROB_OPTION: 197d7cc7a51Sjca c = icp6s_oparamprob_option; 198d7cc7a51Sjca break; 199f6e55599Sitojun } 200f6e55599Sitojun break; 201f6e55599Sitojun case ND_REDIRECT: 202d7cc7a51Sjca c = icp6s_oredirect; 203d7cc7a51Sjca break; 204f6e55599Sitojun } 205d7cc7a51Sjca 206d7cc7a51Sjca icmp6stat_inc(c); 207f6e55599Sitojun } 208f6e55599Sitojun 209287546eaSitojun /* 2104d113176Sitojun * Register a Path MTU Discovery callback. 2114d113176Sitojun */ 2124d113176Sitojun void 213a8cd54d7Sphessler icmp6_mtudisc_callback_register(void (*func)(struct sockaddr_in6 *, u_int)) 2144d113176Sitojun { 2154d113176Sitojun struct icmp6_mtudisc_callback *mc; 2164d113176Sitojun 217db445dd7Sbluhm LIST_FOREACH(mc, &icmp6_mtudisc_callbacks, mc_list) { 2184d113176Sitojun if (mc->mc_func == func) 2194d113176Sitojun return; 2204d113176Sitojun } 2214d113176Sitojun 2224d113176Sitojun mc = malloc(sizeof(*mc), M_PCB, M_NOWAIT); 2234d113176Sitojun if (mc == NULL) 224ab0ea4a9Snayden panic("%s", __func__); 2254d113176Sitojun 2264d113176Sitojun mc->mc_func = func; 2274d113176Sitojun LIST_INSERT_HEAD(&icmp6_mtudisc_callbacks, mc, mc_list); 2284d113176Sitojun } 2294d113176Sitojun 23016bb085cSdenis struct mbuf * 23116bb085cSdenis icmp6_do_error(struct mbuf *m, int type, int code, int param) 232287546eaSitojun { 233287546eaSitojun struct ip6_hdr *oip6, *nip6; 234287546eaSitojun struct icmp6_hdr *icmp6; 235f4f4d166Sitojun u_int preplen; 236287546eaSitojun int off; 23770b0445bSitojun int nxt; 238287546eaSitojun 23907ba5f7cSjca icmp6stat_inc(icp6s_error); 240287546eaSitojun 241f6e55599Sitojun /* count per-type-code statistics */ 24207ba5f7cSjca icmp6_errcount(type, code); 243f6e55599Sitojun 244287546eaSitojun if (m->m_len < sizeof(struct ip6_hdr)) { 245287546eaSitojun m = m_pullup(m, sizeof(struct ip6_hdr)); 246287546eaSitojun if (m == NULL) 24716bb085cSdenis return (NULL); 248287546eaSitojun } 249287546eaSitojun oip6 = mtod(m, struct ip6_hdr *); 250287546eaSitojun 251287546eaSitojun /* 25234deef1eSitojun * If the destination address of the erroneous packet is a multicast 25334deef1eSitojun * address, or the packet was sent using link-layer multicast, 25434deef1eSitojun * we should basically suppress sending an error (RFC 2463, Section 25534deef1eSitojun * 2.4). 25634deef1eSitojun * We have two exceptions (the item e.2 in that section): 2574bf5f3fdSjasper * - the Packet Too Big message can be sent for path MTU discovery. 25834deef1eSitojun * - the Parameter Problem Message that can be allowed an icmp6 error 25934deef1eSitojun * in the option type field. This check has been done in 26034deef1eSitojun * ip6_unknown_opt(), so we can just check the type and code. 261287546eaSitojun */ 262287546eaSitojun if ((m->m_flags & (M_BCAST|M_MCAST) || 263287546eaSitojun IN6_IS_ADDR_MULTICAST(&oip6->ip6_dst)) && 264287546eaSitojun (type != ICMP6_PACKET_TOO_BIG && 265287546eaSitojun (type != ICMP6_PARAM_PROB || 266287546eaSitojun code != ICMP6_PARAMPROB_OPTION))) 267287546eaSitojun goto freeit; 268287546eaSitojun 26934deef1eSitojun /* 27034deef1eSitojun * RFC 2463, 2.4 (e.5): source address check. 27134deef1eSitojun * XXX: the case of anycast source? 27234deef1eSitojun */ 273287546eaSitojun if (IN6_IS_ADDR_UNSPECIFIED(&oip6->ip6_src) || 274287546eaSitojun IN6_IS_ADDR_MULTICAST(&oip6->ip6_src)) 275287546eaSitojun goto freeit; 276287546eaSitojun 277287546eaSitojun /* 27870b0445bSitojun * If we are about to send ICMPv6 against ICMPv6 error/redirect, 27970b0445bSitojun * don't do it. 280287546eaSitojun */ 28170b0445bSitojun nxt = -1; 2822be6c224Sitojun off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 28370b0445bSitojun if (off >= 0 && nxt == IPPROTO_ICMPV6) { 284287546eaSitojun struct icmp6_hdr *icp; 285287546eaSitojun 286287546eaSitojun IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off, 287287546eaSitojun sizeof(*icp)); 288287546eaSitojun if (icp == NULL) { 28907ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 29016bb085cSdenis return (NULL); 291287546eaSitojun } 29270b0445bSitojun if (icp->icmp6_type < ICMP6_ECHO_REQUEST || 29370b0445bSitojun icp->icmp6_type == ND_REDIRECT) { 294287546eaSitojun /* 295287546eaSitojun * ICMPv6 error 296287546eaSitojun * Special case: for redirect (which is 297287546eaSitojun * informational) we must not send icmp6 error. 298287546eaSitojun */ 29907ba5f7cSjca icmp6stat_inc(icp6s_canterror); 300287546eaSitojun goto freeit; 301287546eaSitojun } else { 30270b0445bSitojun /* ICMPv6 informational - send the error */ 303287546eaSitojun } 30434deef1eSitojun } 30534deef1eSitojun else { 30670b0445bSitojun /* non-ICMPv6 - send the error */ 307287546eaSitojun } 308287546eaSitojun 309287546eaSitojun oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ 310287546eaSitojun 311287546eaSitojun /* Finally, do rate limitation check. */ 312287546eaSitojun if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { 31307ba5f7cSjca icmp6stat_inc(icp6s_toofreq); 314287546eaSitojun goto freeit; 315287546eaSitojun } 316287546eaSitojun 317287546eaSitojun /* 318287546eaSitojun * OK, ICMP6 can be generated. 319287546eaSitojun */ 320287546eaSitojun 321287546eaSitojun if (m->m_pkthdr.len >= ICMPV6_PLD_MAXLEN) 322287546eaSitojun m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); 323287546eaSitojun 324f4f4d166Sitojun preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 325f4f4d166Sitojun M_PREPEND(m, preplen, M_DONTWAIT); 326f4f4d166Sitojun if (m && m->m_len < preplen) 327f4f4d166Sitojun m = m_pullup(m, preplen); 328287546eaSitojun if (m == NULL) { 329b79da24aSitojun nd6log((LOG_DEBUG, "ENOBUFS in icmp6_error %d\n", __LINE__)); 33016bb085cSdenis return (NULL); 331287546eaSitojun } 332287546eaSitojun 333287546eaSitojun nip6 = mtod(m, struct ip6_hdr *); 334287546eaSitojun nip6->ip6_src = oip6->ip6_src; 335287546eaSitojun nip6->ip6_dst = oip6->ip6_dst; 336287546eaSitojun 337d4070096Sitojun if (IN6_IS_SCOPE_EMBED(&oip6->ip6_src)) 338287546eaSitojun oip6->ip6_src.s6_addr16[1] = 0; 339d4070096Sitojun if (IN6_IS_SCOPE_EMBED(&oip6->ip6_dst)) 340287546eaSitojun oip6->ip6_dst.s6_addr16[1] = 0; 341287546eaSitojun 342287546eaSitojun icmp6 = (struct icmp6_hdr *)(nip6 + 1); 343287546eaSitojun icmp6->icmp6_type = type; 344287546eaSitojun icmp6->icmp6_code = code; 345287546eaSitojun icmp6->icmp6_pptr = htonl((u_int32_t)param); 346287546eaSitojun 347a78c7ca7Sitojun /* 348a78c7ca7Sitojun * icmp6_reflect() is designed to be in the input path. 349678831beSjsg * icmp6_error() can be called from both input and output path, 350a78c7ca7Sitojun * and if we are in output path rcvif could contain bogus value. 351fb492c37Smpi * clear m->m_pkthdr.ph_ifidx for safety, we should have enough 352fb492c37Smpi * scope information in ip header (nip6). 353a78c7ca7Sitojun */ 354fb492c37Smpi m->m_pkthdr.ph_ifidx = 0; 355a78c7ca7Sitojun 35607ba5f7cSjca icmp6stat_inc(icp6s_outhist + type); 35770b0445bSitojun 35816bb085cSdenis return (m); 35970b0445bSitojun 36070b0445bSitojun freeit: 36170b0445bSitojun /* 362678831beSjsg * If we can't tell whether or not we can generate ICMP6, free it. 36370b0445bSitojun */ 36416bb085cSdenis return (m_freem(m)); 36516bb085cSdenis } 36616bb085cSdenis 36716bb085cSdenis /* 36816bb085cSdenis * Generate an error packet of type error in response to bad IP6 packet. 36916bb085cSdenis */ 37016bb085cSdenis void 37116bb085cSdenis icmp6_error(struct mbuf *m, int type, int code, int param) 37216bb085cSdenis { 37316bb085cSdenis struct mbuf *n; 37416bb085cSdenis 37516bb085cSdenis n = icmp6_do_error(m, type, code, param); 37616bb085cSdenis if (n != NULL) { 37716bb085cSdenis /* header order: IPv6 - ICMPv6 */ 378bcb2a527Sbluhm if (!icmp6_reflect(&n, sizeof(struct ip6_hdr), NULL)) 37916bb085cSdenis ip6_send(n); 38016bb085cSdenis } 381287546eaSitojun } 382287546eaSitojun 383287546eaSitojun /* 384287546eaSitojun * Process a received ICMP6 message. 385287546eaSitojun */ 386287546eaSitojun int 387459fa0feSbluhm icmp6_input(struct mbuf **mp, int *offp, int proto, int af) 388287546eaSitojun { 3899444e888Sclaudio #if NCARP > 0 3903a2d9b45Smpi struct ifnet *ifp; 3919444e888Sclaudio #endif 392287546eaSitojun struct mbuf *m = *mp, *n; 393287546eaSitojun struct ip6_hdr *ip6, *nip6; 394287546eaSitojun struct icmp6_hdr *icmp6, *nicmp6; 395287546eaSitojun int off = *offp; 396bc5cfb1fSkn int icmp6len = m->m_pkthdr.len - off; 397287546eaSitojun int code, sum, noff; 398bbcf0337Smpi char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; 399287546eaSitojun 400287546eaSitojun /* 401287546eaSitojun * Locate icmp6 structure in mbuf, and check 402287546eaSitojun * that not corrupted and of at least minimum length 403287546eaSitojun */ 404287546eaSitojun 405287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 406287546eaSitojun if (icmp6len < sizeof(struct icmp6_hdr)) { 40707ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 408287546eaSitojun goto freeit; 409287546eaSitojun } 410287546eaSitojun 411287546eaSitojun /* 412287546eaSitojun * calculate the checksum 413287546eaSitojun */ 414287546eaSitojun IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, sizeof(*icmp6)); 415287546eaSitojun if (icmp6 == NULL) { 41607ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 417287546eaSitojun return IPPROTO_DONE; 418287546eaSitojun } 419287546eaSitojun code = icmp6->icmp6_code; 420287546eaSitojun 421287546eaSitojun if ((sum = in6_cksum(m, IPPROTO_ICMPV6, off, icmp6len)) != 0) { 422b79da24aSitojun nd6log((LOG_ERR, 423287546eaSitojun "ICMP6 checksum error(%d|%x) %s\n", 424bbcf0337Smpi icmp6->icmp6_type, sum, 425bbcf0337Smpi inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)))); 42607ba5f7cSjca icmp6stat_inc(icp6s_checksum); 427287546eaSitojun goto freeit; 428287546eaSitojun } 429287546eaSitojun 430d2d95ac1Sbluhm #if NPF > 0 431d2d95ac1Sbluhm if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) { 432d2d95ac1Sbluhm switch (icmp6->icmp6_type) { 433d2d95ac1Sbluhm /* 434d2d95ac1Sbluhm * These ICMP6 types map to other connections. They must be 435d2d95ac1Sbluhm * delivered to pr_ctlinput() also for diverted connections. 436d2d95ac1Sbluhm */ 437d2d95ac1Sbluhm case ICMP6_DST_UNREACH: 438d2d95ac1Sbluhm case ICMP6_PACKET_TOO_BIG: 439d2d95ac1Sbluhm case ICMP6_TIME_EXCEEDED: 440d2d95ac1Sbluhm case ICMP6_PARAM_PROB: 44117b8cf86Sbluhm /* 44217b8cf86Sbluhm * Do not use the divert-to property of the TCP or UDP 44317b8cf86Sbluhm * rule when doing the PCB lookup for the raw socket. 44417b8cf86Sbluhm */ 44517b8cf86Sbluhm m->m_pkthdr.pf.flags &=~ PF_TAG_DIVERTED; 446d2d95ac1Sbluhm break; 447d2d95ac1Sbluhm default: 448d2d95ac1Sbluhm goto raw; 449d2d95ac1Sbluhm } 450d2d95ac1Sbluhm } 451d2d95ac1Sbluhm #endif /* NPF */ 452d2d95ac1Sbluhm 453ca90c36bSmpf #if NCARP > 0 4549444e888Sclaudio ifp = if_get(m->m_pkthdr.ph_ifidx); 4559444e888Sclaudio if (ifp == NULL) 4569444e888Sclaudio goto freeit; 4579444e888Sclaudio 4583664055bSbluhm if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST && 4593664055bSbluhm carp_lsdrop(ifp, m, AF_INET6, ip6->ip6_src.s6_addr32, 46076dda2b0Sfriehm ip6->ip6_dst.s6_addr32, 1)) { 461b6b2f5e0Sjsg if_put(ifp); 462ca90c36bSmpf goto freeit; 463b6b2f5e0Sjsg } 4649444e888Sclaudio 4659444e888Sclaudio if_put(ifp); 466ca90c36bSmpf #endif 46707ba5f7cSjca icmp6stat_inc(icp6s_inhist + icmp6->icmp6_type); 468287546eaSitojun 469287546eaSitojun switch (icmp6->icmp6_type) { 470287546eaSitojun case ICMP6_DST_UNREACH: 471287546eaSitojun switch (code) { 472287546eaSitojun case ICMP6_DST_UNREACH_NOROUTE: 473287546eaSitojun code = PRC_UNREACH_NET; 474287546eaSitojun break; 475287546eaSitojun case ICMP6_DST_UNREACH_ADMIN: 476f4f4d166Sitojun code = PRC_UNREACH_PROTOCOL; /* is this a good code? */ 477287546eaSitojun break; 478f4f4d166Sitojun case ICMP6_DST_UNREACH_ADDR: 479f4f4d166Sitojun code = PRC_HOSTDEAD; 480f4f4d166Sitojun break; 481f4f4d166Sitojun case ICMP6_DST_UNREACH_BEYONDSCOPE: 482f4f4d166Sitojun /* I mean "source address was incorrect." */ 483f4f4d166Sitojun code = PRC_PARAMPROB; 484f4f4d166Sitojun break; 485287546eaSitojun case ICMP6_DST_UNREACH_NOPORT: 486287546eaSitojun code = PRC_UNREACH_PORT; 487287546eaSitojun break; 488287546eaSitojun default: 489287546eaSitojun goto badcode; 490287546eaSitojun } 491287546eaSitojun goto deliver; 492287546eaSitojun 493287546eaSitojun case ICMP6_PACKET_TOO_BIG: 4948418e62bSclaudio /* MTU is checked in icmp6_mtudisc_update. */ 495287546eaSitojun code = PRC_MSGSIZE; 496287546eaSitojun 497f4f4d166Sitojun /* 498f4f4d166Sitojun * Updating the path MTU will be done after examining 499f4f4d166Sitojun * intermediate extension headers. 500f4f4d166Sitojun */ 501287546eaSitojun goto deliver; 502287546eaSitojun 503287546eaSitojun case ICMP6_TIME_EXCEEDED: 504287546eaSitojun switch (code) { 505287546eaSitojun case ICMP6_TIME_EXCEED_TRANSIT: 50612df4ed1Sitojun code = PRC_TIMXCEED_INTRANS; 50712df4ed1Sitojun break; 508287546eaSitojun case ICMP6_TIME_EXCEED_REASSEMBLY: 50912df4ed1Sitojun code = PRC_TIMXCEED_REASS; 510287546eaSitojun break; 511287546eaSitojun default: 512287546eaSitojun goto badcode; 513287546eaSitojun } 514287546eaSitojun goto deliver; 515287546eaSitojun 516287546eaSitojun case ICMP6_PARAM_PROB: 517287546eaSitojun switch (code) { 518287546eaSitojun case ICMP6_PARAMPROB_NEXTHEADER: 519287546eaSitojun code = PRC_UNREACH_PROTOCOL; 520287546eaSitojun break; 521287546eaSitojun case ICMP6_PARAMPROB_HEADER: 522287546eaSitojun case ICMP6_PARAMPROB_OPTION: 523287546eaSitojun code = PRC_PARAMPROB; 524287546eaSitojun break; 525287546eaSitojun default: 526287546eaSitojun goto badcode; 527287546eaSitojun } 528287546eaSitojun goto deliver; 529287546eaSitojun 530287546eaSitojun case ICMP6_ECHO_REQUEST: 531287546eaSitojun if (code != 0) 532287546eaSitojun goto badcode; 53312df4ed1Sitojun /* 53412df4ed1Sitojun * Copy mbuf to send to two data paths: userland socket(s), 53512df4ed1Sitojun * and to the querier (echo reply). 53612df4ed1Sitojun * m: a copy for socket, n: a copy for querier 53712df4ed1Sitojun */ 53812df4ed1Sitojun if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 53912df4ed1Sitojun /* Give up local */ 54012df4ed1Sitojun n = m; 541eabae73bSbluhm m = *mp = NULL; 54212df4ed1Sitojun goto deliverecho; 543287546eaSitojun } 54412df4ed1Sitojun /* 54512df4ed1Sitojun * If the first mbuf is shared, or the first mbuf is too short, 54612df4ed1Sitojun * copy the first part of the data into a fresh mbuf. 54712df4ed1Sitojun * Otherwise, we will wrongly overwrite both copies. 54812df4ed1Sitojun */ 54912df4ed1Sitojun if ((n->m_flags & M_EXT) != 0 || 55012df4ed1Sitojun n->m_len < off + sizeof(struct icmp6_hdr)) { 551287546eaSitojun struct mbuf *n0 = n; 552f4f4d166Sitojun const int maxlen = sizeof(*nip6) + sizeof(*nicmp6); 553287546eaSitojun 554287546eaSitojun /* 555287546eaSitojun * Prepare an internal mbuf. m_pullup() doesn't 556287546eaSitojun * always copy the length we specified. 557287546eaSitojun */ 558f4f4d166Sitojun if (maxlen >= MCLBYTES) { 559f4f4d166Sitojun /* Give up remote */ 560f4f4d166Sitojun m_freem(n0); 561f4f4d166Sitojun break; 562f4f4d166Sitojun } 563287546eaSitojun MGETHDR(n, M_DONTWAIT, n0->m_type); 564f4f4d166Sitojun if (n && maxlen >= MHLEN) { 565f4f4d166Sitojun MCLGET(n, M_DONTWAIT); 566f4f4d166Sitojun if ((n->m_flags & M_EXT) == 0) { 567f4f4d166Sitojun m_free(n); 568f4f4d166Sitojun n = NULL; 569f4f4d166Sitojun } 570f4f4d166Sitojun } 571287546eaSitojun if (n == NULL) { 57212df4ed1Sitojun /* Give up local */ 573287546eaSitojun m_freem(n0); 57412df4ed1Sitojun n = m; 575eabae73bSbluhm m = *mp = NULL; 57612df4ed1Sitojun goto deliverecho; 577287546eaSitojun } 578406cff18Sangelos M_MOVE_PKTHDR(n, n0); 579287546eaSitojun /* 580287546eaSitojun * Copy IPv6 and ICMPv6 only. 581287546eaSitojun */ 582287546eaSitojun nip6 = mtod(n, struct ip6_hdr *); 583287546eaSitojun bcopy(ip6, nip6, sizeof(struct ip6_hdr)); 584287546eaSitojun nicmp6 = (struct icmp6_hdr *)(nip6 + 1); 585287546eaSitojun bcopy(icmp6, nicmp6, sizeof(struct icmp6_hdr)); 586287546eaSitojun noff = sizeof(struct ip6_hdr); 5871e2390d3Sitojun n->m_len = noff + sizeof(struct icmp6_hdr); 588287546eaSitojun /* 589287546eaSitojun * Adjust mbuf. ip6_plen will be adjusted in 590287546eaSitojun * ip6_output(). 5911e2390d3Sitojun * n->m_pkthdr.len == n0->m_pkthdr.len at this point. 592287546eaSitojun */ 5931e2390d3Sitojun n->m_pkthdr.len += noff + sizeof(struct icmp6_hdr); 5941e2390d3Sitojun n->m_pkthdr.len -= (off + sizeof(struct icmp6_hdr)); 595287546eaSitojun m_adj(n0, off + sizeof(struct icmp6_hdr)); 596287546eaSitojun n->m_next = n0; 597287546eaSitojun } else { 59812df4ed1Sitojun deliverecho: 599d00fca26Sitojun IP6_EXTHDR_GET(nicmp6, struct icmp6_hdr *, n, off, 600d00fca26Sitojun sizeof(*nicmp6)); 601287546eaSitojun noff = off; 602287546eaSitojun } 60381139384Srzalamena if (n) { 604287546eaSitojun nicmp6->icmp6_type = ICMP6_ECHO_REPLY; 605287546eaSitojun nicmp6->icmp6_code = 0; 60607ba5f7cSjca icmp6stat_inc(icp6s_reflect); 60707ba5f7cSjca icmp6stat_inc(icp6s_outhist + ICMP6_ECHO_REPLY); 608bcb2a527Sbluhm if (!icmp6_reflect(&n, noff, NULL)) 60916bb085cSdenis ip6_send(n); 610287546eaSitojun } 61112df4ed1Sitojun if (!m) 61212df4ed1Sitojun goto freeit; 613287546eaSitojun break; 614287546eaSitojun 615287546eaSitojun case ICMP6_ECHO_REPLY: 616287546eaSitojun if (code != 0) 617287546eaSitojun goto badcode; 618287546eaSitojun break; 619287546eaSitojun 62022a58e88Sitojun case MLD_LISTENER_QUERY: 62122a58e88Sitojun case MLD_LISTENER_REPORT: 62222a58e88Sitojun if (icmp6len < sizeof(struct mld_hdr)) 623287546eaSitojun goto badlen; 624f4f4d166Sitojun if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 625f4f4d166Sitojun /* give up local */ 626287546eaSitojun mld6_input(m, off); 627f4f4d166Sitojun m = NULL; 628f4f4d166Sitojun goto freeit; 629f4f4d166Sitojun } 630f4f4d166Sitojun mld6_input(n, off); 631287546eaSitojun /* m stays. */ 632287546eaSitojun break; 633287546eaSitojun 63422a58e88Sitojun case MLD_LISTENER_DONE: 63522a58e88Sitojun if (icmp6len < sizeof(struct mld_hdr)) /* necessary? */ 636287546eaSitojun goto badlen; 637287546eaSitojun break; /* nothing to be done in kernel */ 638287546eaSitojun 63922a58e88Sitojun case MLD_MTRACE_RESP: 64022a58e88Sitojun case MLD_MTRACE: 64134deef1eSitojun /* XXX: these two are experimental. not officially defined. */ 642287546eaSitojun /* XXX: per-interface statistics? */ 643f4f4d166Sitojun break; /* just pass it to applications */ 644287546eaSitojun 645287546eaSitojun case ICMP6_WRUREQUEST: /* ICMP6_FQDN_QUERY */ 64643f29087Sbenno /* IPv6 Node Information Queries are not supported */ 647afcb17fcSitojun break; 648287546eaSitojun case ICMP6_WRUREPLY: 649287546eaSitojun break; 650287546eaSitojun 651287546eaSitojun case ND_ROUTER_SOLICIT: 652287546eaSitojun case ND_ROUTER_ADVERT: 653287546eaSitojun if (code != 0) 654287546eaSitojun goto badcode; 65579e100a6Sflorian if ((icmp6->icmp6_type == ND_ROUTER_SOLICIT && icmp6len < 65679e100a6Sflorian sizeof(struct nd_router_solicit)) || 65779e100a6Sflorian (icmp6->icmp6_type == ND_ROUTER_ADVERT && icmp6len < 65879e100a6Sflorian sizeof(struct nd_router_advert))) 659287546eaSitojun goto badlen; 66079e100a6Sflorian 661f4f4d166Sitojun if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 662f4f4d166Sitojun /* give up local */ 66379e100a6Sflorian nd6_rtr_cache(m, off, icmp6len, 66479e100a6Sflorian icmp6->icmp6_type); 665f4f4d166Sitojun m = NULL; 666f4f4d166Sitojun goto freeit; 667f4f4d166Sitojun } 66879e100a6Sflorian nd6_rtr_cache(n, off, icmp6len, icmp6->icmp6_type); 669287546eaSitojun /* m stays. */ 670287546eaSitojun break; 671287546eaSitojun 672287546eaSitojun case ND_NEIGHBOR_SOLICIT: 673287546eaSitojun if (code != 0) 674287546eaSitojun goto badcode; 675287546eaSitojun if (icmp6len < sizeof(struct nd_neighbor_solicit)) 676287546eaSitojun goto badlen; 677f4f4d166Sitojun if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 678f4f4d166Sitojun /* give up local */ 679287546eaSitojun nd6_ns_input(m, off, icmp6len); 680f4f4d166Sitojun m = NULL; 681f4f4d166Sitojun goto freeit; 682f4f4d166Sitojun } 683f4f4d166Sitojun nd6_ns_input(n, off, icmp6len); 684287546eaSitojun /* m stays. */ 685287546eaSitojun break; 686287546eaSitojun 687287546eaSitojun case ND_NEIGHBOR_ADVERT: 688287546eaSitojun if (code != 0) 689287546eaSitojun goto badcode; 690287546eaSitojun if (icmp6len < sizeof(struct nd_neighbor_advert)) 691287546eaSitojun goto badlen; 692f4f4d166Sitojun if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 693f4f4d166Sitojun /* give up local */ 694287546eaSitojun nd6_na_input(m, off, icmp6len); 695f4f4d166Sitojun m = NULL; 696f4f4d166Sitojun goto freeit; 697f4f4d166Sitojun } 698f4f4d166Sitojun nd6_na_input(n, off, icmp6len); 699287546eaSitojun /* m stays. */ 700287546eaSitojun break; 701287546eaSitojun 702287546eaSitojun case ND_REDIRECT: 703287546eaSitojun if (code != 0) 704287546eaSitojun goto badcode; 705287546eaSitojun if (icmp6len < sizeof(struct nd_redirect)) 706287546eaSitojun goto badlen; 707f4f4d166Sitojun if ((n = m_copym(m, 0, M_COPYALL, M_DONTWAIT)) == NULL) { 708f4f4d166Sitojun /* give up local */ 709287546eaSitojun icmp6_redirect_input(m, off); 710f4f4d166Sitojun m = NULL; 711f4f4d166Sitojun goto freeit; 712f4f4d166Sitojun } 713f4f4d166Sitojun icmp6_redirect_input(n, off); 714287546eaSitojun /* m stays. */ 715287546eaSitojun break; 716287546eaSitojun 717287546eaSitojun case ICMP6_ROUTER_RENUMBERING: 718287546eaSitojun if (code != ICMP6_ROUTER_RENUMBERING_COMMAND && 719287546eaSitojun code != ICMP6_ROUTER_RENUMBERING_RESULT) 720287546eaSitojun goto badcode; 721287546eaSitojun if (icmp6len < sizeof(struct icmp6_router_renum)) 722287546eaSitojun goto badlen; 723287546eaSitojun break; 724287546eaSitojun 725287546eaSitojun default: 726b79da24aSitojun nd6log((LOG_DEBUG, 7279444e888Sclaudio "icmp6_input: unknown type %d(src=%s, dst=%s, ifid=%u)\n", 728bbcf0337Smpi icmp6->icmp6_type, 729bbcf0337Smpi inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)), 730bbcf0337Smpi inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst)), 7319444e888Sclaudio m->m_pkthdr.ph_ifidx)); 732287546eaSitojun if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST) { 733287546eaSitojun /* ICMPv6 error: MUST deliver it by spec... */ 734287546eaSitojun code = PRC_NCMDS; 735287546eaSitojun /* deliver */ 736287546eaSitojun } else { 737287546eaSitojun /* ICMPv6 informational: MUST not deliver */ 738287546eaSitojun break; 739287546eaSitojun } 740287546eaSitojun deliver: 7417ee01f6fSitojun if (icmp6_notify_error(m, off, icmp6len, code)) { 7427ee01f6fSitojun /* In this case, m should've been freed. */ 7437ee01f6fSitojun return (IPPROTO_DONE); 7447ee01f6fSitojun } 7457ee01f6fSitojun break; 7467ee01f6fSitojun 7477ee01f6fSitojun badcode: 74807ba5f7cSjca icmp6stat_inc(icp6s_badcode); 7497ee01f6fSitojun break; 7507ee01f6fSitojun 7517ee01f6fSitojun badlen: 75207ba5f7cSjca icmp6stat_inc(icp6s_badlen); 7537ee01f6fSitojun break; 7547ee01f6fSitojun } 7557ee01f6fSitojun 756d2d95ac1Sbluhm #if NPF > 0 757d2d95ac1Sbluhm raw: 758d2d95ac1Sbluhm #endif 7597ee01f6fSitojun /* deliver the packet to appropriate sockets */ 7602ed633ffSbluhm return rip6_input(mp, offp, proto, af); 7617ee01f6fSitojun 7627ee01f6fSitojun freeit: 7637ee01f6fSitojun m_freem(m); 7647ee01f6fSitojun return IPPROTO_DONE; 7657ee01f6fSitojun } 7667ee01f6fSitojun 767454f069bSjsing int 768ee37ea65Smcbride icmp6_notify_error(struct mbuf *m, int off, int icmp6len, int code) 7697ee01f6fSitojun { 7707ee01f6fSitojun struct icmp6_hdr *icmp6; 7717ee01f6fSitojun struct ip6_hdr *eip6; 7727ee01f6fSitojun u_int32_t notifymtu; 7737ee01f6fSitojun struct sockaddr_in6 icmp6src, icmp6dst; 7747ee01f6fSitojun 775287546eaSitojun if (icmp6len < sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr)) { 77607ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 777287546eaSitojun goto freeit; 778287546eaSitojun } 779287546eaSitojun IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, 780287546eaSitojun sizeof(*icmp6) + sizeof(struct ip6_hdr)); 781287546eaSitojun if (icmp6 == NULL) { 78207ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 7837ee01f6fSitojun return (-1); 784287546eaSitojun } 7857ee01f6fSitojun eip6 = (struct ip6_hdr *)(icmp6 + 1); 786287546eaSitojun 787287546eaSitojun /* Detect the upper level protocol */ 788287546eaSitojun { 7894da85d9aSbluhm void (*ctlfunc)(int, struct sockaddr *, u_int, void *); 790287546eaSitojun u_int8_t nxt = eip6->ip6_nxt; 791287546eaSitojun int eoff = off + sizeof(struct icmp6_hdr) + 792287546eaSitojun sizeof(struct ip6_hdr); 793287546eaSitojun struct ip6ctlparam ip6cp; 794f4f4d166Sitojun struct in6_addr *finaldst = NULL; 7957ee01f6fSitojun int icmp6type = icmp6->icmp6_type; 796f4f4d166Sitojun struct ip6_frag *fh; 797f4f4d166Sitojun struct ip6_rthdr *rth; 798f4f4d166Sitojun struct ip6_rthdr0 *rth0; 799f4f4d166Sitojun int rthlen; 800287546eaSitojun 80134deef1eSitojun while (1) { /* XXX: should avoid infinite loop explicitly? */ 802287546eaSitojun struct ip6_ext *eh; 803287546eaSitojun 804287546eaSitojun switch (nxt) { 805287546eaSitojun case IPPROTO_HOPOPTS: 806287546eaSitojun case IPPROTO_DSTOPTS: 807287546eaSitojun case IPPROTO_AH: 808287546eaSitojun IP6_EXTHDR_GET(eh, struct ip6_ext *, m, 809287546eaSitojun eoff, sizeof(*eh)); 810287546eaSitojun if (eh == NULL) { 81107ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 8127ee01f6fSitojun return (-1); 813287546eaSitojun } 814f4f4d166Sitojun 815287546eaSitojun if (nxt == IPPROTO_AH) 816287546eaSitojun eoff += (eh->ip6e_len + 2) << 2; 817287546eaSitojun else 818287546eaSitojun eoff += (eh->ip6e_len + 1) << 3; 819287546eaSitojun nxt = eh->ip6e_nxt; 820287546eaSitojun break; 821f4f4d166Sitojun case IPPROTO_ROUTING: 822f4f4d166Sitojun /* 823f4f4d166Sitojun * When the erroneous packet contains a 824f4f4d166Sitojun * routing header, we should examine the 825f4f4d166Sitojun * header to determine the final destination. 826f4f4d166Sitojun * Otherwise, we can't properly update 827f4f4d166Sitojun * information that depends on the final 828f4f4d166Sitojun * destination (e.g. path MTU). 829f4f4d166Sitojun */ 830f4f4d166Sitojun IP6_EXTHDR_GET(rth, struct ip6_rthdr *, m, 831f4f4d166Sitojun eoff, sizeof(*rth)); 832f4f4d166Sitojun if (rth == NULL) { 83307ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 8347ee01f6fSitojun return (-1); 835f4f4d166Sitojun } 836f4f4d166Sitojun rthlen = (rth->ip6r_len + 1) << 3; 837f4f4d166Sitojun /* 838f4f4d166Sitojun * XXX: currently there is no 839f4f4d166Sitojun * officially defined type other 840f4f4d166Sitojun * than type-0. 841f4f4d166Sitojun * Note that if the segment left field 842f4f4d166Sitojun * is 0, all intermediate hops must 843f4f4d166Sitojun * have been passed. 844f4f4d166Sitojun */ 845f4f4d166Sitojun if (rth->ip6r_segleft && 846f4f4d166Sitojun rth->ip6r_type == IPV6_RTHDR_TYPE_0) { 847f4f4d166Sitojun int hops; 848f4f4d166Sitojun 849f4f4d166Sitojun IP6_EXTHDR_GET(rth0, 850f4f4d166Sitojun struct ip6_rthdr0 *, m, 851f4f4d166Sitojun eoff, rthlen); 852f4f4d166Sitojun if (rth0 == NULL) { 85307ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 8547ee01f6fSitojun return (-1); 855f4f4d166Sitojun } 856f4f4d166Sitojun /* just ignore a bogus header */ 857f4f4d166Sitojun if ((rth0->ip6r0_len % 2) == 0 && 858f4f4d166Sitojun (hops = rth0->ip6r0_len/2)) 859f4f4d166Sitojun finaldst = (struct in6_addr *)(rth0 + 1) + (hops - 1); 860f4f4d166Sitojun } 861f4f4d166Sitojun eoff += rthlen; 862f4f4d166Sitojun nxt = rth->ip6r_nxt; 863f4f4d166Sitojun break; 864f4f4d166Sitojun case IPPROTO_FRAGMENT: 865f4f4d166Sitojun IP6_EXTHDR_GET(fh, struct ip6_frag *, m, 866f4f4d166Sitojun eoff, sizeof(*fh)); 867f4f4d166Sitojun if (fh == NULL) { 86807ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 8697ee01f6fSitojun return (-1); 870f4f4d166Sitojun } 871f4f4d166Sitojun /* 872f4f4d166Sitojun * Data after a fragment header is meaningless 873f4f4d166Sitojun * unless it is the first fragment, but 874f4f4d166Sitojun * we'll go to the notify label for path MTU 875f4f4d166Sitojun * discovery. 876f4f4d166Sitojun */ 877f4f4d166Sitojun if (fh->ip6f_offlg & IP6F_OFF_MASK) 878f4f4d166Sitojun goto notify; 879f4f4d166Sitojun 880f4f4d166Sitojun eoff += sizeof(struct ip6_frag); 881f4f4d166Sitojun nxt = fh->ip6f_nxt; 882f4f4d166Sitojun break; 883287546eaSitojun default: 884f4f4d166Sitojun /* 885f4f4d166Sitojun * This case includes ESP and the No Next 886f4f4d166Sitojun * Header. In such cases going to the notify 887f4f4d166Sitojun * label does not have any meaning 888f4f4d166Sitojun * (i.e. ctlfunc will be NULL), but we go 889f4f4d166Sitojun * anyway since we might have to update 890f4f4d166Sitojun * path MTU information. 891f4f4d166Sitojun */ 892287546eaSitojun goto notify; 893287546eaSitojun } 894287546eaSitojun } 895287546eaSitojun notify: 896287546eaSitojun IP6_EXTHDR_GET(icmp6, struct icmp6_hdr *, m, off, 897287546eaSitojun sizeof(*icmp6) + sizeof(struct ip6_hdr)); 898287546eaSitojun if (icmp6 == NULL) { 89907ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 9007ee01f6fSitojun return (-1); 901287546eaSitojun } 9027ee01f6fSitojun 9037ee01f6fSitojun eip6 = (struct ip6_hdr *)(icmp6 + 1); 9047ee01f6fSitojun bzero(&icmp6dst, sizeof(icmp6dst)); 9057ee01f6fSitojun icmp6dst.sin6_len = sizeof(struct sockaddr_in6); 9067ee01f6fSitojun icmp6dst.sin6_family = AF_INET6; 907f4f4d166Sitojun if (finaldst == NULL) 9087ee01f6fSitojun icmp6dst.sin6_addr = eip6->ip6_dst; 9097ee01f6fSitojun else 9107ee01f6fSitojun icmp6dst.sin6_addr = *finaldst; 9111a3ab866Smpi icmp6dst.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx, 9121a3ab866Smpi &icmp6dst.sin6_addr); 913952c6363Sbluhm if (in6_embedscope(&icmp6dst.sin6_addr, &icmp6dst, 914952c6363Sbluhm NULL, NULL)) { 915df8d9afdSjsg /* should be impossible */ 9167ee01f6fSitojun nd6log((LOG_DEBUG, 9177ee01f6fSitojun "icmp6_notify_error: in6_embedscope failed\n")); 9187ee01f6fSitojun goto freeit; 9197ee01f6fSitojun } 9207ee01f6fSitojun 9217ee01f6fSitojun /* 9227ee01f6fSitojun * retrieve parameters from the inner IPv6 header, and convert 9237ee01f6fSitojun * them into sockaddr structures. 9247ee01f6fSitojun */ 9257ee01f6fSitojun bzero(&icmp6src, sizeof(icmp6src)); 9267ee01f6fSitojun icmp6src.sin6_len = sizeof(struct sockaddr_in6); 9277ee01f6fSitojun icmp6src.sin6_family = AF_INET6; 9287ee01f6fSitojun icmp6src.sin6_addr = eip6->ip6_src; 9291a3ab866Smpi icmp6src.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx, 9301a3ab866Smpi &icmp6src.sin6_addr); 931952c6363Sbluhm if (in6_embedscope(&icmp6src.sin6_addr, &icmp6src, 932952c6363Sbluhm NULL, NULL)) { 933df8d9afdSjsg /* should be impossible */ 9347ee01f6fSitojun nd6log((LOG_DEBUG, 9357ee01f6fSitojun "icmp6_notify_error: in6_embedscope failed\n")); 9367ee01f6fSitojun goto freeit; 9377ee01f6fSitojun } 9387ee01f6fSitojun icmp6src.sin6_flowinfo = 9397ee01f6fSitojun (eip6->ip6_flow & IPV6_FLOWLABEL_MASK); 9407ee01f6fSitojun 9417ee01f6fSitojun if (finaldst == NULL) 9427ee01f6fSitojun finaldst = &eip6->ip6_dst; 9434d113176Sitojun ip6cp.ip6c_m = m; 9444d113176Sitojun ip6cp.ip6c_icmp6 = icmp6; 9454d113176Sitojun ip6cp.ip6c_ip6 = (struct ip6_hdr *)(icmp6 + 1); 9464d113176Sitojun ip6cp.ip6c_off = eoff; 9474d113176Sitojun ip6cp.ip6c_finaldst = finaldst; 9487ee01f6fSitojun ip6cp.ip6c_src = &icmp6src; 9497ee01f6fSitojun ip6cp.ip6c_nxt = nxt; 950ba0d80aaShenning #if NPF > 0 951ba0d80aaShenning pf_pkt_addr_changed(m); 952ba0d80aaShenning #endif 9537ee01f6fSitojun 9547ee01f6fSitojun if (icmp6type == ICMP6_PACKET_TOO_BIG) { 9557ee01f6fSitojun notifymtu = ntohl(icmp6->icmp6_mtu); 9567ee01f6fSitojun ip6cp.ip6c_cmdarg = (void *)¬ifymtu; 9577ee01f6fSitojun } 958f4f4d166Sitojun 9594da85d9aSbluhm ctlfunc = inet6sw[ip6_protox[nxt]].pr_ctlinput; 960c3c56496Sbluhm if (ctlfunc) 9614da85d9aSbluhm (*ctlfunc)(code, sin6tosa(&icmp6dst), 9625ee8afe3Smpi m->m_pkthdr.ph_rtableid, &ip6cp); 963287546eaSitojun } 9647ee01f6fSitojun return (0); 965287546eaSitojun 966287546eaSitojun freeit: 967287546eaSitojun m_freem(m); 9687ee01f6fSitojun return (-1); 969287546eaSitojun } 970287546eaSitojun 9714d113176Sitojun void 972ee37ea65Smcbride icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated) 973f4f4d166Sitojun { 9744d113176Sitojun unsigned long rtcount; 9754d113176Sitojun struct icmp6_mtudisc_callback *mc; 9764d113176Sitojun struct in6_addr *dst = ip6cp->ip6c_finaldst; 9774d113176Sitojun struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; 9784d113176Sitojun struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */ 979f4f4d166Sitojun u_int mtu = ntohl(icmp6->icmp6_mtu); 980f4f4d166Sitojun struct rtentry *rt = NULL; 981f4f4d166Sitojun struct sockaddr_in6 sin6; 982f4f4d166Sitojun 983f3785e27Sbluhm if (mtu < IPV6_MMTU) 9848418e62bSclaudio return; 9858418e62bSclaudio 9868418e62bSclaudio /* 9874d113176Sitojun * allow non-validated cases if memory is plenty, to make traffic 9884d113176Sitojun * from non-connected pcb happy. 9894d113176Sitojun */ 9902028c273Sclaudio rtcount = rt_timer_queue_count(&icmp6_mtudisc_timeout_q); 9914d113176Sitojun if (validated) { 99252166b8fSitojun if (0 <= icmp6_mtudisc_hiwat && rtcount > icmp6_mtudisc_hiwat) 9934d113176Sitojun return; 99452166b8fSitojun else if (0 <= icmp6_mtudisc_lowat && 99552166b8fSitojun rtcount > icmp6_mtudisc_lowat) { 9964d113176Sitojun /* 9974d113176Sitojun * XXX nuke a victim, install the new one. 9984d113176Sitojun */ 9994d113176Sitojun } 10004d113176Sitojun } else { 100152166b8fSitojun if (0 <= icmp6_mtudisc_lowat && rtcount > icmp6_mtudisc_lowat) 10024d113176Sitojun return; 10034d113176Sitojun } 10044d113176Sitojun 1005f4f4d166Sitojun bzero(&sin6, sizeof(sin6)); 1006f4f4d166Sitojun sin6.sin6_family = PF_INET6; 1007f4f4d166Sitojun sin6.sin6_len = sizeof(struct sockaddr_in6); 1008f4f4d166Sitojun sin6.sin6_addr = *dst; 100984797020Sitojun /* XXX normally, this won't happen */ 101084797020Sitojun if (IN6_IS_ADDR_LINKLOCAL(dst)) { 1011fb492c37Smpi sin6.sin6_addr.s6_addr16[1] = htons(m->m_pkthdr.ph_ifidx); 101284797020Sitojun } 10131a3ab866Smpi sin6.sin6_scope_id = in6_addr2scopeid(m->m_pkthdr.ph_ifidx, 1014a8cd54d7Sphessler &sin6.sin6_addr); 1015a8cd54d7Sphessler 101659caf375Sbluhm rt = icmp6_mtudisc_clone(&sin6, m->m_pkthdr.ph_rtableid, 0); 1017f4f4d166Sitojun 1018d082851aSmpi if (rt != NULL && ISSET(rt->rt_flags, RTF_HOST) && 1019*84d9c64aSbluhm !(rt->rt_locks & RTV_MTU)) { 1020*84d9c64aSbluhm u_int rtmtu; 1021*84d9c64aSbluhm 1022*84d9c64aSbluhm rtmtu = atomic_load_int(&rt->rt_mtu); 1023*84d9c64aSbluhm if (rtmtu > mtu || rtmtu == 0) { 1024d082851aSmpi struct ifnet *ifp; 1025d082851aSmpi 1026d082851aSmpi ifp = if_get(rt->rt_ifidx); 1027d082851aSmpi if (ifp != NULL && mtu < ifp->if_mtu) { 102807ba5f7cSjca icmp6stat_inc(icp6s_pmtuchg); 1029*84d9c64aSbluhm atomic_cas_uint(&rt->rt_mtu, rtmtu, mtu); 1030f4f4d166Sitojun } 1031d082851aSmpi if_put(ifp); 1032f4f4d166Sitojun } 1033*84d9c64aSbluhm } 103427ae666cSmpi rtfree(rt); 10354d113176Sitojun 10364d113176Sitojun /* 10374d113176Sitojun * Notify protocols that the MTU for this destination 10384d113176Sitojun * has changed. 10394d113176Sitojun */ 1040db445dd7Sbluhm LIST_FOREACH(mc, &icmp6_mtudisc_callbacks, mc_list) 10415ee8afe3Smpi (*mc->mc_func)(&sin6, m->m_pkthdr.ph_rtableid); 1042f4f4d166Sitojun } 1043f4f4d166Sitojun 1044287546eaSitojun /* 1045287546eaSitojun * Reflect the ip6 packet back to the source. 10460a2ce064Sitojun * OFF points to the icmp6 header, counted from the top of the mbuf. 1047287546eaSitojun */ 104816bb085cSdenis int 1049bcb2a527Sbluhm icmp6_reflect(struct mbuf **mp, size_t off, struct sockaddr *sa) 1050287546eaSitojun { 1051bcb2a527Sbluhm struct mbuf *m = *mp; 105297bb401bSmpi struct rtentry *rt = NULL; 1053287546eaSitojun struct ip6_hdr *ip6; 1054287546eaSitojun struct icmp6_hdr *icmp6; 10550039ae51Sjca struct in6_addr t, *src = NULL; 105652166b8fSitojun struct sockaddr_in6 sa6_src, sa6_dst; 10571662d0a7Sbluhm u_int rtableid; 10588831ce95Sbluhm u_int8_t pfflags; 1059287546eaSitojun 10600d46b56bSbluhm CTASSERT(sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) <= MHLEN); 10610d46b56bSbluhm 1062287546eaSitojun /* too short to reflect */ 1063287546eaSitojun if (off < sizeof(struct ip6_hdr)) { 1064b79da24aSitojun nd6log((LOG_DEBUG, 1065b79da24aSitojun "sanity fail: off=%lx, sizeof(ip6)=%lx in %s:%d\n", 1066287546eaSitojun (u_long)off, (u_long)sizeof(struct ip6_hdr), 1067b79da24aSitojun __FILE__, __LINE__)); 1068287546eaSitojun goto bad; 1069287546eaSitojun } 1070287546eaSitojun 107116bb085cSdenis if (m->m_pkthdr.ph_loopcnt++ >= M_MAXLOOP) { 1072bcb2a527Sbluhm m_freemp(mp); 107316bb085cSdenis return (ELOOP); 107416bb085cSdenis } 10751662d0a7Sbluhm rtableid = m->m_pkthdr.ph_rtableid; 10768831ce95Sbluhm pfflags = m->m_pkthdr.pf.flags; 10771662d0a7Sbluhm m_resethdr(m); 10781662d0a7Sbluhm m->m_pkthdr.ph_rtableid = rtableid; 10798831ce95Sbluhm m->m_pkthdr.pf.flags = pfflags & PF_TAG_GENERATED; 10801662d0a7Sbluhm 1081287546eaSitojun /* 1082287546eaSitojun * If there are extra headers between IPv6 and ICMPv6, strip 1083287546eaSitojun * off that header first. 1084287546eaSitojun */ 1085287546eaSitojun if (off > sizeof(struct ip6_hdr)) { 1086287546eaSitojun size_t l; 1087287546eaSitojun struct ip6_hdr nip6; 1088287546eaSitojun 1089287546eaSitojun l = off - sizeof(struct ip6_hdr); 1090287546eaSitojun m_copydata(m, 0, sizeof(nip6), (caddr_t)&nip6); 1091287546eaSitojun m_adj(m, l); 1092287546eaSitojun l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 1093287546eaSitojun if (m->m_len < l) { 1094bcb2a527Sbluhm if ((m = *mp = m_pullup(m, l)) == NULL) 109516bb085cSdenis return (EMSGSIZE); 1096287546eaSitojun } 1097e3b5aff7Sdhill memcpy(mtod(m, caddr_t), &nip6, sizeof(nip6)); 1098287546eaSitojun } else /* off == sizeof(struct ip6_hdr) */ { 1099287546eaSitojun size_t l; 1100287546eaSitojun l = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); 1101287546eaSitojun if (m->m_len < l) { 1102bcb2a527Sbluhm if ((m = *mp = m_pullup(m, l)) == NULL) 110316bb085cSdenis return (EMSGSIZE); 1104287546eaSitojun } 1105287546eaSitojun } 1106287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 1107287546eaSitojun ip6->ip6_nxt = IPPROTO_ICMPV6; 1108287546eaSitojun icmp6 = (struct icmp6_hdr *)(ip6 + 1); 1109287546eaSitojun 1110287546eaSitojun t = ip6->ip6_dst; 1111287546eaSitojun /* 1112287546eaSitojun * ip6_input() drops a packet if its src is multicast. 1113287546eaSitojun * So, the src is never multicast. 1114287546eaSitojun */ 1115287546eaSitojun ip6->ip6_dst = ip6->ip6_src; 1116287546eaSitojun 111752166b8fSitojun /* 111852166b8fSitojun * XXX: make sure to embed scope zone information, using 111952166b8fSitojun * already embedded IDs or the received interface (if any). 112052166b8fSitojun * Note that rcvif may be NULL. 112152166b8fSitojun * TODO: scoped routing case (XXX). 112252166b8fSitojun */ 112352166b8fSitojun bzero(&sa6_src, sizeof(sa6_src)); 112452166b8fSitojun sa6_src.sin6_family = AF_INET6; 112552166b8fSitojun sa6_src.sin6_len = sizeof(sa6_src); 112652166b8fSitojun sa6_src.sin6_addr = ip6->ip6_dst; 112752166b8fSitojun bzero(&sa6_dst, sizeof(sa6_dst)); 112852166b8fSitojun sa6_dst.sin6_family = AF_INET6; 112952166b8fSitojun sa6_dst.sin6_len = sizeof(sa6_dst); 113052166b8fSitojun sa6_dst.sin6_addr = t; 1131287546eaSitojun 113216bb085cSdenis if (sa == NULL) { 1133287546eaSitojun /* 113416bb085cSdenis * If the incoming packet was addressed directly to us (i.e. 113516bb085cSdenis * unicast), use dst as the src for the reply. The 113616bb085cSdenis * IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED case would be VERY rare, 113716bb085cSdenis * but is possible (for example) when we encounter an error 113816bb085cSdenis * while forwarding procedure destined to a duplicated address 113916bb085cSdenis * of ours. 1140287546eaSitojun */ 11411662d0a7Sbluhm rt = rtalloc(sin6tosa(&sa6_dst), 0, rtableid); 1142270aa588Smpi if (rtisvalid(rt) && ISSET(rt->rt_flags, RTF_LOCAL) && 1143270aa588Smpi !ISSET(ifatoia6(rt->rt_ifa)->ia6_flags, 1144270aa588Smpi IN6_IFF_ANYCAST|IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED)) { 1145287546eaSitojun src = &t; 1146287546eaSitojun } 1147270aa588Smpi rtfree(rt); 1148270aa588Smpi rt = NULL; 114916bb085cSdenis sa = sin6tosa(&sa6_src); 115016bb085cSdenis } 1151287546eaSitojun 11520039ae51Sjca if (src == NULL) { 1153edb65c0dSbluhm struct in6_ifaddr *ia6; 1154edb65c0dSbluhm 1155287546eaSitojun /* 1156f4f4d166Sitojun * This case matches to multicasts, our anycast, or unicasts 115752166b8fSitojun * that we do not own. Select a source address based on the 115852166b8fSitojun * source address of the erroneous packet. 1159287546eaSitojun */ 116016bb085cSdenis rt = rtalloc(sa, RT_RESOLVE, rtableid); 116197bb401bSmpi if (!rtisvalid(rt)) { 116297bb401bSmpi char addr[INET6_ADDRSTRLEN]; 116397bb401bSmpi 116452166b8fSitojun nd6log((LOG_DEBUG, 116597bb401bSmpi "%s: source can't be determined: dst=%s\n", 116697bb401bSmpi __func__, inet_ntop(AF_INET6, &sa6_src.sin6_addr, 116797bb401bSmpi addr, sizeof(addr)))); 116897bb401bSmpi rtfree(rt); 1169287546eaSitojun goto bad; 117052166b8fSitojun } 11717186e918Sflorian ia6 = in6_ifawithscope(rt->rt_ifa->ifa_ifp, &t, rtableid, rt); 1172edb65c0dSbluhm if (ia6 != NULL) 1173edb65c0dSbluhm src = &ia6->ia_addr.sin6_addr; 1174edb65c0dSbluhm if (src == NULL) 117597bb401bSmpi src = &ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr; 117652166b8fSitojun } 1177287546eaSitojun 1178287546eaSitojun ip6->ip6_src = *src; 117997bb401bSmpi rtfree(rt); 1180287546eaSitojun 1181287546eaSitojun ip6->ip6_flow = 0; 11820835e443Sitojun ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 11830835e443Sitojun ip6->ip6_vfc |= IPV6_VERSION; 1184287546eaSitojun ip6->ip6_nxt = IPPROTO_ICMPV6; 118576873ceeSitojun ip6->ip6_hlim = ip6_defhlim; 1186287546eaSitojun 1187287546eaSitojun icmp6->icmp6_cksum = 0; 118859261482Sbluhm m->m_pkthdr.csum_flags = M_ICMP_CSUM_OUT; 1189287546eaSitojun 1190287546eaSitojun /* 119134deef1eSitojun * XXX option handling 1192287546eaSitojun */ 1193287546eaSitojun 1194287546eaSitojun m->m_flags &= ~(M_BCAST|M_MCAST); 119516bb085cSdenis return (0); 1196287546eaSitojun 1197287546eaSitojun bad: 1198bcb2a527Sbluhm m_freemp(mp); 119916bb085cSdenis return (EHOSTUNREACH); 1200287546eaSitojun } 1201287546eaSitojun 1202287546eaSitojun void 1203454f069bSjsing icmp6_fasttimo(void) 1204287546eaSitojun { 1205287546eaSitojun mld6_fasttimeo(); 1206287546eaSitojun } 1207287546eaSitojun 1208454f069bSjsing const char * 1209ee37ea65Smcbride icmp6_redirect_diag(struct in6_addr *src6, struct in6_addr *dst6, 1210ee37ea65Smcbride struct in6_addr *tgt6) 1211287546eaSitojun { 1212bbcf0337Smpi static char buf[1024]; /* XXX */ 1213bbcf0337Smpi char src[INET6_ADDRSTRLEN]; 1214bbcf0337Smpi char dst[INET6_ADDRSTRLEN]; 1215bbcf0337Smpi char tgt[INET6_ADDRSTRLEN]; 1216bbcf0337Smpi 1217287546eaSitojun snprintf(buf, sizeof(buf), "(src=%s dst=%s tgt=%s)", 1218bbcf0337Smpi inet_ntop(AF_INET6, src6, src, sizeof(src)), 1219bbcf0337Smpi inet_ntop(AF_INET6, dst6, dst, sizeof(dst)), 1220bbcf0337Smpi inet_ntop(AF_INET6, tgt6, tgt, sizeof(tgt))); 1221287546eaSitojun return buf; 1222287546eaSitojun } 1223287546eaSitojun 1224287546eaSitojun void 1225ee37ea65Smcbride icmp6_redirect_input(struct mbuf *m, int off) 1226287546eaSitojun { 1227fb492c37Smpi struct ifnet *ifp; 1228287546eaSitojun struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1229f4f4d166Sitojun struct nd_redirect *nd_rd; 1230287546eaSitojun int icmp6len = ntohs(ip6->ip6_plen); 1231287546eaSitojun char *lladdr = NULL; 1232287546eaSitojun int lladdrlen = 0; 1233287546eaSitojun struct rtentry *rt = NULL; 12341f9e444eSbluhm int i_am_router = (atomic_load_int(&ip6_forwarding) != 0); 1235287546eaSitojun int is_router; 1236287546eaSitojun int is_onlink; 1237287546eaSitojun struct in6_addr src6 = ip6->ip6_src; 1238f4f4d166Sitojun struct in6_addr redtgt6; 1239f4f4d166Sitojun struct in6_addr reddst6; 1240eaf13d83Sclaudio struct nd_opts ndopts; 1241bbcf0337Smpi char addr[INET6_ADDRSTRLEN]; 1242287546eaSitojun 1243fb492c37Smpi ifp = if_get(m->m_pkthdr.ph_ifidx); 1244fb492c37Smpi if (ifp == NULL) 1245287546eaSitojun return; 1246287546eaSitojun 1247ab457133Sbluhm /* if we are router, we don't update route by icmp6 redirect */ 12481f9e444eSbluhm if (i_am_router) 1249f4f4d166Sitojun goto freeit; 1250fb92997aSflorian if (!(ifp->if_xflags & IFXF_AUTOCONF6)) 1251f4f4d166Sitojun goto freeit; 1252287546eaSitojun 1253f4f4d166Sitojun IP6_EXTHDR_GET(nd_rd, struct nd_redirect *, m, off, icmp6len); 1254f4f4d166Sitojun if (nd_rd == NULL) { 125507ba5f7cSjca icmp6stat_inc(icp6s_tooshort); 12569444e888Sclaudio if_put(ifp); 1257f4f4d166Sitojun return; 1258f4f4d166Sitojun } 1259f4f4d166Sitojun redtgt6 = nd_rd->nd_rd_target; 1260f4f4d166Sitojun reddst6 = nd_rd->nd_rd_dst; 1261f4f4d166Sitojun 12628a3a9117Sitojun if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) 12638a3a9117Sitojun redtgt6.s6_addr16[1] = htons(ifp->if_index); 12648a3a9117Sitojun if (IN6_IS_ADDR_LINKLOCAL(&reddst6)) 12658a3a9117Sitojun reddst6.s6_addr16[1] = htons(ifp->if_index); 12668a3a9117Sitojun 1267287546eaSitojun /* validation */ 1268287546eaSitojun if (!IN6_IS_ADDR_LINKLOCAL(&src6)) { 1269b79da24aSitojun nd6log((LOG_ERR, 1270287546eaSitojun "ICMP6 redirect sent from %s rejected; " 1271bbcf0337Smpi "must be from linklocal\n", 1272bbcf0337Smpi inet_ntop(AF_INET6, &src6, addr, sizeof(addr)))); 1273b79da24aSitojun goto bad; 1274287546eaSitojun } 1275287546eaSitojun if (ip6->ip6_hlim != 255) { 1276b79da24aSitojun nd6log((LOG_ERR, 1277287546eaSitojun "ICMP6 redirect sent from %s rejected; " 1278287546eaSitojun "hlim=%d (must be 255)\n", 1279bbcf0337Smpi inet_ntop(AF_INET6, &src6, addr, sizeof(addr)), 1280bbcf0337Smpi ip6->ip6_hlim)); 1281b79da24aSitojun goto bad; 1282287546eaSitojun } 128382a49a2dSmikeb if (IN6_IS_ADDR_MULTICAST(&reddst6)) { 128482a49a2dSmikeb nd6log((LOG_ERR, 128582a49a2dSmikeb "ICMP6 redirect rejected; " 128682a49a2dSmikeb "redirect dst must be unicast: %s\n", 128782a49a2dSmikeb icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 128882a49a2dSmikeb goto bad; 128982a49a2dSmikeb } 1290287546eaSitojun { 1291287546eaSitojun /* ip6->ip6_src must be equal to gw for icmp6->icmp6_reddst */ 1292287546eaSitojun struct sockaddr_in6 sin6; 1293287546eaSitojun struct in6_addr *gw6; 1294287546eaSitojun 1295287546eaSitojun bzero(&sin6, sizeof(sin6)); 1296287546eaSitojun sin6.sin6_family = AF_INET6; 1297287546eaSitojun sin6.sin6_len = sizeof(struct sockaddr_in6); 1298e3b5aff7Sdhill memcpy(&sin6.sin6_addr, &reddst6, sizeof(reddst6)); 12995148b194Smpi rt = rtalloc(sin6tosa(&sin6), 0, m->m_pkthdr.ph_rtableid); 1300287546eaSitojun if (rt) { 13018e07a1ffSitojun if (rt->rt_gateway == NULL || 13028e07a1ffSitojun rt->rt_gateway->sa_family != AF_INET6) { 1303b79da24aSitojun nd6log((LOG_ERR, 13048e07a1ffSitojun "ICMP6 redirect rejected; no route " 13058e07a1ffSitojun "with inet6 gateway found for redirect dst: %s\n", 1306b79da24aSitojun icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 130727ae666cSmpi rtfree(rt); 1308b79da24aSitojun goto bad; 13098e07a1ffSitojun } 13108e07a1ffSitojun 1311c3c56496Sbluhm gw6 = &(satosin6(rt->rt_gateway)->sin6_addr); 1312287546eaSitojun if (bcmp(&src6, gw6, sizeof(struct in6_addr)) != 0) { 1313b79da24aSitojun nd6log((LOG_ERR, 1314287546eaSitojun "ICMP6 redirect rejected; " 1315287546eaSitojun "not equal to gw-for-src=%s (must be same): " 1316287546eaSitojun "%s\n", 1317bbcf0337Smpi inet_ntop(AF_INET6, gw6, addr, sizeof(addr)), 1318b79da24aSitojun icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 131927ae666cSmpi rtfree(rt); 1320b79da24aSitojun goto bad; 1321287546eaSitojun } 1322287546eaSitojun } else { 1323b79da24aSitojun nd6log((LOG_ERR, 1324287546eaSitojun "ICMP6 redirect rejected; " 1325287546eaSitojun "no route found for redirect dst: %s\n", 1326b79da24aSitojun icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 1327b79da24aSitojun goto bad; 1328287546eaSitojun } 132927ae666cSmpi rtfree(rt); 1330287546eaSitojun rt = NULL; 1331287546eaSitojun } 1332287546eaSitojun 1333287546eaSitojun is_router = is_onlink = 0; 1334287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&redtgt6)) 1335287546eaSitojun is_router = 1; /* router case */ 1336287546eaSitojun if (bcmp(&redtgt6, &reddst6, sizeof(redtgt6)) == 0) 1337287546eaSitojun is_onlink = 1; /* on-link destination case */ 1338287546eaSitojun if (!is_router && !is_onlink) { 1339b79da24aSitojun nd6log((LOG_ERR, 1340287546eaSitojun "ICMP6 redirect rejected; " 1341287546eaSitojun "neither router case nor onlink case: %s\n", 1342b79da24aSitojun icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 1343b79da24aSitojun goto bad; 1344287546eaSitojun } 1345287546eaSitojun /* validation passed */ 1346287546eaSitojun 1347287546eaSitojun icmp6len -= sizeof(*nd_rd); 13482a46a9b4Skn if (nd6_options(nd_rd + 1, icmp6len, &ndopts) < 0) { 1349b79da24aSitojun nd6log((LOG_INFO, "icmp6_redirect_input: " 1350287546eaSitojun "invalid ND option, rejected: %s\n", 1351b79da24aSitojun icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 1352b79da24aSitojun /* nd6_options have incremented stats */ 1353f4f4d166Sitojun goto freeit; 1354287546eaSitojun } 1355287546eaSitojun 1356287546eaSitojun if (ndopts.nd_opts_tgt_lladdr) { 1357287546eaSitojun lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); 1358287546eaSitojun lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; 1359287546eaSitojun } 1360287546eaSitojun 1361287546eaSitojun if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { 1362b79da24aSitojun nd6log((LOG_INFO, 1363287546eaSitojun "icmp6_redirect_input: lladdrlen mismatch for %s " 1364287546eaSitojun "(if %d, icmp6 packet %d): %s\n", 1365bbcf0337Smpi inet_ntop(AF_INET6, &redtgt6, addr, sizeof(addr)), 1366bbcf0337Smpi ifp->if_addrlen, lladdrlen - 2, 1367b79da24aSitojun icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); 1368b79da24aSitojun goto bad; 1369287546eaSitojun } 1370287546eaSitojun 1371287546eaSitojun /* RFC 2461 8.3 */ 1372287546eaSitojun nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT, 13731f9e444eSbluhm is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER, i_am_router); 1374287546eaSitojun 1375287546eaSitojun if (!is_onlink) { /* better router case. perform rtredirect. */ 1376287546eaSitojun /* perform rtredirect */ 1377287546eaSitojun struct sockaddr_in6 sdst; 1378287546eaSitojun struct sockaddr_in6 sgw; 1379287546eaSitojun struct sockaddr_in6 ssrc; 138052166b8fSitojun unsigned long rtcount; 138152166b8fSitojun struct rtentry *newrt = NULL; 138252166b8fSitojun 138352166b8fSitojun /* 138452166b8fSitojun * do not install redirect route, if the number of entries 138552166b8fSitojun * is too much (> hiwat). note that, the node (= host) will 138652166b8fSitojun * work just fine even if we do not install redirect route 138752166b8fSitojun * (there will be additional hops, though). 138852166b8fSitojun */ 13892028c273Sclaudio rtcount = rt_timer_queue_count(&icmp6_redirect_timeout_q); 1390f3fcf2f3Smcbride if (0 <= ip6_maxdynroutes && rtcount >= ip6_maxdynroutes) 1391f3fcf2f3Smcbride goto freeit; 1392287546eaSitojun 1393287546eaSitojun bzero(&sdst, sizeof(sdst)); 1394287546eaSitojun bzero(&sgw, sizeof(sgw)); 1395287546eaSitojun bzero(&ssrc, sizeof(ssrc)); 1396287546eaSitojun sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6; 1397287546eaSitojun sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len = 1398287546eaSitojun sizeof(struct sockaddr_in6); 1399e3b5aff7Sdhill memcpy(&sgw.sin6_addr, &redtgt6, sizeof(struct in6_addr)); 1400e3b5aff7Sdhill memcpy(&sdst.sin6_addr, &reddst6, sizeof(struct in6_addr)); 1401e3b5aff7Sdhill memcpy(&ssrc.sin6_addr, &src6, sizeof(struct in6_addr)); 140206725911Smpi rtredirect(sin6tosa(&sdst), sin6tosa(&sgw), sin6tosa(&ssrc), 14035ee8afe3Smpi &newrt, m->m_pkthdr.ph_rtableid); 1404aae40be1Sbluhm if (newrt != NULL && icmp6_redirtimeout > 0) { 14052028c273Sclaudio rt_timer_add(newrt, &icmp6_redirect_timeout_q, 140680eefb13Sclaudio m->m_pkthdr.ph_rtableid); 140752166b8fSitojun } 1408aae40be1Sbluhm rtfree(newrt); 1409287546eaSitojun } 1410287546eaSitojun /* finally update cached route in each socket via pfctlinput */ 1411287546eaSitojun { 1412287546eaSitojun struct sockaddr_in6 sdst; 1413287546eaSitojun 1414287546eaSitojun bzero(&sdst, sizeof(sdst)); 1415287546eaSitojun sdst.sin6_family = AF_INET6; 1416287546eaSitojun sdst.sin6_len = sizeof(struct sockaddr_in6); 1417e3b5aff7Sdhill memcpy(&sdst.sin6_addr, &reddst6, sizeof(struct in6_addr)); 1418c3c56496Sbluhm pfctlinput(PRC_REDIRECT_HOST, sin6tosa(&sdst)); 1419287546eaSitojun } 1420f4f4d166Sitojun 1421f4f4d166Sitojun freeit: 14229444e888Sclaudio if_put(ifp); 1423f4f4d166Sitojun m_freem(m); 1424b79da24aSitojun return; 1425b79da24aSitojun 1426b79da24aSitojun bad: 14279444e888Sclaudio if_put(ifp); 142807ba5f7cSjca icmp6stat_inc(icp6s_badredirect); 1429b79da24aSitojun m_freem(m); 1430287546eaSitojun } 1431287546eaSitojun 1432287546eaSitojun void 1433ee37ea65Smcbride icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) 1434287546eaSitojun { 1435d082851aSmpi struct ifnet *ifp = NULL; 1436287546eaSitojun struct in6_addr *ifp_ll6; 1437393feaabSitojun struct in6_addr *nexthop; 1438287546eaSitojun struct ip6_hdr *sip6; /* m0 as struct ip6_hdr */ 1439287546eaSitojun struct mbuf *m = NULL; /* newly allocated one */ 1440287546eaSitojun struct ip6_hdr *ip6; /* m as struct ip6_hdr */ 1441287546eaSitojun struct nd_redirect *nd_rd; 1442287546eaSitojun size_t maxlen; 1443287546eaSitojun u_char *p; 1444cfb6b8dfSitojun struct sockaddr_in6 src_sa; 14451f9e444eSbluhm int i_am_router = (atomic_load_int(&ip6_forwarding) != 0); 1446287546eaSitojun 144707ba5f7cSjca icmp6_errcount(ND_REDIRECT, 0); 1448f6e55599Sitojun 1449287546eaSitojun /* if we are not router, we don't send icmp6 redirect */ 14501f9e444eSbluhm if (!i_am_router) 1451287546eaSitojun goto fail; 1452287546eaSitojun 1453287546eaSitojun /* sanity check */ 14548eadafcdSmpi if (m0 == NULL || !rtisvalid(rt)) 1455287546eaSitojun goto fail; 1456d082851aSmpi 1457d082851aSmpi ifp = if_get(rt->rt_ifidx); 1458d082851aSmpi if (ifp == NULL) 1459d082851aSmpi goto fail; 1460287546eaSitojun 1461287546eaSitojun /* 1462287546eaSitojun * Address check: 1463287546eaSitojun * the source address must identify a neighbor, and 1464287546eaSitojun * the destination address must not be a multicast address 1465287546eaSitojun * [RFC 2461, sec 8.2] 1466287546eaSitojun */ 1467287546eaSitojun sip6 = mtod(m0, struct ip6_hdr *); 1468cfb6b8dfSitojun bzero(&src_sa, sizeof(src_sa)); 1469cfb6b8dfSitojun src_sa.sin6_family = AF_INET6; 1470cfb6b8dfSitojun src_sa.sin6_len = sizeof(src_sa); 1471cfb6b8dfSitojun src_sa.sin6_addr = sip6->ip6_src; 1472cfb6b8dfSitojun /* we don't currently use sin6_scope_id, but eventually use it */ 14731a3ab866Smpi src_sa.sin6_scope_id = in6_addr2scopeid(ifp->if_index, &sip6->ip6_src); 1474cfb6b8dfSitojun if (nd6_is_addr_neighbor(&src_sa, ifp) == 0) 1475287546eaSitojun goto fail; 1476287546eaSitojun if (IN6_IS_ADDR_MULTICAST(&sip6->ip6_dst)) 1477287546eaSitojun goto fail; /* what should we do here? */ 1478287546eaSitojun 1479287546eaSitojun /* rate limit */ 1480287546eaSitojun if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) 1481287546eaSitojun goto fail; 1482287546eaSitojun 1483287546eaSitojun /* 1484287546eaSitojun * Since we are going to append up to 1280 bytes (= IPV6_MMTU), 1485287546eaSitojun * we almost always ask for an mbuf cluster for simplicity. 1486287546eaSitojun * (MHLEN < IPV6_MMTU is almost always true) 1487287546eaSitojun */ 1488f4f4d166Sitojun #if IPV6_MMTU >= MCLBYTES 1489f4f4d166Sitojun # error assumption failed about IPV6_MMTU and MCLBYTES 1490f4f4d166Sitojun #endif 1491287546eaSitojun MGETHDR(m, M_DONTWAIT, MT_HEADER); 1492f4f4d166Sitojun if (m && IPV6_MMTU >= MHLEN) 1493f4f4d166Sitojun MCLGET(m, M_DONTWAIT); 1494287546eaSitojun if (!m) 1495287546eaSitojun goto fail; 1496fb492c37Smpi m->m_pkthdr.ph_ifidx = 0; 14974df4d1f1Sitojun m->m_len = 0; 1498b5b7f62eSclaudio maxlen = m_trailingspace(m); 1499287546eaSitojun maxlen = min(IPV6_MMTU, maxlen); 1500287546eaSitojun /* just for safety */ 1501f4f4d166Sitojun if (maxlen < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 1502f4f4d166Sitojun ((sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7)) { 1503287546eaSitojun goto fail; 1504f4f4d166Sitojun } 1505287546eaSitojun 1506287546eaSitojun { 1507287546eaSitojun /* get ip6 linklocal address for ifp(my outgoing interface). */ 1508b9e83c60Sbluhm struct in6_ifaddr *ia6; 1509b2ea68e0Smpi if ((ia6 = in6ifa_ifpforlinklocal(ifp, IN6_IFF_TENTATIVE| 1510b2ea68e0Smpi IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST)) == NULL) 1511287546eaSitojun goto fail; 1512b9e83c60Sbluhm ifp_ll6 = &ia6->ia_addr.sin6_addr; 1513287546eaSitojun } 1514287546eaSitojun 1515287546eaSitojun /* get ip6 linklocal address for the router. */ 1516287546eaSitojun if (rt->rt_gateway && (rt->rt_flags & RTF_GATEWAY)) { 1517287546eaSitojun struct sockaddr_in6 *sin6; 1518c3c56496Sbluhm sin6 = satosin6(rt->rt_gateway); 1519393feaabSitojun nexthop = &sin6->sin6_addr; 1520393feaabSitojun if (!IN6_IS_ADDR_LINKLOCAL(nexthop)) 1521393feaabSitojun nexthop = NULL; 1522287546eaSitojun } else 1523393feaabSitojun nexthop = NULL; 1524287546eaSitojun 1525287546eaSitojun /* ip6 */ 1526287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 1527287546eaSitojun ip6->ip6_flow = 0; 15280835e443Sitojun ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 15290835e443Sitojun ip6->ip6_vfc |= IPV6_VERSION; 1530287546eaSitojun /* ip6->ip6_plen will be set later */ 1531287546eaSitojun ip6->ip6_nxt = IPPROTO_ICMPV6; 1532287546eaSitojun ip6->ip6_hlim = 255; 1533287546eaSitojun /* ip6->ip6_src must be linklocal addr for my outgoing if. */ 1534287546eaSitojun bcopy(ifp_ll6, &ip6->ip6_src, sizeof(struct in6_addr)); 1535287546eaSitojun bcopy(&sip6->ip6_src, &ip6->ip6_dst, sizeof(struct in6_addr)); 1536287546eaSitojun 1537287546eaSitojun /* ND Redirect */ 1538287546eaSitojun nd_rd = (struct nd_redirect *)(ip6 + 1); 1539287546eaSitojun nd_rd->nd_rd_type = ND_REDIRECT; 1540287546eaSitojun nd_rd->nd_rd_code = 0; 1541287546eaSitojun nd_rd->nd_rd_reserved = 0; 1542287546eaSitojun if (rt->rt_flags & RTF_GATEWAY) { 1543287546eaSitojun /* 1544287546eaSitojun * nd_rd->nd_rd_target must be a link-local address in 1545287546eaSitojun * better router cases. 1546287546eaSitojun */ 1547393feaabSitojun if (!nexthop) 1548287546eaSitojun goto fail; 1549393feaabSitojun bcopy(nexthop, &nd_rd->nd_rd_target, 1550287546eaSitojun sizeof(nd_rd->nd_rd_target)); 1551287546eaSitojun bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, 1552287546eaSitojun sizeof(nd_rd->nd_rd_dst)); 1553287546eaSitojun } else { 1554287546eaSitojun /* make sure redtgt == reddst */ 1555393feaabSitojun nexthop = &sip6->ip6_dst; 1556287546eaSitojun bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_target, 1557287546eaSitojun sizeof(nd_rd->nd_rd_target)); 1558287546eaSitojun bcopy(&sip6->ip6_dst, &nd_rd->nd_rd_dst, 1559287546eaSitojun sizeof(nd_rd->nd_rd_dst)); 1560287546eaSitojun } 1561287546eaSitojun 1562287546eaSitojun p = (u_char *)(nd_rd + 1); 1563287546eaSitojun 1564287546eaSitojun { 1565287546eaSitojun /* target lladdr option */ 156616119bbfSmpi struct rtentry *nrt; 1567287546eaSitojun int len; 1568287546eaSitojun struct sockaddr_dl *sdl; 1569287546eaSitojun struct nd_opt_hdr *nd_opt; 1570287546eaSitojun char *lladdr; 1571287546eaSitojun 1572f4f4d166Sitojun len = sizeof(*nd_opt) + ifp->if_addrlen; 1573f4f4d166Sitojun len = (len + 7) & ~7; /* round by 8 */ 1574f4f4d166Sitojun /* safety check */ 1575f4f4d166Sitojun if (len + (p - (u_char *)ip6) > maxlen) 1576f4f4d166Sitojun goto nolladdropt; 157716119bbfSmpi nrt = nd6_lookup(nexthop, 0, ifp, ifp->if_rdomain); 157816119bbfSmpi if ((nrt != NULL) && 157916119bbfSmpi (nrt->rt_flags & (RTF_GATEWAY|RTF_LLINFO)) == RTF_LLINFO && 158016119bbfSmpi (nrt->rt_gateway->sa_family == AF_LINK) && 1581c7b7b779Sbluhm (sdl = satosdl(nrt->rt_gateway)) && 1582287546eaSitojun sdl->sdl_alen) { 1583287546eaSitojun nd_opt = (struct nd_opt_hdr *)p; 1584287546eaSitojun nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 1585287546eaSitojun nd_opt->nd_opt_len = len >> 3; 1586287546eaSitojun lladdr = (char *)(nd_opt + 1); 1587287546eaSitojun bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen); 1588f4f4d166Sitojun p += len; 1589287546eaSitojun } 159016119bbfSmpi rtfree(nrt); 1591287546eaSitojun } 1592287546eaSitojun nolladdropt:; 1593287546eaSitojun 1594287546eaSitojun m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; 1595287546eaSitojun 1596287546eaSitojun /* just to be safe */ 1597f6e55599Sitojun if (p - (u_char *)ip6 > maxlen) 1598f6e55599Sitojun goto noredhdropt; 1599287546eaSitojun 1600287546eaSitojun { 1601287546eaSitojun /* redirected header option */ 1602287546eaSitojun int len; 1603287546eaSitojun struct nd_opt_rd_hdr *nd_opt_rh; 1604287546eaSitojun 1605287546eaSitojun /* 1606287546eaSitojun * compute the maximum size for icmp6 redirect header option. 1607287546eaSitojun * XXX room for auth header? 1608287546eaSitojun */ 1609287546eaSitojun len = maxlen - (p - (u_char *)ip6); 1610287546eaSitojun len &= ~7; 1611287546eaSitojun 1612287546eaSitojun /* 1613287546eaSitojun * Redirected header option spec (RFC2461 4.6.3) talks nothing 1614287546eaSitojun * about padding/truncate rule for the original IP packet. 16157b208ab5Sitojun * From the discussion on IPv6imp in Feb 1999, 16167b208ab5Sitojun * the consensus was: 1617287546eaSitojun * - "attach as much as possible" is the goal 16187b208ab5Sitojun * - pad if not aligned (original size can be guessed by 16197b208ab5Sitojun * original ip6 header) 1620287546eaSitojun * Following code adds the padding if it is simple enough, 1621287546eaSitojun * and truncates if not. 1622287546eaSitojun */ 1623287546eaSitojun if (len - sizeof(*nd_opt_rh) < m0->m_pkthdr.len) { 1624287546eaSitojun /* not enough room, truncate */ 16257b208ab5Sitojun m_adj(m0, (len - sizeof(*nd_opt_rh)) - 16267b208ab5Sitojun m0->m_pkthdr.len); 1627287546eaSitojun } else { 16287b208ab5Sitojun /* 16297b208ab5Sitojun * enough room, truncate if not aligned. 16307b208ab5Sitojun * we don't pad here for simplicity. 16317b208ab5Sitojun */ 1632287546eaSitojun size_t extra; 1633287546eaSitojun 1634287546eaSitojun extra = m0->m_pkthdr.len % 8; 1635287546eaSitojun if (extra) { 1636287546eaSitojun /* truncate */ 16377b208ab5Sitojun m_adj(m0, -extra); 1638287546eaSitojun } 1639287546eaSitojun len = m0->m_pkthdr.len + sizeof(*nd_opt_rh); 1640287546eaSitojun } 1641287546eaSitojun 1642287546eaSitojun nd_opt_rh = (struct nd_opt_rd_hdr *)p; 1643287546eaSitojun bzero(nd_opt_rh, sizeof(*nd_opt_rh)); 1644287546eaSitojun nd_opt_rh->nd_opt_rh_type = ND_OPT_REDIRECTED_HEADER; 1645287546eaSitojun nd_opt_rh->nd_opt_rh_len = len >> 3; 1646287546eaSitojun p += sizeof(*nd_opt_rh); 1647287546eaSitojun m->m_pkthdr.len = m->m_len = p - (u_char *)ip6; 1648287546eaSitojun 1649287546eaSitojun /* connect m0 to m */ 16507b208ab5Sitojun m->m_pkthdr.len += m0->m_pkthdr.len; 165100b5c815Sitojun m_cat(m, m0); 165236692025Sitojun m0 = NULL; 1653287546eaSitojun } 16547b208ab5Sitojun noredhdropt: 165536692025Sitojun m_freem(m0); 165636692025Sitojun m0 = NULL; 1657287546eaSitojun 1658db004b2cSbrad sip6 = mtod(m, struct ip6_hdr *); 1659287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_src)) 1660287546eaSitojun sip6->ip6_src.s6_addr16[1] = 0; 1661287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&sip6->ip6_dst)) 1662287546eaSitojun sip6->ip6_dst.s6_addr16[1] = 0; 1663287546eaSitojun #if 0 1664287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) 1665287546eaSitojun ip6->ip6_src.s6_addr16[1] = 0; 1666287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) 1667287546eaSitojun ip6->ip6_dst.s6_addr16[1] = 0; 1668287546eaSitojun #endif 1669287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_target)) 1670287546eaSitojun nd_rd->nd_rd_target.s6_addr16[1] = 0; 1671287546eaSitojun if (IN6_IS_ADDR_LINKLOCAL(&nd_rd->nd_rd_dst)) 1672287546eaSitojun nd_rd->nd_rd_dst.s6_addr16[1] = 0; 1673287546eaSitojun 1674287546eaSitojun ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr)); 1675287546eaSitojun 1676287546eaSitojun nd_rd->nd_rd_cksum = 0; 167759261482Sbluhm m->m_pkthdr.csum_flags = M_ICMP_CSUM_OUT; 1678287546eaSitojun 1679287546eaSitojun /* send the packet to outside... */ 168009d936d6Sclaudio ip6_output(m, NULL, NULL, 0, NULL, NULL); 1681486b41d1Sitojun 168207ba5f7cSjca icmp6stat_inc(icp6s_outhist + ND_REDIRECT); 1683287546eaSitojun 1684d082851aSmpi if_put(ifp); 1685287546eaSitojun return; 1686287546eaSitojun 1687287546eaSitojun fail: 1688d082851aSmpi if_put(ifp); 1689287546eaSitojun m_freem(m); 1690287546eaSitojun m_freem(m0); 1691287546eaSitojun } 1692287546eaSitojun 1693ac1b2aeeSitojun /* 1694ac1b2aeeSitojun * ICMPv6 socket option processing. 1695ac1b2aeeSitojun */ 1696ac1b2aeeSitojun int 1697ee37ea65Smcbride icmp6_ctloutput(int op, struct socket *so, int level, int optname, 1698490e0738Sdhill struct mbuf *m) 1699ac1b2aeeSitojun { 1700ac1b2aeeSitojun int error = 0; 1701921ffa12Sbluhm struct inpcb *inp = sotoinpcb(so); 1702ac1b2aeeSitojun 1703e09b1b7cSmpi if (level != IPPROTO_ICMPV6) 1704ac1b2aeeSitojun return EINVAL; 1705ac1b2aeeSitojun 1706ac1b2aeeSitojun switch (op) { 1707ac1b2aeeSitojun case PRCO_SETOPT: 1708ac1b2aeeSitojun switch (optname) { 1709ac1b2aeeSitojun case ICMP6_FILTER: 1710ac1b2aeeSitojun { 1711ac1b2aeeSitojun struct icmp6_filter *p; 1712ac1b2aeeSitojun 171353f19499Sguenther if (m == NULL || m->m_len != sizeof(*p)) { 1714ac1b2aeeSitojun error = EMSGSIZE; 1715ac1b2aeeSitojun break; 1716ac1b2aeeSitojun } 1717ac1b2aeeSitojun p = mtod(m, struct icmp6_filter *); 1718921ffa12Sbluhm if (!p || !inp->inp_icmp6filt) { 1719ac1b2aeeSitojun error = EINVAL; 1720ac1b2aeeSitojun break; 1721ac1b2aeeSitojun } 1722921ffa12Sbluhm bcopy(p, inp->inp_icmp6filt, 1723ac1b2aeeSitojun sizeof(struct icmp6_filter)); 1724ac1b2aeeSitojun error = 0; 1725ac1b2aeeSitojun break; 1726ac1b2aeeSitojun } 1727ac1b2aeeSitojun 1728ac1b2aeeSitojun default: 1729ac1b2aeeSitojun error = ENOPROTOOPT; 1730ac1b2aeeSitojun break; 1731ac1b2aeeSitojun } 1732ac1b2aeeSitojun break; 1733ac1b2aeeSitojun 1734ac1b2aeeSitojun case PRCO_GETOPT: 1735ac1b2aeeSitojun switch (optname) { 1736ac1b2aeeSitojun case ICMP6_FILTER: 1737ac1b2aeeSitojun { 1738ac1b2aeeSitojun struct icmp6_filter *p; 1739ac1b2aeeSitojun 1740921ffa12Sbluhm if (!inp->inp_icmp6filt) { 1741ac1b2aeeSitojun error = EINVAL; 1742ac1b2aeeSitojun break; 1743ac1b2aeeSitojun } 1744ac1b2aeeSitojun m->m_len = sizeof(struct icmp6_filter); 1745ac1b2aeeSitojun p = mtod(m, struct icmp6_filter *); 1746921ffa12Sbluhm bcopy(inp->inp_icmp6filt, p, 1747ac1b2aeeSitojun sizeof(struct icmp6_filter)); 1748ac1b2aeeSitojun error = 0; 1749ac1b2aeeSitojun break; 1750ac1b2aeeSitojun } 1751ac1b2aeeSitojun 1752ac1b2aeeSitojun default: 1753ac1b2aeeSitojun error = ENOPROTOOPT; 1754ac1b2aeeSitojun break; 1755ac1b2aeeSitojun } 1756ac1b2aeeSitojun break; 1757ac1b2aeeSitojun } 1758ac1b2aeeSitojun 1759ac1b2aeeSitojun return (error); 1760ac1b2aeeSitojun } 176193036e4eSangelos 1762287546eaSitojun /* 1763287546eaSitojun * Perform rate limit check. 1764287546eaSitojun * Returns 0 if it is okay to send the icmp6 packet. 1765287546eaSitojun * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate 1766287546eaSitojun * limitation. 1767287546eaSitojun * 1768287546eaSitojun * XXX per-destination/type check necessary? 1769ee37ea65Smcbride * 1770ee37ea65Smcbride * dst - not used at this moment 1771ee37ea65Smcbride * type - not used at this moment 1772ee37ea65Smcbride * code - not used at this moment 1773287546eaSitojun */ 1774454f069bSjsing int 1775ee37ea65Smcbride icmp6_ratelimit(const struct in6_addr *dst, const int type, const int code) 1776287546eaSitojun { 1777f6e55599Sitojun /* PPS limit */ 17781ad7b35bSitojun if (!ppsratecheck(&icmp6errppslim_last, &icmp6errpps_count, 17790e446ca2Sderaadt icmp6errppslim)) 17800e446ca2Sderaadt return 1; /* The packet is subject to rate limit */ 17810e446ca2Sderaadt return 0; /* okay to send */ 1782287546eaSitojun } 1783287546eaSitojun 1784454f069bSjsing struct rtentry * 178559caf375Sbluhm icmp6_mtudisc_clone(struct sockaddr_in6 *dst, u_int rtableid, int ipsec) 1786287546eaSitojun { 1787287546eaSitojun struct rtentry *rt; 1788287546eaSitojun int error; 1789287546eaSitojun 179059caf375Sbluhm rt = rtalloc(sin6tosa(dst), RT_RESOLVE, rtableid); 1791e669a940Sbluhm 1792e669a940Sbluhm /* Check if the route is actually usable */ 179359caf375Sbluhm if (!rtisvalid(rt)) 179459caf375Sbluhm goto bad; 179559caf375Sbluhm /* IPsec needs the route only for PMTU, it can use reject for that */ 179659caf375Sbluhm if (!ipsec && (rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE))) 1797635a4d25Smiko goto bad; 1798287546eaSitojun 179941649a76Sbluhm /* 180041649a76Sbluhm * No PMTU for local routes and permanent neighbors, 180141649a76Sbluhm * ARP and NDP use the same expire timer as the route. 180241649a76Sbluhm */ 180341649a76Sbluhm if (ISSET(rt->rt_flags, RTF_LOCAL) || 1804635a4d25Smiko (ISSET(rt->rt_flags, RTF_LLINFO) && rt->rt_expire == 0)) 1805635a4d25Smiko goto bad; 180641649a76Sbluhm 1807287546eaSitojun /* If we didn't get a host route, allocate one */ 1808287546eaSitojun if ((rt->rt_flags & RTF_HOST) == 0) { 1809287546eaSitojun struct rtentry *nrt; 18109bd6009cSbluhm struct rt_addrinfo info; 18119bd6009cSbluhm struct sockaddr_rtlabel sa_rl; 1812287546eaSitojun 18139bd6009cSbluhm memset(&info, 0, sizeof(info)); 181466f69fe1Smpi info.rti_ifa = rt->rt_ifa; 1815cb24f5e5Sclaudio info.rti_flags = RTF_GATEWAY | RTF_HOST | RTF_DYNAMIC; 181659caf375Sbluhm info.rti_info[RTAX_DST] = sin6tosa(dst); 1817cb24f5e5Sclaudio info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 18189bd6009cSbluhm info.rti_info[RTAX_LABEL] = 18199bd6009cSbluhm rtlabel_id2sa(rt->rt_labelid, &sa_rl); 182081d38878Smpi 18217ffb277fSbluhm error = rtrequest(RTM_ADD, &info, rt->rt_priority, &nrt, 1822e669a940Sbluhm rtableid); 1823635a4d25Smiko if (error) 1824635a4d25Smiko goto bad; 1825287546eaSitojun nrt->rt_rmx = rt->rt_rmx; 1826287546eaSitojun rtfree(rt); 1827287546eaSitojun rt = nrt; 182889d26a9fSclaudio rtm_send(rt, RTM_ADD, 0, rtableid); 1829287546eaSitojun } 18302028c273Sclaudio error = rt_timer_add(rt, &icmp6_mtudisc_timeout_q, rtableid); 1831635a4d25Smiko if (error) 1832635a4d25Smiko goto bad; 1833287546eaSitojun 1834e669a940Sbluhm return (rt); 1835635a4d25Smiko bad: 1836635a4d25Smiko rtfree(rt); 1837635a4d25Smiko return (NULL); 1838287546eaSitojun } 1839287546eaSitojun 1840454f069bSjsing void 184180eefb13Sclaudio icmp6_mtudisc_timeout(struct rtentry *rt, u_int rtableid) 1842287546eaSitojun { 18431bfdf8beSmpi struct ifnet *ifp; 18447fe73b24Smpi 18452b4720fcSmpi NET_ASSERT_LOCKED(); 1846cb24f5e5Sclaudio 18471bfdf8beSmpi ifp = if_get(rt->rt_ifidx); 18481bfdf8beSmpi if (ifp == NULL) 18491bfdf8beSmpi return; 18501bfdf8beSmpi 18511bfdf8beSmpi if ((rt->rt_flags & (RTF_DYNAMIC|RTF_HOST)) == (RTF_DYNAMIC|RTF_HOST)) { 185280eefb13Sclaudio rtdeletemsg(rt, ifp, rtableid); 1853287546eaSitojun } else { 18549b6a6412Sbluhm if (!(rt->rt_locks & RTV_MTU)) 1855*84d9c64aSbluhm atomic_store_int(&rt->rt_mtu, 0); 1856287546eaSitojun } 18571bfdf8beSmpi 18581bfdf8beSmpi if_put(ifp); 1859287546eaSitojun } 1860287546eaSitojun 1861545be9dfSgnezdo const struct sysctl_bounded_args icmpv6ctl_vars[] = { 1862545be9dfSgnezdo { ICMPV6CTL_ND6_DELAY, &nd6_delay, 0, INT_MAX }, 1863545be9dfSgnezdo { ICMPV6CTL_ND6_UMAXTRIES, &nd6_umaxtries, 0, INT_MAX }, 1864545be9dfSgnezdo { ICMPV6CTL_ND6_MMAXTRIES, &nd6_mmaxtries, 0, INT_MAX }, 1865545be9dfSgnezdo { ICMPV6CTL_ERRPPSLIMIT, &icmp6errppslim, -1, 1000 }, 1866545be9dfSgnezdo { ICMPV6CTL_ND6_MAXNUDHINT, &nd6_maxnudhint, 0, INT_MAX }, 1867545be9dfSgnezdo { ICMPV6CTL_MTUDISC_HIWAT, &icmp6_mtudisc_hiwat, -1, INT_MAX }, 1868545be9dfSgnezdo { ICMPV6CTL_MTUDISC_LOWAT, &icmp6_mtudisc_lowat, -1, INT_MAX }, 1869545be9dfSgnezdo { ICMPV6CTL_ND6_DEBUG, &nd6_debug, 0, 1 }, 1870545be9dfSgnezdo }; 18715873a946Smarkus 1872287546eaSitojun int 187307ba5f7cSjca icmp6_sysctl_icmp6stat(void *oldp, size_t *oldlenp, void *newp) 187407ba5f7cSjca { 187507ba5f7cSjca struct icmp6stat *icmp6stat; 187607ba5f7cSjca int ret; 187707ba5f7cSjca 187807ba5f7cSjca CTASSERT(sizeof(*icmp6stat) == icp6s_ncounters * sizeof(uint64_t)); 1879c8915b1eSderaadt icmp6stat = malloc(sizeof(*icmp6stat), M_TEMP, M_WAITOK|M_ZERO); 1880bf0d449cSmpi counters_read(icmp6counters, (uint64_t *)icmp6stat, icp6s_ncounters, 1881bf0d449cSmpi NULL); 188207ba5f7cSjca ret = sysctl_rdstruct(oldp, oldlenp, newp, 188307ba5f7cSjca icmp6stat, sizeof(*icmp6stat)); 188407ba5f7cSjca free(icmp6stat, M_TEMP, sizeof(*icmp6stat)); 188507ba5f7cSjca 188607ba5f7cSjca return (ret); 188707ba5f7cSjca } 188807ba5f7cSjca 188907ba5f7cSjca int 1890ee37ea65Smcbride icmp6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 1891ee37ea65Smcbride void *newp, size_t newlen) 1892287546eaSitojun { 1893df4a15bcSmpi int error; 1894df4a15bcSmpi 1895287546eaSitojun /* All sysctl names at this level are terminal. */ 1896287546eaSitojun if (namelen != 1) 1897aae40be1Sbluhm return (ENOTDIR); 1898287546eaSitojun 1899287546eaSitojun switch (name[0]) { 1900aae40be1Sbluhm case ICMPV6CTL_REDIRTIMEOUT: 1901aae40be1Sbluhm NET_LOCK(); 1902aae40be1Sbluhm error = sysctl_int_bounded(oldp, oldlenp, newp, newlen, 1903aae40be1Sbluhm &icmp6_redirtimeout, 0, INT_MAX); 19042028c273Sclaudio rt_timer_queue_change(&icmp6_redirect_timeout_q, 1905aae40be1Sbluhm icmp6_redirtimeout); 1906aae40be1Sbluhm NET_UNLOCK(); 1907aae40be1Sbluhm break; 1908287546eaSitojun 1909287546eaSitojun case ICMPV6CTL_STATS: 1910aae40be1Sbluhm error = icmp6_sysctl_icmp6stat(oldp, oldlenp, newp); 1911aae40be1Sbluhm break; 1912aae40be1Sbluhm 1913e2c34204Sbluhm case ICMPV6CTL_ND6_QUEUED: 1914e2c34204Sbluhm error = sysctl_rdint(oldp, oldlenp, newp, 1915e2c34204Sbluhm atomic_load_int(&ln_hold_total)); 1916e2c34204Sbluhm break; 1917e2c34204Sbluhm 1918287546eaSitojun default: 1919df4a15bcSmpi NET_LOCK(); 1920545be9dfSgnezdo error = sysctl_bounded_arr(icmpv6ctl_vars, 1921545be9dfSgnezdo nitems(icmpv6ctl_vars), name, namelen, oldp, oldlenp, newp, 1922545be9dfSgnezdo newlen); 1923df4a15bcSmpi NET_UNLOCK(); 1924aae40be1Sbluhm break; 1925df4a15bcSmpi } 1926aae40be1Sbluhm 1927aae40be1Sbluhm return (error); 1928287546eaSitojun } 1929