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$"); 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> 5154263Sshin#include <net/route.h> 52196019Srwatson#include <net/vnet.h> 5354263Sshin 5454263Sshin#include <netinet/in.h> 5554263Sshin#include <netinet/in_systm.h> 5654263Sshin#include <netinet/ip.h> 5754263Sshin#include <netinet/ip_var.h> 5854263Sshin#include <netinet/in_gif.h> 5962587Sitojun#include <netinet/in_var.h> 6062587Sitojun#include <netinet/ip_encap.h> 6155009Sshin#include <netinet/ip_ecn.h> 6262587Sitojun 6354263Sshin#ifdef INET6 6462587Sitojun#include <netinet/ip6.h> 6554263Sshin#endif 6654263Sshin 6754263Sshin#ifdef MROUTING 6854263Sshin#include <netinet/ip_mroute.h> 6954263Sshin#endif /* MROUTING */ 7054263Sshin 7162587Sitojun#include <net/if_gif.h> 7254263Sshin 73105293Sumestatic int gif_validate4(const struct ip *, struct gif_softc *, 74105293Sume struct ifnet *); 75105293Sume 76105293Sumeextern struct domain inetdomain; 77152242Srustruct protosw in_gif_protosw = { 78152242Sru .pr_type = SOCK_RAW, 79152242Sru .pr_domain = &inetdomain, 80152242Sru .pr_protocol = 0/* IPPROTO_IPV[46] */, 81152242Sru .pr_flags = PR_ATOMIC|PR_ADDR, 82152242Sru .pr_input = in_gif_input, 83152242Sru .pr_output = (pr_output_t*)rip_output, 84152242Sru .pr_ctloutput = rip_ctloutput, 85152242Sru .pr_usrreqs = &rip_usrreqs 86105293Sume}; 87105293Sume 88207369SbzVNET_DEFINE(int, ip_gif_ttl) = GIF_TTL; 89207369Sbz#define V_ip_gif_ttl VNET(ip_gif_ttl) 90195699SrwatsonSYSCTL_VNET_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 91195699Srwatson &VNET_NAME(ip_gif_ttl), 0, ""); 9254263Sshin 9354263Sshinint 94169454Srwatsonin_gif_output(struct ifnet *ifp, int family, struct mbuf *m) 9554263Sshin{ 96147256Sbrooks struct gif_softc *sc = ifp->if_softc; 9754263Sshin struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; 9854263Sshin struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; 9954263Sshin struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; 10054263Sshin struct ip iphdr; /* capsule IP header, host byte ordered */ 101153621Sthompsa struct etherip_header eiphdr; 102189494Smarius int error, len, proto; 10354263Sshin u_int8_t tos; 10454263Sshin 105155037Sglebius GIF_LOCK_ASSERT(sc); 106155037Sglebius 10754263Sshin if (sin_src == NULL || sin_dst == NULL || 10854263Sshin sin_src->sin_family != AF_INET || 10954263Sshin sin_dst->sin_family != AF_INET) { 11054263Sshin m_freem(m); 11154263Sshin return EAFNOSUPPORT; 11254263Sshin } 11354263Sshin 11454263Sshin switch (family) { 11562587Sitojun#ifdef INET 11654263Sshin case AF_INET: 11754263Sshin { 11854263Sshin struct ip *ip; 11954263Sshin 12054263Sshin proto = IPPROTO_IPV4; 12154263Sshin if (m->m_len < sizeof(*ip)) { 12254263Sshin m = m_pullup(m, sizeof(*ip)); 12354263Sshin if (!m) 12454263Sshin return ENOBUFS; 12554263Sshin } 12654263Sshin ip = mtod(m, struct ip *); 12754263Sshin tos = ip->ip_tos; 12854263Sshin break; 12954263Sshin } 13095023Ssuz#endif /* INET */ 13154263Sshin#ifdef INET6 13254263Sshin case AF_INET6: 13354263Sshin { 13462587Sitojun struct ip6_hdr *ip6; 13554263Sshin proto = IPPROTO_IPV6; 13654263Sshin if (m->m_len < sizeof(*ip6)) { 13754263Sshin m = m_pullup(m, sizeof(*ip6)); 13854263Sshin if (!m) 13954263Sshin return ENOBUFS; 14054263Sshin } 14154263Sshin ip6 = mtod(m, struct ip6_hdr *); 14254263Sshin tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 14354263Sshin break; 14454263Sshin } 14595023Ssuz#endif /* INET6 */ 146153621Sthompsa case AF_LINK: 147153621Sthompsa proto = IPPROTO_ETHERIP; 148193664Shrs 149193664Shrs /* 150193664Shrs * GIF_SEND_REVETHIP (disabled by default) intentionally 151193664Shrs * sends an EtherIP packet with revered version field in 152193664Shrs * the header. This is a knob for backward compatibility 153193664Shrs * with FreeBSD 7.2R or prior. 154193664Shrs */ 155193664Shrs if ((sc->gif_options & GIF_SEND_REVETHIP)) { 156193664Shrs eiphdr.eip_ver = 0; 157193664Shrs eiphdr.eip_resvl = ETHERIP_VERSION; 158193664Shrs eiphdr.eip_resvh = 0; 159193664Shrs } else { 160193664Shrs eiphdr.eip_ver = ETHERIP_VERSION; 161193664Shrs eiphdr.eip_resvl = 0; 162193664Shrs eiphdr.eip_resvh = 0; 163193664Shrs } 164153621Sthompsa /* prepend Ethernet-in-IP header */ 165243882Sglebius M_PREPEND(m, sizeof(struct etherip_header), M_NOWAIT); 166153621Sthompsa if (m && m->m_len < sizeof(struct etherip_header)) 167153621Sthompsa m = m_pullup(m, sizeof(struct etherip_header)); 168153621Sthompsa if (m == NULL) 169153621Sthompsa return ENOBUFS; 170153621Sthompsa bcopy(&eiphdr, mtod(m, struct etherip_header *), 171153621Sthompsa sizeof(struct etherip_header)); 172273188Shrs tos = 0; 173153621Sthompsa break; 174153621Sthompsa 17554263Sshin default: 17662587Sitojun#ifdef DEBUG 17754263Sshin printf("in_gif_output: warning: unknown family %d passed\n", 17854263Sshin family); 17954263Sshin#endif 18054263Sshin m_freem(m); 18154263Sshin return EAFNOSUPPORT; 18254263Sshin } 18354263Sshin 18454263Sshin bzero(&iphdr, sizeof(iphdr)); 18554263Sshin iphdr.ip_src = sin_src->sin_addr; 18678064Sume /* bidirectional configured tunnel mode */ 18778064Sume if (sin_dst->sin_addr.s_addr != INADDR_ANY) 18878064Sume iphdr.ip_dst = sin_dst->sin_addr; 18978064Sume else { 19078064Sume m_freem(m); 19178064Sume return ENETUNREACH; 19254263Sshin } 19354263Sshin iphdr.ip_p = proto; 19454263Sshin /* version will be set in ip_output() */ 195181803Sbz iphdr.ip_ttl = V_ip_gif_ttl; 196241913Sglebius iphdr.ip_len = htons(m->m_pkthdr.len + sizeof(struct ip)); 197121684Sume ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 198121684Sume &iphdr.ip_tos, &tos); 19954263Sshin 20054263Sshin /* prepend new IP header */ 201189494Smarius len = sizeof(struct ip); 202189494Smarius#ifndef __NO_STRICT_ALIGNMENT 203189494Smarius if (family == AF_LINK) 204189494Smarius len += ETHERIP_ALIGN; 205189494Smarius#endif 206243882Sglebius M_PREPEND(m, len, M_NOWAIT); 207189494Smarius if (m != NULL && m->m_len < len) 208189494Smarius m = m_pullup(m, len); 20954263Sshin if (m == NULL) { 21054263Sshin printf("ENOBUFS in in_gif_output %d\n", __LINE__); 21154263Sshin return ENOBUFS; 21254263Sshin } 213189494Smarius#ifndef __NO_STRICT_ALIGNMENT 214189494Smarius if (family == AF_LINK) { 215189494Smarius len = mtod(m, vm_offset_t) & 3; 216189494Smarius KASSERT(len == 0 || len == ETHERIP_ALIGN, 217189494Smarius ("in_gif_output: unexpected misalignment")); 218189494Smarius m->m_data += len; 219189494Smarius m->m_len -= ETHERIP_ALIGN; 220189494Smarius } 221189494Smarius#endif 22262587Sitojun bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 22354263Sshin 224178888Sjulian M_SETFIB(m, sc->gif_fibnum); 225178888Sjulian 22654263Sshin if (dst->sin_family != sin_dst->sin_family || 22754263Sshin dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 22854263Sshin /* cache route doesn't match */ 229130662Sbms bzero(dst, sizeof(*dst)); 23054263Sshin dst->sin_family = sin_dst->sin_family; 23154263Sshin dst->sin_len = sizeof(struct sockaddr_in); 23254263Sshin dst->sin_addr = sin_dst->sin_addr; 23354263Sshin if (sc->gif_ro.ro_rt) { 23454263Sshin RTFREE(sc->gif_ro.ro_rt); 23554263Sshin sc->gif_ro.ro_rt = NULL; 23654263Sshin } 23762587Sitojun#if 0 238147256Sbrooks GIF2IFP(sc)->if_mtu = GIF_MTU; 23962587Sitojun#endif 24054263Sshin } 24154263Sshin 24254263Sshin if (sc->gif_ro.ro_rt == NULL) { 243178888Sjulian in_rtalloc_ign(&sc->gif_ro, 0, sc->gif_fibnum); 24454263Sshin if (sc->gif_ro.ro_rt == NULL) { 24554263Sshin m_freem(m); 24654263Sshin return ENETUNREACH; 24754263Sshin } 24862587Sitojun 24962587Sitojun /* if it constitutes infinite encapsulation, punt. */ 25062587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 25162587Sitojun m_freem(m); 25295023Ssuz return ENETUNREACH; /* XXX */ 25362587Sitojun } 25462587Sitojun#if 0 25562587Sitojun ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 25662587Sitojun - sizeof(struct ip); 25762587Sitojun#endif 25854263Sshin } 25954263Sshin 260273188Shrs m->m_flags &= ~(M_BCAST|M_MCAST); 261105194Ssam error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 262138470Sglebius 263147256Sbrooks if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 264138653Sglebius sc->gif_ro.ro_rt != NULL) { 265138470Sglebius RTFREE(sc->gif_ro.ro_rt); 266138470Sglebius sc->gif_ro.ro_rt = NULL; 267138470Sglebius } 268138470Sglebius 269120885Sume return (error); 27054263Sshin} 27154263Sshin 27254263Sshinvoid 273169454Srwatsonin_gif_input(struct mbuf *m, int off) 27454263Sshin{ 27554263Sshin struct ifnet *gifp = NULL; 276147503Sbz struct gif_softc *sc; 27754263Sshin struct ip *ip; 27862587Sitojun int af; 27955009Sshin u_int8_t otos; 28082884Sjulian int proto; 28154263Sshin 28254263Sshin ip = mtod(m, struct ip *); 28382884Sjulian proto = ip->ip_p; 28454263Sshin 285147503Sbz sc = (struct gif_softc *)encap_getarg(m); 286147503Sbz if (sc == NULL) { 287147503Sbz m_freem(m); 288196039Srwatson KMOD_IPSTAT_INC(ips_nogif); 289147503Sbz return; 290147503Sbz } 29154263Sshin 292147503Sbz gifp = GIF2IFP(sc); 29362587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 29454263Sshin m_freem(m); 295196039Srwatson KMOD_IPSTAT_INC(ips_nogif); 29654263Sshin return; 29754263Sshin } 29854263Sshin 29955009Sshin otos = ip->ip_tos; 30054263Sshin m_adj(m, off); 30154263Sshin 30254263Sshin switch (proto) { 30362587Sitojun#ifdef INET 30454263Sshin case IPPROTO_IPV4: 30554263Sshin { 30654263Sshin struct ip *ip; 30754263Sshin af = AF_INET; 30854263Sshin if (m->m_len < sizeof(*ip)) { 30954263Sshin m = m_pullup(m, sizeof(*ip)); 31054263Sshin if (!m) 31154263Sshin return; 31254263Sshin } 31354263Sshin ip = mtod(m, struct ip *); 314121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 315121684Sume ECN_ALLOWED : ECN_NOCARE, 316121684Sume &otos, &ip->ip_tos) == 0) { 317121684Sume m_freem(m); 318121684Sume return; 319121684Sume } 32054263Sshin break; 32154263Sshin } 32262587Sitojun#endif 32354263Sshin#ifdef INET6 32454263Sshin case IPPROTO_IPV6: 32554263Sshin { 32654263Sshin struct ip6_hdr *ip6; 327121684Sume u_int8_t itos, oitos; 328121684Sume 32954263Sshin af = AF_INET6; 33054263Sshin if (m->m_len < sizeof(*ip6)) { 33154263Sshin m = m_pullup(m, sizeof(*ip6)); 33254263Sshin if (!m) 33354263Sshin return; 33454263Sshin } 33554263Sshin ip6 = mtod(m, struct ip6_hdr *); 336121684Sume itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 337121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 338121684Sume ECN_ALLOWED : ECN_NOCARE, 339121684Sume &otos, &itos) == 0) { 340121684Sume m_freem(m); 341121684Sume return; 342121684Sume } 343121684Sume if (itos != oitos) { 344121684Sume ip6->ip6_flow &= ~htonl(0xff << 20); 345121684Sume ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 346121684Sume } 34754263Sshin break; 34854263Sshin } 34954263Sshin#endif /* INET6 */ 350153621Sthompsa case IPPROTO_ETHERIP: 351153621Sthompsa af = AF_LINK; 352153621Sthompsa break; 353153621Sthompsa 35454263Sshin default: 355196039Srwatson KMOD_IPSTAT_INC(ips_nogif); 35654263Sshin m_freem(m); 35754263Sshin return; 35854263Sshin } 35954263Sshin gif_input(m, af, gifp); 36054263Sshin return; 36154263Sshin} 36262587Sitojun 36362587Sitojun/* 364105293Sume * validate outer address. 36562587Sitojun */ 366105293Sumestatic int 367169454Srwatsongif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) 36862587Sitojun{ 36962587Sitojun struct sockaddr_in *src, *dst; 37062587Sitojun struct in_ifaddr *ia4; 37162587Sitojun 37262587Sitojun src = (struct sockaddr_in *)sc->gif_psrc; 37362587Sitojun dst = (struct sockaddr_in *)sc->gif_pdst; 37462587Sitojun 37562587Sitojun /* check for address match */ 376105293Sume if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 377105293Sume dst->sin_addr.s_addr != ip->ip_src.s_addr) 37862587Sitojun return 0; 37962587Sitojun 38062587Sitojun /* martian filters on outer source - NOT done in ip_input! */ 381105293Sume if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 38262587Sitojun return 0; 383105293Sume switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 38462587Sitojun case 0: case 127: case 255: 38562587Sitojun return 0; 38662587Sitojun } 387194951Srwatson 38862587Sitojun /* reject packets with broadcast on source */ 389194951Srwatson /* XXXRW: should use hash lists? */ 390194951Srwatson IN_IFADDR_RLOCK(); 391181803Sbz TAILQ_FOREACH(ia4, &V_in_ifaddrhead, ia_link) { 39262587Sitojun if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 39362587Sitojun continue; 394194951Srwatson if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { 395194951Srwatson IN_IFADDR_RUNLOCK(); 39662587Sitojun return 0; 397194951Srwatson } 39862587Sitojun } 399194951Srwatson IN_IFADDR_RUNLOCK(); 40062587Sitojun 40162587Sitojun /* ingress filters on outer source */ 402147256Sbrooks if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 40362587Sitojun struct sockaddr_in sin; 40462587Sitojun struct rtentry *rt; 40562587Sitojun 40662587Sitojun bzero(&sin, sizeof(sin)); 40762587Sitojun sin.sin_family = AF_INET; 40862587Sitojun sin.sin_len = sizeof(struct sockaddr_in); 409105293Sume sin.sin_addr = ip->ip_src; 410178888Sjulian /* XXX MRT check for the interface we would use on output */ 411178888Sjulian rt = in_rtalloc1((struct sockaddr *)&sin, 0, 412178888Sjulian 0UL, sc->gif_fibnum); 413105293Sume if (!rt || rt->rt_ifp != ifp) { 41478064Sume#if 0 41578064Sume log(LOG_WARNING, "%s: packet from 0x%x dropped " 416147256Sbrooks "due to ingress filter\n", if_name(GIF2IFP(sc)), 41778064Sume (u_int32_t)ntohl(sin.sin_addr.s_addr)); 41878064Sume#endif 41978064Sume if (rt) 420172307Scsjp RTFREE_LOCKED(rt); 42162587Sitojun return 0; 42262587Sitojun } 423172307Scsjp RTFREE_LOCKED(rt); 42462587Sitojun } 42562587Sitojun 42678064Sume return 32 * 2; 42762587Sitojun} 428105293Sume 429105293Sume/* 430105293Sume * we know that we are in IFF_UP, outer address available, and outer family 431105293Sume * matched the physical addr family. see gif_encapcheck(). 432105293Sume */ 433105293Sumeint 434169454Srwatsongif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg) 435105293Sume{ 436105293Sume struct ip ip; 437105293Sume struct gif_softc *sc; 438105293Sume struct ifnet *ifp; 439105293Sume 440105293Sume /* sanity check done in caller */ 441105293Sume sc = (struct gif_softc *)arg; 442105293Sume 443105293Sume /* LINTED const cast */ 444105293Sume m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 445105293Sume ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 446105293Sume 447105293Sume return gif_validate4(&ip, sc, ifp); 448105293Sume} 449105293Sume 450105293Sumeint 451169454Srwatsonin_gif_attach(struct gif_softc *sc) 452105293Sume{ 453105293Sume sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 454105293Sume &in_gif_protosw, sc); 455105293Sume if (sc->encap_cookie4 == NULL) 456105293Sume return EEXIST; 457105293Sume return 0; 458105293Sume} 459105293Sume 460105293Sumeint 461169454Srwatsonin_gif_detach(struct gif_softc *sc) 462105293Sume{ 463105293Sume int error; 464105293Sume 465105293Sume error = encap_detach(sc->encap_cookie4); 466105293Sume if (error == 0) 467105293Sume sc->encap_cookie4 = NULL; 468105293Sume return error; 469105293Sume} 470