1*c77b3186Sbluhm /* $OpenBSD: ip6_input.c,v 1.267 2024/11/21 20:15:44 bluhm Exp $ */ 23d5b41e1Sitojun /* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun 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_input.c 8.2 (Berkeley) 1/4/94 62287546eaSitojun */ 63287546eaSitojun 6430620b12Sfrantzen #include "pf.h" 65ca90c36bSmpf #include "carp.h" 6630620b12Sfrantzen 67287546eaSitojun #include <sys/param.h> 68287546eaSitojun #include <sys/systm.h> 69287546eaSitojun #include <sys/mbuf.h> 70287546eaSitojun #include <sys/domain.h> 71e6c6495dSderaadt #include <sys/sysctl.h> 72287546eaSitojun #include <sys/protosw.h> 73287546eaSitojun #include <sys/socket.h> 74287546eaSitojun #include <sys/socketvar.h> 75287546eaSitojun #include <sys/errno.h> 76287546eaSitojun #include <sys/time.h> 77638c25e3Stedu #include <sys/timeout.h> 78287546eaSitojun #include <sys/kernel.h> 79287546eaSitojun #include <sys/syslog.h> 80528ff394Ssashan #include <sys/task.h> 81287546eaSitojun 82287546eaSitojun #include <net/if.h> 830deb6685Smpi #include <net/if_var.h> 84287546eaSitojun #include <net/if_types.h> 85287546eaSitojun #include <net/route.h> 86287546eaSitojun #include <net/netisr.h> 87287546eaSitojun 88287546eaSitojun #include <netinet/in.h> 89ae079d85Sangelos 90287546eaSitojun #include <netinet/ip.h> 91ae079d85Sangelos 92287546eaSitojun #include <netinet/in_pcb.h> 93528ff394Ssashan #include <netinet/ip_var.h> 94287546eaSitojun #include <netinet6/in6_var.h> 959798906bSflorian #include <netinet6/in6_ifattach.h> 96fa86ee14Sitojun #include <netinet/ip6.h> 97287546eaSitojun #include <netinet6/ip6_var.h> 98fa86ee14Sitojun #include <netinet/icmp6.h> 99287546eaSitojun #include <netinet6/nd6.h> 100287546eaSitojun 101287546eaSitojun #include "gif.h" 102287546eaSitojun #include "bpfilter.h" 103287546eaSitojun 104ab46c28dSderaadt #ifdef MROUTING 105ab46c28dSderaadt #include <netinet6/ip6_mroute.h> 106ab46c28dSderaadt #endif 107ab46c28dSderaadt 10830620b12Sfrantzen #if NPF > 0 10930620b12Sfrantzen #include <net/pfvar.h> 11030620b12Sfrantzen #endif 11130620b12Sfrantzen 112ca90c36bSmpf #if NCARP > 0 113ca90c36bSmpf #include <netinet/ip_carp.h> 114ca90c36bSmpf #endif 115ca90c36bSmpf 11655159beeSbluhm struct niqueue ip6intrq = NIQUEUE_INITIALIZER(IPQ_MAXLEN, NETISR_IPV6); 11755159beeSbluhm 11831e14cacSjca struct cpumem *ip6counters; 119287546eaSitojun 1209798906bSflorian uint8_t ip6_soiikey[IP6_SOIIKEY_LEN]; 1219798906bSflorian 122ab457133Sbluhm int ip6_ours(struct mbuf **, int *, int, int, int); 123642d98a2Sbluhm int ip6_check_rh0hdr(struct mbuf *, int *); 124ab457133Sbluhm int ip6_hbhchcheck(struct mbuf **, int *, int *, int); 12580467c39Sbluhm int ip6_hopopts_input(struct mbuf **, int *, u_int32_t *, u_int32_t *); 126d4984c21Sjsing struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int); 1279798906bSflorian int ip6_sysctl_soiikey(void *, size_t *, void *, size_t); 128287546eaSitojun 129528ff394Ssashan static struct mbuf_queue ip6send_mq; 130528ff394Ssashan 131528ff394Ssashan static void ip6_send_dispatch(void *); 132528ff394Ssashan static struct task ip6send_task = 133528ff394Ssashan TASK_INITIALIZER(ip6_send_dispatch, &ip6send_mq); 134528ff394Ssashan 135287546eaSitojun /* 136287546eaSitojun * IP6 initialization: fill in IP6 protocol switch table. 137287546eaSitojun * All protocols not implemented in kernel go to raw IP6 protocol handler. 138287546eaSitojun */ 139287546eaSitojun void 140d4984c21Sjsing ip6_init(void) 141287546eaSitojun { 142036aff27Smpi const struct protosw *pr; 1439063e41fSitojun int i; 144287546eaSitojun 14596be3d96Sbluhm pr = pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); 146bc31a334Skrw if (pr == NULL) 147ab0ea4a9Snayden panic("%s", __func__); 148287546eaSitojun for (i = 0; i < IPPROTO_MAX; i++) 149287546eaSitojun ip6_protox[i] = pr - inet6sw; 15096be3d96Sbluhm for (pr = inet6domain.dom_protosw; 15196be3d96Sbluhm pr < inet6domain.dom_protoswNPROTOSW; pr++) 152287546eaSitojun if (pr->pr_domain->dom_family == PF_INET6 && 15368eb3207Sotto pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW && 15468eb3207Sotto pr->pr_protocol < IPPROTO_MAX) 155287546eaSitojun ip6_protox[pr->pr_protocol] = pr - inet6sw; 156ca6e56f2Sdjm ip6_randomid_init(); 157287546eaSitojun nd6_init(); 158287546eaSitojun frag6_init(); 159528ff394Ssashan 160528ff394Ssashan mq_init(&ip6send_mq, 64, IPL_SOFTNET); 16131e14cacSjca 162599d0588Sjca ip6counters = counters_alloc(ip6s_ncounters); 163ef8e83dbSclaudio #ifdef MROUTING 1642028c273Sclaudio rt_timer_queue_init(&ip6_mrouterq, MCAST_EXPIRE_TIMEOUT, 16538f7d33dSclaudio &mf6c_expire_route); 166ef8e83dbSclaudio #endif 167287546eaSitojun } 168287546eaSitojun 16955159beeSbluhm /* 17055159beeSbluhm * Enqueue packet for local delivery. Queuing is used as a boundary 1716a1c2aefSbluhm * between the network layer (input/forward path) running with 1726a1c2aefSbluhm * NET_LOCK_SHARED() and the transport layer needing it exclusively. 17355159beeSbluhm */ 17455159beeSbluhm int 175ab457133Sbluhm ip6_ours(struct mbuf **mp, int *offp, int nxt, int af, int flags) 17655159beeSbluhm { 1770b448d84Sbluhm /* ip6_hbhchcheck() may be run before, then off and nxt are set */ 1780b448d84Sbluhm if (*offp == 0) { 179ab457133Sbluhm nxt = ip6_hbhchcheck(mp, offp, NULL, flags); 1800b448d84Sbluhm if (nxt == IPPROTO_DONE) 1810b448d84Sbluhm return IPPROTO_DONE; 1820b448d84Sbluhm } 1830b448d84Sbluhm 18455159beeSbluhm /* We are already in a IPv4/IPv6 local deliver loop. */ 18555159beeSbluhm if (af != AF_UNSPEC) 186cfa8497dSbluhm return nxt; 18755159beeSbluhm 1887019ae97Sbluhm nxt = ip_deliver(mp, offp, nxt, AF_INET6, 1); 1897019ae97Sbluhm if (nxt == IPPROTO_DONE) 1907019ae97Sbluhm return IPPROTO_DONE; 1917019ae97Sbluhm 192*c77b3186Sbluhm return ip6_ours_enqueue(mp, offp, nxt); 193*c77b3186Sbluhm } 194*c77b3186Sbluhm 195*c77b3186Sbluhm int 196*c77b3186Sbluhm ip6_ours_enqueue(struct mbuf **mp, int *offp, int nxt) 197*c77b3186Sbluhm { 1980b448d84Sbluhm /* save values for later, use after dequeue */ 1990b448d84Sbluhm if (*offp != sizeof(struct ip6_hdr)) { 2000b448d84Sbluhm struct m_tag *mtag; 2017019ae97Sbluhm struct ipoffnxt *ion; 2020b448d84Sbluhm 2030b448d84Sbluhm /* mbuf tags are expensive, but only used for header options */ 2040b448d84Sbluhm mtag = m_tag_get(PACKET_TAG_IP6_OFFNXT, sizeof(*ion), 2050b448d84Sbluhm M_NOWAIT); 2060b448d84Sbluhm if (mtag == NULL) { 2070b448d84Sbluhm ip6stat_inc(ip6s_idropped); 2080b448d84Sbluhm m_freemp(mp); 2090b448d84Sbluhm return IPPROTO_DONE; 2100b448d84Sbluhm } 2117019ae97Sbluhm ion = (struct ipoffnxt *)(mtag + 1); 2120b448d84Sbluhm ion->ion_off = *offp; 2130b448d84Sbluhm ion->ion_nxt = nxt; 2140b448d84Sbluhm 2150b448d84Sbluhm m_tag_prepend(*mp, mtag); 2160b448d84Sbluhm } 2170b448d84Sbluhm 21855159beeSbluhm niq_enqueue(&ip6intrq, *mp); 21955159beeSbluhm *mp = NULL; 22055159beeSbluhm return IPPROTO_DONE; 22155159beeSbluhm } 22255159beeSbluhm 22355159beeSbluhm /* 22455159beeSbluhm * Dequeue and process locally delivered packets. 2256a1c2aefSbluhm * This is called with exclusive NET_LOCK(). 22655159beeSbluhm */ 22755159beeSbluhm void 22855159beeSbluhm ip6intr(void) 22955159beeSbluhm { 23055159beeSbluhm struct mbuf *m; 23155159beeSbluhm 23255159beeSbluhm while ((m = niq_dequeue(&ip6intrq)) != NULL) { 233cfa8497dSbluhm struct m_tag *mtag; 234cfa8497dSbluhm int off, nxt; 235cfa8497dSbluhm 23655159beeSbluhm #ifdef DIAGNOSTIC 23755159beeSbluhm if ((m->m_flags & M_PKTHDR) == 0) 23855159beeSbluhm panic("ip6intr no HDR"); 23955159beeSbluhm #endif 240cfa8497dSbluhm mtag = m_tag_find(m, PACKET_TAG_IP6_OFFNXT, NULL); 241cfa8497dSbluhm if (mtag != NULL) { 2427019ae97Sbluhm struct ipoffnxt *ion; 243cfa8497dSbluhm 2447019ae97Sbluhm ion = (struct ipoffnxt *)(mtag + 1); 245cfa8497dSbluhm off = ion->ion_off; 246cfa8497dSbluhm nxt = ion->ion_nxt; 247cfa8497dSbluhm 248cfa8497dSbluhm m_tag_delete(m, mtag); 249cfa8497dSbluhm } else { 250cfa8497dSbluhm struct ip6_hdr *ip6; 251cfa8497dSbluhm 252cfa8497dSbluhm ip6 = mtod(m, struct ip6_hdr *); 253cfa8497dSbluhm off = sizeof(struct ip6_hdr); 254cfa8497dSbluhm nxt = ip6->ip6_nxt; 255cfa8497dSbluhm } 2567019ae97Sbluhm nxt = ip_deliver(&m, &off, nxt, AF_INET6, 0); 25755159beeSbluhm KASSERT(nxt == IPPROTO_DONE); 25855159beeSbluhm } 25955159beeSbluhm } 26055159beeSbluhm 261287546eaSitojun void 2624cf8e838Smpi ipv6_input(struct ifnet *ifp, struct mbuf *m) 263287546eaSitojun { 264408c0df6Sbluhm int off, nxt; 265408c0df6Sbluhm 266408c0df6Sbluhm off = 0; 267408c0df6Sbluhm nxt = ip6_input_if(&m, &off, IPPROTO_IPV6, AF_UNSPEC, ifp); 268408c0df6Sbluhm KASSERT(nxt == IPPROTO_DONE); 269408c0df6Sbluhm } 270408c0df6Sbluhm 27114a6ddb0Sdlg struct mbuf * 27214a6ddb0Sdlg ipv6_check(struct ifnet *ifp, struct mbuf *m) 273408c0df6Sbluhm { 274287546eaSitojun struct ip6_hdr *ip6; 275287546eaSitojun 27614a6ddb0Sdlg if (m->m_len < sizeof(*ip6)) { 27714a6ddb0Sdlg m = m_pullup(m, sizeof(*ip6)); 27814a6ddb0Sdlg if (m == NULL) { 27931e14cacSjca ip6stat_inc(ip6s_toosmall); 28014a6ddb0Sdlg return (NULL); 281287546eaSitojun } 282287546eaSitojun } 283287546eaSitojun 284287546eaSitojun ip6 = mtod(m, struct ip6_hdr *); 285287546eaSitojun 286287546eaSitojun if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { 28731e14cacSjca ip6stat_inc(ip6s_badvers); 288287546eaSitojun goto bad; 289287546eaSitojun } 290287546eaSitojun 291287546eaSitojun /* 29234deef1eSitojun * Check against address spoofing/corruption. 293287546eaSitojun */ 294287546eaSitojun if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || 295287546eaSitojun IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { 29634deef1eSitojun /* 29734deef1eSitojun * XXX: "badscope" is not very suitable for a multicast source. 29834deef1eSitojun */ 29931e14cacSjca ip6stat_inc(ip6s_badscope); 300287546eaSitojun goto bad; 301287546eaSitojun } 3025ebd6447Sbluhm if ((IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || 3035ebd6447Sbluhm IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) && 3040237948fSmpi (ifp->if_flags & IFF_LOOPBACK) == 0) { 30531e14cacSjca ip6stat_inc(ip6s_badscope); 3065ebd6447Sbluhm goto bad; 3075ebd6447Sbluhm } 308bc3c7896Smpi /* Drop packets if interface ID portion is already filled. */ 309bc3c7896Smpi if (((IN6_IS_SCOPE_EMBED(&ip6->ip6_src) && ip6->ip6_src.s6_addr16[1]) || 310bc3c7896Smpi (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst) && ip6->ip6_dst.s6_addr16[1])) && 311bc3c7896Smpi (ifp->if_flags & IFF_LOOPBACK) == 0) { 31231e14cacSjca ip6stat_inc(ip6s_badscope); 313bc3c7896Smpi goto bad; 314bc3c7896Smpi } 3153ecd4d56Sitojun if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) && 3163ecd4d56Sitojun !(m->m_flags & M_LOOP)) { 3173ecd4d56Sitojun /* 3183ecd4d56Sitojun * In this case, the packet should come from the loopback 3193ecd4d56Sitojun * interface. However, we cannot just check the if_flags, 3203ecd4d56Sitojun * because ip6_mloopback() passes the "actual" interface 3213ecd4d56Sitojun * as the outgoing/incoming interface. 3223ecd4d56Sitojun */ 32331e14cacSjca ip6stat_inc(ip6s_badscope); 3243ecd4d56Sitojun goto bad; 3253ecd4d56Sitojun } 3263ecd4d56Sitojun 32770b0445bSitojun /* 32834deef1eSitojun * The following check is not documented in specs. A malicious 32934deef1eSitojun * party may be able to use IPv4 mapped addr to confuse tcp/udp stack 33034deef1eSitojun * and bypass security checks (act as if it was from 127.0.0.1 by using 33170b0445bSitojun * IPv6 src ::ffff:127.0.0.1). Be cautious. 33235c96f18Sitojun * 33334deef1eSitojun * This check chokes if we are in an SIIT cloud. As none of BSDs 33434deef1eSitojun * support IPv4-less kernel compilation, we cannot support SIIT 33534deef1eSitojun * environment at all. So, it makes more sense for us to reject any 33634deef1eSitojun * malicious packets for non-SIIT environment, than try to do a 337d8a7e3a7Sitojun * partial support for SIIT environment. 33870b0445bSitojun */ 33970b0445bSitojun if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || 34070b0445bSitojun IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { 34131e14cacSjca ip6stat_inc(ip6s_badscope); 34270b0445bSitojun goto bad; 34370b0445bSitojun } 344769273cbSmpi 34570b0445bSitojun /* 34670b0445bSitojun * Reject packets with IPv4 compatible addresses (auto tunnel). 34770b0445bSitojun * 348769273cbSmpi * The code forbids automatic tunneling as per RFC4213. 34970b0445bSitojun */ 35070b0445bSitojun if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) || 35170b0445bSitojun IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { 35231e14cacSjca ip6stat_inc(ip6s_badscope); 35370b0445bSitojun goto bad; 35470b0445bSitojun } 3553d5b41e1Sitojun 35614a6ddb0Sdlg return (m); 35714a6ddb0Sdlg bad: 35814a6ddb0Sdlg m_freem(m); 35914a6ddb0Sdlg return (NULL); 36014a6ddb0Sdlg } 36114a6ddb0Sdlg 36214a6ddb0Sdlg int 36314a6ddb0Sdlg ip6_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp) 36414a6ddb0Sdlg { 36500dd3069Sbluhm struct route ro; 366677ce7a9Sdlg struct mbuf *m; 36714a6ddb0Sdlg struct ip6_hdr *ip6; 36800dd3069Sbluhm struct rtentry *rt; 36914a6ddb0Sdlg int ours = 0; 37014a6ddb0Sdlg u_int16_t src_scope, dst_scope; 37114a6ddb0Sdlg #if NPF > 0 37214a6ddb0Sdlg struct in6_addr odst; 37314a6ddb0Sdlg #endif 374ab457133Sbluhm int flags = 0; 37514a6ddb0Sdlg 37614a6ddb0Sdlg KASSERT(*offp == 0); 37714a6ddb0Sdlg 37800dd3069Sbluhm ro.ro_rt = NULL; 37914a6ddb0Sdlg ip6stat_inc(ip6s_total); 38014a6ddb0Sdlg m = *mp = ipv6_check(ifp, *mp); 38114a6ddb0Sdlg if (m == NULL) 38214a6ddb0Sdlg goto bad; 38314a6ddb0Sdlg 384afdfad04Sdlg ip6 = mtod(m, struct ip6_hdr *); 385afdfad04Sdlg 38614a6ddb0Sdlg #if NCARP > 0 38714a6ddb0Sdlg if (carp_lsdrop(ifp, m, AF_INET6, ip6->ip6_src.s6_addr32, 38814a6ddb0Sdlg ip6->ip6_dst.s6_addr32, (ip6->ip6_nxt == IPPROTO_ICMPV6 ? 0 : 1))) 38914a6ddb0Sdlg goto bad; 39014a6ddb0Sdlg #endif 39114a6ddb0Sdlg ip6stat_inc(ip6s_nxthist + ip6->ip6_nxt); 39214a6ddb0Sdlg 393bc3c7896Smpi /* 394bc3c7896Smpi * If the packet has been received on a loopback interface it 395df8d9afdSjsg * can be destined to any local address, not necessarily to 396bc3c7896Smpi * an address configured on `ifp'. 397bc3c7896Smpi */ 398cd5ec7edSmpi if (ifp->if_flags & IFF_LOOPBACK) { 399cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) { 400cd5ec7edSmpi src_scope = ip6->ip6_src.s6_addr16[1]; 401cd5ec7edSmpi ip6->ip6_src.s6_addr16[1] = 0; 402cd5ec7edSmpi } 403cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) { 404cd5ec7edSmpi dst_scope = ip6->ip6_dst.s6_addr16[1]; 405cd5ec7edSmpi ip6->ip6_dst.s6_addr16[1] = 0; 406cd5ec7edSmpi } 407cd5ec7edSmpi } 408cd5ec7edSmpi 4096104dc03Sdhartmei #if NPF > 0 4106104dc03Sdhartmei /* 4116104dc03Sdhartmei * Packet filter 4126104dc03Sdhartmei */ 4136104dc03Sdhartmei odst = ip6->ip6_dst; 414408c0df6Sbluhm if (pf_test(AF_INET6, PF_IN, ifp, mp) != PF_PASS) 4156104dc03Sdhartmei goto bad; 416408c0df6Sbluhm m = *mp; 4176104dc03Sdhartmei if (m == NULL) 4185d8daa1dSclaudio goto bad; 4196104dc03Sdhartmei 4206104dc03Sdhartmei ip6 = mtod(m, struct ip6_hdr *); 421ab457133Sbluhm if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) 422ab457133Sbluhm SET(flags, IPV6_REDIRECT); 4236104dc03Sdhartmei #endif 4246104dc03Sdhartmei 4251f9e444eSbluhm switch (atomic_load_int(&ip6_forwarding)) { 42628c60e63Sbluhm case 2: 42728c60e63Sbluhm SET(flags, IPV6_FORWARDING_IPSEC); 42828c60e63Sbluhm /* FALLTHROUGH */ 42928c60e63Sbluhm case 1: 430ab457133Sbluhm SET(flags, IPV6_FORWARDING); 43128c60e63Sbluhm break; 43228c60e63Sbluhm } 433ab457133Sbluhm 434642d98a2Sbluhm /* 435cd5ec7edSmpi * Without embedded scope ID we cannot find link-local 436cd5ec7edSmpi * addresses in the routing table. 437cd5ec7edSmpi */ 438cd5ec7edSmpi if (ifp->if_flags & IFF_LOOPBACK) { 439cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) 440cd5ec7edSmpi ip6->ip6_src.s6_addr16[1] = src_scope; 441cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) 442cd5ec7edSmpi ip6->ip6_dst.s6_addr16[1] = dst_scope; 443cd5ec7edSmpi } else { 444cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_src)) 445cd5ec7edSmpi ip6->ip6_src.s6_addr16[1] = htons(ifp->if_index); 446cd5ec7edSmpi if (IN6_IS_SCOPE_EMBED(&ip6->ip6_dst)) 447cd5ec7edSmpi ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index); 448cd5ec7edSmpi } 449cd5ec7edSmpi 450cd5ec7edSmpi /* 451642d98a2Sbluhm * Be more secure than RFC5095 and scan for type 0 routing headers. 452642d98a2Sbluhm * If pf has already scanned the header chain, do not do it twice. 453642d98a2Sbluhm */ 454642d98a2Sbluhm if (!(m->m_pkthdr.pf.flags & PF_TAG_PROCESSED) && 455408c0df6Sbluhm ip6_check_rh0hdr(m, offp)) { 45631e14cacSjca ip6stat_inc(ip6s_badoptions); 457408c0df6Sbluhm icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, *offp); 458408c0df6Sbluhm m = *mp = NULL; 459408c0df6Sbluhm goto bad; 460642d98a2Sbluhm } 461642d98a2Sbluhm 4620b43e8a0Sphessler #if NPF > 0 4637d717e4fSbluhm if (pf_ouraddr(m) == 1) { 464ab457133Sbluhm nxt = ip6_ours(mp, offp, nxt, af, flags); 4651f32dc8dSmpi goto out; 466a892881fSmarkus } 4670b43e8a0Sphessler #endif 468a892881fSmarkus 469287546eaSitojun /* 470287546eaSitojun * Multicast check 471287546eaSitojun */ 472287546eaSitojun if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 4737e5860a9Smpi /* 4747e5860a9Smpi * Make sure M_MCAST is set. It should theoretically 4757e5860a9Smpi * already be there, but let's play safe because upper 4767e5860a9Smpi * layers check for this flag. 4777e5860a9Smpi */ 4787e5860a9Smpi m->m_flags |= M_MCAST; 4797e5860a9Smpi 480287546eaSitojun /* 481287546eaSitojun * See if we belong to the destination multicast group on the 482287546eaSitojun * arrival interface. 483287546eaSitojun */ 48434dbf6d6Smpi if (in6_hasmulti(&ip6->ip6_dst, ifp)) 485287546eaSitojun ours = 1; 4866ce3ccbdSmpi 487d9c6379eSclaudio #ifdef MROUTING 4885d692803Srzalamena if (ip6_mforwarding && ip6_mrouter[ifp->if_rdomain]) { 4897a3dea7eSbluhm int error; 4907a3dea7eSbluhm 491ab457133Sbluhm nxt = ip6_hbhchcheck(&m, offp, &ours, flags); 4925b1db52dSbluhm if (nxt == IPPROTO_DONE) 493d88f5bbfSbluhm goto out; 4946ce3ccbdSmpi 4956ce3ccbdSmpi ip6 = mtod(m, struct ip6_hdr *); 4966ce3ccbdSmpi 4976ce3ccbdSmpi /* 4986ce3ccbdSmpi * If we are acting as a multicast router, all 4996ce3ccbdSmpi * incoming multicast packets are passed to the 5006ce3ccbdSmpi * kernel-level multicast forwarding function. 5016ce3ccbdSmpi * The packet is returned (relatively) intact; if 5026ce3ccbdSmpi * ip6_mforward() returns a non-zero value, the packet 5036ce3ccbdSmpi * must be discarded, else it may be accepted below. 5046ce3ccbdSmpi */ 5051f32dc8dSmpi KERNEL_LOCK(); 50628c60e63Sbluhm error = ip6_mforward(ip6, ifp, m, flags); 5077a3dea7eSbluhm KERNEL_UNLOCK(); 5087a3dea7eSbluhm if (error) { 50931e14cacSjca ip6stat_inc(ip6s_cantforward); 5107a3dea7eSbluhm goto bad; 5116ce3ccbdSmpi } 5127a3dea7eSbluhm 5137a3dea7eSbluhm if (ours) { 51498e6f338Sbluhm if (af == AF_UNSPEC) 515ab457133Sbluhm nxt = ip6_ours(mp, offp, nxt, af, 516ab457133Sbluhm flags); 517d88f5bbfSbluhm goto out; 5186ce3ccbdSmpi } 5197a3dea7eSbluhm goto bad; 5207a3dea7eSbluhm } 521d9c6379eSclaudio #endif 5226ce3ccbdSmpi if (!ours) { 52331e14cacSjca ip6stat_inc(ip6s_notmember); 524f737337cSmpf if (!IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst)) 52531e14cacSjca ip6stat_inc(ip6s_cantforward); 526287546eaSitojun goto bad; 527287546eaSitojun } 528ab457133Sbluhm nxt = ip6_ours(mp, offp, nxt, af, flags); 5291f32dc8dSmpi goto out; 530287546eaSitojun } 531287546eaSitojun 532d1e0275eSmpi 533287546eaSitojun /* 534287546eaSitojun * Unicast check 535287546eaSitojun */ 53600dd3069Sbluhm rt = route6_mpath(&ro, &ip6->ip6_dst, &ip6->ip6_src, 537d1e0275eSmpi m->m_pkthdr.ph_rtableid); 538287546eaSitojun 539287546eaSitojun /* 540bc3c7896Smpi * Accept the packet if the route to the destination is marked 541bc3c7896Smpi * as local. 542287546eaSitojun */ 54300dd3069Sbluhm if (rt != NULL && ISSET(rt->rt_flags, RTF_LOCAL)) { 544d1e0275eSmpi struct in6_ifaddr *ia6 = ifatoia6(rt->rt_ifa); 545144d7e8eSsashan 546ab457133Sbluhm if (!ISSET(flags, IPV6_FORWARDING) && 547ab457133Sbluhm rt->rt_ifidx != ifp->if_index && 548144d7e8eSsashan !((ifp->if_flags & IFF_LOOPBACK) || 549fe572d31Ssashan (ifp->if_type == IFT_ENC) || 550fe572d31Ssashan (m->m_pkthdr.pf.flags & PF_TAG_TRANSLATE_LOCALHOST))) { 551144d7e8eSsashan /* received on wrong interface */ 552144d7e8eSsashan #if NCARP > 0 553144d7e8eSsashan struct ifnet *out_if; 554144d7e8eSsashan 555144d7e8eSsashan /* 556144d7e8eSsashan * Virtual IPs on carp interfaces need to be checked 557144d7e8eSsashan * also against the parent interface and other carp 558144d7e8eSsashan * interfaces sharing the same parent. 559144d7e8eSsashan */ 560144d7e8eSsashan out_if = if_get(rt->rt_ifidx); 561144d7e8eSsashan if (!(out_if && carp_strict_addr_chk(out_if, ifp))) { 562144d7e8eSsashan ip6stat_inc(ip6s_wrongif); 563144d7e8eSsashan if_put(out_if); 564144d7e8eSsashan goto bad; 565144d7e8eSsashan } 566144d7e8eSsashan if_put(out_if); 567144d7e8eSsashan #else 568144d7e8eSsashan ip6stat_inc(ip6s_wrongif); 569144d7e8eSsashan goto bad; 570144d7e8eSsashan #endif 571144d7e8eSsashan } 5721b46b530Sitojun /* 5731b46b530Sitojun * packets to a tentative, duplicated, or somehow invalid 5741b46b530Sitojun * address must not be accepted. 5751b46b530Sitojun */ 576b2ea68e0Smpi if ((ia6->ia6_flags & (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED))) { 577bbcf0337Smpi char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; 578bbcf0337Smpi 579bbcf0337Smpi inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)); 580bbcf0337Smpi inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst)); 5811b46b530Sitojun /* address is not ready, so discard the packet. */ 582b79da24aSitojun nd6log((LOG_INFO, 5834cf8e838Smpi "%s: packet to an unready address %s->%s\n", 5844cf8e838Smpi __func__, src, dst)); 5851b46b530Sitojun 5861b46b530Sitojun goto bad; 587b2ea68e0Smpi } else { 588ab457133Sbluhm nxt = ip6_ours(mp, offp, nxt, af, flags); 5891f32dc8dSmpi goto out; 590287546eaSitojun } 591287546eaSitojun } 592287546eaSitojun 593ca90c36bSmpf #if NCARP > 0 5943664055bSbluhm if (ip6->ip6_nxt == IPPROTO_ICMPV6 && 5953664055bSbluhm carp_lsdrop(ifp, m, AF_INET6, ip6->ip6_src.s6_addr32, 59676dda2b0Sfriehm ip6->ip6_dst.s6_addr32, 1)) 597ca90c36bSmpf goto bad; 598ca90c36bSmpf #endif 599287546eaSitojun /* 600287546eaSitojun * Now there is no reason to process the packet if it's not our own 601287546eaSitojun * and we're not a router. 602287546eaSitojun */ 603ab457133Sbluhm if (!ISSET(flags, IPV6_FORWARDING)) { 60431e14cacSjca ip6stat_inc(ip6s_cantforward); 605287546eaSitojun goto bad; 606287546eaSitojun } 607287546eaSitojun 608ab457133Sbluhm nxt = ip6_hbhchcheck(&m, offp, &ours, flags); 6095b1db52dSbluhm if (nxt == IPPROTO_DONE) 610d88f5bbfSbluhm goto out; 6112a116b59Sitojun 612d88f5bbfSbluhm if (ours) { 61398e6f338Sbluhm if (af == AF_UNSPEC) 614ab457133Sbluhm nxt = ip6_ours(mp, offp, nxt, af, flags); 615d88f5bbfSbluhm goto out; 616d88f5bbfSbluhm } 6172a116b59Sitojun 618a0744d33Sbluhm #ifdef IPSEC 619a0744d33Sbluhm if (ipsec_in_use) { 620a0744d33Sbluhm int rv; 621a0744d33Sbluhm 622408c0df6Sbluhm rv = ipsec_forward_check(m, *offp, AF_INET6); 623a0744d33Sbluhm if (rv != 0) { 624e6246f3dSkettenis ip6stat_inc(ip6s_cantforward); 625a0744d33Sbluhm goto bad; 626a0744d33Sbluhm } 627a0744d33Sbluhm /* 628a0744d33Sbluhm * Fall through, forward packet. Outbound IPsec policy 629a0744d33Sbluhm * checking will occur in ip6_forward(). 630a0744d33Sbluhm */ 631a0744d33Sbluhm } 632a0744d33Sbluhm #endif /* IPSEC */ 633a0744d33Sbluhm 634ab457133Sbluhm ip6_forward(m, &ro, flags); 635408c0df6Sbluhm *mp = NULL; 636652f3531Sbluhm rtfree(ro.ro_rt); 637408c0df6Sbluhm return IPPROTO_DONE; 638d88f5bbfSbluhm bad: 639408c0df6Sbluhm nxt = IPPROTO_DONE; 640408c0df6Sbluhm m_freemp(mp); 641d88f5bbfSbluhm out: 64200dd3069Sbluhm rtfree(ro.ro_rt); 643408c0df6Sbluhm return nxt; 644287546eaSitojun } 645d88f5bbfSbluhm 646bb8a5d95Sbluhm /* On error free mbuf and return IPPROTO_DONE. */ 647ba38042cSmpi int 648ab457133Sbluhm ip6_hbhchcheck(struct mbuf **mp, int *offp, int *oursp, int flags) 649ba38042cSmpi { 650ba38042cSmpi struct ip6_hdr *ip6; 651ba38042cSmpi u_int32_t plen, rtalert = ~0; 6525b1db52dSbluhm int nxt; 653ba38042cSmpi 654bb8a5d95Sbluhm ip6 = mtod(*mp, struct ip6_hdr *); 655ba38042cSmpi 656ba38042cSmpi /* 657ba38042cSmpi * Process Hop-by-Hop options header if it's contained. 658ba38042cSmpi * m may be modified in ip6_hopopts_input(). 659ba38042cSmpi * If a JumboPayload option is included, plen will also be modified. 660ba38042cSmpi */ 661ba38042cSmpi plen = (u_int32_t)ntohs(ip6->ip6_plen); 662ba38042cSmpi *offp = sizeof(struct ip6_hdr); 663ba38042cSmpi if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { 664ba38042cSmpi struct ip6_hbh *hbh; 665ba38042cSmpi 66680467c39Sbluhm if (ip6_hopopts_input(mp, offp, &plen, &rtalert)) 667408c0df6Sbluhm goto bad; /* m have already been freed */ 668ba38042cSmpi 669ba38042cSmpi /* adjust pointer */ 670bb8a5d95Sbluhm ip6 = mtod(*mp, struct ip6_hdr *); 671ba38042cSmpi 672ba38042cSmpi /* 673ba38042cSmpi * if the payload length field is 0 and the next header field 674ba38042cSmpi * indicates Hop-by-Hop Options header, then a Jumbo Payload 675ba38042cSmpi * option MUST be included. 676ba38042cSmpi */ 677ba38042cSmpi if (ip6->ip6_plen == 0 && plen == 0) { 678ba38042cSmpi /* 679ba38042cSmpi * Note that if a valid jumbo payload option is 680ba38042cSmpi * contained, ip6_hopopts_input() must set a valid 681ba38042cSmpi * (non-zero) payload length to the variable plen. 682ba38042cSmpi */ 68331e14cacSjca ip6stat_inc(ip6s_badoptions); 684bb8a5d95Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 685ba38042cSmpi ICMP6_PARAMPROB_HEADER, 686ba38042cSmpi (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); 687408c0df6Sbluhm goto bad; 688ba38042cSmpi } 689bb8a5d95Sbluhm IP6_EXTHDR_GET(hbh, struct ip6_hbh *, *mp, 690bb8a5d95Sbluhm sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); 691ba38042cSmpi if (hbh == NULL) { 69231e14cacSjca ip6stat_inc(ip6s_tooshort); 693408c0df6Sbluhm goto bad; 694ba38042cSmpi } 6955b1db52dSbluhm nxt = hbh->ip6h_nxt; 696ba38042cSmpi 697ba38042cSmpi /* 698ba38042cSmpi * accept the packet if a router alert option is included 699ba38042cSmpi * and we act as an IPv6 router. 700ba38042cSmpi */ 701ab457133Sbluhm if (rtalert != ~0 && ISSET(flags, IPV6_FORWARDING) && 702ab457133Sbluhm oursp != NULL) 703ba38042cSmpi *oursp = 1; 704ba38042cSmpi } else 7055b1db52dSbluhm nxt = ip6->ip6_nxt; 706ba38042cSmpi 707ba38042cSmpi /* 708ba38042cSmpi * Check that the amount of data in the buffers 709ba38042cSmpi * is as at least much as the IPv6 header would have us expect. 710ba38042cSmpi * Trim mbufs if longer than we expect. 711ba38042cSmpi * Drop packet if shorter than we expect. 712ba38042cSmpi */ 713bb8a5d95Sbluhm if ((*mp)->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { 71431e14cacSjca ip6stat_inc(ip6s_tooshort); 715bb8a5d95Sbluhm m_freemp(mp); 716408c0df6Sbluhm goto bad; 717ba38042cSmpi } 718bb8a5d95Sbluhm if ((*mp)->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { 719bb8a5d95Sbluhm if ((*mp)->m_len == (*mp)->m_pkthdr.len) { 720bb8a5d95Sbluhm (*mp)->m_len = sizeof(struct ip6_hdr) + plen; 721bb8a5d95Sbluhm (*mp)->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; 722ba38042cSmpi } else { 723bb8a5d95Sbluhm m_adj((*mp), sizeof(struct ip6_hdr) + plen - 724bb8a5d95Sbluhm (*mp)->m_pkthdr.len); 725ba38042cSmpi } 726ba38042cSmpi } 727ba38042cSmpi 7285b1db52dSbluhm return nxt; 729408c0df6Sbluhm bad: 7305b1db52dSbluhm return IPPROTO_DONE; 731ba38042cSmpi } 732ba38042cSmpi 733642d98a2Sbluhm /* scan packet for RH0 routing header. Mostly stolen from pf.c:pf_test() */ 734642d98a2Sbluhm int 735642d98a2Sbluhm ip6_check_rh0hdr(struct mbuf *m, int *offp) 736642d98a2Sbluhm { 737642d98a2Sbluhm struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 738642d98a2Sbluhm struct ip6_rthdr rthdr; 739642d98a2Sbluhm struct ip6_ext opt6; 740642d98a2Sbluhm u_int8_t proto = ip6->ip6_nxt; 741642d98a2Sbluhm int done = 0, lim, off, rh_cnt = 0; 742642d98a2Sbluhm 743642d98a2Sbluhm off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); 744642d98a2Sbluhm lim = min(m->m_pkthdr.len, ntohs(ip6->ip6_plen) + sizeof(*ip6)); 745642d98a2Sbluhm do { 746642d98a2Sbluhm switch (proto) { 747642d98a2Sbluhm case IPPROTO_ROUTING: 748642d98a2Sbluhm if (rh_cnt++) { 749ec18dcd3Sjca /* more than one rh header present */ 750f42e2e0aSbluhm *offp = off; 751642d98a2Sbluhm return (1); 752642d98a2Sbluhm } 753642d98a2Sbluhm 754642d98a2Sbluhm if (off + sizeof(rthdr) > lim) { 755642d98a2Sbluhm /* packet to short to make sense */ 756f42e2e0aSbluhm *offp = off; 757642d98a2Sbluhm return (1); 758642d98a2Sbluhm } 759642d98a2Sbluhm 7605c7fed39Sdlg m_copydata(m, off, sizeof(rthdr), &rthdr); 761642d98a2Sbluhm 762642d98a2Sbluhm if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) { 763f42e2e0aSbluhm *offp = off + 764f42e2e0aSbluhm offsetof(struct ip6_rthdr, ip6r_type); 765642d98a2Sbluhm return (1); 766642d98a2Sbluhm } 767642d98a2Sbluhm 768642d98a2Sbluhm off += (rthdr.ip6r_len + 1) * 8; 769642d98a2Sbluhm proto = rthdr.ip6r_nxt; 770642d98a2Sbluhm break; 771642d98a2Sbluhm case IPPROTO_AH: 772642d98a2Sbluhm case IPPROTO_HOPOPTS: 773642d98a2Sbluhm case IPPROTO_DSTOPTS: 774642d98a2Sbluhm /* get next header and header length */ 775642d98a2Sbluhm if (off + sizeof(opt6) > lim) { 776642d98a2Sbluhm /* 777642d98a2Sbluhm * Packet to short to make sense, we could 778642d98a2Sbluhm * reject the packet but as a router we 779642d98a2Sbluhm * should not do that so forward it. 780642d98a2Sbluhm */ 781642d98a2Sbluhm return (0); 782642d98a2Sbluhm } 783642d98a2Sbluhm 7845c7fed39Sdlg m_copydata(m, off, sizeof(opt6), &opt6); 785642d98a2Sbluhm 786642d98a2Sbluhm if (proto == IPPROTO_AH) 787642d98a2Sbluhm off += (opt6.ip6e_len + 2) * 4; 788642d98a2Sbluhm else 789642d98a2Sbluhm off += (opt6.ip6e_len + 1) * 8; 790642d98a2Sbluhm proto = opt6.ip6e_nxt; 791642d98a2Sbluhm break; 792642d98a2Sbluhm case IPPROTO_FRAGMENT: 793642d98a2Sbluhm default: 794642d98a2Sbluhm /* end of header stack */ 795642d98a2Sbluhm done = 1; 796642d98a2Sbluhm break; 797642d98a2Sbluhm } 798642d98a2Sbluhm } while (!done); 799642d98a2Sbluhm 800642d98a2Sbluhm return (0); 801642d98a2Sbluhm } 802642d98a2Sbluhm 803287546eaSitojun /* 804287546eaSitojun * Hop-by-Hop options header processing. If a valid jumbo payload option is 805287546eaSitojun * included, the real payload length will be stored in plenp. 806bb8a5d95Sbluhm * On error free mbuf and return -1. 807ee37ea65Smcbride * 808ee37ea65Smcbride * rtalertp - XXX: should be stored in a more smart way 809287546eaSitojun */ 810d4984c21Sjsing int 81180467c39Sbluhm ip6_hopopts_input(struct mbuf **mp, int *offp, u_int32_t *plenp, 81280467c39Sbluhm u_int32_t *rtalertp) 813287546eaSitojun { 814287546eaSitojun int off = *offp, hbhlen; 815287546eaSitojun struct ip6_hbh *hbh; 816287546eaSitojun 817287546eaSitojun /* validation of the length of the header */ 818bb8a5d95Sbluhm IP6_EXTHDR_GET(hbh, struct ip6_hbh *, *mp, 819287546eaSitojun sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); 820287546eaSitojun if (hbh == NULL) { 82131e14cacSjca ip6stat_inc(ip6s_tooshort); 822287546eaSitojun return -1; 823287546eaSitojun } 824287546eaSitojun hbhlen = (hbh->ip6h_len + 1) << 3; 825bb8a5d95Sbluhm IP6_EXTHDR_GET(hbh, struct ip6_hbh *, *mp, sizeof(struct ip6_hdr), 826287546eaSitojun hbhlen); 827287546eaSitojun if (hbh == NULL) { 82831e14cacSjca ip6stat_inc(ip6s_tooshort); 829287546eaSitojun return -1; 830287546eaSitojun } 831287546eaSitojun off += hbhlen; 832287546eaSitojun hbhlen -= sizeof(struct ip6_hbh); 833287546eaSitojun 83480467c39Sbluhm if (ip6_process_hopopts(mp, (u_int8_t *)hbh + sizeof(struct ip6_hbh), 835287546eaSitojun hbhlen, rtalertp, plenp) < 0) 836287546eaSitojun return (-1); 837287546eaSitojun 838287546eaSitojun *offp = off; 839287546eaSitojun return (0); 840287546eaSitojun } 841287546eaSitojun 842287546eaSitojun /* 843287546eaSitojun * Search header for all Hop-by-hop options and process each option. 844287546eaSitojun * This function is separate from ip6_hopopts_input() in order to 845287546eaSitojun * handle a case where the sending node itself process its hop-by-hop 846287546eaSitojun * options header. In such a case, the function is called from ip6_output(). 84780467c39Sbluhm * On error free mbuf and return -1. 848d8a7e3a7Sitojun * 849d8a7e3a7Sitojun * The function assumes that hbh header is located right after the IPv6 header 850d8a7e3a7Sitojun * (RFC2460 p7), opthead is pointer into data content in m, and opthead to 851d8a7e3a7Sitojun * opthead + hbhlen is located in continuous memory region. 852287546eaSitojun */ 853287546eaSitojun int 85480467c39Sbluhm ip6_process_hopopts(struct mbuf **mp, u_int8_t *opthead, int hbhlen, 855ee37ea65Smcbride u_int32_t *rtalertp, u_int32_t *plenp) 856287546eaSitojun { 857287546eaSitojun struct ip6_hdr *ip6; 858287546eaSitojun int optlen = 0; 859287546eaSitojun u_int8_t *opt = opthead; 860287546eaSitojun u_int16_t rtalert_val; 8612a116b59Sitojun u_int32_t jumboplen; 862d8a7e3a7Sitojun const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); 863287546eaSitojun 864287546eaSitojun for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { 865287546eaSitojun switch (*opt) { 866287546eaSitojun case IP6OPT_PAD1: 867287546eaSitojun optlen = 1; 868287546eaSitojun break; 869287546eaSitojun case IP6OPT_PADN: 870287546eaSitojun if (hbhlen < IP6OPT_MINLEN) { 87131e14cacSjca ip6stat_inc(ip6s_toosmall); 872287546eaSitojun goto bad; 873287546eaSitojun } 874287546eaSitojun optlen = *(opt + 1) + 2; 875287546eaSitojun break; 8766b532452Sitojun case IP6OPT_ROUTER_ALERT: 877287546eaSitojun /* XXX may need check for alignment */ 878287546eaSitojun if (hbhlen < IP6OPT_RTALERT_LEN) { 87931e14cacSjca ip6stat_inc(ip6s_toosmall); 880287546eaSitojun goto bad; 881287546eaSitojun } 882d8a7e3a7Sitojun if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { 883d8a7e3a7Sitojun /* XXX stat */ 88480467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 885d8a7e3a7Sitojun ICMP6_PARAMPROB_HEADER, 886d8a7e3a7Sitojun erroff + opt + 1 - opthead); 887d8a7e3a7Sitojun return (-1); 888d8a7e3a7Sitojun } 889287546eaSitojun optlen = IP6OPT_RTALERT_LEN; 890985a0e17Sdhill memcpy((caddr_t)&rtalert_val, (caddr_t)(opt + 2), 2); 891287546eaSitojun *rtalertp = ntohs(rtalert_val); 892287546eaSitojun break; 893287546eaSitojun case IP6OPT_JUMBO: 894287546eaSitojun /* XXX may need check for alignment */ 895287546eaSitojun if (hbhlen < IP6OPT_JUMBO_LEN) { 89631e14cacSjca ip6stat_inc(ip6s_toosmall); 897287546eaSitojun goto bad; 898287546eaSitojun } 899d8a7e3a7Sitojun if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { 900d8a7e3a7Sitojun /* XXX stat */ 90180467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 902d8a7e3a7Sitojun ICMP6_PARAMPROB_HEADER, 903d8a7e3a7Sitojun erroff + opt + 1 - opthead); 904d8a7e3a7Sitojun return (-1); 905d8a7e3a7Sitojun } 906287546eaSitojun optlen = IP6OPT_JUMBO_LEN; 907287546eaSitojun 908287546eaSitojun /* 909287546eaSitojun * IPv6 packets that have non 0 payload length 91035c96f18Sitojun * must not contain a jumbo payload option. 911287546eaSitojun */ 91280467c39Sbluhm ip6 = mtod(*mp, struct ip6_hdr *); 9132a116b59Sitojun if (ip6->ip6_plen) { 91431e14cacSjca ip6stat_inc(ip6s_badoptions); 91580467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 916287546eaSitojun ICMP6_PARAMPROB_HEADER, 917d8a7e3a7Sitojun erroff + opt - opthead); 918287546eaSitojun return (-1); 919287546eaSitojun } 9202a116b59Sitojun 9212a116b59Sitojun /* 9222a116b59Sitojun * We may see jumbolen in unaligned location, so 923985a0e17Sdhill * we'd need to perform memcpy(). 9242a116b59Sitojun */ 925985a0e17Sdhill memcpy(&jumboplen, opt + 2, sizeof(jumboplen)); 9262a116b59Sitojun jumboplen = (u_int32_t)htonl(jumboplen); 9272a116b59Sitojun 9282a116b59Sitojun #if 1 9292a116b59Sitojun /* 9302a116b59Sitojun * if there are multiple jumbo payload options, 9312a116b59Sitojun * *plenp will be non-zero and the packet will be 9322a116b59Sitojun * rejected. 9332a116b59Sitojun * the behavior may need some debate in ipngwg - 9342a116b59Sitojun * multiple options does not make sense, however, 9352a116b59Sitojun * there's no explicit mention in specification. 9362a116b59Sitojun */ 9372a116b59Sitojun if (*plenp != 0) { 93831e14cacSjca ip6stat_inc(ip6s_badoptions); 93980467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 9402a116b59Sitojun ICMP6_PARAMPROB_HEADER, 941d8a7e3a7Sitojun erroff + opt + 2 - opthead); 9422a116b59Sitojun return (-1); 9432a116b59Sitojun } 9442a116b59Sitojun #endif 9452a116b59Sitojun 9462a116b59Sitojun /* 9472a116b59Sitojun * jumbo payload length must be larger than 65535. 9482a116b59Sitojun */ 9492a116b59Sitojun if (jumboplen <= IPV6_MAXPACKET) { 95031e14cacSjca ip6stat_inc(ip6s_badoptions); 95180467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 9522a116b59Sitojun ICMP6_PARAMPROB_HEADER, 953d8a7e3a7Sitojun erroff + opt + 2 - opthead); 9542a116b59Sitojun return (-1); 9552a116b59Sitojun } 9562a116b59Sitojun *plenp = jumboplen; 9572a116b59Sitojun 958287546eaSitojun break; 959287546eaSitojun default: /* unknown option */ 960287546eaSitojun if (hbhlen < IP6OPT_MINLEN) { 96131e14cacSjca ip6stat_inc(ip6s_toosmall); 962287546eaSitojun goto bad; 963287546eaSitojun } 96480467c39Sbluhm optlen = ip6_unknown_opt(mp, opt, 965d8a7e3a7Sitojun erroff + opt - opthead); 966d8a7e3a7Sitojun if (optlen == -1) 967287546eaSitojun return (-1); 968287546eaSitojun optlen += 2; 969287546eaSitojun break; 970287546eaSitojun } 971287546eaSitojun } 972287546eaSitojun 973287546eaSitojun return (0); 974287546eaSitojun 975287546eaSitojun bad: 97680467c39Sbluhm m_freemp(mp); 977287546eaSitojun return (-1); 978287546eaSitojun } 979287546eaSitojun 980287546eaSitojun /* 981287546eaSitojun * Unknown option processing. 982287546eaSitojun * The third argument `off' is the offset from the IPv6 header to the option, 98317a5d70bSstsp * which allows returning an ICMPv6 error even if the IPv6 header and the 98417a5d70bSstsp * option header are not continuous. 98580467c39Sbluhm * On error free mbuf and return -1. 986287546eaSitojun */ 987287546eaSitojun int 98880467c39Sbluhm ip6_unknown_opt(struct mbuf **mp, u_int8_t *optp, int off) 989287546eaSitojun { 990287546eaSitojun struct ip6_hdr *ip6; 991287546eaSitojun 992287546eaSitojun switch (IP6OPT_TYPE(*optp)) { 993287546eaSitojun case IP6OPT_TYPE_SKIP: /* ignore the option */ 994287546eaSitojun return ((int)*(optp + 1)); 995287546eaSitojun case IP6OPT_TYPE_DISCARD: /* silently discard */ 99680467c39Sbluhm m_freemp(mp); 997287546eaSitojun return (-1); 998287546eaSitojun case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ 99931e14cacSjca ip6stat_inc(ip6s_badoptions); 100080467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); 1001287546eaSitojun return (-1); 1002287546eaSitojun case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ 100331e14cacSjca ip6stat_inc(ip6s_badoptions); 100480467c39Sbluhm ip6 = mtod(*mp, struct ip6_hdr *); 1005287546eaSitojun if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || 100680467c39Sbluhm ((*mp)->m_flags & (M_BCAST|M_MCAST))) 100780467c39Sbluhm m_freemp(mp); 1008287546eaSitojun else 100980467c39Sbluhm icmp6_error(*mp, ICMP6_PARAM_PROB, 1010287546eaSitojun ICMP6_PARAMPROB_OPTION, off); 1011287546eaSitojun return (-1); 1012287546eaSitojun } 1013287546eaSitojun 101480467c39Sbluhm m_freemp(mp); /* XXX: NOTREACHED */ 1015287546eaSitojun return (-1); 1016287546eaSitojun } 1017287546eaSitojun 1018287546eaSitojun /* 1019287546eaSitojun * Create the "control" list for this pcb. 1020287546eaSitojun * 102196be3d96Sbluhm * The routine will be called from upper layer handlers like udp_input(). 102296be3d96Sbluhm * Thus the routine assumes that the caller (udp_input) have already 1023287546eaSitojun * called IP6_EXTHDR_CHECK() and all the extension headers are located in the 1024287546eaSitojun * very first mbuf on the mbuf chain. 1025287546eaSitojun * We may want to add some infinite loop prevention or sanity checks for safety. 1026287546eaSitojun * (This applies only when you are using KAME mbuf chain restriction, i.e. 1027287546eaSitojun * you are using IP6_EXTHDR_CHECK() not m_pulldown()) 1028287546eaSitojun */ 1029287546eaSitojun void 1030921ffa12Sbluhm ip6_savecontrol(struct inpcb *inp, struct mbuf *m, struct mbuf **mp) 1031287546eaSitojun { 10326b532452Sitojun struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1033287546eaSitojun 1034921ffa12Sbluhm if (inp->inp_socket->so_options & SO_TIMESTAMP) { 1035287546eaSitojun struct timeval tv; 1036287546eaSitojun 10379f78c7ffSdlg m_microtime(m, &tv); 1038287546eaSitojun *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), 1039287546eaSitojun SCM_TIMESTAMP, SOL_SOCKET); 1040287546eaSitojun if (*mp) 1041287546eaSitojun mp = &(*mp)->m_next; 1042287546eaSitojun } 1043287546eaSitojun 1044287546eaSitojun /* RFC 2292 sec. 5 */ 1045921ffa12Sbluhm if ((inp->inp_flags & IN6P_PKTINFO) != 0) { 10467ee01f6fSitojun struct in6_pktinfo pi6; 1047985a0e17Sdhill memcpy(&pi6.ipi6_addr, &ip6->ip6_dst, sizeof(struct in6_addr)); 1048d4070096Sitojun if (IN6_IS_SCOPE_EMBED(&pi6.ipi6_addr)) 1049287546eaSitojun pi6.ipi6_addr.s6_addr16[1] = 0; 1050fb492c37Smpi pi6.ipi6_ifindex = m ? m->m_pkthdr.ph_ifidx : 0; 1051287546eaSitojun *mp = sbcreatecontrol((caddr_t) &pi6, 10526b532452Sitojun sizeof(struct in6_pktinfo), 10533c677259Stedu IPV6_PKTINFO, IPPROTO_IPV6); 1054287546eaSitojun if (*mp) 1055287546eaSitojun mp = &(*mp)->m_next; 1056287546eaSitojun } 10576b532452Sitojun 1058921ffa12Sbluhm if ((inp->inp_flags & IN6P_HOPLIMIT) != 0) { 1059287546eaSitojun int hlim = ip6->ip6_hlim & 0xff; 1060765c819cSitojun *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), 10613c677259Stedu IPV6_HOPLIMIT, IPPROTO_IPV6); 1062287546eaSitojun if (*mp) 1063287546eaSitojun mp = &(*mp)->m_next; 1064287546eaSitojun } 10656b532452Sitojun 1066921ffa12Sbluhm if ((inp->inp_flags & IN6P_TCLASS) != 0) { 10676b532452Sitojun u_int32_t flowinfo; 10686b532452Sitojun int tclass; 10696b532452Sitojun 10706b532452Sitojun flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK); 10716b532452Sitojun flowinfo >>= 20; 10726b532452Sitojun 10736b532452Sitojun tclass = flowinfo & 0xff; 10746b532452Sitojun *mp = sbcreatecontrol((caddr_t)&tclass, sizeof(tclass), 10756b532452Sitojun IPV6_TCLASS, IPPROTO_IPV6); 10766b532452Sitojun if (*mp) 10776b532452Sitojun mp = &(*mp)->m_next; 10786b532452Sitojun } 1079287546eaSitojun 1080287546eaSitojun /* 10814a483a86Sitojun * IPV6_HOPOPTS socket option. Recall that we required super-user 10824a483a86Sitojun * privilege for the option (see ip6_ctloutput), but it might be too 10834a483a86Sitojun * strict, since there might be some hop-by-hop options which can be 10844a483a86Sitojun * returned to normal user. 10856b532452Sitojun * See also RFC 2292 section 6 (or RFC 3542 section 8). 1086287546eaSitojun */ 1087921ffa12Sbluhm if ((inp->inp_flags & IN6P_HOPOPTS) != 0) { 1088287546eaSitojun /* 1089e3392f90Ssthen * Check if a hop-by-hop options header is contained in the 1090287546eaSitojun * received packet, and if so, store the options as ancillary 1091287546eaSitojun * data. Note that a hop-by-hop options header must be 10926b532452Sitojun * just after the IPv6 header, which is assured through the 10936b532452Sitojun * IPv6 input processing. 1094287546eaSitojun */ 1095287546eaSitojun struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1096287546eaSitojun if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { 1097287546eaSitojun struct ip6_hbh *hbh; 10986b532452Sitojun int hbhlen = 0; 10999c0ba9dfSitojun struct mbuf *ext; 1100287546eaSitojun 11019c0ba9dfSitojun ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr), 11029c0ba9dfSitojun ip6->ip6_nxt); 11039c0ba9dfSitojun if (ext == NULL) { 110431e14cacSjca ip6stat_inc(ip6s_tooshort); 1105287546eaSitojun return; 1106287546eaSitojun } 11079c0ba9dfSitojun hbh = mtod(ext, struct ip6_hbh *); 1108287546eaSitojun hbhlen = (hbh->ip6h_len + 1) << 3; 11099c0ba9dfSitojun if (hbhlen != ext->m_len) { 11109c0ba9dfSitojun m_freem(ext); 111131e14cacSjca ip6stat_inc(ip6s_tooshort); 1112287546eaSitojun return; 1113287546eaSitojun } 1114287546eaSitojun 1115287546eaSitojun /* 11166b532452Sitojun * XXX: We copy the whole header even if a 11176b532452Sitojun * jumbo payload option is included, the option which 11186b532452Sitojun * is to be removed before returning according to 11196b532452Sitojun * RFC2292. 11206b532452Sitojun * Note: this constraint is removed in RFC3542. 1121287546eaSitojun */ 1122287546eaSitojun *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, 11233c677259Stedu IPV6_HOPOPTS, 11246b532452Sitojun IPPROTO_IPV6); 1125287546eaSitojun if (*mp) 1126287546eaSitojun mp = &(*mp)->m_next; 11279c0ba9dfSitojun m_freem(ext); 1128287546eaSitojun } 1129287546eaSitojun } 1130287546eaSitojun 1131287546eaSitojun /* IPV6_DSTOPTS and IPV6_RTHDR socket options */ 1132921ffa12Sbluhm if ((inp->inp_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) { 1133287546eaSitojun struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 11349c0ba9dfSitojun int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr); 1135287546eaSitojun 1136287546eaSitojun /* 1137287546eaSitojun * Search for destination options headers or routing 1138287546eaSitojun * header(s) through the header chain, and stores each 1139287546eaSitojun * header as ancillary data. 1140287546eaSitojun * Note that the order of the headers remains in 1141287546eaSitojun * the chain of ancillary data. 1142287546eaSitojun */ 1143287546eaSitojun while (1) { /* is explicit loop prevention necessary? */ 11449c0ba9dfSitojun struct ip6_ext *ip6e = NULL; 1145287546eaSitojun int elen; 11469c0ba9dfSitojun struct mbuf *ext = NULL; 11479c0ba9dfSitojun 11489c0ba9dfSitojun /* 11499c0ba9dfSitojun * if it is not an extension header, don't try to 11509c0ba9dfSitojun * pull it from the chain. 11519c0ba9dfSitojun */ 11529c0ba9dfSitojun switch (nxt) { 11539c0ba9dfSitojun case IPPROTO_DSTOPTS: 11549c0ba9dfSitojun case IPPROTO_ROUTING: 11559c0ba9dfSitojun case IPPROTO_HOPOPTS: 11569c0ba9dfSitojun case IPPROTO_AH: /* is it possible? */ 11579c0ba9dfSitojun break; 11589c0ba9dfSitojun default: 11599c0ba9dfSitojun goto loopend; 11609c0ba9dfSitojun } 1161287546eaSitojun 11629c0ba9dfSitojun ext = ip6_pullexthdr(m, off, nxt); 11639c0ba9dfSitojun if (ext == NULL) { 116431e14cacSjca ip6stat_inc(ip6s_tooshort); 1165287546eaSitojun return; 1166287546eaSitojun } 11679c0ba9dfSitojun ip6e = mtod(ext, struct ip6_ext *); 1168287546eaSitojun if (nxt == IPPROTO_AH) 1169287546eaSitojun elen = (ip6e->ip6e_len + 2) << 2; 1170287546eaSitojun else 1171287546eaSitojun elen = (ip6e->ip6e_len + 1) << 3; 11729c0ba9dfSitojun if (elen != ext->m_len) { 11739c0ba9dfSitojun m_freem(ext); 117431e14cacSjca ip6stat_inc(ip6s_tooshort); 1175287546eaSitojun return; 1176287546eaSitojun } 1177287546eaSitojun 1178287546eaSitojun switch (nxt) { 1179287546eaSitojun case IPPROTO_DSTOPTS: 1180921ffa12Sbluhm if (!(inp->inp_flags & IN6P_DSTOPTS)) 1181287546eaSitojun break; 1182287546eaSitojun 1183287546eaSitojun *mp = sbcreatecontrol((caddr_t)ip6e, elen, 11843c677259Stedu IPV6_DSTOPTS, 11856b532452Sitojun IPPROTO_IPV6); 1186287546eaSitojun if (*mp) 1187287546eaSitojun mp = &(*mp)->m_next; 1188287546eaSitojun break; 1189287546eaSitojun 1190287546eaSitojun case IPPROTO_ROUTING: 1191921ffa12Sbluhm if (!(inp->inp_flags & IN6P_RTHDR)) 1192287546eaSitojun break; 1193287546eaSitojun 1194287546eaSitojun *mp = sbcreatecontrol((caddr_t)ip6e, elen, 11953c677259Stedu IPV6_RTHDR, 11966b532452Sitojun IPPROTO_IPV6); 1197287546eaSitojun if (*mp) 1198287546eaSitojun mp = &(*mp)->m_next; 1199287546eaSitojun break; 1200287546eaSitojun 1201287546eaSitojun case IPPROTO_HOPOPTS: 1202287546eaSitojun case IPPROTO_AH: /* is it possible? */ 1203287546eaSitojun break; 12049c0ba9dfSitojun 12059c0ba9dfSitojun default: 12069c0ba9dfSitojun /* 12079c0ba9dfSitojun * other cases have been filtered in the above. 12089c0ba9dfSitojun * none will visit this case. here we supply 12099c0ba9dfSitojun * the code just in case (nxt overwritten or 12109c0ba9dfSitojun * other cases). 12119c0ba9dfSitojun */ 12129c0ba9dfSitojun m_freem(ext); 12139c0ba9dfSitojun goto loopend; 12149c0ba9dfSitojun 1215287546eaSitojun } 1216287546eaSitojun 1217287546eaSitojun /* proceed with the next header. */ 1218287546eaSitojun off += elen; 1219287546eaSitojun nxt = ip6e->ip6e_nxt; 12209c0ba9dfSitojun ip6e = NULL; 12219c0ba9dfSitojun m_freem(ext); 12229c0ba9dfSitojun ext = NULL; 1223287546eaSitojun } 1224287546eaSitojun loopend: 1225f119cf51Sderaadt ; 1226287546eaSitojun } 1227287546eaSitojun } 1228287546eaSitojun 12299c0ba9dfSitojun /* 12309c0ba9dfSitojun * pull single extension header from mbuf chain. returns single mbuf that 12319c0ba9dfSitojun * contains the result, or NULL on error. 12329c0ba9dfSitojun */ 1233d4984c21Sjsing struct mbuf * 1234ee37ea65Smcbride ip6_pullexthdr(struct mbuf *m, size_t off, int nxt) 12359c0ba9dfSitojun { 12369c0ba9dfSitojun struct ip6_ext ip6e; 12379c0ba9dfSitojun size_t elen; 12389c0ba9dfSitojun struct mbuf *n; 12399c0ba9dfSitojun 12409c0ba9dfSitojun #ifdef DIAGNOSTIC 12419c0ba9dfSitojun switch (nxt) { 12429c0ba9dfSitojun case IPPROTO_DSTOPTS: 12439c0ba9dfSitojun case IPPROTO_ROUTING: 12449c0ba9dfSitojun case IPPROTO_HOPOPTS: 12459c0ba9dfSitojun case IPPROTO_AH: /* is it possible? */ 12469c0ba9dfSitojun break; 12479c0ba9dfSitojun default: 12489c0ba9dfSitojun printf("ip6_pullexthdr: invalid nxt=%d\n", nxt); 12499c0ba9dfSitojun } 12509c0ba9dfSitojun #endif 12519c0ba9dfSitojun 1252574b3a4fSmpi if (off + sizeof(ip6e) > m->m_pkthdr.len) 1253574b3a4fSmpi return NULL; 1254574b3a4fSmpi 12555c7fed39Sdlg m_copydata(m, off, sizeof(ip6e), &ip6e); 12569c0ba9dfSitojun if (nxt == IPPROTO_AH) 12579c0ba9dfSitojun elen = (ip6e.ip6e_len + 2) << 2; 12589c0ba9dfSitojun else 12599c0ba9dfSitojun elen = (ip6e.ip6e_len + 1) << 3; 12609c0ba9dfSitojun 1261574b3a4fSmpi if (off + elen > m->m_pkthdr.len) 1262574b3a4fSmpi return NULL; 1263574b3a4fSmpi 12649c0ba9dfSitojun MGET(n, M_DONTWAIT, MT_DATA); 12659c0ba9dfSitojun if (n && elen >= MLEN) { 12669c0ba9dfSitojun MCLGET(n, M_DONTWAIT); 12679c0ba9dfSitojun if ((n->m_flags & M_EXT) == 0) { 12689c0ba9dfSitojun m_free(n); 12699c0ba9dfSitojun n = NULL; 12709c0ba9dfSitojun } 12719c0ba9dfSitojun } 12720b5e72a3Sbluhm if (n == NULL) { 12730b5e72a3Sbluhm ip6stat_inc(ip6s_idropped); 12749c0ba9dfSitojun return NULL; 12750b5e72a3Sbluhm } 12769c0ba9dfSitojun 12779c0ba9dfSitojun n->m_len = 0; 1278b5b7f62eSclaudio if (elen >= m_trailingspace(n)) { 12799c0ba9dfSitojun m_free(n); 12809c0ba9dfSitojun return NULL; 12819c0ba9dfSitojun } 12829c0ba9dfSitojun 12839c0ba9dfSitojun m_copydata(m, off, elen, mtod(n, caddr_t)); 12849c0ba9dfSitojun n->m_len = elen; 12859c0ba9dfSitojun return n; 12869c0ba9dfSitojun } 12879c0ba9dfSitojun 1288287546eaSitojun /* 12898398ca50Sbluhm * Get offset to the previous header followed by the header 1290287546eaSitojun * currently processed. 1291287546eaSitojun */ 12928398ca50Sbluhm int 1293ee37ea65Smcbride ip6_get_prevhdr(struct mbuf *m, int off) 1294287546eaSitojun { 1295287546eaSitojun struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1296287546eaSitojun 12978398ca50Sbluhm if (off == sizeof(struct ip6_hdr)) { 12988398ca50Sbluhm return offsetof(struct ip6_hdr, ip6_nxt); 12998398ca50Sbluhm } else if (off < sizeof(struct ip6_hdr)) { 13008398ca50Sbluhm panic("%s: off < sizeof(struct ip6_hdr)", __func__); 13018398ca50Sbluhm } else { 13028398ca50Sbluhm int len, nlen, nxt; 13038398ca50Sbluhm struct ip6_ext ip6e; 1304287546eaSitojun 1305287546eaSitojun nxt = ip6->ip6_nxt; 1306287546eaSitojun len = sizeof(struct ip6_hdr); 13078398ca50Sbluhm nlen = 0; 1308287546eaSitojun while (len < off) { 13095c7fed39Sdlg m_copydata(m, len, sizeof(ip6e), &ip6e); 1310287546eaSitojun 1311287546eaSitojun switch (nxt) { 1312287546eaSitojun case IPPROTO_FRAGMENT: 13138398ca50Sbluhm nlen = sizeof(struct ip6_frag); 1314287546eaSitojun break; 1315287546eaSitojun case IPPROTO_AH: 13168398ca50Sbluhm nlen = (ip6e.ip6e_len + 2) << 2; 1317287546eaSitojun break; 1318287546eaSitojun default: 13198398ca50Sbluhm nlen = (ip6e.ip6e_len + 1) << 3; 1320287546eaSitojun break; 1321287546eaSitojun } 13228398ca50Sbluhm len += nlen; 13238398ca50Sbluhm nxt = ip6e.ip6e_nxt; 1324287546eaSitojun } 13258398ca50Sbluhm 13268398ca50Sbluhm return (len - nlen); 1327287546eaSitojun } 1328287546eaSitojun } 1329287546eaSitojun 1330287546eaSitojun /* 133170b0445bSitojun * get next header offset. m will be retained. 133270b0445bSitojun */ 133370b0445bSitojun int 1334ee37ea65Smcbride ip6_nexthdr(struct mbuf *m, int off, int proto, int *nxtp) 133570b0445bSitojun { 133670b0445bSitojun struct ip6_hdr ip6; 133770b0445bSitojun struct ip6_ext ip6e; 133870b0445bSitojun struct ip6_frag fh; 133970b0445bSitojun 134070b0445bSitojun /* just in case */ 134170b0445bSitojun if (m == NULL) 1342ab0ea4a9Snayden panic("%s: m == NULL", __func__); 134370b0445bSitojun if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off) 134470b0445bSitojun return -1; 134570b0445bSitojun 134670b0445bSitojun switch (proto) { 134770b0445bSitojun case IPPROTO_IPV6: 134870b0445bSitojun if (m->m_pkthdr.len < off + sizeof(ip6)) 134970b0445bSitojun return -1; 13505c7fed39Sdlg m_copydata(m, off, sizeof(ip6), &ip6); 135170b0445bSitojun if (nxtp) 135270b0445bSitojun *nxtp = ip6.ip6_nxt; 135370b0445bSitojun off += sizeof(ip6); 135470b0445bSitojun return off; 135570b0445bSitojun 135670b0445bSitojun case IPPROTO_FRAGMENT: 135770b0445bSitojun /* 135870b0445bSitojun * terminate parsing if it is not the first fragment, 135970b0445bSitojun * it does not make sense to parse through it. 136070b0445bSitojun */ 136170b0445bSitojun if (m->m_pkthdr.len < off + sizeof(fh)) 136270b0445bSitojun return -1; 13635c7fed39Sdlg m_copydata(m, off, sizeof(fh), &fh); 13645d8f5256Sitojun if ((fh.ip6f_offlg & IP6F_OFF_MASK) != 0) 136570b0445bSitojun return -1; 136670b0445bSitojun if (nxtp) 136770b0445bSitojun *nxtp = fh.ip6f_nxt; 136870b0445bSitojun off += sizeof(struct ip6_frag); 136970b0445bSitojun return off; 137070b0445bSitojun 137170b0445bSitojun case IPPROTO_AH: 137270b0445bSitojun if (m->m_pkthdr.len < off + sizeof(ip6e)) 137370b0445bSitojun return -1; 13745c7fed39Sdlg m_copydata(m, off, sizeof(ip6e), &ip6e); 137570b0445bSitojun if (nxtp) 137670b0445bSitojun *nxtp = ip6e.ip6e_nxt; 137770b0445bSitojun off += (ip6e.ip6e_len + 2) << 2; 13786a6e82cfSitojun if (m->m_pkthdr.len < off) 13796a6e82cfSitojun return -1; 138070b0445bSitojun return off; 138170b0445bSitojun 138270b0445bSitojun case IPPROTO_HOPOPTS: 138370b0445bSitojun case IPPROTO_ROUTING: 138470b0445bSitojun case IPPROTO_DSTOPTS: 138570b0445bSitojun if (m->m_pkthdr.len < off + sizeof(ip6e)) 138670b0445bSitojun return -1; 13875c7fed39Sdlg m_copydata(m, off, sizeof(ip6e), &ip6e); 138870b0445bSitojun if (nxtp) 138970b0445bSitojun *nxtp = ip6e.ip6e_nxt; 139070b0445bSitojun off += (ip6e.ip6e_len + 1) << 3; 13916a6e82cfSitojun if (m->m_pkthdr.len < off) 13926a6e82cfSitojun return -1; 139370b0445bSitojun return off; 139470b0445bSitojun 139570b0445bSitojun case IPPROTO_NONE: 139670b0445bSitojun case IPPROTO_ESP: 139770b0445bSitojun case IPPROTO_IPCOMP: 139870b0445bSitojun /* give up */ 139970b0445bSitojun return -1; 140070b0445bSitojun 140170b0445bSitojun default: 140270b0445bSitojun return -1; 140370b0445bSitojun } 140470b0445bSitojun 140570b0445bSitojun return -1; 140670b0445bSitojun } 140770b0445bSitojun 140870b0445bSitojun /* 140970b0445bSitojun * get offset for the last header in the chain. m will be kept untainted. 141070b0445bSitojun */ 141170b0445bSitojun int 1412ee37ea65Smcbride ip6_lasthdr(struct mbuf *m, int off, int proto, int *nxtp) 141370b0445bSitojun { 141470b0445bSitojun int newoff; 141570b0445bSitojun int nxt; 141670b0445bSitojun 141770b0445bSitojun if (!nxtp) { 141870b0445bSitojun nxt = -1; 141970b0445bSitojun nxtp = &nxt; 142070b0445bSitojun } 142170b0445bSitojun while (1) { 142270b0445bSitojun newoff = ip6_nexthdr(m, off, proto, nxtp); 142370b0445bSitojun if (newoff < 0) 142470b0445bSitojun return off; 142570b0445bSitojun else if (newoff < off) 142670b0445bSitojun return -1; /* invalid */ 142770b0445bSitojun else if (newoff == off) 142870b0445bSitojun return newoff; 142970b0445bSitojun 143070b0445bSitojun off = newoff; 143170b0445bSitojun proto = *nxtp; 143270b0445bSitojun } 143370b0445bSitojun } 143470b0445bSitojun 143570b0445bSitojun /* 1436287546eaSitojun * System control for IP6 1437287546eaSitojun */ 1438287546eaSitojun 143994334c66Smpi const u_char inet6ctlerrmap[PRC_NCMDS] = { 1440287546eaSitojun 0, 0, 0, 0, 1441287546eaSitojun 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, 1442287546eaSitojun EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, 1443287546eaSitojun EMSGSIZE, EHOSTUNREACH, 0, 0, 1444287546eaSitojun 0, 0, 0, 0, 1445287546eaSitojun ENOPROTOOPT 1446287546eaSitojun }; 1447287546eaSitojun 144837cac5fdSgnezdo #ifdef MROUTING 144937cac5fdSgnezdo extern int ip6_mrtproto; 145037cac5fdSgnezdo #endif 145137cac5fdSgnezdo 14521f9e444eSbluhm const struct sysctl_bounded_args ipv6ctl_vars_unlocked[] = { 14531f9e444eSbluhm { IPV6CTL_FORWARDING, &ip6_forwarding, 0, 2 }, 14549c4c1a6fSbluhm { IPV6CTL_SENDREDIRECTS, &ip6_sendredirects, 0, 1 }, 14551f9e444eSbluhm }; 14561f9e444eSbluhm 14573eea75c8Sgnezdo const struct sysctl_bounded_args ipv6ctl_vars[] = { 1458b32486e3Sbluhm { IPV6CTL_DAD_PENDING, &ip6_dad_pending, SYSCTL_INT_READONLY }, 145937cac5fdSgnezdo #ifdef MROUTING 1460b32486e3Sbluhm { IPV6CTL_MRTPROTO, &ip6_mrtproto, SYSCTL_INT_READONLY }, 146137cac5fdSgnezdo #endif 14623eea75c8Sgnezdo { IPV6CTL_DEFHLIM, &ip6_defhlim, 0, 255 }, 14633eea75c8Sgnezdo { IPV6CTL_MAXFRAGPACKETS, &ip6_maxfragpackets, 0, 1000 }, 14643eea75c8Sgnezdo { IPV6CTL_LOG_INTERVAL, &ip6_log_interval, 0, INT_MAX }, 14653eea75c8Sgnezdo { IPV6CTL_HDRNESTLIMIT, &ip6_hdrnestlimit, 0, 100 }, 14663eea75c8Sgnezdo { IPV6CTL_DAD_COUNT, &ip6_dad_count, 0, 10 }, 14673eea75c8Sgnezdo { IPV6CTL_AUTO_FLOWLABEL, &ip6_auto_flowlabel, 0, 1 }, 14683eea75c8Sgnezdo { IPV6CTL_DEFMCASTHLIM, &ip6_defmcasthlim, 0, 255 }, 14693eea75c8Sgnezdo { IPV6CTL_USE_DEPRECATED, &ip6_use_deprecated, 0, 1 }, 14703eea75c8Sgnezdo { IPV6CTL_MAXFRAGS, &ip6_maxfrags, 0, 1000 }, 14713eea75c8Sgnezdo { IPV6CTL_MFORWARDING, &ip6_mforwarding, 0, 1 }, 14723eea75c8Sgnezdo { IPV6CTL_MCAST_PMTU, &ip6_mcast_pmtu, 0, 1 }, 14733eea75c8Sgnezdo { IPV6CTL_NEIGHBORGCTHRESH, &ip6_neighborgcthresh, -1, 5 * 2048 }, 14743eea75c8Sgnezdo { IPV6CTL_MAXDYNROUTES, &ip6_maxdynroutes, -1, 5 * 4096 }, 14753eea75c8Sgnezdo }; 14765873a946Smarkus 1477287546eaSitojun int 147831e14cacSjca ip6_sysctl_ip6stat(void *oldp, size_t *oldlenp, void *newp) 147931e14cacSjca { 148031e14cacSjca struct ip6stat *ip6stat; 148131e14cacSjca int ret; 148231e14cacSjca 148331e14cacSjca CTASSERT(sizeof(*ip6stat) == (ip6s_ncounters * sizeof(uint64_t))); 148431e14cacSjca 148531e14cacSjca ip6stat = malloc(sizeof(*ip6stat), M_TEMP, M_WAITOK); 1486bf0d449cSmpi counters_read(ip6counters, (uint64_t *)ip6stat, ip6s_ncounters, NULL); 148731e14cacSjca ret = sysctl_rdstruct(oldp, oldlenp, newp, 148831e14cacSjca ip6stat, sizeof(*ip6stat)); 148931e14cacSjca free(ip6stat, M_TEMP, sizeof(*ip6stat)); 149031e14cacSjca 149131e14cacSjca return (ret); 149231e14cacSjca } 149331e14cacSjca 149431e14cacSjca int 14959798906bSflorian ip6_sysctl_soiikey(void *oldp, size_t *oldlenp, void *newp, size_t newlen) 14969798906bSflorian { 1497ac97bf37Smpi uint8_t oldkey[IP6_SOIIKEY_LEN]; 14989798906bSflorian int error; 14999798906bSflorian 15003e676399Smpi error = suser(curproc); 15019798906bSflorian if (error != 0) 15029798906bSflorian return (error); 15039798906bSflorian 15049798906bSflorian memcpy(oldkey, ip6_soiikey, sizeof(oldkey)); 15059798906bSflorian 15069798906bSflorian error = sysctl_struct(oldp, oldlenp, newp, newlen, ip6_soiikey, 15079798906bSflorian sizeof(ip6_soiikey)); 15089798906bSflorian 15099798906bSflorian return (error); 15109798906bSflorian } 15119798906bSflorian 15129798906bSflorian int 1513ee37ea65Smcbride ip6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 1514ee37ea65Smcbride void *newp, size_t newlen) 1515287546eaSitojun { 1516ab46c28dSderaadt #ifdef MROUTING 1517ab46c28dSderaadt extern struct mrt6stat mrt6stat; 1518ab46c28dSderaadt #endif 1519caa7f414Sbluhm int oldval, error; 15204eb8db84Sbluhm 152167451457Sdlg /* Almost all sysctl names at this level are terminal. */ 152267451457Sdlg if (namelen != 1 && name[0] != IPV6CTL_IFQUEUE) 152367451457Sdlg return (ENOTDIR); 1524287546eaSitojun 1525287546eaSitojun switch (name[0]) { 1526ab46c28dSderaadt case IPV6CTL_STATS: 152731e14cacSjca return (ip6_sysctl_ip6stat(oldp, oldlenp, newp)); 1528ab46c28dSderaadt #ifdef MROUTING 1529719a3091Sclaudio case IPV6CTL_MRTSTATS: 1530ab46c28dSderaadt if (newp != NULL) 1531ab46c28dSderaadt return (EPERM); 1532df4a15bcSmpi NET_LOCK(); 1533df4a15bcSmpi error = sysctl_struct(oldp, oldlenp, newp, newlen, 1534df4a15bcSmpi &mrt6stat, sizeof(mrt6stat)); 1535df4a15bcSmpi NET_UNLOCK(); 1536df4a15bcSmpi return (error); 1537719a3091Sclaudio case IPV6CTL_MRTMIF: 1538719a3091Sclaudio if (newp) 1539719a3091Sclaudio return (EPERM); 1540df4a15bcSmpi NET_LOCK(); 1541df4a15bcSmpi error = mrt6_sysctl_mif(oldp, oldlenp); 1542df4a15bcSmpi NET_UNLOCK(); 1543df4a15bcSmpi return (error); 1544719a3091Sclaudio case IPV6CTL_MRTMFC: 1545719a3091Sclaudio if (newp) 1546719a3091Sclaudio return (EPERM); 1547df4a15bcSmpi NET_LOCK(); 1548df4a15bcSmpi error = mrt6_sysctl_mfc(oldp, oldlenp); 1549df4a15bcSmpi NET_UNLOCK(); 1550df4a15bcSmpi return (error); 1551ab46c28dSderaadt #else 1552719a3091Sclaudio case IPV6CTL_MRTSTATS: 1553719a3091Sclaudio case IPV6CTL_MRTPROTO: 1554719a3091Sclaudio case IPV6CTL_MRTMIF: 1555719a3091Sclaudio case IPV6CTL_MRTMFC: 1556ab46c28dSderaadt return (EOPNOTSUPP); 1557ab46c28dSderaadt #endif 15580e446ca2Sderaadt case IPV6CTL_MTUDISCTIMEOUT: 1559df4a15bcSmpi NET_LOCK(); 1560aae40be1Sbluhm error = sysctl_int_bounded(oldp, oldlenp, newp, newlen, 1561aae40be1Sbluhm &ip6_mtudisc_timeout, 0, INT_MAX); 15622028c273Sclaudio rt_timer_queue_change(&icmp6_mtudisc_timeout_q, 15630e446ca2Sderaadt ip6_mtudisc_timeout); 1564df4a15bcSmpi NET_UNLOCK(); 15650e446ca2Sderaadt return (error); 156667451457Sdlg case IPV6CTL_IFQUEUE: 156755159beeSbluhm return (sysctl_niq(name + 1, namelen - 1, 156855159beeSbluhm oldp, oldlenp, newp, newlen, &ip6intrq)); 15699798906bSflorian case IPV6CTL_SOIIKEY: 15709798906bSflorian return (ip6_sysctl_soiikey(oldp, oldlenp, newp, newlen)); 1571caa7f414Sbluhm case IPV6CTL_MULTIPATH: 1572caa7f414Sbluhm NET_LOCK(); 1573caa7f414Sbluhm oldval = ip6_multipath; 1574caa7f414Sbluhm error = sysctl_int_bounded(oldp, oldlenp, newp, newlen, 1575caa7f414Sbluhm &ip6_multipath, 0, 1); 1576caa7f414Sbluhm if (oldval != ip6_multipath) 1577caa7f414Sbluhm atomic_inc_long(&rtgeneration); 1578caa7f414Sbluhm NET_UNLOCK(); 1579caa7f414Sbluhm return (error); 15801f9e444eSbluhm case IPV6CTL_FORWARDING: 15819c4c1a6fSbluhm case IPV6CTL_SENDREDIRECTS: 15821f9e444eSbluhm return (sysctl_bounded_arr( 15831f9e444eSbluhm ipv6ctl_vars_unlocked, nitems(ipv6ctl_vars_unlocked), 15841f9e444eSbluhm name, namelen, oldp, oldlenp, newp, newlen)); 1585287546eaSitojun default: 1586df4a15bcSmpi NET_LOCK(); 15873eea75c8Sgnezdo error = sysctl_bounded_arr(ipv6ctl_vars, nitems(ipv6ctl_vars), 15883eea75c8Sgnezdo name, namelen, oldp, oldlenp, newp, newlen); 1589df4a15bcSmpi NET_UNLOCK(); 1590df4a15bcSmpi return (error); 1591df4a15bcSmpi } 1592287546eaSitojun /* NOTREACHED */ 1593287546eaSitojun } 1594528ff394Ssashan 1595528ff394Ssashan void 1596528ff394Ssashan ip6_send_dispatch(void *xmq) 1597528ff394Ssashan { 1598528ff394Ssashan struct mbuf_queue *mq = xmq; 1599528ff394Ssashan struct mbuf *m; 1600528ff394Ssashan struct mbuf_list ml; 1601528ff394Ssashan 1602528ff394Ssashan mq_delist(mq, &ml); 16032b4720fcSmpi if (ml_empty(&ml)) 16042b4720fcSmpi return; 16052b4720fcSmpi 1606fe6e30d1Sbluhm NET_LOCK_SHARED(); 1607528ff394Ssashan while ((m = ml_dequeue(&ml)) != NULL) { 160875f91b61Sclaudio ip6_output(m, NULL, NULL, 0, NULL, NULL); 1609528ff394Ssashan } 1610fe6e30d1Sbluhm NET_UNLOCK_SHARED(); 1611528ff394Ssashan } 1612528ff394Ssashan 1613528ff394Ssashan void 1614528ff394Ssashan ip6_send(struct mbuf *m) 1615528ff394Ssashan { 1616528ff394Ssashan mq_enqueue(&ip6send_mq, m); 1617a1654542Smpi task_add(net_tq(0), &ip6send_task); 1618528ff394Ssashan } 1619