in_gif.c revision 269699
178064Sume/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ 262587Sitojun 3139823Simp/*- 454263Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 554263Sshin * All rights reserved. 654263Sshin * 754263Sshin * Redistribution and use in source and binary forms, with or without 854263Sshin * modification, are permitted provided that the following conditions 954263Sshin * are met: 1054263Sshin * 1. Redistributions of source code must retain the above copyright 1154263Sshin * notice, this list of conditions and the following disclaimer. 1254263Sshin * 2. Redistributions in binary form must reproduce the above copyright 1354263Sshin * notice, this list of conditions and the following disclaimer in the 1454263Sshin * documentation and/or other materials provided with the distribution. 1554263Sshin * 3. Neither the name of the project nor the names of its contributors 1654263Sshin * may be used to endorse or promote products derived from this software 1754263Sshin * without specific prior written permission. 1854263Sshin * 1954263Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2054263Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2154263Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2254263Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2354263Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2454263Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2554263Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2654263Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2754263Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2854263Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2954263Sshin * SUCH DAMAGE. 3054263Sshin */ 3154263Sshin 32172467Ssilby#include <sys/cdefs.h> 33172467Ssilby__FBSDID("$FreeBSD: head/sys/netinet/in_gif.c 269699 2014-08-08 01:57:15Z kevlo $"); 34172467Ssilby 3554263Sshin#include "opt_mrouting.h" 3662587Sitojun#include "opt_inet.h" 3754263Sshin#include "opt_inet6.h" 3854263Sshin 3954263Sshin#include <sys/param.h> 4054263Sshin#include <sys/systm.h> 4154263Sshin#include <sys/socket.h> 4254263Sshin#include <sys/sockio.h> 4354263Sshin#include <sys/mbuf.h> 4454263Sshin#include <sys/errno.h> 4554263Sshin#include <sys/kernel.h> 4654263Sshin#include <sys/sysctl.h> 47105293Sume#include <sys/protosw.h> 4862587Sitojun#include <sys/malloc.h> 4962587Sitojun 5054263Sshin#include <net/if.h> 51257176Sglebius#include <net/if_var.h> 5254263Sshin#include <net/route.h> 53196019Srwatson#include <net/vnet.h> 5454263Sshin 5554263Sshin#include <netinet/in.h> 5654263Sshin#include <netinet/in_systm.h> 5754263Sshin#include <netinet/ip.h> 5854263Sshin#include <netinet/ip_var.h> 5954263Sshin#include <netinet/in_gif.h> 6062587Sitojun#include <netinet/in_var.h> 6162587Sitojun#include <netinet/ip_encap.h> 6255009Sshin#include <netinet/ip_ecn.h> 6362587Sitojun 6454263Sshin#ifdef INET6 6562587Sitojun#include <netinet/ip6.h> 6654263Sshin#endif 6754263Sshin 6854263Sshin#ifdef MROUTING 6954263Sshin#include <netinet/ip_mroute.h> 7054263Sshin#endif /* MROUTING */ 7154263Sshin 7262587Sitojun#include <net/if_gif.h> 7354263Sshin 74105293Sumestatic int gif_validate4(const struct ip *, struct gif_softc *, 75105293Sume struct ifnet *); 76105293Sume 77105293Sumeextern struct domain inetdomain; 78152242Srustruct protosw in_gif_protosw = { 79152242Sru .pr_type = SOCK_RAW, 80152242Sru .pr_domain = &inetdomain, 81152242Sru .pr_protocol = 0/* IPPROTO_IPV[46] */, 82152242Sru .pr_flags = PR_ATOMIC|PR_ADDR, 83152242Sru .pr_input = in_gif_input, 84269699Skevlo .pr_output = (pr_output_t *)rip_output, 85152242Sru .pr_ctloutput = rip_ctloutput, 86152242Sru .pr_usrreqs = &rip_usrreqs 87105293Sume}; 88105293Sume 89207369SbzVNET_DEFINE(int, ip_gif_ttl) = GIF_TTL; 90207369Sbz#define V_ip_gif_ttl VNET(ip_gif_ttl) 91195699SrwatsonSYSCTL_VNET_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 92195699Srwatson &VNET_NAME(ip_gif_ttl), 0, ""); 9354263Sshin 9454263Sshinint 95169454Srwatsonin_gif_output(struct ifnet *ifp, int family, struct mbuf *m) 9654263Sshin{ 97147256Sbrooks struct gif_softc *sc = ifp->if_softc; 9854263Sshin struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; 9954263Sshin struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; 10054263Sshin struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; 10154263Sshin struct ip iphdr; /* capsule IP header, host byte ordered */ 102153621Sthompsa struct etherip_header eiphdr; 103189494Smarius int error, len, proto; 10454263Sshin u_int8_t tos; 10554263Sshin 106155037Sglebius GIF_LOCK_ASSERT(sc); 107155037Sglebius 10854263Sshin if (sin_src == NULL || sin_dst == NULL || 10954263Sshin sin_src->sin_family != AF_INET || 11054263Sshin sin_dst->sin_family != AF_INET) { 11154263Sshin m_freem(m); 11254263Sshin return EAFNOSUPPORT; 11354263Sshin } 11454263Sshin 11554263Sshin switch (family) { 11662587Sitojun#ifdef INET 11754263Sshin case AF_INET: 11854263Sshin { 11954263Sshin struct ip *ip; 12054263Sshin 12154263Sshin proto = IPPROTO_IPV4; 12254263Sshin if (m->m_len < sizeof(*ip)) { 12354263Sshin m = m_pullup(m, sizeof(*ip)); 12454263Sshin if (!m) 12554263Sshin return ENOBUFS; 12654263Sshin } 12754263Sshin ip = mtod(m, struct ip *); 12854263Sshin tos = ip->ip_tos; 12954263Sshin break; 13054263Sshin } 13195023Ssuz#endif /* INET */ 13254263Sshin#ifdef INET6 13354263Sshin case AF_INET6: 13454263Sshin { 13562587Sitojun struct ip6_hdr *ip6; 13654263Sshin proto = IPPROTO_IPV6; 13754263Sshin if (m->m_len < sizeof(*ip6)) { 13854263Sshin m = m_pullup(m, sizeof(*ip6)); 13954263Sshin if (!m) 14054263Sshin return ENOBUFS; 14154263Sshin } 14254263Sshin ip6 = mtod(m, struct ip6_hdr *); 14354263Sshin tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 14454263Sshin break; 14554263Sshin } 14695023Ssuz#endif /* INET6 */ 147153621Sthompsa case AF_LINK: 148153621Sthompsa proto = IPPROTO_ETHERIP; 149193664Shrs 150193664Shrs /* 151193664Shrs * GIF_SEND_REVETHIP (disabled by default) intentionally 152193664Shrs * sends an EtherIP packet with revered version field in 153193664Shrs * the header. This is a knob for backward compatibility 154193664Shrs * with FreeBSD 7.2R or prior. 155193664Shrs */ 156193664Shrs if ((sc->gif_options & GIF_SEND_REVETHIP)) { 157193664Shrs eiphdr.eip_ver = 0; 158193664Shrs eiphdr.eip_resvl = ETHERIP_VERSION; 159193664Shrs eiphdr.eip_resvh = 0; 160193664Shrs } else { 161193664Shrs eiphdr.eip_ver = ETHERIP_VERSION; 162193664Shrs eiphdr.eip_resvl = 0; 163193664Shrs eiphdr.eip_resvh = 0; 164193664Shrs } 165153621Sthompsa /* prepend Ethernet-in-IP header */ 166243882Sglebius M_PREPEND(m, sizeof(struct etherip_header), M_NOWAIT); 167153621Sthompsa if (m && m->m_len < sizeof(struct etherip_header)) 168153621Sthompsa m = m_pullup(m, sizeof(struct etherip_header)); 169153621Sthompsa if (m == NULL) 170153621Sthompsa return ENOBUFS; 171153621Sthompsa bcopy(&eiphdr, mtod(m, struct etherip_header *), 172153621Sthompsa sizeof(struct etherip_header)); 173269054Shrs tos = 0; 174153621Sthompsa break; 175153621Sthompsa 17654263Sshin default: 17762587Sitojun#ifdef DEBUG 17854263Sshin printf("in_gif_output: warning: unknown family %d passed\n", 17954263Sshin family); 18054263Sshin#endif 18154263Sshin m_freem(m); 18254263Sshin return EAFNOSUPPORT; 18354263Sshin } 18454263Sshin 18554263Sshin bzero(&iphdr, sizeof(iphdr)); 18654263Sshin iphdr.ip_src = sin_src->sin_addr; 18778064Sume /* bidirectional configured tunnel mode */ 18878064Sume if (sin_dst->sin_addr.s_addr != INADDR_ANY) 18978064Sume iphdr.ip_dst = sin_dst->sin_addr; 19078064Sume else { 19178064Sume m_freem(m); 19278064Sume return ENETUNREACH; 19354263Sshin } 19454263Sshin iphdr.ip_p = proto; 19554263Sshin /* version will be set in ip_output() */ 196181803Sbz iphdr.ip_ttl = V_ip_gif_ttl; 197241913Sglebius iphdr.ip_len = htons(m->m_pkthdr.len + sizeof(struct ip)); 198121684Sume ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 199121684Sume &iphdr.ip_tos, &tos); 20054263Sshin 20154263Sshin /* prepend new IP header */ 202189494Smarius len = sizeof(struct ip); 203189494Smarius#ifndef __NO_STRICT_ALIGNMENT 204189494Smarius if (family == AF_LINK) 205189494Smarius len += ETHERIP_ALIGN; 206189494Smarius#endif 207243882Sglebius M_PREPEND(m, len, M_NOWAIT); 208189494Smarius if (m != NULL && m->m_len < len) 209189494Smarius m = m_pullup(m, len); 21054263Sshin if (m == NULL) { 21154263Sshin printf("ENOBUFS in in_gif_output %d\n", __LINE__); 21254263Sshin return ENOBUFS; 21354263Sshin } 214189494Smarius#ifndef __NO_STRICT_ALIGNMENT 215189494Smarius if (family == AF_LINK) { 216189494Smarius len = mtod(m, vm_offset_t) & 3; 217189494Smarius KASSERT(len == 0 || len == ETHERIP_ALIGN, 218189494Smarius ("in_gif_output: unexpected misalignment")); 219189494Smarius m->m_data += len; 220189494Smarius m->m_len -= ETHERIP_ALIGN; 221189494Smarius } 222189494Smarius#endif 22362587Sitojun bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 22454263Sshin 225178888Sjulian M_SETFIB(m, sc->gif_fibnum); 226178888Sjulian 22754263Sshin if (dst->sin_family != sin_dst->sin_family || 22854263Sshin dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 22954263Sshin /* cache route doesn't match */ 230130662Sbms bzero(dst, sizeof(*dst)); 23154263Sshin dst->sin_family = sin_dst->sin_family; 23254263Sshin dst->sin_len = sizeof(struct sockaddr_in); 23354263Sshin dst->sin_addr = sin_dst->sin_addr; 23454263Sshin if (sc->gif_ro.ro_rt) { 23554263Sshin RTFREE(sc->gif_ro.ro_rt); 23654263Sshin sc->gif_ro.ro_rt = NULL; 23754263Sshin } 23862587Sitojun#if 0 239147256Sbrooks GIF2IFP(sc)->if_mtu = GIF_MTU; 24062587Sitojun#endif 24154263Sshin } 24254263Sshin 24354263Sshin if (sc->gif_ro.ro_rt == NULL) { 244178888Sjulian in_rtalloc_ign(&sc->gif_ro, 0, sc->gif_fibnum); 24554263Sshin if (sc->gif_ro.ro_rt == NULL) { 24654263Sshin m_freem(m); 24754263Sshin return ENETUNREACH; 24854263Sshin } 24962587Sitojun 25062587Sitojun /* if it constitutes infinite encapsulation, punt. */ 25162587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 25262587Sitojun m_freem(m); 25395023Ssuz return ENETUNREACH; /* XXX */ 25462587Sitojun } 25562587Sitojun#if 0 25662587Sitojun ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 25762587Sitojun - sizeof(struct ip); 25862587Sitojun#endif 25954263Sshin } 26054263Sshin 261269054Shrs m->m_flags &= ~(M_BCAST|M_MCAST); 262105194Ssam error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 263138470Sglebius 264147256Sbrooks if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 265138653Sglebius sc->gif_ro.ro_rt != NULL) { 266138470Sglebius RTFREE(sc->gif_ro.ro_rt); 267138470Sglebius sc->gif_ro.ro_rt = NULL; 268138470Sglebius } 269138470Sglebius 270120885Sume return (error); 27154263Sshin} 27254263Sshin 273269699Skevloint 274269699Skevloin_gif_input(struct mbuf **mp, int *offp, int proto) 27554263Sshin{ 276269699Skevlo struct mbuf *m; 27754263Sshin struct ifnet *gifp = NULL; 278147503Sbz struct gif_softc *sc; 27954263Sshin struct ip *ip; 28062587Sitojun int af; 281269699Skevlo int off; 28255009Sshin u_int8_t otos; 28354263Sshin 284269699Skevlo m = *mp; 28554263Sshin ip = mtod(m, struct ip *); 286269699Skevlo off = *offp; 287269699Skevlo *mp = NULL; 28854263Sshin 289147503Sbz sc = (struct gif_softc *)encap_getarg(m); 290147503Sbz if (sc == NULL) { 291147503Sbz m_freem(m); 292196039Srwatson KMOD_IPSTAT_INC(ips_nogif); 293269699Skevlo return (IPPROTO_DONE); 294147503Sbz } 29554263Sshin 296147503Sbz gifp = GIF2IFP(sc); 29762587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 29854263Sshin m_freem(m); 299196039Srwatson KMOD_IPSTAT_INC(ips_nogif); 300269699Skevlo return (IPPROTO_DONE); 30154263Sshin } 30254263Sshin 30355009Sshin otos = ip->ip_tos; 30454263Sshin m_adj(m, off); 30554263Sshin 30654263Sshin switch (proto) { 30762587Sitojun#ifdef INET 30854263Sshin case IPPROTO_IPV4: 30954263Sshin { 31054263Sshin struct ip *ip; 31154263Sshin af = AF_INET; 31254263Sshin if (m->m_len < sizeof(*ip)) { 31354263Sshin m = m_pullup(m, sizeof(*ip)); 31454263Sshin if (!m) 315269699Skevlo return (IPPROTO_DONE); 31654263Sshin } 31754263Sshin ip = mtod(m, struct ip *); 318121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 319121684Sume ECN_ALLOWED : ECN_NOCARE, 320121684Sume &otos, &ip->ip_tos) == 0) { 321121684Sume m_freem(m); 322269699Skevlo return (IPPROTO_DONE); 323121684Sume } 32454263Sshin break; 32554263Sshin } 32662587Sitojun#endif 32754263Sshin#ifdef INET6 32854263Sshin case IPPROTO_IPV6: 32954263Sshin { 33054263Sshin struct ip6_hdr *ip6; 331121684Sume u_int8_t itos, oitos; 332121684Sume 33354263Sshin af = AF_INET6; 33454263Sshin if (m->m_len < sizeof(*ip6)) { 33554263Sshin m = m_pullup(m, sizeof(*ip6)); 33654263Sshin if (!m) 337269699Skevlo return (IPPROTO_DONE); 33854263Sshin } 33954263Sshin ip6 = mtod(m, struct ip6_hdr *); 340121684Sume itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 341121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 342121684Sume ECN_ALLOWED : ECN_NOCARE, 343121684Sume &otos, &itos) == 0) { 344121684Sume m_freem(m); 345269699Skevlo return (IPPROTO_DONE); 346121684Sume } 347121684Sume if (itos != oitos) { 348121684Sume ip6->ip6_flow &= ~htonl(0xff << 20); 349121684Sume ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 350121684Sume } 35154263Sshin break; 35254263Sshin } 35354263Sshin#endif /* INET6 */ 354153621Sthompsa case IPPROTO_ETHERIP: 355153621Sthompsa af = AF_LINK; 356153621Sthompsa break; 357153621Sthompsa 35854263Sshin default: 359196039Srwatson KMOD_IPSTAT_INC(ips_nogif); 36054263Sshin m_freem(m); 361269699Skevlo return (IPPROTO_DONE); 36254263Sshin } 36354263Sshin gif_input(m, af, gifp); 364269699Skevlo return (IPPROTO_DONE); 36554263Sshin} 36662587Sitojun 36762587Sitojun/* 368105293Sume * validate outer address. 36962587Sitojun */ 370105293Sumestatic int 371169454Srwatsongif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) 37262587Sitojun{ 37362587Sitojun struct sockaddr_in *src, *dst; 37462587Sitojun struct in_ifaddr *ia4; 37562587Sitojun 37662587Sitojun src = (struct sockaddr_in *)sc->gif_psrc; 37762587Sitojun dst = (struct sockaddr_in *)sc->gif_pdst; 37862587Sitojun 37962587Sitojun /* check for address match */ 380105293Sume if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 381105293Sume dst->sin_addr.s_addr != ip->ip_src.s_addr) 38262587Sitojun return 0; 38362587Sitojun 38462587Sitojun /* martian filters on outer source - NOT done in ip_input! */ 385105293Sume if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 38662587Sitojun return 0; 387105293Sume switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 38862587Sitojun case 0: case 127: case 255: 38962587Sitojun return 0; 39062587Sitojun } 391194951Srwatson 39262587Sitojun /* reject packets with broadcast on source */ 393194951Srwatson /* XXXRW: should use hash lists? */ 394194951Srwatson IN_IFADDR_RLOCK(); 395181803Sbz TAILQ_FOREACH(ia4, &V_in_ifaddrhead, ia_link) { 39662587Sitojun if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 39762587Sitojun continue; 398194951Srwatson if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { 399194951Srwatson IN_IFADDR_RUNLOCK(); 40062587Sitojun return 0; 401194951Srwatson } 40262587Sitojun } 403194951Srwatson IN_IFADDR_RUNLOCK(); 40462587Sitojun 40562587Sitojun /* ingress filters on outer source */ 406147256Sbrooks if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 40762587Sitojun struct sockaddr_in sin; 40862587Sitojun struct rtentry *rt; 40962587Sitojun 41062587Sitojun bzero(&sin, sizeof(sin)); 41162587Sitojun sin.sin_family = AF_INET; 41262587Sitojun sin.sin_len = sizeof(struct sockaddr_in); 413105293Sume sin.sin_addr = ip->ip_src; 414178888Sjulian /* XXX MRT check for the interface we would use on output */ 415178888Sjulian rt = in_rtalloc1((struct sockaddr *)&sin, 0, 416178888Sjulian 0UL, sc->gif_fibnum); 417105293Sume if (!rt || rt->rt_ifp != ifp) { 41878064Sume#if 0 41978064Sume log(LOG_WARNING, "%s: packet from 0x%x dropped " 420147256Sbrooks "due to ingress filter\n", if_name(GIF2IFP(sc)), 42178064Sume (u_int32_t)ntohl(sin.sin_addr.s_addr)); 42278064Sume#endif 42378064Sume if (rt) 424172307Scsjp RTFREE_LOCKED(rt); 42562587Sitojun return 0; 42662587Sitojun } 427172307Scsjp RTFREE_LOCKED(rt); 42862587Sitojun } 42962587Sitojun 43078064Sume return 32 * 2; 43162587Sitojun} 432105293Sume 433105293Sume/* 434105293Sume * we know that we are in IFF_UP, outer address available, and outer family 435105293Sume * matched the physical addr family. see gif_encapcheck(). 436105293Sume */ 437105293Sumeint 438169454Srwatsongif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg) 439105293Sume{ 440105293Sume struct ip ip; 441105293Sume struct gif_softc *sc; 442105293Sume struct ifnet *ifp; 443105293Sume 444105293Sume /* sanity check done in caller */ 445105293Sume sc = (struct gif_softc *)arg; 446105293Sume 447105293Sume /* LINTED const cast */ 448105293Sume m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 449105293Sume ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 450105293Sume 451105293Sume return gif_validate4(&ip, sc, ifp); 452105293Sume} 453105293Sume 454105293Sumeint 455169454Srwatsonin_gif_attach(struct gif_softc *sc) 456105293Sume{ 457105293Sume sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 458105293Sume &in_gif_protosw, sc); 459105293Sume if (sc->encap_cookie4 == NULL) 460105293Sume return EEXIST; 461105293Sume return 0; 462105293Sume} 463105293Sume 464105293Sumeint 465169454Srwatsonin_gif_detach(struct gif_softc *sc) 466105293Sume{ 467105293Sume int error; 468105293Sume 469105293Sume error = encap_detach(sc->encap_cookie4); 470105293Sume if (error == 0) 471105293Sume sc->encap_cookie4 = NULL; 472105293Sume return error; 473105293Sume} 474