ip6_input.c revision 125776
1105197Ssam/* $FreeBSD: head/sys/netinet6/ip6_input.c 125776 2004-02-13 14:50:01Z ume $ */ 2105197Ssam/* $KAME: ip6_input.c,v 1.259 2002/01/21 04:58:09 jinmei Exp $ */ 3105197Ssam 4139823Simp/* 5105197Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6105197Ssam * All rights reserved. 7105197Ssam * 8105197Ssam * Redistribution and use in source and binary forms, with or without 9105197Ssam * modification, are permitted provided that the following conditions 10105197Ssam * are met: 11105197Ssam * 1. Redistributions of source code must retain the above copyright 12105197Ssam * notice, this list of conditions and the following disclaimer. 13105197Ssam * 2. Redistributions in binary form must reproduce the above copyright 14105197Ssam * notice, this list of conditions and the following disclaimer in the 15105197Ssam * documentation and/or other materials provided with the distribution. 16105197Ssam * 3. Neither the name of the project nor the names of its contributors 17105197Ssam * may be used to endorse or promote products derived from this software 18105197Ssam * without specific prior written permission. 19105197Ssam * 20105197Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21105197Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22105197Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23105197Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24105197Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25105197Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26105197Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27105197Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28105197Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29105197Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30105197Ssam * SUCH DAMAGE. 31105197Ssam */ 32105197Ssam 33105197Ssam/* 34105197Ssam * Copyright (c) 1982, 1986, 1988, 1993 35105197Ssam * The Regents of the University of California. All rights reserved. 36105197Ssam * 37105197Ssam * Redistribution and use in source and binary forms, with or without 38105197Ssam * modification, are permitted provided that the following conditions 39105197Ssam * are met: 40105197Ssam * 1. Redistributions of source code must retain the above copyright 41105197Ssam * notice, this list of conditions and the following disclaimer. 42105197Ssam * 2. Redistributions in binary form must reproduce the above copyright 43105197Ssam * notice, this list of conditions and the following disclaimer in the 44105197Ssam * documentation and/or other materials provided with the distribution. 45119643Ssam * 3. All advertising materials mentioning features or use of this software 46119643Ssam * must display the following acknowledgement: 47105197Ssam * This product includes software developed by the University of 48105197Ssam * California, Berkeley and its contributors. 49105197Ssam * 4. Neither the name of the University nor the names of its contributors 50105197Ssam * may be used to endorse or promote products derived from this software 51105197Ssam * without specific prior written permission. 52105197Ssam * 53105197Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54105197Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55105197Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56105197Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57158767Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58105197Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59183550Szec * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60105197Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61105197Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62105197Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63105197Ssam * SUCH DAMAGE. 64105197Ssam * 65105197Ssam * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 66105197Ssam */ 67105197Ssam 68105197Ssam#include "opt_ip6fw.h" 69105197Ssam#include "opt_inet.h" 70105197Ssam#include "opt_inet6.h" 71105197Ssam#include "opt_ipsec.h" 72105197Ssam#include "opt_pfil_hooks.h" 73105197Ssam#include "opt_random_ip_id.h" 74105197Ssam 75105197Ssam#include <sys/param.h> 76105197Ssam#include <sys/systm.h> 77105197Ssam#include <sys/malloc.h> 78185571Sbz#include <sys/mbuf.h> 79105197Ssam#include <sys/proc.h> 80105197Ssam#include <sys/domain.h> 81105197Ssam#include <sys/protosw.h> 82185571Sbz#include <sys/socket.h> 83105197Ssam#include <sys/socketvar.h> 84105197Ssam#include <sys/errno.h> 85105197Ssam#include <sys/time.h> 86105197Ssam#include <sys/kernel.h> 87105197Ssam#include <sys/syslog.h> 88105197Ssam 89105197Ssam#include <net/if.h> 90105197Ssam#include <net/if_types.h> 91105197Ssam#include <net/if_dl.h> 92105197Ssam#include <net/route.h> 93105197Ssam#include <net/netisr.h> 94105197Ssam#ifdef PFIL_HOOKS 95105197Ssam#include <net/pfil.h> 96105197Ssam#endif 97105197Ssam 98105197Ssam#include <netinet/in.h> 99105197Ssam#include <netinet/in_systm.h> 100105197Ssam#ifdef INET 101105197Ssam#include <netinet/ip.h> 102181803Sbz#include <netinet/ip_icmp.h> 103105197Ssam#endif /* INET */ 104105197Ssam#include <netinet/ip6.h> 105105197Ssam#include <netinet6/in6_var.h> 106105197Ssam#include <netinet6/ip6_var.h> 107105197Ssam#include <netinet/in_pcb.h> 108105197Ssam#include <netinet/icmp6.h> 109105197Ssam#include <netinet6/scope6_var.h> 110105197Ssam#include <netinet6/in6_ifattach.h> 111105197Ssam#include <netinet6/nd6.h> 112105197Ssam#include <netinet6/in6_prefix.h> 113105197Ssam 114105197Ssam#ifdef IPSEC 115105197Ssam#include <netinet6/ipsec.h> 116105197Ssam#ifdef INET6 117105197Ssam#include <netinet6/ipsec6.h> 118185088Szec#endif 119185088Szec#endif 120185088Szec 121185088Szec#ifdef FAST_IPSEC 122185088Szec#include <netipsec/ipsec.h> 123185088Szec#include <netipsec/ipsec6.h> 124185088Szec#define IPSEC 125185088Szec#endif /* FAST_IPSEC */ 126185088Szec 127185088Szec#include <netinet6/ip6_fw.h> 128185088Szec 129105197Ssam#include <netinet6/ip6protosw.h> 130185088Szec 131105197Ssam#include <net/net_osdep.h> 132185088Szec 133185088Szecextern struct domain inet6domain; 134185088Szec 135185088Szecu_char ip6_protox[IPPROTO_MAX]; 136105197Ssamstatic struct ifqueue ip6intrq; 137185088Szecstatic int ip6qmaxlen = IFQ_MAXLEN; 138185088Szecstruct in6_ifaddr *in6_ifaddr; 139185088Szec 140185088Szecextern struct callout in6_tmpaddrtimer_ch; 141185088Szec 142185088Szecint ip6_forward_srcrt; /* XXX */ 143119643Ssamint ip6_sourcecheck; /* XXX */ 144120585Ssamint ip6_sourcecheck_interval; /* XXX */ 145120585Ssam 146120585Ssamint ip6_ours_check_algorithm; 147120585Ssam 148120585Ssam#ifdef PFIL_HOOKS 149120585Ssamstruct pfil_head inet6_pfil_hook; 150120585Ssam#endif 151120585Ssam 152119643Ssam/* firewall hooks */ 153120585Ssamip6_fw_chk_t *ip6_fw_chk_ptr; 154120585Ssamip6_fw_ctl_t *ip6_fw_ctl_ptr; 155120585Ssamint ip6_fw_enable = 1; 156120585Ssam 157120585Ssamstruct ip6stat ip6stat; 158120585Ssam 159120585Ssamstatic void ip6_init2 __P((void *)); 160120585Ssamstatic struct ip6aux *ip6_setdstifaddr __P((struct mbuf *, struct in6_ifaddr *)); 161119643Ssamstatic int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); 162119643Ssam#ifdef PULLDOWN_TEST 163120585Ssamstatic struct mbuf *ip6_pullexthdr __P((struct mbuf *, size_t, int)); 164120585Ssam#endif 165120585Ssam 166120585Ssam/* 167120585Ssam * IP6 initialization: fill in IP6 protocol switch table. 168120585Ssam * All protocols not implemented in kernel go to raw IP6 protocol handler. 169120585Ssam */ 170119643Ssamvoid 171120585Ssamip6_init() 172120585Ssam{ 173120585Ssam struct ip6protosw *pr; 174120585Ssam int i; 175120585Ssam 176120585Ssam#ifdef DIAGNOSTIC 177120585Ssam if (sizeof(struct protosw) != sizeof(struct ip6protosw)) 178119643Ssam panic("sizeof(protosw) != sizeof(ip6protosw)"); 179120585Ssam#endif 180120585Ssam pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); 181120585Ssam if (pr == 0) 182120585Ssam panic("ip6_init"); 183120585Ssam for (i = 0; i < IPPROTO_MAX; i++) 184120585Ssam ip6_protox[i] = pr - inet6sw; 185120585Ssam for (pr = (struct ip6protosw *)inet6domain.dom_protosw; 186105197Ssam pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW; pr++) 187105197Ssam if (pr->pr_domain->dom_family == PF_INET6 && 188128856Ssam pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) 189105197Ssam ip6_protox[pr->pr_protocol] = pr - inet6sw; 190105197Ssam#ifdef PFIL_HOOKS 191128856Ssam inet6_pfil_hook.ph_type = PFIL_TYPE_AF; 192128856Ssam inet6_pfil_hook.ph_af = AF_INET6; 193128856Ssam if ((i = pfil_head_register(&inet6_pfil_hook)) != 0) 194185348Szec printf("%s: WARNING: unable to register pfil hook, " 195105197Ssam "error %d\n", __func__, i); 196105197Ssam#endif /* PFIL_HOOKS */ 197105197Ssam ip6intrq.ifq_maxlen = ip6qmaxlen; 198185348Szec mtx_init(&ip6intrq.ifq_mtx, "ip6_inq", NULL, MTX_DEF); 199105197Ssam netisr_register(NETISR_IPV6, ip6_input, &ip6intrq, 0); 200105197Ssam scope6_init(); 201105197Ssam addrsel_policy_init(); 202105197Ssam nd6_init(); 203105197Ssam frag6_init(); 204105197Ssam#ifndef RANDOM_IP_ID 205105197Ssam ip6_flow_seq = arc4random(); 206105197Ssam#endif 207105197Ssam ip6_desync_factor = arc4random() % MAX_TEMP_DESYNC_FACTOR; 208105197Ssam} 209105197Ssam 210105197Ssamstatic void 211105197Ssamip6_init2(dummy) 212105197Ssam void *dummy; 213105197Ssam{ 214105197Ssam 215105197Ssam /* nd6_timer_init */ 216105197Ssam callout_init(&nd6_timer_ch, 0); 217105197Ssam callout_reset(&nd6_timer_ch, hz, nd6_timer, NULL); 218105197Ssam 219105197Ssam /* router renumbering prefix list maintenance */ 220105197Ssam callout_init(&in6_rr_timer_ch, 0); 221105197Ssam callout_reset(&in6_rr_timer_ch, hz, in6_rr_timer, NULL); 222105197Ssam 223105197Ssam /* timer for regeneranation of temporary addresses randomize ID */ 224105197Ssam callout_init(&in6_tmpaddrtimer_ch, 0); 225105197Ssam callout_reset(&in6_tmpaddrtimer_ch, 226105197Ssam (ip6_temp_preferred_lifetime - ip6_desync_factor - 227105197Ssam ip6_temp_regen_advance) * hz, 228105197Ssam in6_tmpaddrtimer, NULL); 229105197Ssam} 230105197Ssam 231105197Ssam/* cheat */ 232105197Ssam/* This must be after route_init(), which is now SI_ORDER_THIRD */ 233105197SsamSYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL); 234105197Ssam 235105197Ssamextern struct route_in6 ip6_forward_rt; 236105197Ssam 237105197Ssamvoid 238105197Ssamip6_input(m) 239105197Ssam struct mbuf *m; 240105197Ssam{ 241105197Ssam struct ip6_hdr *ip6; 242105197Ssam int off = sizeof(struct ip6_hdr), nest; 243105197Ssam u_int32_t plen; 244105197Ssam u_int32_t rtalert = ~0; 245105197Ssam int nxt, ours = 0; 246105197Ssam struct ifnet *deliverifp = NULL; 247105197Ssam#ifdef PFIL_HOOKS 248105197Ssam struct in6_addr odst; 249105197Ssam#endif 250105197Ssam int srcrt = 0; 251105197Ssam 252183550Szec GIANT_REQUIRED; /* XXX for now */ 253183550Szec#ifdef IPSEC 254105197Ssam /* 255105197Ssam * should the inner packet be considered authentic? 256183550Szec * see comment in ah4_input(). 257183550Szec */ 258105197Ssam if (m) { 259105197Ssam m->m_flags &= ~M_AUTHIPHDR; 260183550Szec m->m_flags &= ~M_AUTHIPDGM; 261183550Szec } 262105197Ssam#endif 263105197Ssam 264183550Szec /* 265183550Szec * make sure we don't have onion peering information into m_tag. 266105197Ssam */ 267105197Ssam ip6_delaux(m); 268183550Szec 269183550Szec /* 270105197Ssam * mbuf statistics 271105197Ssam */ 272183550Szec if (m->m_flags & M_EXT) { 273183550Szec if (m->m_next) 274105197Ssam ip6stat.ip6s_mext2m++; 275105197Ssam else 276183550Szec ip6stat.ip6s_mext1++; 277183550Szec } else { 278105197Ssam#define M2MMAX (sizeof(ip6stat.ip6s_m2m)/sizeof(ip6stat.ip6s_m2m[0])) 279105197Ssam if (m->m_next) { 280183550Szec if (m->m_flags & M_LOOP) { 281183550Szec ip6stat.ip6s_m2m[loif[0].if_index]++; /* XXX */ 282105197Ssam } else if (m->m_pkthdr.rcvif->if_index < M2MMAX) 283105197Ssam ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; 284183550Szec else 285183550Szec ip6stat.ip6s_m2m[0]++; 286105197Ssam } else 287105197Ssam ip6stat.ip6s_m1++; 288183550Szec#undef M2MMAX 289183550Szec } 290105197Ssam 291105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); 292183550Szec ip6stat.ip6s_total++; 293183550Szec 294105197Ssam#ifndef PULLDOWN_TEST 295105197Ssam /* 296183550Szec * L2 bridge code and some other code can return mbuf chain 297183550Szec * that does not conform to KAME requirement. too bad. 298105197Ssam * XXX: fails to join if interface MTU > MCLBYTES. jumbogram? 299105197Ssam */ 300105197Ssam if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) { 301105197Ssam struct mbuf *n; 302105197Ssam 303105197Ssam MGETHDR(n, M_DONTWAIT, MT_HEADER); 304105197Ssam if (n) 305105197Ssam M_MOVE_PKTHDR(n, m); 306105197Ssam if (n && n->m_pkthdr.len > MHLEN) { 307105197Ssam MCLGET(n, M_DONTWAIT); 308105197Ssam if ((n->m_flags & M_EXT) == 0) { 309105197Ssam m_freem(n); 310105197Ssam n = NULL; 311105197Ssam } 312105197Ssam } 313105197Ssam if (n == NULL) { 314105197Ssam m_freem(m); 315105197Ssam return; /* ENOBUFS */ 316105197Ssam } 317105197Ssam 318105197Ssam m_copydata(m, 0, n->m_pkthdr.len, mtod(n, caddr_t)); 319105197Ssam n->m_len = n->m_pkthdr.len; 320105197Ssam m_freem(m); 321105197Ssam m = n; 322105197Ssam } 323105197Ssam IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /* nothing */); 324105197Ssam#endif 325105197Ssam 326105197Ssam if (m->m_len < sizeof(struct ip6_hdr)) { 327105197Ssam struct ifnet *inifp; 328105197Ssam inifp = m->m_pkthdr.rcvif; 329105197Ssam if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { 330105197Ssam ip6stat.ip6s_toosmall++; 331119643Ssam in6_ifstat_inc(inifp, ifs6_in_hdrerr); 332119643Ssam return; 333119643Ssam } 334119643Ssam } 335119643Ssam 336119643Ssam ip6 = mtod(m, struct ip6_hdr *); 337119643Ssam 338105197Ssam if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { 339105197Ssam ip6stat.ip6s_badvers++; 340105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); 341105197Ssam goto bad; 342105197Ssam } 343105197Ssam 344105197Ssam#ifdef PFIL_HOOKS 345105197Ssam /* 346105197Ssam * Run through list of hooks for input packets. 347105197Ssam * 348105197Ssam * NB: Beware of the destination address changing 349105197Ssam * (e.g. by NAT rewriting). When this happens, 350105197Ssam * tell ip6_forward to do the right thing. 351105197Ssam */ 352105197Ssam odst = ip6->ip6_dst; 353105197Ssam if (pfil_run_hooks(&inet6_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN)) 354105197Ssam return; 355105197Ssam if (m == NULL) /* consumed by filter */ 356105197Ssam return; 357105197Ssam ip6 = mtod(m, struct ip6_hdr *); 358105197Ssam srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); 359105197Ssam#endif /* PFIL_HOOKS */ 360105197Ssam 361105197Ssam ip6stat.ip6s_nxthist[ip6->ip6_nxt]++; 362105197Ssam 363105197Ssam /* 364105197Ssam * Check with the firewall... 365105197Ssam */ 366105197Ssam if (ip6_fw_enable && ip6_fw_chk_ptr) { 367105197Ssam u_short port = 0; 368105197Ssam /* If ipfw says divert, we have to just drop packet */ 369105197Ssam /* use port as a dummy argument */ 370105197Ssam if ((*ip6_fw_chk_ptr)(&ip6, NULL, &port, &m)) { 371105197Ssam m_freem(m); 372105197Ssam m = NULL; 373105197Ssam } 374105197Ssam if (!m) 375105197Ssam return; 376105197Ssam } 377105197Ssam 378105197Ssam /* 379105197Ssam * Check against address spoofing/corruption. 380105197Ssam */ 381105197Ssam if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || 382105197Ssam IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { 383105197Ssam /* 384105197Ssam * XXX: "badscope" is not very suitable for a multicast source. 385119643Ssam */ 386105197Ssam ip6stat.ip6s_badscope++; 387105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); 388105197Ssam goto bad; 389105197Ssam } 390105197Ssam if ((IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || 391105197Ssam IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) && 392105197Ssam (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { 393105197Ssam ip6stat.ip6s_badscope++; 394105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); 395105197Ssam goto bad; 396105197Ssam } 397105197Ssam 398105197Ssam /* 399105197Ssam * The following check is not documented in specs. A malicious 400105197Ssam * party may be able to use IPv4 mapped addr to confuse tcp/udp stack 401105197Ssam * and bypass security checks (act as if it was from 127.0.0.1 by using 402105197Ssam * IPv6 src ::ffff:127.0.0.1). Be cautious. 403105197Ssam * 404105197Ssam * This check chokes if we are in an SIIT cloud. As none of BSDs 405105197Ssam * support IPv4-less kernel compilation, we cannot support SIIT 406105197Ssam * environment at all. So, it makes more sense for us to reject any 407105197Ssam * malicious packets for non-SIIT environment, than try to do a 408105197Ssam * partial support for SIIT environment. 409105197Ssam */ 410105197Ssam if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || 411105197Ssam IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { 412105197Ssam ip6stat.ip6s_badscope++; 413105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); 414105197Ssam goto bad; 415105197Ssam } 416105197Ssam#if 0 417105197Ssam /* 418105197Ssam * Reject packets with IPv4 compatible addresses (auto tunnel). 419105197Ssam * 420105197Ssam * The code forbids auto tunnel relay case in RFC1933 (the check is 421105197Ssam * stronger than RFC1933). We may want to re-enable it if mech-xx 422105197Ssam * is revised to forbid relaying case. 423105197Ssam */ 424105197Ssam if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) || 425105197Ssam IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { 426105197Ssam ip6stat.ip6s_badscope++; 427105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); 428105197Ssam goto bad; 429105197Ssam } 430105197Ssam#endif 431157123Sgnn 432157123Sgnn /* drop packets if interface ID portion is already filled */ 433157123Sgnn if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { 434157123Sgnn if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src) && 435105197Ssam ip6->ip6_src.s6_addr16[1]) { 436105197Ssam ip6stat.ip6s_badscope++; 437105197Ssam goto bad; 438105197Ssam } 439105197Ssam if ((IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) || 440105197Ssam IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) && 441105197Ssam ip6->ip6_dst.s6_addr16[1]) { 442105197Ssam ip6stat.ip6s_badscope++; 443105197Ssam goto bad; 444105197Ssam } 445105197Ssam } 446105197Ssam 447105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) 448105197Ssam ip6->ip6_src.s6_addr16[1] 449105197Ssam = htons(m->m_pkthdr.rcvif->if_index); 450105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) 451105197Ssam ip6->ip6_dst.s6_addr16[1] 452105197Ssam = htons(m->m_pkthdr.rcvif->if_index); 453105197Ssam 454105197Ssam /* 455105197Ssam * Multicast check 456105197Ssam */ 457105197Ssam if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 458105197Ssam struct in6_multi *in6m = 0; 459105197Ssam 460105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast); 461105197Ssam /* 462105197Ssam * See if we belong to the destination multicast group on the 463105197Ssam * arrival interface. 464105197Ssam */ 465105197Ssam IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m); 466105197Ssam if (in6m) 467105197Ssam ours = 1; 468105197Ssam else if (!ip6_mrouter) { 469105197Ssam ip6stat.ip6s_notmember++; 470105197Ssam ip6stat.ip6s_cantforward++; 471105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); 472105197Ssam goto bad; 473105197Ssam } 474105197Ssam deliverifp = m->m_pkthdr.rcvif; 475105197Ssam goto hbhcheck; 476105197Ssam } 477105197Ssam 478105197Ssam /* 479105197Ssam * Unicast check 480105197Ssam */ 481105197Ssam if (ip6_forward_rt.ro_rt != NULL && 482105197Ssam (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && 483105197Ssam IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, 484105197Ssam &((struct sockaddr_in6 *)(&ip6_forward_rt.ro_dst))->sin6_addr)) 485105197Ssam ip6stat.ip6s_forward_cachehit++; 486105197Ssam else { 487105197Ssam struct sockaddr_in6 *dst6; 488105197Ssam 489105197Ssam if (ip6_forward_rt.ro_rt) { 490105197Ssam /* route is down or destination is different */ 491105197Ssam ip6stat.ip6s_forward_cachemiss++; 492105197Ssam RTFREE(ip6_forward_rt.ro_rt); 493105197Ssam ip6_forward_rt.ro_rt = 0; 494105197Ssam } 495105197Ssam 496105197Ssam bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); 497105197Ssam dst6 = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst; 498105197Ssam dst6->sin6_len = sizeof(struct sockaddr_in6); 499105197Ssam dst6->sin6_family = AF_INET6; 500105197Ssam dst6->sin6_addr = ip6->ip6_dst; 501105197Ssam 502157123Sgnn rtalloc((struct route *)&ip6_forward_rt); 503157123Sgnn } 504157123Sgnn 505157123Sgnn#define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key)) 506105197Ssam 507105197Ssam /* 508105197Ssam * Accept the packet if the forwarding interface to the destination 509105197Ssam * according to the routing table is the loopback interface, 510105197Ssam * unless the associated route has a gateway. 511105197Ssam * Note that this approach causes to accept a packet if there is a 512105197Ssam * route to the loopback interface for the destination of the packet. 513158767Spjd * But we think it's even useful in some situations, e.g. when using 514158767Spjd * a special daemon which wants to intercept the packet. 515158767Spjd * 516105197Ssam * XXX: some OSes automatically make a cloned route for the destination 517158767Spjd * of an outgoing packet. If the outgoing interface of the packet 518158767Spjd * is a loopback one, the kernel would consider the packet to be 519158767Spjd * accepted, even if we have no such address assinged on the interface. 520158767Spjd * We check the cloned flag of the route entry to reject such cases, 521158767Spjd * assuming that route entries for our own addresses are not made by 522158767Spjd * cloning (it should be true because in6_addloop explicitly installs 523158767Spjd * the host route). However, we might have to do an explicit check 524158767Spjd * while it would be less efficient. Or, should we rather install a 525158767Spjd * reject route for such a case? 526158767Spjd */ 527158767Spjd if (ip6_forward_rt.ro_rt && 528158767Spjd (ip6_forward_rt.ro_rt->rt_flags & 529158767Spjd (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && 530158767Spjd#ifdef RTF_WASCLONED 531158767Spjd !(ip6_forward_rt.ro_rt->rt_flags & RTF_WASCLONED) && 532158767Spjd#endif 533158767Spjd#ifdef RTF_CLONED 534105197Ssam !(ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) && 535105197Ssam#endif 536120585Ssam#if 0 537105197Ssam /* 538105197Ssam * The check below is redundant since the comparison of 539120585Ssam * the destination and the key of the rtentry has 540105197Ssam * already done through looking up the routing table. 541105197Ssam */ 542135947Ssam IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, 543105197Ssam &rt6_key(ip6_forward_rt.ro_rt)->sin6_addr) 544105197Ssam#endif 545135947Ssam ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { 546135947Ssam struct in6_ifaddr *ia6 = 547135947Ssam (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa; 548135947Ssam 549135947Ssam /* 550135947Ssam * record address information into m_tag. 551135947Ssam */ 552135947Ssam (void)ip6_setdstifaddr(m, ia6); 553135947Ssam 554135947Ssam /* 555135947Ssam * packets to a tentative, duplicated, or somehow invalid 556105197Ssam * address must not be accepted. 557105197Ssam */ 558105197Ssam if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) { 559105197Ssam /* this address is ready */ 560105197Ssam ours = 1; 561105197Ssam deliverifp = ia6->ia_ifp; /* correct? */ 562105197Ssam /* Count the packet in the ip address stats */ 563183550Szec ia6->ia_ifa.if_ipackets++; 564183550Szec ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; 565105197Ssam goto hbhcheck; 566181803Sbz } else { 567105197Ssam /* address is not ready, so discard the packet. */ 568105197Ssam nd6log((LOG_INFO, 569105197Ssam "ip6_input: packet to an unready address %s->%s\n", 570105197Ssam ip6_sprintf(&ip6->ip6_src), 571105197Ssam ip6_sprintf(&ip6->ip6_dst))); 572105197Ssam 573105197Ssam goto bad; 574105197Ssam } 575105197Ssam } 576105197Ssam 577105197Ssam /* 578105197Ssam * FAITH (Firewall Aided Internet Translator) 579183550Szec */ 580105197Ssam if (ip6_keepfaith) { 581105197Ssam if (ip6_forward_rt.ro_rt && ip6_forward_rt.ro_rt->rt_ifp 582120585Ssam && ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) { 583120585Ssam /* XXX do we need more sanity checks? */ 584120585Ssam ours = 1; 585105197Ssam deliverifp = ip6_forward_rt.ro_rt->rt_ifp; /* faith */ 586105197Ssam goto hbhcheck; 587120585Ssam } 588105197Ssam } 589105197Ssam 590105197Ssam /* 591105197Ssam * Now there is no reason to process the packet if it's not our own 592105197Ssam * and we're not a router. 593105197Ssam */ 594120585Ssam if (!ip6_forwarding) { 595181803Sbz ip6stat.ip6s_cantforward++; 596105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); 597105197Ssam goto bad; 598105197Ssam } 599105197Ssam 600105197Ssam hbhcheck: 601105197Ssam /* 602105197Ssam * record address information into m_tag, if we don't have one yet. 603105197Ssam * note that we are unable to record it, if the address is not listed 604105197Ssam * as our interface address (e.g. multicast addresses, addresses 605105197Ssam * within FAITH prefixes and such). 606105197Ssam */ 607105197Ssam if (deliverifp && !ip6_getdstifaddr(m)) { 608105197Ssam struct in6_ifaddr *ia6; 609120585Ssam 610105197Ssam ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); 611105197Ssam if (ia6) { 612105197Ssam if (!ip6_setdstifaddr(m, ia6)) { 613105197Ssam /* 614105197Ssam * XXX maybe we should drop the packet here, 615120585Ssam * as we could not provide enough information 616105197Ssam * to the upper layers. 617105197Ssam */ 618120585Ssam } 619105197Ssam } 620105197Ssam } 621105197Ssam 622105197Ssam /* 623105197Ssam * Process Hop-by-Hop options header if it's contained. 624105197Ssam * m may be modified in ip6_hopopts_input(). 625105197Ssam * If a JumboPayload option is included, plen will also be modified. 626105197Ssam */ 627105197Ssam plen = (u_int32_t)ntohs(ip6->ip6_plen); 628105197Ssam if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { 629105197Ssam struct ip6_hbh *hbh; 630105197Ssam 631105197Ssam if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) { 632105197Ssam#if 0 /*touches NULL pointer*/ 633105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); 634105197Ssam#endif 635105197Ssam return; /* m have already been freed */ 636183550Szec } 637105197Ssam 638105197Ssam /* adjust pointer */ 639120585Ssam ip6 = mtod(m, struct ip6_hdr *); 640120585Ssam 641120585Ssam /* 642105197Ssam * if the payload length field is 0 and the next header field 643105197Ssam * indicates Hop-by-Hop Options header, then a Jumbo Payload 644120585Ssam * option MUST be included. 645105197Ssam */ 646105197Ssam if (ip6->ip6_plen == 0 && plen == 0) { 647105197Ssam /* 648105197Ssam * Note that if a valid jumbo payload option is 649105197Ssam * contained, ip6_hopopts_input() must set a valid 650105197Ssam * (non-zero) payload length to the variable plen. 651105197Ssam */ 652120585Ssam ip6stat.ip6s_badoptions++; 653181803Sbz in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); 654105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); 655105197Ssam icmp6_error(m, ICMP6_PARAM_PROB, 656105197Ssam ICMP6_PARAMPROB_HEADER, 657105197Ssam (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); 658105197Ssam return; 659105197Ssam } 660105197Ssam#ifndef PULLDOWN_TEST 661105197Ssam /* ip6_hopopts_input() ensures that mbuf is contiguous */ 662105197Ssam hbh = (struct ip6_hbh *)(ip6 + 1); 663105197Ssam#else 664105197Ssam IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), 665105197Ssam sizeof(struct ip6_hbh)); 666105197Ssam if (hbh == NULL) { 667105197Ssam ip6stat.ip6s_tooshort++; 668105197Ssam return; 669105197Ssam } 670105197Ssam#endif 671105197Ssam nxt = hbh->ip6h_nxt; 672105197Ssam 673120585Ssam /* 674105197Ssam * accept the packet if a router alert option is included 675105197Ssam * and we act as an IPv6 router. 676105197Ssam */ 677105197Ssam if (rtalert != ~0 && ip6_forwarding) 678105197Ssam ours = 1; 679120585Ssam } else 680105197Ssam nxt = ip6->ip6_nxt; 681105197Ssam 682120585Ssam /* 683105197Ssam * Check that the amount of data in the buffers 684105197Ssam * is as at least much as the IPv6 header would have us expect. 685105197Ssam * Trim mbufs if longer than we expect. 686105197Ssam * Drop packet if shorter than we expect. 687191599Sbz */ 688105197Ssam if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { 689105197Ssam ip6stat.ip6s_tooshort++; 690105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); 691105197Ssam goto bad; 692105197Ssam } 693105197Ssam if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { 694105197Ssam if (m->m_len == m->m_pkthdr.len) { 695105197Ssam m->m_len = sizeof(struct ip6_hdr) + plen; 696105197Ssam m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; 697105197Ssam } else 698105197Ssam m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len); 699183550Szec } 700105197Ssam 701105197Ssam /* 702105197Ssam * Forward if desirable. 703105197Ssam */ 704105197Ssam if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 705105197Ssam /* 706120585Ssam * If we are acting as a multicast router, all 707105197Ssam * incoming multicast packets are passed to the 708105197Ssam * kernel-level multicast forwarding function. 709120585Ssam * The packet is returned (relatively) intact; if 710120585Ssam * ip6_mforward() returns a non-zero value, the packet 711105197Ssam * must be discarded, else it may be accepted below. 712105197Ssam */ 713105197Ssam if (ip6_mrouter && ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) { 714105197Ssam ip6stat.ip6s_cantforward++; 715120585Ssam m_freem(m); 716181803Sbz return; 717105197Ssam } 718105197Ssam if (!ours) { 719105197Ssam m_freem(m); 720105197Ssam return; 721105197Ssam } 722105197Ssam } else if (!ours) { 723105197Ssam ip6_forward(m, srcrt); 724105197Ssam return; 725105197Ssam } 726105197Ssam 727105197Ssam ip6 = mtod(m, struct ip6_hdr *); 728105197Ssam 729105197Ssam /* 730105197Ssam * Malicious party may be able to use IPv4 mapped addr to confuse 731105197Ssam * tcp/udp stack and bypass security checks (act as if it was from 732105197Ssam * 127.0.0.1 by using IPv6 src ::ffff:127.0.0.1). Be cautious. 733105197Ssam * 734105197Ssam * For SIIT end node behavior, you may want to disable the check. 735105197Ssam * However, you will become vulnerable to attacks using IPv4 mapped 736105197Ssam * source. 737105197Ssam */ 738105197Ssam if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || 739105197Ssam IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { 740105197Ssam ip6stat.ip6s_badscope++; 741105197Ssam in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); 742105197Ssam goto bad; 743105197Ssam } 744105197Ssam 745105197Ssam /* 746105197Ssam * Tell launch routine the next header 747105197Ssam */ 748105197Ssam ip6stat.ip6s_delivered++; 749105197Ssam in6_ifstat_inc(deliverifp, ifs6_in_deliver); 750105197Ssam nest = 0; 751105197Ssam 752105197Ssam while (nxt != IPPROTO_DONE) { 753105197Ssam if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { 754105197Ssam ip6stat.ip6s_toomanyhdr++; 755105197Ssam goto bad; 756105197Ssam } 757120585Ssam 758105197Ssam /* 759105197Ssam * protection against faulty packet - there should be 760120585Ssam * more sanity checks in header chain processing. 761105197Ssam */ 762105197Ssam if (m->m_pkthdr.len < off) { 763105197Ssam ip6stat.ip6s_tooshort++; 764191599Sbz in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); 765105197Ssam goto bad; 766105197Ssam } 767105197Ssam 768105197Ssam#ifdef IPSEC 769105197Ssam /* 770105197Ssam * enforce IPsec policy checking if we are seeing last header. 771105197Ssam * note that we do not visit this with protocols with pcb layer 772105197Ssam * code - like udp/tcp/raw ip. 773105197Ssam */ 774105197Ssam if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && 775183550Szec ipsec6_in_reject(m, NULL)) { 776105197Ssam ipsec6stat.in_polvio++; 777105197Ssam goto bad; 778105197Ssam } 779120585Ssam#endif 780120585Ssam nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); 781120585Ssam } 782105197Ssam return; 783120585Ssam bad: 784105197Ssam m_freem(m); 785105197Ssam} 786105197Ssam 787105197Ssam/* 788105197Ssam * set/grab in6_ifaddr correspond to IPv6 destination address. 789105197Ssam * XXX backward compatibility wrapper 790105197Ssam */ 791120585Ssamstatic struct ip6aux * 792119643Ssamip6_setdstifaddr(m, ia6) 793119643Ssam struct mbuf *m; 794119643Ssam struct in6_ifaddr *ia6; 795105197Ssam{ 796105197Ssam struct ip6aux *ip6a; 797105197Ssam 798105197Ssam ip6a = ip6_addaux(m); 799105197Ssam if (ip6a) 800105197Ssam ip6a->ip6a_dstia6 = ia6; 801105197Ssam return ip6a; /* NULL if failed to set */ 802120585Ssam} 803105197Ssam 804105197Ssamstruct in6_ifaddr * 805105197Ssamip6_getdstifaddr(m) 806105197Ssam struct mbuf *m; 807105197Ssam{ 808105197Ssam struct ip6aux *ip6a; 809105197Ssam 810105197Ssam ip6a = ip6_findaux(m); 811105197Ssam if (ip6a) 812105197Ssam return ip6a->ip6a_dstia6; 813105197Ssam else 814105197Ssam return NULL; 815105197Ssam} 816105197Ssam 817105197Ssam/* 818105197Ssam * Hop-by-Hop options header processing. If a valid jumbo payload option is 819105197Ssam * included, the real payload length will be stored in plenp. 820105197Ssam */ 821105197Ssamstatic int 822105197Ssamip6_hopopts_input(plenp, rtalertp, mp, offp) 823105197Ssam u_int32_t *plenp; 824105197Ssam u_int32_t *rtalertp; /* XXX: should be stored more smart way */ 825105197Ssam struct mbuf **mp; 826105197Ssam int *offp; 827105197Ssam{ 828105197Ssam struct mbuf *m = *mp; 829105197Ssam int off = *offp, hbhlen; 830105197Ssam struct ip6_hbh *hbh; 831105197Ssam u_int8_t *opt; 832105197Ssam 833105197Ssam /* validation of the length of the header */ 834105197Ssam#ifndef PULLDOWN_TEST 835105197Ssam IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1); 836105197Ssam hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); 837105197Ssam hbhlen = (hbh->ip6h_len + 1) << 3; 838105197Ssam 839105197Ssam IP6_EXTHDR_CHECK(m, off, hbhlen, -1); 840105197Ssam hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); 841105197Ssam#else 842105197Ssam IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, 843105197Ssam sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); 844105197Ssam if (hbh == NULL) { 845105197Ssam ip6stat.ip6s_tooshort++; 846105197Ssam return -1; 847120585Ssam } 848120585Ssam hbhlen = (hbh->ip6h_len + 1) << 3; 849105197Ssam IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), 850105197Ssam hbhlen); 851105197Ssam if (hbh == NULL) { 852105197Ssam ip6stat.ip6s_tooshort++; 853105197Ssam return -1; 854120585Ssam } 855105197Ssam#endif 856105197Ssam off += hbhlen; 857105197Ssam hbhlen -= sizeof(struct ip6_hbh); 858105197Ssam opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh); 859105197Ssam 860105197Ssam if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh), 861105197Ssam hbhlen, rtalertp, plenp) < 0) 862105197Ssam return (-1); 863105197Ssam 864105197Ssam *offp = off; 865105197Ssam *mp = m; 866105197Ssam return (0); 867105197Ssam} 868105197Ssam 869105197Ssam/* 870128856Ssam * Search header for all Hop-by-hop options and process each option. 871183550Szec * This function is separate from ip6_hopopts_input() in order to 872105197Ssam * handle a case where the sending node itself process its hop-by-hop 873105197Ssam * options header. In such a case, the function is called from ip6_output(). 874128856Ssam * 875128856Ssam * The function assumes that hbh header is located right after the IPv6 header 876105197Ssam * (RFC2460 p7), opthead is pointer into data content in m, and opthead to 877120585Ssam * opthead + hbhlen is located in continuous memory region. 878181803Sbz */ 879105197Ssamint 880105197Ssamip6_process_hopopts(m, opthead, hbhlen, rtalertp, plenp) 881119643Ssam struct mbuf *m; 882181803Sbz u_int8_t *opthead; 883128856Ssam int hbhlen; 884128856Ssam u_int32_t *rtalertp; 885128856Ssam u_int32_t *plenp; 886128856Ssam{ 887128856Ssam struct ip6_hdr *ip6; 888128856Ssam int optlen = 0; 889120585Ssam u_int8_t *opt = opthead; 890105197Ssam u_int16_t rtalert_val; 891119643Ssam u_int32_t jumboplen; 892105197Ssam const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); 893120585Ssam 894105197Ssam for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { 895105197Ssam switch (*opt) { 896105197Ssam case IP6OPT_PAD1: 897105197Ssam optlen = 1; 898105197Ssam break; 899128856Ssam case IP6OPT_PADN: 900128856Ssam if (hbhlen < IP6OPT_MINLEN) { 901105197Ssam ip6stat.ip6s_toosmall++; 902105197Ssam goto bad; 903105197Ssam } 904105197Ssam optlen = *(opt + 1) + 2; 905105197Ssam break; 906128856Ssam case IP6OPT_ROUTER_ALERT: 907105197Ssam /* XXX may need check for alignment */ 908105197Ssam if (hbhlen < IP6OPT_RTALERT_LEN) { 909105197Ssam ip6stat.ip6s_toosmall++; 910105197Ssam goto bad; 911105197Ssam } 912105197Ssam if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { 913105197Ssam /* XXX stat */ 914105197Ssam icmp6_error(m, ICMP6_PARAM_PROB, 915105197Ssam ICMP6_PARAMPROB_HEADER, 916105197Ssam erroff + opt + 1 - opthead); 917105197Ssam return (-1); 918105197Ssam } 919183550Szec optlen = IP6OPT_RTALERT_LEN; 920105197Ssam bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); 921105197Ssam *rtalertp = ntohs(rtalert_val); 922105197Ssam break; 923105197Ssam case IP6OPT_JUMBO: 924105197Ssam /* XXX may need check for alignment */ 925120585Ssam if (hbhlen < IP6OPT_JUMBO_LEN) { 926105197Ssam ip6stat.ip6s_toosmall++; 927105197Ssam goto bad; 928105197Ssam } 929105197Ssam if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { 930105197Ssam /* XXX stat */ 931105197Ssam icmp6_error(m, ICMP6_PARAM_PROB, 932105197Ssam ICMP6_PARAMPROB_HEADER, 933120585Ssam erroff + opt + 1 - opthead); 934105197Ssam return (-1); 935105197Ssam } 936105197Ssam optlen = IP6OPT_JUMBO_LEN; 937105197Ssam 938105197Ssam /* 939105197Ssam * IPv6 packets that have non 0 payload length 940105197Ssam * must not contain a jumbo payload option. 941105197Ssam */ 942105197Ssam ip6 = mtod(m, struct ip6_hdr *); 943120585Ssam if (ip6->ip6_plen) { 944120585Ssam ip6stat.ip6s_badoptions++; 945120585Ssam icmp6_error(m, ICMP6_PARAM_PROB, 946105197Ssam ICMP6_PARAMPROB_HEADER, 947105197Ssam erroff + opt - opthead); 948181803Sbz return (-1); 949157123Sgnn } 950157123Sgnn 951105197Ssam /* 952105197Ssam * We may see jumbolen in unaligned location, so 953105197Ssam * we'd need to perform bcopy(). 954105197Ssam */ 955105197Ssam bcopy(opt + 2, &jumboplen, sizeof(jumboplen)); 956105197Ssam jumboplen = (u_int32_t)htonl(jumboplen); 957125876Sguido 958157123Sgnn#if 1 959157123Sgnn /* 960105197Ssam * if there are multiple jumbo payload options, 961105197Ssam * *plenp will be non-zero and the packet will be 962105197Ssam * rejected. 963105197Ssam * the behavior may need some debate in ipngwg - 964105197Ssam * multiple options does not make sense, however, 965105197Ssam * there's no explicit mention in specification. 966105197Ssam */ 967105197Ssam if (*plenp != 0) { 968105197Ssam ip6stat.ip6s_badoptions++; 969105197Ssam icmp6_error(m, ICMP6_PARAM_PROB, 970177553Sbz ICMP6_PARAMPROB_HEADER, 971105197Ssam erroff + opt + 2 - opthead); 972125508Ssam return (-1); 973105197Ssam } 974105197Ssam#endif 975105197Ssam 976120585Ssam /* 977125508Ssam * jumbo payload length must be larger than 65535. 978125508Ssam */ 979125508Ssam if (jumboplen <= IPV6_MAXPACKET) { 980125508Ssam ip6stat.ip6s_badoptions++; 981125508Ssam icmp6_error(m, ICMP6_PARAM_PROB, 982105197Ssam ICMP6_PARAMPROB_HEADER, 983125508Ssam erroff + opt + 2 - opthead); 984105197Ssam return (-1); 985105197Ssam } 986105197Ssam *plenp = jumboplen; 987105197Ssam 988105197Ssam break; 989105197Ssam default: /* unknown option */ 990105197Ssam if (hbhlen < IP6OPT_MINLEN) { 991105197Ssam ip6stat.ip6s_toosmall++; 992105197Ssam goto bad; 993105197Ssam } 994105197Ssam optlen = ip6_unknown_opt(opt, m, 995105197Ssam erroff + opt - opthead); 996105197Ssam if (optlen == -1) 997105197Ssam return (-1); 998105197Ssam optlen += 2; 999128860Ssam break; 1000128860Ssam } 1001105197Ssam } 1002105197Ssam 1003105197Ssam return (0); 1004105197Ssam 1005105197Ssam bad: 1006105197Ssam m_freem(m); 1007105197Ssam return (-1); 1008105197Ssam} 1009105197Ssam 1010105197Ssam/* 1011105197Ssam * Unknown option processing. 1012105197Ssam * The third argument `off' is the offset from the IPv6 header to the option, 1013105197Ssam * which is necessary if the IPv6 header the and option header and IPv6 header 1014105197Ssam * is not continuous in order to return an ICMPv6 error. 1015105197Ssam */ 1016105197Ssamint 1017105197Ssamip6_unknown_opt(optp, m, off) 1018105197Ssam u_int8_t *optp; 1019105197Ssam struct mbuf *m; 1020105197Ssam int off; 1021105197Ssam{ 1022105197Ssam struct ip6_hdr *ip6; 1023105197Ssam 1024105197Ssam switch (IP6OPT_TYPE(*optp)) { 1025105197Ssam case IP6OPT_TYPE_SKIP: /* ignore the option */ 1026105197Ssam return ((int)*(optp + 1)); 1027105197Ssam case IP6OPT_TYPE_DISCARD: /* silently discard */ 1028105197Ssam m_freem(m); 1029105197Ssam return (-1); 1030105197Ssam case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ 1031105197Ssam ip6stat.ip6s_badoptions++; 1032105197Ssam icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); 1033158767Spjd return (-1); 1034105197Ssam case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ 1035120585Ssam ip6stat.ip6s_badoptions++; 1036120585Ssam ip6 = mtod(m, struct ip6_hdr *); 1037105197Ssam if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || 1038120585Ssam (m->m_flags & (M_BCAST|M_MCAST))) 1039119643Ssam m_freem(m); 1040105197Ssam else 1041105197Ssam icmp6_error(m, ICMP6_PARAM_PROB, 1042105197Ssam ICMP6_PARAMPROB_OPTION, off); 1043105197Ssam return (-1); 1044105197Ssam } 1045105197Ssam 1046105197Ssam m_freem(m); /* XXX: NOTREACHED */ 1047105197Ssam return (-1); 1048105197Ssam} 1049105197Ssam 1050105197Ssam/* 1051105197Ssam * Create the "control" list for this pcb. 1052105197Ssam * The function will not modify mbuf chain at all. 1053105197Ssam * 1054105197Ssam * with KAME mbuf chain restriction: 1055105197Ssam * The routine will be called from upper layer handlers like tcp6_input(). 1056105197Ssam * Thus the routine assumes that the caller (tcp6_input) have already 1057105197Ssam * called IP6_EXTHDR_CHECK() and all the extension headers are located in the 1058105197Ssam * very first mbuf on the mbuf chain. 1059105197Ssam */ 1060105197Ssamvoid 1061105197Ssamip6_savecontrol(in6p, m, mp) 1062105197Ssam struct inpcb *in6p; 1063105197Ssam struct mbuf *m, **mp; 1064105197Ssam{ 1065183550Szec#define IS2292(x, y) ((in6p->in6p_flags & IN6P_RFC2292) ? (x) : (y)) 1066105197Ssam struct thread *td = curthread; /* XXX */ 1067105197Ssam int privileged = 0; 1068128856Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1069128856Ssam 1070105197Ssam if (td && !suser(td)) 1071120585Ssam privileged++; 1072105197Ssam 1073105197Ssam#ifdef SO_TIMESTAMP 1074120585Ssam if ((in6p->in6p_socket->so_options & SO_TIMESTAMP) != 0) { 1075105197Ssam struct timeval tv; 1076105197Ssam 1077105197Ssam microtime(&tv); 1078105197Ssam *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), 1079105197Ssam SCM_TIMESTAMP, SOL_SOCKET); 1080105197Ssam if (*mp) 1081105197Ssam mp = &(*mp)->m_next; 1082120585Ssam } 1083181803Sbz#endif 1084128856Ssam 1085128856Ssam if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) 1086128856Ssam return; 1087128856Ssam 1088128856Ssam /* RFC 2292 sec. 5 */ 1089128856Ssam if ((in6p->in6p_flags & IN6P_PKTINFO) != 0) { 1090181803Sbz struct in6_pktinfo pi6; 1091105197Ssam 1092128856Ssam bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); 1093105197Ssam in6_clearscope(&pi6.ipi6_addr); /* XXX */ 1094105197Ssam pi6.ipi6_ifindex = 1095105197Ssam (m && m->m_pkthdr.rcvif) ? m->m_pkthdr.rcvif->if_index : 0; 1096120585Ssam 1097105197Ssam *mp = sbcreatecontrol((caddr_t) &pi6, 1098105197Ssam sizeof(struct in6_pktinfo), 1099105197Ssam IS2292(IPV6_2292PKTINFO, IPV6_PKTINFO), IPPROTO_IPV6); 1100105197Ssam if (*mp) 1101105197Ssam mp = &(*mp)->m_next; 1102105197Ssam } 1103105197Ssam 1104105197Ssam if ((in6p->in6p_flags & IN6P_HOPLIMIT) != 0) { 1105105197Ssam int hlim = ip6->ip6_hlim & 0xff; 1106105197Ssam 1107105197Ssam *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), 1108105197Ssam IS2292(IPV6_2292HOPLIMIT, IPV6_HOPLIMIT), IPPROTO_IPV6); 1109105197Ssam if (*mp) 1110105197Ssam mp = &(*mp)->m_next; 1111105197Ssam } 1112105197Ssam 1113158767Spjd if ((in6p->in6p_flags & IN6P_TCLASS) != 0) { 1114105197Ssam u_int32_t flowinfo; 1115105197Ssam int tclass; 1116105197Ssam 1117105197Ssam flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK); 1118105197Ssam flowinfo >>= 20; 1119105197Ssam 1120120585Ssam tclass = flowinfo & 0xff; 1121105197Ssam *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass), 1122105197Ssam IPV6_TCLASS, IPPROTO_IPV6); 1123120585Ssam if (*mp) 1124105197Ssam mp = &(*mp)->m_next; 1125105197Ssam } 1126105197Ssam 1127105197Ssam /* 1128105197Ssam * IPV6_HOPOPTS socket option. We require super-user privilege 1129105197Ssam * for the option, but it might be too strict, since there might 1130105197Ssam * be some hop-by-hop options which can be returned to normal user. 1131105197Ssam * See RFC 2292 section 6. 1132105197Ssam */ 1133105197Ssam if ((in6p->in6p_flags & IN6P_HOPOPTS) != 0) { 1134105197Ssam#ifdef DIAGNOSTIC 1135183550Szec if (!privileged) 1136105197Ssam panic("IN6P_HOPOPTS is set for unprivileged socket"); 1137105197Ssam#endif 1138120585Ssam /* 1139105197Ssam * Check if a hop-by-hop options header is contatined in the 1140120585Ssam * received packet, and if so, store the options as ancillary 1141105197Ssam * data. Note that a hop-by-hop options header must be 1142105197Ssam * just after the IPv6 header, which is assured through the 1143105197Ssam * IPv6 input processing. 1144120585Ssam */ 1145120585Ssam if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { 1146105197Ssam struct ip6_hbh *hbh; 1147105197Ssam int hbhlen = 0; 1148105197Ssam#ifdef PULLDOWN_TEST 1149105197Ssam struct mbuf *ext; 1150105197Ssam#endif 1151120585Ssam 1152105197Ssam#ifndef PULLDOWN_TEST 1153105197Ssam hbh = (struct ip6_hbh *)(ip6 + 1); 1154105197Ssam hbhlen = (hbh->ip6h_len + 1) << 3; 1155105197Ssam#else 1156105197Ssam ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr), 1157105197Ssam ip6->ip6_nxt); 1158105197Ssam if (ext == NULL) { 1159105197Ssam ip6stat.ip6s_tooshort++; 1160105197Ssam return; 1161183550Szec } 1162120585Ssam hbh = mtod(ext, struct ip6_hbh *); 1163105197Ssam hbhlen = (hbh->ip6h_len + 1) << 3; 1164105197Ssam if (hbhlen != ext->m_len) { 1165186141Sbz m_freem(ext); 1166105197Ssam ip6stat.ip6s_tooshort++; 1167105197Ssam return; 1168105197Ssam } 1169105197Ssam#endif 1170105197Ssam 1171186141Sbz /* 1172105197Ssam * XXX: We copy the whole header even if a 1173186141Sbz * jumbo payload option is included, the option which 1174105197Ssam * is to be removed before returning according to 1175105197Ssam * RFC2292. 1176105197Ssam * Note: this constraint is removed in 2292bis. 1177105197Ssam */ 1178105197Ssam *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, 1179105197Ssam IS2292(IPV6_2292HOPOPTS, IPV6_HOPOPTS), 1180105197Ssam IPPROTO_IPV6); 1181105197Ssam if (*mp) 1182186141Sbz mp = &(*mp)->m_next; 1183105197Ssam#ifdef PULLDOWN_TEST 1184120585Ssam m_freem(ext); 1185120585Ssam#endif 1186105197Ssam } 1187105197Ssam } 1188105197Ssam 1189105197Ssam if ((in6p->in6p_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) { 1190105197Ssam int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr); 1191105197Ssam 1192105197Ssam /* 1193120585Ssam * Search for destination options headers or routing 1194105197Ssam * header(s) through the header chain, and stores each 1195105197Ssam * header as ancillary data. 1196105197Ssam * Note that the order of the headers remains in 1197105197Ssam * the chain of ancillary data. 1198105197Ssam */ 1199120585Ssam while (1) { /* is explicit loop prevention necessary? */ 1200120585Ssam struct ip6_ext *ip6e = NULL; 1201105197Ssam int elen; 1202105197Ssam#ifdef PULLDOWN_TEST 1203105197Ssam struct mbuf *ext = NULL; 1204105197Ssam#endif 1205105197Ssam 1206105197Ssam /* 1207105197Ssam * if it is not an extension header, don't try to 1208105197Ssam * pull it from the chain. 1209105197Ssam */ 1210105197Ssam switch (nxt) { 1211105197Ssam case IPPROTO_DSTOPTS: 1212183550Szec case IPPROTO_ROUTING: 1213105197Ssam case IPPROTO_HOPOPTS: 1214105197Ssam case IPPROTO_AH: /* is it possible? */ 1215120585Ssam break; 1216105197Ssam default: 1217158767Spjd goto loopend; 1218158767Spjd } 1219158767Spjd 1220158767Spjd#ifndef PULLDOWN_TEST 1221105197Ssam if (off + sizeof(*ip6e) > m->m_len) 1222105197Ssam goto loopend; 1223158767Spjd ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); 1224158767Spjd if (nxt == IPPROTO_AH) 1225158767Spjd elen = (ip6e->ip6e_len + 2) << 2; 1226158767Spjd else 1227105197Ssam elen = (ip6e->ip6e_len + 1) << 3; 1228105197Ssam if (off + elen > m->m_len) 1229105197Ssam goto loopend; 1230105197Ssam#else 1231105197Ssam ext = ip6_pullexthdr(m, off, nxt); 1232105197Ssam if (ext == NULL) { 1233105197Ssam ip6stat.ip6s_tooshort++; 1234105197Ssam return; 1235105197Ssam } 1236105197Ssam ip6e = mtod(ext, struct ip6_ext *); 1237119643Ssam if (nxt == IPPROTO_AH) 1238105197Ssam elen = (ip6e->ip6e_len + 2) << 2; 1239120585Ssam else 1240120585Ssam elen = (ip6e->ip6e_len + 1) << 3; 1241105197Ssam if (elen != ext->m_len) { 1242105197Ssam m_freem(ext); 1243105197Ssam ip6stat.ip6s_tooshort++; 1244120585Ssam return; 1245120585Ssam } 1246105197Ssam#endif 1247105197Ssam 1248105197Ssam switch (nxt) { 1249105197Ssam case IPPROTO_DSTOPTS: 1250105197Ssam if (!(in6p->in6p_flags & IN6P_DSTOPTS)) 1251119643Ssam break; 1252105197Ssam 1253105197Ssam /* 1254105197Ssam * We also require super-user privilege for 1255105197Ssam * the option. See comments on IN6_HOPOPTS. 1256105197Ssam */ 1257105197Ssam if (!privileged) 1258119643Ssam break; 1259105197Ssam 1260119643Ssam *mp = sbcreatecontrol((caddr_t)ip6e, elen, 1261105197Ssam IS2292(IPV6_2292DSTOPTS, IPV6_DSTOPTS), 1262105197Ssam IPPROTO_IPV6); 1263105197Ssam if (*mp) 1264105197Ssam mp = &(*mp)->m_next; 1265105197Ssam break; 1266105197Ssam case IPPROTO_ROUTING: 1267105197Ssam if (!in6p->in6p_flags & IN6P_RTHDR) 1268105197Ssam break; 1269105197Ssam 1270105197Ssam *mp = sbcreatecontrol((caddr_t)ip6e, elen, 1271183550Szec IS2292(IPV6_2292RTHDR, IPV6_RTHDR), 1272105197Ssam IPPROTO_IPV6); 1273105197Ssam if (*mp) 1274120585Ssam mp = &(*mp)->m_next; 1275105197Ssam break; 1276120585Ssam case IPPROTO_HOPOPTS: 1277181803Sbz case IPPROTO_AH: /* is it possible? */ 1278105197Ssam break; 1279105197Ssam 1280105197Ssam default: 1281105197Ssam /* 1282119643Ssam * other cases have been filtered in the above. 1283105197Ssam * none will visit this case. here we supply 1284105197Ssam * the code just in case (nxt overwritten or 1285120585Ssam * other cases). 1286105197Ssam */ 1287119643Ssam#ifdef PULLDOWN_TEST 1288105197Ssam m_freem(ext); 1289105197Ssam#endif 1290105197Ssam goto loopend; 1291105197Ssam 1292105197Ssam } 1293105197Ssam 1294105197Ssam /* proceed with the next header. */ 1295105197Ssam off += elen; 1296105197Ssam nxt = ip6e->ip6e_nxt; 1297105197Ssam ip6e = NULL; 1298183550Szec#ifdef PULLDOWN_TEST 1299105197Ssam m_freem(ext); 1300105197Ssam ext = NULL; 1301120585Ssam#endif 1302181803Sbz } 1303105197Ssam loopend: 1304105197Ssam ; 1305105197Ssam } 1306105197Ssam 1307119643Ssam#undef IS2292 1308105197Ssam} 1309105197Ssam 1310105197Ssamvoid 1311181803Sbzip6_notify_pmtu(in6p, dst, mtu) 1312105197Ssam struct inpcb *in6p; 1313105197Ssam struct sockaddr_in6 *dst; 1314105197Ssam u_int32_t *mtu; 1315105197Ssam{ 1316119643Ssam struct socket *so; 1317105197Ssam struct mbuf *m_mtu; 1318105197Ssam struct ip6_mtuinfo mtuctl; 1319119643Ssam 1320120585Ssam so = in6p->inp_socket; 1321105197Ssam 1322119643Ssam if (mtu == NULL) 1323105197Ssam return; 1324105197Ssam 1325105197Ssam#ifdef DIAGNOSTIC 1326105197Ssam if (so == NULL) /* I believe this is impossible */ 1327105197Ssam panic("ip6_notify_pmtu: socket is NULL"); 1328183550Szec#endif 1329105197Ssam 1330105197Ssam bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */ 1331105197Ssam mtuctl.ip6m_mtu = *mtu; 1332119643Ssam mtuctl.ip6m_addr = *dst; 1333105197Ssam in6_recoverscope(&mtuctl.ip6m_addr, &mtuctl.ip6m_addr.sin6_addr, NULL); 1334120585Ssam 1335105197Ssam if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl), 1336105197Ssam IPV6_PATHMTU, IPPROTO_IPV6)) == NULL) 1337105197Ssam return; 1338105197Ssam 1339105197Ssam if (sbappendaddr(&so->so_rcv, (struct sockaddr *)dst, NULL, m_mtu) 1340120585Ssam == 0) { 1341105197Ssam m_freem(m_mtu); 1342105197Ssam /* XXX: should count statistics */ 1343105197Ssam } else 1344105197Ssam sorwakeup(so); 1345119643Ssam 1346119643Ssam return; 1347119643Ssam} 1348120585Ssam 1349119643Ssam#ifdef PULLDOWN_TEST 1350119643Ssam/* 1351119643Ssam * pull single extension header from mbuf chain. returns single mbuf that 1352105197Ssam * contains the result, or NULL on error. 1353105197Ssam */ 1354105197Ssamstatic struct mbuf * 1355105197Ssamip6_pullexthdr(m, off, nxt) 1356105197Ssam struct mbuf *m; 1357105197Ssam size_t off; 1358105197Ssam int nxt; 1359105197Ssam{ 1360105197Ssam struct ip6_ext ip6e; 1361105197Ssam size_t elen; 1362105197Ssam struct mbuf *n; 1363183550Szec 1364105197Ssam#ifdef DIAGNOSTIC 1365105197Ssam switch (nxt) { 1366120585Ssam case IPPROTO_DSTOPTS: 1367127972Spjd case IPPROTO_ROUTING: 1368120585Ssam case IPPROTO_HOPOPTS: 1369105197Ssam case IPPROTO_AH: /* is it possible? */ 1370120585Ssam break; 1371105197Ssam default: 1372105197Ssam printf("ip6_pullexthdr: invalid nxt=%d\n", nxt); 1373105197Ssam } 1374105197Ssam#endif 1375105197Ssam 1376105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); 1377105197Ssam if (nxt == IPPROTO_AH) 1378105197Ssam elen = (ip6e.ip6e_len + 2) << 2; 1379105197Ssam else 1380105197Ssam elen = (ip6e.ip6e_len + 1) << 3; 1381105197Ssam 1382105197Ssam MGET(n, M_DONTWAIT, MT_DATA); 1383105197Ssam if (n && elen >= MLEN) { 1384105197Ssam MCLGET(n, M_DONTWAIT); 1385105197Ssam if ((n->m_flags & M_EXT) == 0) { 1386105197Ssam m_free(n); 1387105197Ssam n = NULL; 1388105197Ssam } 1389105197Ssam } 1390105197Ssam if (!n) 1391105197Ssam return NULL; 1392105197Ssam 1393105197Ssam n->m_len = 0; 1394105197Ssam if (elen >= M_TRAILINGSPACE(n)) { 1395105197Ssam m_free(n); 1396105197Ssam return NULL; 1397105197Ssam } 1398105197Ssam 1399105197Ssam m_copydata(m, off, elen, mtod(n, caddr_t)); 1400120585Ssam n->m_len = elen; 1401120585Ssam return n; 1402105197Ssam} 1403105197Ssam#endif 1404105197Ssam 1405105197Ssam/* 1406105197Ssam * Get pointer to the previous header followed by the header 1407105197Ssam * currently processed. 1408105197Ssam * XXX: This function supposes that 1409105197Ssam * M includes all headers, 1410105197Ssam * the next header field and the header length field of each header 1411105197Ssam * are valid, and 1412105197Ssam * the sum of each header length equals to OFF. 1413120585Ssam * Because of these assumptions, this function must be called very 1414120585Ssam * carefully. Moreover, it will not be used in the near future when 1415105197Ssam * we develop `neater' mechanism to process extension headers. 1416105197Ssam */ 1417105197Ssamchar * 1418105197Ssamip6_get_prevhdr(m, off) 1419105197Ssam struct mbuf *m; 1420105197Ssam int off; 1421119643Ssam{ 1422119643Ssam struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1423105197Ssam 1424105197Ssam if (off == sizeof(struct ip6_hdr)) 1425120585Ssam return (&ip6->ip6_nxt); 1426105197Ssam else { 1427105197Ssam int len, nxt; 1428105197Ssam struct ip6_ext *ip6e = NULL; 1429105197Ssam 1430105197Ssam nxt = ip6->ip6_nxt; 1431105197Ssam len = sizeof(struct ip6_hdr); 1432105197Ssam while (len < off) { 1433105197Ssam ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); 1434105197Ssam 1435105197Ssam switch (nxt) { 1436105197Ssam case IPPROTO_FRAGMENT: 1437105197Ssam len += sizeof(struct ip6_frag); 1438105197Ssam break; 1439120585Ssam case IPPROTO_AH: 1440105197Ssam len += (ip6e->ip6e_len + 2) << 2; 1441105197Ssam break; 1442105197Ssam default: 1443105197Ssam len += (ip6e->ip6e_len + 1) << 3; 1444105197Ssam break; 1445105197Ssam } 1446105197Ssam nxt = ip6e->ip6e_nxt; 1447105197Ssam } 1448105197Ssam if (ip6e) 1449105197Ssam return (&ip6e->ip6e_nxt); 1450105197Ssam else 1451105197Ssam return NULL; 1452105197Ssam } 1453105197Ssam} 1454120585Ssam 1455105197Ssam/* 1456105197Ssam * get next header offset. m will be retained. 1457105197Ssam */ 1458105197Ssamint 1459105197Ssamip6_nexthdr(m, off, proto, nxtp) 1460105197Ssam struct mbuf *m; 1461105197Ssam int off; 1462105197Ssam int proto; 1463105197Ssam int *nxtp; 1464105197Ssam{ 1465105197Ssam struct ip6_hdr ip6; 1466105197Ssam struct ip6_ext ip6e; 1467105197Ssam struct ip6_frag fh; 1468105197Ssam 1469105197Ssam /* just in case */ 1470105197Ssam if (m == NULL) 1471105197Ssam panic("ip6_nexthdr: m == NULL"); 1472105197Ssam if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off) 1473105197Ssam return -1; 1474105197Ssam 1475105197Ssam switch (proto) { 1476120585Ssam case IPPROTO_IPV6: 1477105197Ssam if (m->m_pkthdr.len < off + sizeof(ip6)) 1478120585Ssam return -1; 1479105197Ssam m_copydata(m, off, sizeof(ip6), (caddr_t)&ip6); 1480105197Ssam if (nxtp) 1481105197Ssam *nxtp = ip6.ip6_nxt; 1482105197Ssam off += sizeof(ip6); 1483105197Ssam return off; 1484105197Ssam 1485105197Ssam case IPPROTO_FRAGMENT: 1486105197Ssam /* 1487105197Ssam * terminate parsing if it is not the first fragment, 1488105197Ssam * it does not make sense to parse through it. 1489105197Ssam */ 1490105197Ssam if (m->m_pkthdr.len < off + sizeof(fh)) 1491105197Ssam return -1; 1492105197Ssam m_copydata(m, off, sizeof(fh), (caddr_t)&fh); 1493105197Ssam /* IP6F_OFF_MASK = 0xfff8(BigEndian), 0xf8ff(LittleEndian) */ 1494105197Ssam if (fh.ip6f_offlg & IP6F_OFF_MASK) 1495105197Ssam return -1; 1496105197Ssam if (nxtp) 1497105197Ssam *nxtp = fh.ip6f_nxt; 1498105197Ssam off += sizeof(struct ip6_frag); 1499105197Ssam return off; 1500105197Ssam 1501120585Ssam case IPPROTO_AH: 1502120585Ssam if (m->m_pkthdr.len < off + sizeof(ip6e)) 1503105197Ssam return -1; 1504105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); 1505105197Ssam if (nxtp) 1506105197Ssam *nxtp = ip6e.ip6e_nxt; 1507105197Ssam off += (ip6e.ip6e_len + 2) << 2; 1508105197Ssam return off; 1509105197Ssam 1510105197Ssam case IPPROTO_HOPOPTS: 1511105197Ssam case IPPROTO_ROUTING: 1512105197Ssam case IPPROTO_DSTOPTS: 1513105197Ssam if (m->m_pkthdr.len < off + sizeof(ip6e)) 1514105197Ssam return -1; 1515105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); 1516105197Ssam if (nxtp) 1517105197Ssam *nxtp = ip6e.ip6e_nxt; 1518105197Ssam off += (ip6e.ip6e_len + 1) << 3; 1519120585Ssam return off; 1520120585Ssam 1521120585Ssam case IPPROTO_NONE: 1522105197Ssam case IPPROTO_ESP: 1523105197Ssam case IPPROTO_IPCOMP: 1524105197Ssam /* give up */ 1525105197Ssam return -1; 1526105197Ssam 1527105197Ssam default: 1528105197Ssam return -1; 1529105197Ssam } 1530105197Ssam 1531105197Ssam return -1; 1532105197Ssam} 1533105197Ssam 1534105197Ssam/* 1535120585Ssam * get offset for the last header in the chain. m will be kept untainted. 1536120585Ssam */ 1537120585Ssamint 1538105197Ssamip6_lasthdr(m, off, proto, nxtp) 1539105197Ssam struct mbuf *m; 1540105197Ssam int off; 1541105197Ssam int proto; 1542105197Ssam int *nxtp; 1543105197Ssam{ 1544105197Ssam int newoff; 1545105197Ssam int nxt; 1546105197Ssam 1547105197Ssam if (!nxtp) { 1548105197Ssam nxt = -1; 1549105197Ssam nxtp = &nxt; 1550105197Ssam } 1551105197Ssam while (1) { 1552105197Ssam newoff = ip6_nexthdr(m, off, proto, nxtp); 1553105197Ssam if (newoff < 0) 1554120585Ssam return off; 1555120585Ssam else if (newoff < off) 1556105197Ssam return -1; /* invalid */ 1557105197Ssam else if (newoff == off) 1558105197Ssam return newoff; 1559105197Ssam 1560105197Ssam off = newoff; 1561105197Ssam proto = *nxtp; 1562105197Ssam } 1563105197Ssam} 1564105197Ssam 1565105197Ssamstruct ip6aux * 1566105197Ssamip6_addaux(m) 1567120585Ssam struct mbuf *m; 1568105197Ssam{ 1569105197Ssam struct m_tag *mtag; 1570105197Ssam 1571105197Ssam mtag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL); 1572105197Ssam if (!mtag) { 1573105197Ssam mtag = m_tag_get(PACKET_TAG_IPV6_INPUT, sizeof(struct ip6aux), 1574105197Ssam M_NOWAIT); 1575105197Ssam if (mtag) { 1576105197Ssam m_tag_prepend(m, mtag); 1577105197Ssam bzero(mtag + 1, sizeof(struct ip6aux)); 1578105197Ssam } 1579105197Ssam } 1580105197Ssam return mtag ? (struct ip6aux *)(mtag + 1) : NULL; 1581105197Ssam} 1582105197Ssam 1583105197Ssamstruct ip6aux * 1584105197Ssamip6_findaux(m) 1585105197Ssam struct mbuf *m; 1586105197Ssam{ 1587105197Ssam struct m_tag *mtag; 1588105197Ssam 1589105197Ssam mtag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL); 1590105197Ssam return mtag ? (struct ip6aux *)(mtag + 1) : NULL; 1591105197Ssam} 1592105197Ssam 1593105197Ssamvoid 1594105197Ssamip6_delaux(m) 1595105197Ssam struct mbuf *m; 1596105197Ssam{ 1597105197Ssam struct m_tag *mtag; 1598105197Ssam 1599105197Ssam mtag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL); 1600105197Ssam if (mtag) 1601105197Ssam m_tag_delete(m, mtag); 1602120585Ssam} 1603105197Ssam 1604105197Ssam/* 1605105197Ssam * System control for IP6 1606105197Ssam */ 1607105197Ssam 1608105197Ssamu_char inet6ctlerrmap[PRC_NCMDS] = { 1609105197Ssam 0, 0, 0, 0, 1610105197Ssam 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, 1611105197Ssam EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, 1612105197Ssam EMSGSIZE, EHOSTUNREACH, 0, 0, 1613105197Ssam 0, 0, 0, 0, 1614105197Ssam ENOPROTOOPT 1615105197Ssam}; 1616105197Ssam