in_gif.c revision 178888
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 178888 2008-05-09 23:03:00Z julian $"); 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> 4854263Sshin 4962587Sitojun#include <sys/malloc.h> 5062587Sitojun 5154263Sshin#include <net/if.h> 5254263Sshin#include <net/route.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 8891324Sbrooksstatic int ip_gif_ttl = GIF_TTL; 8954263SshinSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 9054263Sshin &ip_gif_ttl, 0, ""); 9154263Sshin 9254263Sshinint 93169454Srwatsonin_gif_output(struct ifnet *ifp, int family, struct mbuf *m) 9454263Sshin{ 95147256Sbrooks struct gif_softc *sc = ifp->if_softc; 9654263Sshin struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; 9754263Sshin struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; 9854263Sshin struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; 9954263Sshin struct ip iphdr; /* capsule IP header, host byte ordered */ 100153621Sthompsa struct etherip_header eiphdr; 10154263Sshin int proto, error; 10254263Sshin u_int8_t tos; 10354263Sshin 104155037Sglebius GIF_LOCK_ASSERT(sc); 105155037Sglebius 10654263Sshin if (sin_src == NULL || sin_dst == NULL || 10754263Sshin sin_src->sin_family != AF_INET || 10854263Sshin sin_dst->sin_family != AF_INET) { 10954263Sshin m_freem(m); 11054263Sshin return EAFNOSUPPORT; 11154263Sshin } 11254263Sshin 11354263Sshin switch (family) { 11462587Sitojun#ifdef INET 11554263Sshin case AF_INET: 11654263Sshin { 11754263Sshin struct ip *ip; 11854263Sshin 11954263Sshin proto = IPPROTO_IPV4; 12054263Sshin if (m->m_len < sizeof(*ip)) { 12154263Sshin m = m_pullup(m, sizeof(*ip)); 12254263Sshin if (!m) 12354263Sshin return ENOBUFS; 12454263Sshin } 12554263Sshin ip = mtod(m, struct ip *); 12654263Sshin tos = ip->ip_tos; 12754263Sshin break; 12854263Sshin } 12995023Ssuz#endif /* INET */ 13054263Sshin#ifdef INET6 13154263Sshin case AF_INET6: 13254263Sshin { 13362587Sitojun struct ip6_hdr *ip6; 13454263Sshin proto = IPPROTO_IPV6; 13554263Sshin if (m->m_len < sizeof(*ip6)) { 13654263Sshin m = m_pullup(m, sizeof(*ip6)); 13754263Sshin if (!m) 13854263Sshin return ENOBUFS; 13954263Sshin } 14054263Sshin ip6 = mtod(m, struct ip6_hdr *); 14154263Sshin tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 14254263Sshin break; 14354263Sshin } 14495023Ssuz#endif /* INET6 */ 145153621Sthompsa case AF_LINK: 146153621Sthompsa proto = IPPROTO_ETHERIP; 147153621Sthompsa eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK; 148153621Sthompsa eiphdr.eip_pad = 0; 149153621Sthompsa /* prepend Ethernet-in-IP header */ 150153621Sthompsa M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); 151153621Sthompsa if (m && m->m_len < sizeof(struct etherip_header)) 152153621Sthompsa m = m_pullup(m, sizeof(struct etherip_header)); 153153621Sthompsa if (m == NULL) 154153621Sthompsa return ENOBUFS; 155153621Sthompsa bcopy(&eiphdr, mtod(m, struct etherip_header *), 156153621Sthompsa sizeof(struct etherip_header)); 157153621Sthompsa break; 158153621Sthompsa 15954263Sshin default: 16062587Sitojun#ifdef DEBUG 16154263Sshin printf("in_gif_output: warning: unknown family %d passed\n", 16254263Sshin family); 16354263Sshin#endif 16454263Sshin m_freem(m); 16554263Sshin return EAFNOSUPPORT; 16654263Sshin } 16754263Sshin 16854263Sshin bzero(&iphdr, sizeof(iphdr)); 16954263Sshin iphdr.ip_src = sin_src->sin_addr; 17078064Sume /* bidirectional configured tunnel mode */ 17178064Sume if (sin_dst->sin_addr.s_addr != INADDR_ANY) 17278064Sume iphdr.ip_dst = sin_dst->sin_addr; 17378064Sume else { 17478064Sume m_freem(m); 17578064Sume return ENETUNREACH; 17654263Sshin } 17754263Sshin iphdr.ip_p = proto; 17854263Sshin /* version will be set in ip_output() */ 17954263Sshin iphdr.ip_ttl = ip_gif_ttl; 18054263Sshin iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); 181121684Sume ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 182121684Sume &iphdr.ip_tos, &tos); 18354263Sshin 18454263Sshin /* prepend new IP header */ 185111119Simp M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 18654263Sshin if (m && m->m_len < sizeof(struct ip)) 18754263Sshin m = m_pullup(m, sizeof(struct ip)); 18854263Sshin if (m == NULL) { 18954263Sshin printf("ENOBUFS in in_gif_output %d\n", __LINE__); 19054263Sshin return ENOBUFS; 19154263Sshin } 19262587Sitojun bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 19354263Sshin 194178888Sjulian M_SETFIB(m, sc->gif_fibnum); 195178888Sjulian 19654263Sshin if (dst->sin_family != sin_dst->sin_family || 19754263Sshin dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 19854263Sshin /* cache route doesn't match */ 199130662Sbms bzero(dst, sizeof(*dst)); 20054263Sshin dst->sin_family = sin_dst->sin_family; 20154263Sshin dst->sin_len = sizeof(struct sockaddr_in); 20254263Sshin dst->sin_addr = sin_dst->sin_addr; 20354263Sshin if (sc->gif_ro.ro_rt) { 20454263Sshin RTFREE(sc->gif_ro.ro_rt); 20554263Sshin sc->gif_ro.ro_rt = NULL; 20654263Sshin } 20762587Sitojun#if 0 208147256Sbrooks GIF2IFP(sc)->if_mtu = GIF_MTU; 20962587Sitojun#endif 21054263Sshin } 21154263Sshin 21254263Sshin if (sc->gif_ro.ro_rt == NULL) { 213178888Sjulian in_rtalloc_ign(&sc->gif_ro, 0, sc->gif_fibnum); 21454263Sshin if (sc->gif_ro.ro_rt == NULL) { 21554263Sshin m_freem(m); 21654263Sshin return ENETUNREACH; 21754263Sshin } 21862587Sitojun 21962587Sitojun /* if it constitutes infinite encapsulation, punt. */ 22062587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 22162587Sitojun m_freem(m); 22295023Ssuz return ENETUNREACH; /* XXX */ 22362587Sitojun } 22462587Sitojun#if 0 22562587Sitojun ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 22662587Sitojun - sizeof(struct ip); 22762587Sitojun#endif 22854263Sshin } 22954263Sshin 230105194Ssam error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 231138470Sglebius 232147256Sbrooks if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 233138653Sglebius sc->gif_ro.ro_rt != NULL) { 234138470Sglebius RTFREE(sc->gif_ro.ro_rt); 235138470Sglebius sc->gif_ro.ro_rt = NULL; 236138470Sglebius } 237138470Sglebius 238120885Sume return (error); 23954263Sshin} 24054263Sshin 24154263Sshinvoid 242169454Srwatsonin_gif_input(struct mbuf *m, int off) 24354263Sshin{ 24454263Sshin struct ifnet *gifp = NULL; 245147503Sbz struct gif_softc *sc; 24654263Sshin struct ip *ip; 24762587Sitojun int af; 24855009Sshin u_int8_t otos; 24982884Sjulian int proto; 25054263Sshin 25154263Sshin ip = mtod(m, struct ip *); 25282884Sjulian proto = ip->ip_p; 25354263Sshin 254147503Sbz sc = (struct gif_softc *)encap_getarg(m); 255147503Sbz if (sc == NULL) { 256147503Sbz m_freem(m); 257147503Sbz ipstat.ips_nogif++; 258147503Sbz return; 259147503Sbz } 26054263Sshin 261147503Sbz gifp = GIF2IFP(sc); 26262587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 26354263Sshin m_freem(m); 26454263Sshin ipstat.ips_nogif++; 26554263Sshin return; 26654263Sshin } 26754263Sshin 26855009Sshin otos = ip->ip_tos; 26954263Sshin m_adj(m, off); 27054263Sshin 27154263Sshin switch (proto) { 27262587Sitojun#ifdef INET 27354263Sshin case IPPROTO_IPV4: 27454263Sshin { 27554263Sshin struct ip *ip; 27654263Sshin af = AF_INET; 27754263Sshin if (m->m_len < sizeof(*ip)) { 27854263Sshin m = m_pullup(m, sizeof(*ip)); 27954263Sshin if (!m) 28054263Sshin return; 28154263Sshin } 28254263Sshin ip = mtod(m, struct ip *); 283121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 284121684Sume ECN_ALLOWED : ECN_NOCARE, 285121684Sume &otos, &ip->ip_tos) == 0) { 286121684Sume m_freem(m); 287121684Sume return; 288121684Sume } 28954263Sshin break; 29054263Sshin } 29162587Sitojun#endif 29254263Sshin#ifdef INET6 29354263Sshin case IPPROTO_IPV6: 29454263Sshin { 29554263Sshin struct ip6_hdr *ip6; 296121684Sume u_int8_t itos, oitos; 297121684Sume 29854263Sshin af = AF_INET6; 29954263Sshin if (m->m_len < sizeof(*ip6)) { 30054263Sshin m = m_pullup(m, sizeof(*ip6)); 30154263Sshin if (!m) 30254263Sshin return; 30354263Sshin } 30454263Sshin ip6 = mtod(m, struct ip6_hdr *); 305121684Sume itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 306121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 307121684Sume ECN_ALLOWED : ECN_NOCARE, 308121684Sume &otos, &itos) == 0) { 309121684Sume m_freem(m); 310121684Sume return; 311121684Sume } 312121684Sume if (itos != oitos) { 313121684Sume ip6->ip6_flow &= ~htonl(0xff << 20); 314121684Sume ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 315121684Sume } 31654263Sshin break; 31754263Sshin } 31854263Sshin#endif /* INET6 */ 319153621Sthompsa case IPPROTO_ETHERIP: 320153621Sthompsa af = AF_LINK; 321153621Sthompsa break; 322153621Sthompsa 32354263Sshin default: 32454263Sshin ipstat.ips_nogif++; 32554263Sshin m_freem(m); 32654263Sshin return; 32754263Sshin } 32854263Sshin gif_input(m, af, gifp); 32954263Sshin return; 33054263Sshin} 33162587Sitojun 33262587Sitojun/* 333105293Sume * validate outer address. 33462587Sitojun */ 335105293Sumestatic int 336169454Srwatsongif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) 33762587Sitojun{ 33862587Sitojun struct sockaddr_in *src, *dst; 33962587Sitojun struct in_ifaddr *ia4; 34062587Sitojun 34162587Sitojun src = (struct sockaddr_in *)sc->gif_psrc; 34262587Sitojun dst = (struct sockaddr_in *)sc->gif_pdst; 34362587Sitojun 34462587Sitojun /* check for address match */ 345105293Sume if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 346105293Sume dst->sin_addr.s_addr != ip->ip_src.s_addr) 34762587Sitojun return 0; 34862587Sitojun 34962587Sitojun /* martian filters on outer source - NOT done in ip_input! */ 350105293Sume if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 35162587Sitojun return 0; 352105293Sume switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 35362587Sitojun case 0: case 127: case 255: 35462587Sitojun return 0; 35562587Sitojun } 35662587Sitojun /* reject packets with broadcast on source */ 357120891Sume TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link) { 35862587Sitojun if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 35962587Sitojun continue; 360105293Sume if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) 36162587Sitojun return 0; 36262587Sitojun } 36362587Sitojun 36462587Sitojun /* ingress filters on outer source */ 365147256Sbrooks if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 36662587Sitojun struct sockaddr_in sin; 36762587Sitojun struct rtentry *rt; 36862587Sitojun 36962587Sitojun bzero(&sin, sizeof(sin)); 37062587Sitojun sin.sin_family = AF_INET; 37162587Sitojun sin.sin_len = sizeof(struct sockaddr_in); 372105293Sume sin.sin_addr = ip->ip_src; 373178888Sjulian /* XXX MRT check for the interface we would use on output */ 374178888Sjulian rt = in_rtalloc1((struct sockaddr *)&sin, 0, 375178888Sjulian 0UL, sc->gif_fibnum); 376105293Sume if (!rt || rt->rt_ifp != ifp) { 37778064Sume#if 0 37878064Sume log(LOG_WARNING, "%s: packet from 0x%x dropped " 379147256Sbrooks "due to ingress filter\n", if_name(GIF2IFP(sc)), 38078064Sume (u_int32_t)ntohl(sin.sin_addr.s_addr)); 38178064Sume#endif 38278064Sume if (rt) 383172307Scsjp RTFREE_LOCKED(rt); 38462587Sitojun return 0; 38562587Sitojun } 386172307Scsjp RTFREE_LOCKED(rt); 38762587Sitojun } 38862587Sitojun 38978064Sume return 32 * 2; 39062587Sitojun} 391105293Sume 392105293Sume/* 393105293Sume * we know that we are in IFF_UP, outer address available, and outer family 394105293Sume * matched the physical addr family. see gif_encapcheck(). 395105293Sume */ 396105293Sumeint 397169454Srwatsongif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg) 398105293Sume{ 399105293Sume struct ip ip; 400105293Sume struct gif_softc *sc; 401105293Sume struct ifnet *ifp; 402105293Sume 403105293Sume /* sanity check done in caller */ 404105293Sume sc = (struct gif_softc *)arg; 405105293Sume 406105293Sume /* LINTED const cast */ 407105293Sume m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 408105293Sume ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 409105293Sume 410105293Sume return gif_validate4(&ip, sc, ifp); 411105293Sume} 412105293Sume 413105293Sumeint 414169454Srwatsonin_gif_attach(struct gif_softc *sc) 415105293Sume{ 416105293Sume sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 417105293Sume &in_gif_protosw, sc); 418105293Sume if (sc->encap_cookie4 == NULL) 419105293Sume return EEXIST; 420105293Sume return 0; 421105293Sume} 422105293Sume 423105293Sumeint 424169454Srwatsonin_gif_detach(struct gif_softc *sc) 425105293Sume{ 426105293Sume int error; 427105293Sume 428105293Sume error = encap_detach(sc->encap_cookie4); 429105293Sume if (error == 0) 430105293Sume sc->encap_cookie4 = NULL; 431105293Sume return error; 432105293Sume} 433