in6_gif.c revision 272859
166458Sdfr/*- 266458Sdfr * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 366458Sdfr * All rights reserved. 466458Sdfr * 566458Sdfr * Redistribution and use in source and binary forms, with or without 666458Sdfr * modification, are permitted provided that the following conditions 766458Sdfr * are met: 866458Sdfr * 1. Redistributions of source code must retain the above copyright 966458Sdfr * notice, this list of conditions and the following disclaimer. 1066458Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1166458Sdfr * notice, this list of conditions and the following disclaimer in the 1266458Sdfr * documentation and/or other materials provided with the distribution. 1366458Sdfr * 3. Neither the name of the project nor the names of its contributors 1466458Sdfr * may be used to endorse or promote products derived from this software 1566458Sdfr * without specific prior written permission. 1666458Sdfr * 1766458Sdfr * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1866458Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1966458Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2066458Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2166458Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2266458Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2366458Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2466458Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2566458Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2666458Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2766458Sdfr * SUCH DAMAGE. 2866458Sdfr * 2966458Sdfr * $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $ 30135581Smarcel */ 3166458Sdfr 32185163Smarcel#include <sys/cdefs.h> 33185163Smarcel__FBSDID("$FreeBSD: stable/10/sys/netinet6/in6_gif.c 272859 2014-10-09 23:29:44Z hrs $"); 34185163Smarcel 35185162Skmacy#include "opt_inet.h" 3666458Sdfr#include "opt_inet6.h" 3766458Sdfr 3866458Sdfr#include <sys/param.h> 3966458Sdfr#include <sys/systm.h> 4066458Sdfr#include <sys/socket.h> 4166458Sdfr#include <sys/sockio.h> 4266458Sdfr#include <sys/mbuf.h> 4366458Sdfr#include <sys/errno.h> 44135581Smarcel#include <sys/kernel.h> 45135581Smarcel#include <sys/queue.h> 46135581Smarcel#include <sys/syslog.h> 47135581Smarcel#include <sys/sysctl.h> 48135581Smarcel#include <sys/protosw.h> 49179382Smarcel#include <sys/malloc.h> 5096956Smarcel 5166458Sdfr#include <net/if.h> 5266458Sdfr#include <net/route.h> 5366458Sdfr 5466458Sdfr#include <netinet/in.h> 55135581Smarcel#include <netinet/in_systm.h> 56135581Smarcel#ifdef INET 5766458Sdfr#include <netinet/ip.h> 58135581Smarcel#endif 5996956Smarcel#include <netinet/ip_encap.h> 6096956Smarcel#ifdef INET6 6166458Sdfr#include <netinet/ip6.h> 6266458Sdfr#include <netinet6/ip6_var.h> 63135581Smarcel#include <netinet6/in6_gif.h> 64135581Smarcel#include <netinet6/in6_var.h> 6566458Sdfr#endif 66135581Smarcel#include <netinet6/ip6protosw.h> 6796956Smarcel#include <netinet/ip_ecn.h> 6896956Smarcel#ifdef INET6 6966458Sdfr#include <netinet6/ip6_ecn.h> 7066458Sdfr#endif 71135581Smarcel 72135581Smarcel#include <net/if_gif.h> 7366458Sdfr 74135581SmarcelVNET_DEFINE(int, ip6_gif_hlim) = GIF_HLIM; 7596956Smarcel#define V_ip6_gif_hlim VNET(ip6_gif_hlim) 7696956Smarcel 7766458SdfrSYSCTL_DECL(_net_inet6_ip6); 7866458SdfrSYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim, CTLFLAG_RW, 79135581Smarcel &VNET_NAME(ip6_gif_hlim), 0, ""); 80135581Smarcel 8166458Sdfrstatic int gif_validate6(const struct ip6_hdr *, struct gif_softc *, 82135581Smarcel struct ifnet *); 8396956Smarcel 8496956Smarcelextern struct domain inet6domain; 8566458Sdfrstruct ip6protosw in6_gif_protosw = { 8666458Sdfr .pr_type = SOCK_RAW, 87135581Smarcel .pr_domain = &inet6domain, 88135581Smarcel .pr_protocol = 0, /* IPPROTO_IPV[46] */ 89135581Smarcel .pr_flags = PR_ATOMIC|PR_ADDR, 90135581Smarcel .pr_input = in6_gif_input, 91135581Smarcel .pr_output = rip6_output, 92135581Smarcel .pr_ctloutput = rip6_ctloutput, 93135581Smarcel .pr_usrreqs = &rip6_usrreqs 94135581Smarcel}; 95135581Smarcel 96135581Smarcelint 97135581Smarcelin6_gif_output(struct ifnet *ifp, 98135581Smarcel int family, /* family of the packet to be encapsulate */ 99135581Smarcel struct mbuf *m) 100135581Smarcel{ 101135581Smarcel struct gif_softc *sc = ifp->if_softc; 102135581Smarcel struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; 103135581Smarcel struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; 104135581Smarcel struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; 105135581Smarcel struct ip6_hdr *ip6; 106135581Smarcel struct etherip_header eiphdr; 107135581Smarcel int error, len, proto; 108135581Smarcel u_int8_t itos, otos; 109135581Smarcel 110135581Smarcel GIF_LOCK_ASSERT(sc); 111135581Smarcel 112135581Smarcel if (sin6_src == NULL || sin6_dst == NULL || 113135581Smarcel sin6_src->sin6_family != AF_INET6 || 114135581Smarcel sin6_dst->sin6_family != AF_INET6) { 115135581Smarcel m_freem(m); 116135581Smarcel return EAFNOSUPPORT; 117135581Smarcel } 118135581Smarcel 119135581Smarcel switch (family) { 120135581Smarcel#ifdef INET 121135581Smarcel case AF_INET: 122135581Smarcel { 123135581Smarcel struct ip *ip; 124135581Smarcel 125135581Smarcel proto = IPPROTO_IPV4; 126135581Smarcel if (m->m_len < sizeof(*ip)) { 127135581Smarcel m = m_pullup(m, sizeof(*ip)); 128135581Smarcel if (!m) 129135581Smarcel return ENOBUFS; 130135581Smarcel } 131135581Smarcel ip = mtod(m, struct ip *); 132135581Smarcel itos = ip->ip_tos; 133135581Smarcel break; 134135581Smarcel } 135135581Smarcel#endif 136135581Smarcel#ifdef INET6 13766458Sdfr case AF_INET6: 138135581Smarcel { 139135581Smarcel struct ip6_hdr *ip6; 140135581Smarcel proto = IPPROTO_IPV6; 141135581Smarcel if (m->m_len < sizeof(*ip6)) { 14266458Sdfr m = m_pullup(m, sizeof(*ip6)); 14367351Sjhb if (!m) 14467351Sjhb return ENOBUFS; 145171662Smarcel } 146171662Smarcel ip6 = mtod(m, struct ip6_hdr *); 147148067Sjhb itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 148171662Smarcel break; 149171662Smarcel } 150171662Smarcel#endif 151135581Smarcel case AF_LINK: 152135583Smarcel proto = IPPROTO_ETHERIP; 153135581Smarcel 154135581Smarcel /* 155135581Smarcel * GIF_SEND_REVETHIP (disabled by default) intentionally 156135581Smarcel * sends an EtherIP packet with revered version field in 157135581Smarcel * the header. This is a knob for backward compatibility 158135581Smarcel * with FreeBSD 7.2R or prior. 159135581Smarcel */ 160135583Smarcel if ((sc->gif_options & GIF_SEND_REVETHIP)) { 161135581Smarcel eiphdr.eip_ver = 0; 16266458Sdfr eiphdr.eip_resvl = ETHERIP_VERSION; 163135583Smarcel eiphdr.eip_resvh = 0; 164135581Smarcel } else { 165135581Smarcel eiphdr.eip_ver = ETHERIP_VERSION; 166135581Smarcel eiphdr.eip_resvl = 0; 167135581Smarcel eiphdr.eip_resvh = 0; 168135581Smarcel } 169135581Smarcel /* prepend Ethernet-in-IP header */ 170135581Smarcel M_PREPEND(m, sizeof(struct etherip_header), M_NOWAIT); 171135583Smarcel if (m && m->m_len < sizeof(struct etherip_header)) 172135581Smarcel m = m_pullup(m, sizeof(struct etherip_header)); 17366458Sdfr if (m == NULL) 174135581Smarcel return ENOBUFS; 175135581Smarcel bcopy(&eiphdr, mtod(m, struct etherip_header *), 176135581Smarcel sizeof(struct etherip_header)); 177135581Smarcel itos = 0; 17866458Sdfr break; 179135581Smarcel 180135581Smarcel default: 181135581Smarcel#ifdef DEBUG 182135581Smarcel printf("in6_gif_output: warning: unknown family %d passed\n", 18366458Sdfr family); 184135581Smarcel#endif 185135581Smarcel m_freem(m); 186135581Smarcel return EAFNOSUPPORT; 187135581Smarcel } 18866458Sdfr 189135581Smarcel /* prepend new IP header */ 190135581Smarcel len = sizeof(struct ip6_hdr); 191135581Smarcel#ifndef __NO_STRICT_ALIGNMENT 192135581Smarcel if (family == AF_LINK) 19366458Sdfr len += ETHERIP_ALIGN; 19466458Sdfr#endif 19566458Sdfr M_PREPEND(m, len, M_NOWAIT); 196135581Smarcel if (m != NULL && m->m_len < len) 197135581Smarcel m = m_pullup(m, len); 198135581Smarcel if (m == NULL) { 199135581Smarcel printf("ENOBUFS in in6_gif_output %d\n", __LINE__); 20066458Sdfr return ENOBUFS; 201135581Smarcel } 202135581Smarcel#ifndef __NO_STRICT_ALIGNMENT 203135581Smarcel if (family == AF_LINK) { 204135581Smarcel len = mtod(m, vm_offset_t) & 3; 20566458Sdfr KASSERT(len == 0 || len == ETHERIP_ALIGN, 206135581Smarcel ("in6_gif_output: unexpected misalignment")); 207135581Smarcel m->m_data += len; 208135581Smarcel m->m_len -= ETHERIP_ALIGN; 209135581Smarcel } 21066458Sdfr#endif 211135581Smarcel 212135581Smarcel ip6 = mtod(m, struct ip6_hdr *); 213135581Smarcel ip6->ip6_flow = 0; 214135581Smarcel ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 21566458Sdfr ip6->ip6_vfc |= IPV6_VERSION; 216135581Smarcel ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); 217135581Smarcel ip6->ip6_nxt = proto; 218135581Smarcel ip6->ip6_hlim = V_ip6_gif_hlim; 219135581Smarcel ip6->ip6_src = sin6_src->sin6_addr; 220135581Smarcel /* bidirectional configured tunnel mode */ 221135581Smarcel if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) 222135581Smarcel ip6->ip6_dst = sin6_dst->sin6_addr; 223135581Smarcel else { 224135581Smarcel m_freem(m); 225135581Smarcel return ENETUNREACH; 226135581Smarcel } 227135581Smarcel ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 22867351Sjhb &otos, &itos); 229135581Smarcel ip6->ip6_flow &= ~htonl(0xff << 20); 230135581Smarcel ip6->ip6_flow |= htonl((u_int32_t)otos << 20); 231135581Smarcel 232135581Smarcel M_SETFIB(m, sc->gif_fibnum); 233135581Smarcel 234135581Smarcel if (dst->sin6_family != sin6_dst->sin6_family || 235135581Smarcel !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { 236135581Smarcel /* cache route doesn't match */ 237135581Smarcel bzero(dst, sizeof(*dst)); 238135581Smarcel dst->sin6_family = sin6_dst->sin6_family; 239135581Smarcel dst->sin6_len = sizeof(struct sockaddr_in6); 240135581Smarcel dst->sin6_addr = sin6_dst->sin6_addr; 24167351Sjhb if (sc->gif_ro6.ro_rt) { 242135581Smarcel RTFREE(sc->gif_ro6.ro_rt); 243135581Smarcel sc->gif_ro6.ro_rt = NULL; 244135581Smarcel } 245135581Smarcel#if 0 246135581Smarcel GIF2IFP(sc)->if_mtu = GIF_MTU; 247135581Smarcel#endif 248135581Smarcel } 249135581Smarcel 250135581Smarcel if (sc->gif_ro6.ro_rt == NULL) { 251135581Smarcel in6_rtalloc(&sc->gif_ro6, sc->gif_fibnum); 252135581Smarcel if (sc->gif_ro6.ro_rt == NULL) { 253135581Smarcel m_freem(m); 25467351Sjhb return ENETUNREACH; 255135581Smarcel } 256135581Smarcel 257135581Smarcel /* if it constitutes infinite encapsulation, punt. */ 258135581Smarcel if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 259135581Smarcel m_freem(m); 260135581Smarcel return ENETUNREACH; /*XXX*/ 261135581Smarcel } 262135581Smarcel#if 0 263135581Smarcel ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu 264135581Smarcel - sizeof(struct ip6_hdr); 265135581Smarcel#endif 266135581Smarcel } 26767351Sjhb 268171662Smarcel m->m_flags &= ~(M_BCAST|M_MCAST); 269148067Sjhb#ifdef IPV6_MINMTU 270148067Sjhb /* 271148067Sjhb * force fragmentation to minimum MTU, to avoid path MTU discovery. 272148067Sjhb * it is too painful to ask for resend of inner packet, to achieve 273148067Sjhb * path MTU discovery for encapsulated packets. 274148067Sjhb */ 275148067Sjhb error = ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL); 276148067Sjhb#else 277148067Sjhb error = ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL); 278148067Sjhb#endif 279148067Sjhb 280148067Sjhb if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 281148067Sjhb sc->gif_ro6.ro_rt != NULL) { 282135581Smarcel RTFREE(sc->gif_ro6.ro_rt); 283135581Smarcel sc->gif_ro6.ro_rt = NULL; 28466458Sdfr } 28566458Sdfr 28666458Sdfr return (error); 28766458Sdfr} 28866458Sdfr 28966458Sdfrint 290135581Smarcelin6_gif_input(struct mbuf **mp, int *offp, int proto) 29166458Sdfr{ 292135581Smarcel struct mbuf *m = *mp; 29366458Sdfr struct ifnet *gifp = NULL; 29466458Sdfr struct gif_softc *sc; 29567351Sjhb struct ip6_hdr *ip6; 296135581Smarcel int af = 0; 29767351Sjhb u_int32_t otos; 298135581Smarcel 29967351Sjhb ip6 = mtod(m, struct ip6_hdr *); 30067351Sjhb 30166458Sdfr sc = (struct gif_softc *)encap_getarg(m); 30266458Sdfr if (sc == NULL) { 30366458Sdfr m_freem(m); 30466458Sdfr IP6STAT_INC(ip6s_nogif); 30566458Sdfr return IPPROTO_DONE; 30666458Sdfr } 307135581Smarcel 30866458Sdfr gifp = GIF2IFP(sc); 309135581Smarcel if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 31066458Sdfr m_freem(m); 31166458Sdfr IP6STAT_INC(ip6s_nogif); 31267351Sjhb return IPPROTO_DONE; 313135581Smarcel } 31467351Sjhb 315135581Smarcel otos = ip6->ip6_flow; 31667351Sjhb m_adj(m, *offp); 31767351Sjhb 318135581Smarcel switch (proto) { 319135581Smarcel#ifdef INET 320135581Smarcel case IPPROTO_IPV4: 321135581Smarcel { 322135581Smarcel struct ip *ip; 323135581Smarcel u_int8_t otos8; 324135581Smarcel af = AF_INET; 325135581Smarcel otos8 = (ntohl(otos) >> 20) & 0xff; 32666458Sdfr if (m->m_len < sizeof(*ip)) { 327171662Smarcel m = m_pullup(m, sizeof(*ip)); 328171662Smarcel if (!m) 329171662Smarcel return IPPROTO_DONE; 330171662Smarcel } 331171662Smarcel ip = mtod(m, struct ip *); 332171662Smarcel if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 333171662Smarcel ECN_ALLOWED : ECN_NOCARE, 334171662Smarcel &otos8, &ip->ip_tos) == 0) { 335135581Smarcel m_freem(m); 336135581Smarcel return IPPROTO_DONE; 33766937Sdfr } 338135581Smarcel break; 33966937Sdfr } 34066937Sdfr#endif /* INET */ 34166937Sdfr#ifdef INET6 342135581Smarcel case IPPROTO_IPV6: 34366937Sdfr { 34466937Sdfr struct ip6_hdr *ip6; 345135581Smarcel af = AF_INET6; 346135581Smarcel if (m->m_len < sizeof(*ip6)) { 34766937Sdfr m = m_pullup(m, sizeof(*ip6)); 348135581Smarcel if (!m) 34966937Sdfr return IPPROTO_DONE; 35066937Sdfr } 35166937Sdfr ip6 = mtod(m, struct ip6_hdr *); 352135581Smarcel if (ip6_ecn_egress((gifp->if_flags & IFF_LINK1) ? 35366937Sdfr ECN_ALLOWED : ECN_NOCARE, 35466937Sdfr &otos, &ip6->ip6_flow) == 0) { 355135581Smarcel m_freem(m); 356135581Smarcel return IPPROTO_DONE; 357173970Sjasone } 35866937Sdfr break; 359150627Sjhb } 360150627Sjhb#endif 361150627Sjhb case IPPROTO_ETHERIP: 362150627Sjhb af = AF_LINK; 363150627Sjhb break; 364150627Sjhb 365150627Sjhb default: 366150627Sjhb IP6STAT_INC(ip6s_nogif); 367150627Sjhb m_freem(m); 368150627Sjhb return IPPROTO_DONE; 369150627Sjhb } 370150627Sjhb 371150627Sjhb gif_input(m, af, gifp); 372150627Sjhb return IPPROTO_DONE; 373150627Sjhb} 374150627Sjhb 375150627Sjhb/* 376150627Sjhb * validate outer address. 377150627Sjhb */ 378177276Spjdstatic int 379177276Spjdgif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc, 380177276Spjd struct ifnet *ifp) 381177276Spjd{ 382177276Spjd struct sockaddr_in6 *src, *dst; 383177276Spjd 384177276Spjd src = (struct sockaddr_in6 *)sc->gif_psrc; 385177276Spjd dst = (struct sockaddr_in6 *)sc->gif_pdst; 386177276Spjd 387177276Spjd /* 388177276Spjd * Check for address match. Note that the check is for an incoming 389262004Smarcel * packet. We should compare the *source* address in our configuration 390262004Smarcel * and the *destination* address of the packet, and vice versa. 391262004Smarcel */ 392262004Smarcel if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) || 393262004Smarcel !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src)) 394262004Smarcel return 0; 395262004Smarcel 396262004Smarcel /* martian filters on outer source - done in ip6_input */ 397262004Smarcel 398262004Smarcel /* ingress filters on outer source */ 399262004Smarcel if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 400262004Smarcel struct sockaddr_in6 sin6; 401262004Smarcel struct rtentry *rt; 402262004Smarcel 403262004Smarcel bzero(&sin6, sizeof(sin6)); 404262004Smarcel sin6.sin6_family = AF_INET6; 405262004Smarcel sin6.sin6_len = sizeof(struct sockaddr_in6); 406262004Smarcel sin6.sin6_addr = ip6->ip6_src; 407262004Smarcel sin6.sin6_scope_id = 0; /* XXX */ 408262004Smarcel 409262004Smarcel rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, 0UL, 410262004Smarcel sc->gif_fibnum); 411262004Smarcel if (!rt || rt->rt_ifp != ifp) { 412262004Smarcel#if 0 413262004Smarcel char ip6buf[INET6_ADDRSTRLEN]; 414262004Smarcel log(LOG_WARNING, "%s: packet from %s dropped " 415262004Smarcel "due to ingress filter\n", if_name(GIF2IFP(sc)), 416262004Smarcel ip6_sprintf(ip6buf, &sin6.sin6_addr)); 41766458Sdfr#endif 418 if (rt) 419 RTFREE_LOCKED(rt); 420 return 0; 421 } 422 RTFREE_LOCKED(rt); 423 } 424 425 return 128 * 2; 426} 427 428/* 429 * we know that we are in IFF_UP, outer address available, and outer family 430 * matched the physical addr family. see gif_encapcheck(). 431 * sanity check for arg should have been done in the caller. 432 */ 433int 434gif_encapcheck6(const struct mbuf *m, int off, int proto, void *arg) 435{ 436 struct ip6_hdr ip6; 437 struct gif_softc *sc; 438 struct ifnet *ifp; 439 440 /* sanity check done in caller */ 441 sc = (struct gif_softc *)arg; 442 443 /* LINTED const cast */ 444 m_copydata(m, 0, sizeof(ip6), (caddr_t)&ip6); 445 ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 446 447 return gif_validate6(&ip6, sc, ifp); 448} 449 450int 451in6_gif_attach(struct gif_softc *sc) 452{ 453 sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck, 454 (void *)&in6_gif_protosw, sc); 455 if (sc->encap_cookie6 == NULL) 456 return EEXIST; 457 return 0; 458} 459 460int 461in6_gif_detach(struct gif_softc *sc) 462{ 463 int error; 464 465 error = encap_detach(sc->encap_cookie6); 466 if (error == 0) 467 sc->encap_cookie6 = NULL; 468 return error; 469} 470