in_gif.c revision 181803
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 181803 2008-08-17 23:27:27Z bz $"); 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> 50181803Sbz#include <sys/vimage.h> 5162587Sitojun 5254263Sshin#include <net/if.h> 5354263Sshin#include <net/route.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, 84152242Sru .pr_output = (pr_output_t*)rip_output, 85152242Sru .pr_ctloutput = rip_ctloutput, 86152242Sru .pr_usrreqs = &rip_usrreqs 87105293Sume}; 88105293Sume 8991324Sbrooksstatic int ip_gif_ttl = GIF_TTL; 9054263SshinSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 9154263Sshin &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; 10254263Sshin int proto, error; 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; 148153621Sthompsa eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK; 149153621Sthompsa eiphdr.eip_pad = 0; 150153621Sthompsa /* prepend Ethernet-in-IP header */ 151153621Sthompsa M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); 152153621Sthompsa if (m && m->m_len < sizeof(struct etherip_header)) 153153621Sthompsa m = m_pullup(m, sizeof(struct etherip_header)); 154153621Sthompsa if (m == NULL) 155153621Sthompsa return ENOBUFS; 156153621Sthompsa bcopy(&eiphdr, mtod(m, struct etherip_header *), 157153621Sthompsa sizeof(struct etherip_header)); 158153621Sthompsa break; 159153621Sthompsa 16054263Sshin default: 16162587Sitojun#ifdef DEBUG 16254263Sshin printf("in_gif_output: warning: unknown family %d passed\n", 16354263Sshin family); 16454263Sshin#endif 16554263Sshin m_freem(m); 16654263Sshin return EAFNOSUPPORT; 16754263Sshin } 16854263Sshin 16954263Sshin bzero(&iphdr, sizeof(iphdr)); 17054263Sshin iphdr.ip_src = sin_src->sin_addr; 17178064Sume /* bidirectional configured tunnel mode */ 17278064Sume if (sin_dst->sin_addr.s_addr != INADDR_ANY) 17378064Sume iphdr.ip_dst = sin_dst->sin_addr; 17478064Sume else { 17578064Sume m_freem(m); 17678064Sume return ENETUNREACH; 17754263Sshin } 17854263Sshin iphdr.ip_p = proto; 17954263Sshin /* version will be set in ip_output() */ 180181803Sbz iphdr.ip_ttl = V_ip_gif_ttl; 18154263Sshin iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); 182121684Sume ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 183121684Sume &iphdr.ip_tos, &tos); 18454263Sshin 18554263Sshin /* prepend new IP header */ 186111119Simp M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 18754263Sshin if (m && m->m_len < sizeof(struct ip)) 18854263Sshin m = m_pullup(m, sizeof(struct ip)); 18954263Sshin if (m == NULL) { 19054263Sshin printf("ENOBUFS in in_gif_output %d\n", __LINE__); 19154263Sshin return ENOBUFS; 19254263Sshin } 19362587Sitojun bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 19454263Sshin 195178888Sjulian M_SETFIB(m, sc->gif_fibnum); 196178888Sjulian 19754263Sshin if (dst->sin_family != sin_dst->sin_family || 19854263Sshin dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 19954263Sshin /* cache route doesn't match */ 200130662Sbms bzero(dst, sizeof(*dst)); 20154263Sshin dst->sin_family = sin_dst->sin_family; 20254263Sshin dst->sin_len = sizeof(struct sockaddr_in); 20354263Sshin dst->sin_addr = sin_dst->sin_addr; 20454263Sshin if (sc->gif_ro.ro_rt) { 20554263Sshin RTFREE(sc->gif_ro.ro_rt); 20654263Sshin sc->gif_ro.ro_rt = NULL; 20754263Sshin } 20862587Sitojun#if 0 209147256Sbrooks GIF2IFP(sc)->if_mtu = GIF_MTU; 21062587Sitojun#endif 21154263Sshin } 21254263Sshin 21354263Sshin if (sc->gif_ro.ro_rt == NULL) { 214178888Sjulian in_rtalloc_ign(&sc->gif_ro, 0, sc->gif_fibnum); 21554263Sshin if (sc->gif_ro.ro_rt == NULL) { 21654263Sshin m_freem(m); 21754263Sshin return ENETUNREACH; 21854263Sshin } 21962587Sitojun 22062587Sitojun /* if it constitutes infinite encapsulation, punt. */ 22162587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 22262587Sitojun m_freem(m); 22395023Ssuz return ENETUNREACH; /* XXX */ 22462587Sitojun } 22562587Sitojun#if 0 22662587Sitojun ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 22762587Sitojun - sizeof(struct ip); 22862587Sitojun#endif 22954263Sshin } 23054263Sshin 231105194Ssam error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 232138470Sglebius 233147256Sbrooks if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 234138653Sglebius sc->gif_ro.ro_rt != NULL) { 235138470Sglebius RTFREE(sc->gif_ro.ro_rt); 236138470Sglebius sc->gif_ro.ro_rt = NULL; 237138470Sglebius } 238138470Sglebius 239120885Sume return (error); 24054263Sshin} 24154263Sshin 24254263Sshinvoid 243169454Srwatsonin_gif_input(struct mbuf *m, int off) 24454263Sshin{ 24554263Sshin struct ifnet *gifp = NULL; 246147503Sbz struct gif_softc *sc; 24754263Sshin struct ip *ip; 24862587Sitojun int af; 24955009Sshin u_int8_t otos; 25082884Sjulian int proto; 25154263Sshin 25254263Sshin ip = mtod(m, struct ip *); 25382884Sjulian proto = ip->ip_p; 25454263Sshin 255147503Sbz sc = (struct gif_softc *)encap_getarg(m); 256147503Sbz if (sc == NULL) { 257147503Sbz m_freem(m); 258181803Sbz V_ipstat.ips_nogif++; 259147503Sbz return; 260147503Sbz } 26154263Sshin 262147503Sbz gifp = GIF2IFP(sc); 26362587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 26454263Sshin m_freem(m); 265181803Sbz V_ipstat.ips_nogif++; 26654263Sshin return; 26754263Sshin } 26854263Sshin 26955009Sshin otos = ip->ip_tos; 27054263Sshin m_adj(m, off); 27154263Sshin 27254263Sshin switch (proto) { 27362587Sitojun#ifdef INET 27454263Sshin case IPPROTO_IPV4: 27554263Sshin { 27654263Sshin struct ip *ip; 27754263Sshin af = AF_INET; 27854263Sshin if (m->m_len < sizeof(*ip)) { 27954263Sshin m = m_pullup(m, sizeof(*ip)); 28054263Sshin if (!m) 28154263Sshin return; 28254263Sshin } 28354263Sshin ip = mtod(m, struct ip *); 284121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 285121684Sume ECN_ALLOWED : ECN_NOCARE, 286121684Sume &otos, &ip->ip_tos) == 0) { 287121684Sume m_freem(m); 288121684Sume return; 289121684Sume } 29054263Sshin break; 29154263Sshin } 29262587Sitojun#endif 29354263Sshin#ifdef INET6 29454263Sshin case IPPROTO_IPV6: 29554263Sshin { 29654263Sshin struct ip6_hdr *ip6; 297121684Sume u_int8_t itos, oitos; 298121684Sume 29954263Sshin af = AF_INET6; 30054263Sshin if (m->m_len < sizeof(*ip6)) { 30154263Sshin m = m_pullup(m, sizeof(*ip6)); 30254263Sshin if (!m) 30354263Sshin return; 30454263Sshin } 30554263Sshin ip6 = mtod(m, struct ip6_hdr *); 306121684Sume itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 307121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 308121684Sume ECN_ALLOWED : ECN_NOCARE, 309121684Sume &otos, &itos) == 0) { 310121684Sume m_freem(m); 311121684Sume return; 312121684Sume } 313121684Sume if (itos != oitos) { 314121684Sume ip6->ip6_flow &= ~htonl(0xff << 20); 315121684Sume ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 316121684Sume } 31754263Sshin break; 31854263Sshin } 31954263Sshin#endif /* INET6 */ 320153621Sthompsa case IPPROTO_ETHERIP: 321153621Sthompsa af = AF_LINK; 322153621Sthompsa break; 323153621Sthompsa 32454263Sshin default: 325181803Sbz V_ipstat.ips_nogif++; 32654263Sshin m_freem(m); 32754263Sshin return; 32854263Sshin } 32954263Sshin gif_input(m, af, gifp); 33054263Sshin return; 33154263Sshin} 33262587Sitojun 33362587Sitojun/* 334105293Sume * validate outer address. 33562587Sitojun */ 336105293Sumestatic int 337169454Srwatsongif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) 33862587Sitojun{ 33962587Sitojun struct sockaddr_in *src, *dst; 34062587Sitojun struct in_ifaddr *ia4; 34162587Sitojun 34262587Sitojun src = (struct sockaddr_in *)sc->gif_psrc; 34362587Sitojun dst = (struct sockaddr_in *)sc->gif_pdst; 34462587Sitojun 34562587Sitojun /* check for address match */ 346105293Sume if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 347105293Sume dst->sin_addr.s_addr != ip->ip_src.s_addr) 34862587Sitojun return 0; 34962587Sitojun 35062587Sitojun /* martian filters on outer source - NOT done in ip_input! */ 351105293Sume if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 35262587Sitojun return 0; 353105293Sume switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 35462587Sitojun case 0: case 127: case 255: 35562587Sitojun return 0; 35662587Sitojun } 35762587Sitojun /* reject packets with broadcast on source */ 358181803Sbz TAILQ_FOREACH(ia4, &V_in_ifaddrhead, ia_link) { 35962587Sitojun if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 36062587Sitojun continue; 361105293Sume if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) 36262587Sitojun return 0; 36362587Sitojun } 36462587Sitojun 36562587Sitojun /* ingress filters on outer source */ 366147256Sbrooks if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 36762587Sitojun struct sockaddr_in sin; 36862587Sitojun struct rtentry *rt; 36962587Sitojun 37062587Sitojun bzero(&sin, sizeof(sin)); 37162587Sitojun sin.sin_family = AF_INET; 37262587Sitojun sin.sin_len = sizeof(struct sockaddr_in); 373105293Sume sin.sin_addr = ip->ip_src; 374178888Sjulian /* XXX MRT check for the interface we would use on output */ 375178888Sjulian rt = in_rtalloc1((struct sockaddr *)&sin, 0, 376178888Sjulian 0UL, sc->gif_fibnum); 377105293Sume if (!rt || rt->rt_ifp != ifp) { 37878064Sume#if 0 37978064Sume log(LOG_WARNING, "%s: packet from 0x%x dropped " 380147256Sbrooks "due to ingress filter\n", if_name(GIF2IFP(sc)), 38178064Sume (u_int32_t)ntohl(sin.sin_addr.s_addr)); 38278064Sume#endif 38378064Sume if (rt) 384172307Scsjp RTFREE_LOCKED(rt); 38562587Sitojun return 0; 38662587Sitojun } 387172307Scsjp RTFREE_LOCKED(rt); 38862587Sitojun } 38962587Sitojun 39078064Sume return 32 * 2; 39162587Sitojun} 392105293Sume 393105293Sume/* 394105293Sume * we know that we are in IFF_UP, outer address available, and outer family 395105293Sume * matched the physical addr family. see gif_encapcheck(). 396105293Sume */ 397105293Sumeint 398169454Srwatsongif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg) 399105293Sume{ 400105293Sume struct ip ip; 401105293Sume struct gif_softc *sc; 402105293Sume struct ifnet *ifp; 403105293Sume 404105293Sume /* sanity check done in caller */ 405105293Sume sc = (struct gif_softc *)arg; 406105293Sume 407105293Sume /* LINTED const cast */ 408105293Sume m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 409105293Sume ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 410105293Sume 411105293Sume return gif_validate4(&ip, sc, ifp); 412105293Sume} 413105293Sume 414105293Sumeint 415169454Srwatsonin_gif_attach(struct gif_softc *sc) 416105293Sume{ 417105293Sume sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 418105293Sume &in_gif_protosw, sc); 419105293Sume if (sc->encap_cookie4 == NULL) 420105293Sume return EEXIST; 421105293Sume return 0; 422105293Sume} 423105293Sume 424105293Sumeint 425169454Srwatsonin_gif_detach(struct gif_softc *sc) 426105293Sume{ 427105293Sume int error; 428105293Sume 429105293Sume error = encap_detach(sc->encap_cookie4); 430105293Sume if (error == 0) 431105293Sume sc->encap_cookie4 = NULL; 432105293Sume return error; 433105293Sume} 434