in_gif.c revision 169454
162587Sitojun/* $FreeBSD: head/sys/netinet/in_gif.c 169454 2007-05-10 15:58:48Z rwatson $ */ 278064Sume/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ 362587Sitojun 4139823Simp/*- 554263Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 654263Sshin * All rights reserved. 754263Sshin * 854263Sshin * Redistribution and use in source and binary forms, with or without 954263Sshin * modification, are permitted provided that the following conditions 1054263Sshin * are met: 1154263Sshin * 1. Redistributions of source code must retain the above copyright 1254263Sshin * notice, this list of conditions and the following disclaimer. 1354263Sshin * 2. Redistributions in binary form must reproduce the above copyright 1454263Sshin * notice, this list of conditions and the following disclaimer in the 1554263Sshin * documentation and/or other materials provided with the distribution. 1654263Sshin * 3. Neither the name of the project nor the names of its contributors 1754263Sshin * may be used to endorse or promote products derived from this software 1854263Sshin * without specific prior written permission. 1954263Sshin * 2054263Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2154263Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2254263Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2354263Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2454263Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2554263Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2654263Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2754263Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2854263Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2954263Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3054263Sshin * SUCH DAMAGE. 3154263Sshin */ 3254263Sshin 3354263Sshin#include "opt_mrouting.h" 3462587Sitojun#include "opt_inet.h" 3554263Sshin#include "opt_inet6.h" 3654263Sshin 3754263Sshin#include <sys/param.h> 3854263Sshin#include <sys/systm.h> 3954263Sshin#include <sys/socket.h> 4054263Sshin#include <sys/sockio.h> 4154263Sshin#include <sys/mbuf.h> 4254263Sshin#include <sys/errno.h> 4354263Sshin#include <sys/kernel.h> 4454263Sshin#include <sys/sysctl.h> 45105293Sume#include <sys/protosw.h> 4654263Sshin 4762587Sitojun#include <sys/malloc.h> 4862587Sitojun 4954263Sshin#include <net/if.h> 5054263Sshin#include <net/route.h> 5154263Sshin 5254263Sshin#include <netinet/in.h> 5354263Sshin#include <netinet/in_systm.h> 5454263Sshin#include <netinet/ip.h> 5554263Sshin#include <netinet/ip_var.h> 5654263Sshin#include <netinet/in_gif.h> 5762587Sitojun#include <netinet/in_var.h> 5862587Sitojun#include <netinet/ip_encap.h> 5955009Sshin#include <netinet/ip_ecn.h> 6062587Sitojun 6154263Sshin#ifdef INET6 6262587Sitojun#include <netinet/ip6.h> 6354263Sshin#endif 6454263Sshin 6554263Sshin#ifdef MROUTING 6654263Sshin#include <netinet/ip_mroute.h> 6754263Sshin#endif /* MROUTING */ 6854263Sshin 6962587Sitojun#include <net/if_gif.h> 7054263Sshin 71105293Sumestatic int gif_validate4(const struct ip *, struct gif_softc *, 72105293Sume struct ifnet *); 73105293Sume 74105293Sumeextern struct domain inetdomain; 75152242Srustruct protosw in_gif_protosw = { 76152242Sru .pr_type = SOCK_RAW, 77152242Sru .pr_domain = &inetdomain, 78152242Sru .pr_protocol = 0/* IPPROTO_IPV[46] */, 79152242Sru .pr_flags = PR_ATOMIC|PR_ADDR, 80152242Sru .pr_input = in_gif_input, 81152242Sru .pr_output = (pr_output_t*)rip_output, 82152242Sru .pr_ctloutput = rip_ctloutput, 83152242Sru .pr_usrreqs = &rip_usrreqs 84105293Sume}; 85105293Sume 8691324Sbrooksstatic int ip_gif_ttl = GIF_TTL; 8754263SshinSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 8854263Sshin &ip_gif_ttl, 0, ""); 8954263Sshin 9054263Sshinint 91169454Srwatsonin_gif_output(struct ifnet *ifp, int family, struct mbuf *m) 9254263Sshin{ 93147256Sbrooks struct gif_softc *sc = ifp->if_softc; 9454263Sshin struct sockaddr_in *dst = (struct sockaddr_in *)&sc->gif_ro.ro_dst; 9554263Sshin struct sockaddr_in *sin_src = (struct sockaddr_in *)sc->gif_psrc; 9654263Sshin struct sockaddr_in *sin_dst = (struct sockaddr_in *)sc->gif_pdst; 9754263Sshin struct ip iphdr; /* capsule IP header, host byte ordered */ 98153621Sthompsa struct etherip_header eiphdr; 9954263Sshin int proto, error; 10054263Sshin u_int8_t tos; 10154263Sshin 102155037Sglebius GIF_LOCK_ASSERT(sc); 103155037Sglebius 10454263Sshin if (sin_src == NULL || sin_dst == NULL || 10554263Sshin sin_src->sin_family != AF_INET || 10654263Sshin sin_dst->sin_family != AF_INET) { 10754263Sshin m_freem(m); 10854263Sshin return EAFNOSUPPORT; 10954263Sshin } 11054263Sshin 11154263Sshin switch (family) { 11262587Sitojun#ifdef INET 11354263Sshin case AF_INET: 11454263Sshin { 11554263Sshin struct ip *ip; 11654263Sshin 11754263Sshin proto = IPPROTO_IPV4; 11854263Sshin if (m->m_len < sizeof(*ip)) { 11954263Sshin m = m_pullup(m, sizeof(*ip)); 12054263Sshin if (!m) 12154263Sshin return ENOBUFS; 12254263Sshin } 12354263Sshin ip = mtod(m, struct ip *); 12454263Sshin tos = ip->ip_tos; 12554263Sshin break; 12654263Sshin } 12795023Ssuz#endif /* INET */ 12854263Sshin#ifdef INET6 12954263Sshin case AF_INET6: 13054263Sshin { 13162587Sitojun struct ip6_hdr *ip6; 13254263Sshin proto = IPPROTO_IPV6; 13354263Sshin if (m->m_len < sizeof(*ip6)) { 13454263Sshin m = m_pullup(m, sizeof(*ip6)); 13554263Sshin if (!m) 13654263Sshin return ENOBUFS; 13754263Sshin } 13854263Sshin ip6 = mtod(m, struct ip6_hdr *); 13954263Sshin tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 14054263Sshin break; 14154263Sshin } 14295023Ssuz#endif /* INET6 */ 143153621Sthompsa case AF_LINK: 144153621Sthompsa proto = IPPROTO_ETHERIP; 145153621Sthompsa eiphdr.eip_ver = ETHERIP_VERSION & ETHERIP_VER_VERS_MASK; 146153621Sthompsa eiphdr.eip_pad = 0; 147153621Sthompsa /* prepend Ethernet-in-IP header */ 148153621Sthompsa M_PREPEND(m, sizeof(struct etherip_header), M_DONTWAIT); 149153621Sthompsa if (m && m->m_len < sizeof(struct etherip_header)) 150153621Sthompsa m = m_pullup(m, sizeof(struct etherip_header)); 151153621Sthompsa if (m == NULL) 152153621Sthompsa return ENOBUFS; 153153621Sthompsa bcopy(&eiphdr, mtod(m, struct etherip_header *), 154153621Sthompsa sizeof(struct etherip_header)); 155153621Sthompsa break; 156153621Sthompsa 15754263Sshin default: 15862587Sitojun#ifdef DEBUG 15954263Sshin printf("in_gif_output: warning: unknown family %d passed\n", 16054263Sshin family); 16154263Sshin#endif 16254263Sshin m_freem(m); 16354263Sshin return EAFNOSUPPORT; 16454263Sshin } 16554263Sshin 16654263Sshin bzero(&iphdr, sizeof(iphdr)); 16754263Sshin iphdr.ip_src = sin_src->sin_addr; 16878064Sume /* bidirectional configured tunnel mode */ 16978064Sume if (sin_dst->sin_addr.s_addr != INADDR_ANY) 17078064Sume iphdr.ip_dst = sin_dst->sin_addr; 17178064Sume else { 17278064Sume m_freem(m); 17378064Sume return ENETUNREACH; 17454263Sshin } 17554263Sshin iphdr.ip_p = proto; 17654263Sshin /* version will be set in ip_output() */ 17754263Sshin iphdr.ip_ttl = ip_gif_ttl; 17854263Sshin iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); 179121684Sume ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, 180121684Sume &iphdr.ip_tos, &tos); 18154263Sshin 18254263Sshin /* prepend new IP header */ 183111119Simp M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 18454263Sshin if (m && m->m_len < sizeof(struct ip)) 18554263Sshin m = m_pullup(m, sizeof(struct ip)); 18654263Sshin if (m == NULL) { 18754263Sshin printf("ENOBUFS in in_gif_output %d\n", __LINE__); 18854263Sshin return ENOBUFS; 18954263Sshin } 19062587Sitojun bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 19154263Sshin 19254263Sshin if (dst->sin_family != sin_dst->sin_family || 19354263Sshin dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 19454263Sshin /* cache route doesn't match */ 195130662Sbms bzero(dst, sizeof(*dst)); 19654263Sshin dst->sin_family = sin_dst->sin_family; 19754263Sshin dst->sin_len = sizeof(struct sockaddr_in); 19854263Sshin dst->sin_addr = sin_dst->sin_addr; 19954263Sshin if (sc->gif_ro.ro_rt) { 20054263Sshin RTFREE(sc->gif_ro.ro_rt); 20154263Sshin sc->gif_ro.ro_rt = NULL; 20254263Sshin } 20362587Sitojun#if 0 204147256Sbrooks GIF2IFP(sc)->if_mtu = GIF_MTU; 20562587Sitojun#endif 20654263Sshin } 20754263Sshin 20854263Sshin if (sc->gif_ro.ro_rt == NULL) { 209128210Sluigi rtalloc_ign(&sc->gif_ro, 0); 21054263Sshin if (sc->gif_ro.ro_rt == NULL) { 21154263Sshin m_freem(m); 21254263Sshin return ENETUNREACH; 21354263Sshin } 21462587Sitojun 21562587Sitojun /* if it constitutes infinite encapsulation, punt. */ 21662587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 21762587Sitojun m_freem(m); 21895023Ssuz return ENETUNREACH; /* XXX */ 21962587Sitojun } 22062587Sitojun#if 0 22162587Sitojun ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 22262587Sitojun - sizeof(struct ip); 22362587Sitojun#endif 22454263Sshin } 22554263Sshin 226105194Ssam error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 227138470Sglebius 228147256Sbrooks if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && 229138653Sglebius sc->gif_ro.ro_rt != NULL) { 230138470Sglebius RTFREE(sc->gif_ro.ro_rt); 231138470Sglebius sc->gif_ro.ro_rt = NULL; 232138470Sglebius } 233138470Sglebius 234120885Sume return (error); 23554263Sshin} 23654263Sshin 23754263Sshinvoid 238169454Srwatsonin_gif_input(struct mbuf *m, int off) 23954263Sshin{ 24054263Sshin struct ifnet *gifp = NULL; 241147503Sbz struct gif_softc *sc; 24254263Sshin struct ip *ip; 24362587Sitojun int af; 24455009Sshin u_int8_t otos; 24582884Sjulian int proto; 24654263Sshin 24754263Sshin ip = mtod(m, struct ip *); 24882884Sjulian proto = ip->ip_p; 24954263Sshin 250147503Sbz sc = (struct gif_softc *)encap_getarg(m); 251147503Sbz if (sc == NULL) { 252147503Sbz m_freem(m); 253147503Sbz ipstat.ips_nogif++; 254147503Sbz return; 255147503Sbz } 25654263Sshin 257147503Sbz gifp = GIF2IFP(sc); 25862587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 25954263Sshin m_freem(m); 26054263Sshin ipstat.ips_nogif++; 26154263Sshin return; 26254263Sshin } 26354263Sshin 26455009Sshin otos = ip->ip_tos; 26554263Sshin m_adj(m, off); 26654263Sshin 26754263Sshin switch (proto) { 26862587Sitojun#ifdef INET 26954263Sshin case IPPROTO_IPV4: 27054263Sshin { 27154263Sshin struct ip *ip; 27254263Sshin af = AF_INET; 27354263Sshin if (m->m_len < sizeof(*ip)) { 27454263Sshin m = m_pullup(m, sizeof(*ip)); 27554263Sshin if (!m) 27654263Sshin return; 27754263Sshin } 27854263Sshin ip = mtod(m, struct ip *); 279121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 280121684Sume ECN_ALLOWED : ECN_NOCARE, 281121684Sume &otos, &ip->ip_tos) == 0) { 282121684Sume m_freem(m); 283121684Sume return; 284121684Sume } 28554263Sshin break; 28654263Sshin } 28762587Sitojun#endif 28854263Sshin#ifdef INET6 28954263Sshin case IPPROTO_IPV6: 29054263Sshin { 29154263Sshin struct ip6_hdr *ip6; 292121684Sume u_int8_t itos, oitos; 293121684Sume 29454263Sshin af = AF_INET6; 29554263Sshin if (m->m_len < sizeof(*ip6)) { 29654263Sshin m = m_pullup(m, sizeof(*ip6)); 29754263Sshin if (!m) 29854263Sshin return; 29954263Sshin } 30054263Sshin ip6 = mtod(m, struct ip6_hdr *); 301121684Sume itos = oitos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 302121684Sume if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? 303121684Sume ECN_ALLOWED : ECN_NOCARE, 304121684Sume &otos, &itos) == 0) { 305121684Sume m_freem(m); 306121684Sume return; 307121684Sume } 308121684Sume if (itos != oitos) { 309121684Sume ip6->ip6_flow &= ~htonl(0xff << 20); 310121684Sume ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 311121684Sume } 31254263Sshin break; 31354263Sshin } 31454263Sshin#endif /* INET6 */ 315153621Sthompsa case IPPROTO_ETHERIP: 316153621Sthompsa af = AF_LINK; 317153621Sthompsa break; 318153621Sthompsa 31954263Sshin default: 32054263Sshin ipstat.ips_nogif++; 32154263Sshin m_freem(m); 32254263Sshin return; 32354263Sshin } 32454263Sshin gif_input(m, af, gifp); 32554263Sshin return; 32654263Sshin} 32762587Sitojun 32862587Sitojun/* 329105293Sume * validate outer address. 33062587Sitojun */ 331105293Sumestatic int 332169454Srwatsongif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) 33362587Sitojun{ 33462587Sitojun struct sockaddr_in *src, *dst; 33562587Sitojun struct in_ifaddr *ia4; 33662587Sitojun 33762587Sitojun src = (struct sockaddr_in *)sc->gif_psrc; 33862587Sitojun dst = (struct sockaddr_in *)sc->gif_pdst; 33962587Sitojun 34062587Sitojun /* check for address match */ 341105293Sume if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 342105293Sume dst->sin_addr.s_addr != ip->ip_src.s_addr) 34362587Sitojun return 0; 34462587Sitojun 34562587Sitojun /* martian filters on outer source - NOT done in ip_input! */ 346105293Sume if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 34762587Sitojun return 0; 348105293Sume switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 34962587Sitojun case 0: case 127: case 255: 35062587Sitojun return 0; 35162587Sitojun } 35262587Sitojun /* reject packets with broadcast on source */ 353120891Sume TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link) { 35462587Sitojun if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 35562587Sitojun continue; 356105293Sume if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) 35762587Sitojun return 0; 35862587Sitojun } 35962587Sitojun 36062587Sitojun /* ingress filters on outer source */ 361147256Sbrooks if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) { 36262587Sitojun struct sockaddr_in sin; 36362587Sitojun struct rtentry *rt; 36462587Sitojun 36562587Sitojun bzero(&sin, sizeof(sin)); 36662587Sitojun sin.sin_family = AF_INET; 36762587Sitojun sin.sin_len = sizeof(struct sockaddr_in); 368105293Sume sin.sin_addr = ip->ip_src; 36962587Sitojun rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); 370105293Sume if (!rt || rt->rt_ifp != ifp) { 37178064Sume#if 0 37278064Sume log(LOG_WARNING, "%s: packet from 0x%x dropped " 373147256Sbrooks "due to ingress filter\n", if_name(GIF2IFP(sc)), 37478064Sume (u_int32_t)ntohl(sin.sin_addr.s_addr)); 37578064Sume#endif 37678064Sume if (rt) 37778064Sume rtfree(rt); 37862587Sitojun return 0; 37962587Sitojun } 38062587Sitojun rtfree(rt); 38162587Sitojun } 38262587Sitojun 38378064Sume return 32 * 2; 38462587Sitojun} 385105293Sume 386105293Sume/* 387105293Sume * we know that we are in IFF_UP, outer address available, and outer family 388105293Sume * matched the physical addr family. see gif_encapcheck(). 389105293Sume */ 390105293Sumeint 391169454Srwatsongif_encapcheck4(const struct mbuf *m, int off, int proto, void *arg) 392105293Sume{ 393105293Sume struct ip ip; 394105293Sume struct gif_softc *sc; 395105293Sume struct ifnet *ifp; 396105293Sume 397105293Sume /* sanity check done in caller */ 398105293Sume sc = (struct gif_softc *)arg; 399105293Sume 400105293Sume /* LINTED const cast */ 401105293Sume m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 402105293Sume ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 403105293Sume 404105293Sume return gif_validate4(&ip, sc, ifp); 405105293Sume} 406105293Sume 407105293Sumeint 408169454Srwatsonin_gif_attach(struct gif_softc *sc) 409105293Sume{ 410105293Sume sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 411105293Sume &in_gif_protosw, sc); 412105293Sume if (sc->encap_cookie4 == NULL) 413105293Sume return EEXIST; 414105293Sume return 0; 415105293Sume} 416105293Sume 417105293Sumeint 418169454Srwatsonin_gif_detach(struct gif_softc *sc) 419105293Sume{ 420105293Sume int error; 421105293Sume 422105293Sume error = encap_detach(sc->encap_cookie4); 423105293Sume if (error == 0) 424105293Sume sc->encap_cookie4 = NULL; 425105293Sume return error; 426105293Sume} 427