in_gif.c revision 105293
162587Sitojun/* $FreeBSD: head/sys/netinet/in_gif.c 105293 2002-10-16 19:49:37Z ume $ */ 278064Sume/* $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $ */ 362587Sitojun 454263Sshin/* 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 7154263Sshin#include <net/net_osdep.h> 7254263Sshin 73105293Sumestatic int gif_validate4(const struct ip *, struct gif_softc *, 74105293Sume struct ifnet *); 75105293Sume 76105293Sumeextern struct domain inetdomain; 77105293Sumestruct protosw in_gif_protosw = 78105293Sume{ SOCK_RAW, &inetdomain, 0/*IPPROTO_IPV[46]*/, PR_ATOMIC|PR_ADDR, 79105293Sume in_gif_input, (pr_output_t*)rip_output, 0, rip_ctloutput, 80105293Sume 0, 81105293Sume 0, 0, 0, 0, 82105293Sume &rip_usrreqs 83105293Sume}; 84105293Sume 8591324Sbrooksstatic int ip_gif_ttl = GIF_TTL; 8654263SshinSYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_RW, 8754263Sshin &ip_gif_ttl, 0, ""); 8854263Sshin 8954263Sshinint 9054263Sshinin_gif_output(ifp, family, m, rt) 9154263Sshin struct ifnet *ifp; 9254263Sshin int family; 9354263Sshin struct mbuf *m; 9454263Sshin struct rtentry *rt; 9554263Sshin{ 9678064Sume struct gif_softc *sc = (struct gif_softc*)ifp; 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 */ 10154263Sshin int proto, error; 10254263Sshin u_int8_t tos; 10354263Sshin 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 */ 14354263Sshin default: 14462587Sitojun#ifdef DEBUG 14554263Sshin printf("in_gif_output: warning: unknown family %d passed\n", 14654263Sshin family); 14754263Sshin#endif 14854263Sshin m_freem(m); 14954263Sshin return EAFNOSUPPORT; 15054263Sshin } 15154263Sshin 15254263Sshin bzero(&iphdr, sizeof(iphdr)); 15354263Sshin iphdr.ip_src = sin_src->sin_addr; 15478064Sume /* bidirectional configured tunnel mode */ 15578064Sume if (sin_dst->sin_addr.s_addr != INADDR_ANY) 15678064Sume iphdr.ip_dst = sin_dst->sin_addr; 15778064Sume else { 15878064Sume m_freem(m); 15978064Sume return ENETUNREACH; 16054263Sshin } 16154263Sshin iphdr.ip_p = proto; 16254263Sshin /* version will be set in ip_output() */ 16354263Sshin iphdr.ip_ttl = ip_gif_ttl; 16454263Sshin iphdr.ip_len = m->m_pkthdr.len + sizeof(struct ip); 16555009Sshin if (ifp->if_flags & IFF_LINK1) 16655009Sshin ip_ecn_ingress(ECN_ALLOWED, &iphdr.ip_tos, &tos); 16778064Sume else 16878064Sume ip_ecn_ingress(ECN_NOCARE, &iphdr.ip_tos, &tos); 16954263Sshin 17054263Sshin /* prepend new IP header */ 17154263Sshin M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); 17254263Sshin if (m && m->m_len < sizeof(struct ip)) 17354263Sshin m = m_pullup(m, sizeof(struct ip)); 17454263Sshin if (m == NULL) { 17554263Sshin printf("ENOBUFS in in_gif_output %d\n", __LINE__); 17654263Sshin return ENOBUFS; 17754263Sshin } 17862587Sitojun bcopy(&iphdr, mtod(m, struct ip *), sizeof(struct ip)); 17954263Sshin 18054263Sshin if (dst->sin_family != sin_dst->sin_family || 18154263Sshin dst->sin_addr.s_addr != sin_dst->sin_addr.s_addr) { 18254263Sshin /* cache route doesn't match */ 18354263Sshin dst->sin_family = sin_dst->sin_family; 18454263Sshin dst->sin_len = sizeof(struct sockaddr_in); 18554263Sshin dst->sin_addr = sin_dst->sin_addr; 18654263Sshin if (sc->gif_ro.ro_rt) { 18754263Sshin RTFREE(sc->gif_ro.ro_rt); 18854263Sshin sc->gif_ro.ro_rt = NULL; 18954263Sshin } 19062587Sitojun#if 0 19162587Sitojun sc->gif_if.if_mtu = GIF_MTU; 19262587Sitojun#endif 19354263Sshin } 19454263Sshin 19554263Sshin if (sc->gif_ro.ro_rt == NULL) { 19654263Sshin rtalloc(&sc->gif_ro); 19754263Sshin if (sc->gif_ro.ro_rt == NULL) { 19854263Sshin m_freem(m); 19954263Sshin return ENETUNREACH; 20054263Sshin } 20162587Sitojun 20262587Sitojun /* if it constitutes infinite encapsulation, punt. */ 20362587Sitojun if (sc->gif_ro.ro_rt->rt_ifp == ifp) { 20462587Sitojun m_freem(m); 20595023Ssuz return ENETUNREACH; /* XXX */ 20662587Sitojun } 20762587Sitojun#if 0 20862587Sitojun ifp->if_mtu = sc->gif_ro.ro_rt->rt_ifp->if_mtu 20962587Sitojun - sizeof(struct ip); 21062587Sitojun#endif 21154263Sshin } 21254263Sshin 213105194Ssam error = ip_output(m, NULL, &sc->gif_ro, 0, NULL, NULL); 21454263Sshin return(error); 21554263Sshin} 21654263Sshin 21754263Sshinvoid 21882884Sjulianin_gif_input(m, off) 21962587Sitojun struct mbuf *m; 22079106Sbrooks int off; 22154263Sshin{ 22254263Sshin struct ifnet *gifp = NULL; 22354263Sshin struct ip *ip; 22462587Sitojun int af; 22555009Sshin u_int8_t otos; 22682884Sjulian int proto; 22754263Sshin 22854263Sshin ip = mtod(m, struct ip *); 22982884Sjulian proto = ip->ip_p; 23054263Sshin 23162587Sitojun gifp = (struct ifnet *)encap_getarg(m); 23254263Sshin 23362587Sitojun if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { 23454263Sshin m_freem(m); 23554263Sshin ipstat.ips_nogif++; 23654263Sshin return; 23754263Sshin } 23854263Sshin 23955009Sshin otos = ip->ip_tos; 24054263Sshin m_adj(m, off); 24154263Sshin 24254263Sshin switch (proto) { 24362587Sitojun#ifdef INET 24454263Sshin case IPPROTO_IPV4: 24554263Sshin { 24654263Sshin struct ip *ip; 24754263Sshin af = AF_INET; 24854263Sshin if (m->m_len < sizeof(*ip)) { 24954263Sshin m = m_pullup(m, sizeof(*ip)); 25054263Sshin if (!m) 25154263Sshin return; 25254263Sshin } 25354263Sshin ip = mtod(m, struct ip *); 25455009Sshin if (gifp->if_flags & IFF_LINK1) 25555009Sshin ip_ecn_egress(ECN_ALLOWED, &otos, &ip->ip_tos); 25678064Sume else 25778064Sume ip_ecn_egress(ECN_NOCARE, &otos, &ip->ip_tos); 25854263Sshin break; 25954263Sshin } 26062587Sitojun#endif 26154263Sshin#ifdef INET6 26254263Sshin case IPPROTO_IPV6: 26354263Sshin { 26454263Sshin struct ip6_hdr *ip6; 26555009Sshin u_int8_t itos; 26654263Sshin af = AF_INET6; 26754263Sshin if (m->m_len < sizeof(*ip6)) { 26854263Sshin m = m_pullup(m, sizeof(*ip6)); 26954263Sshin if (!m) 27054263Sshin return; 27154263Sshin } 27254263Sshin ip6 = mtod(m, struct ip6_hdr *); 27355009Sshin itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; 27455009Sshin if (gifp->if_flags & IFF_LINK1) 27555009Sshin ip_ecn_egress(ECN_ALLOWED, &otos, &itos); 27678064Sume else 27778064Sume ip_ecn_egress(ECN_NOCARE, &otos, &itos); 27854263Sshin ip6->ip6_flow &= ~htonl(0xff << 20); 27955009Sshin ip6->ip6_flow |= htonl((u_int32_t)itos << 20); 28054263Sshin break; 28154263Sshin } 28254263Sshin#endif /* INET6 */ 28354263Sshin default: 28454263Sshin ipstat.ips_nogif++; 28554263Sshin m_freem(m); 28654263Sshin return; 28754263Sshin } 28854263Sshin gif_input(m, af, gifp); 28954263Sshin return; 29054263Sshin} 29162587Sitojun 29262587Sitojun/* 293105293Sume * validate outer address. 29462587Sitojun */ 295105293Sumestatic int 296105293Sumegif_validate4(ip, sc, ifp) 297105293Sume const struct ip *ip; 298105293Sume struct gif_softc *sc; 299105293Sume struct ifnet *ifp; 30062587Sitojun{ 30162587Sitojun struct sockaddr_in *src, *dst; 30262587Sitojun struct in_ifaddr *ia4; 30362587Sitojun 30462587Sitojun src = (struct sockaddr_in *)sc->gif_psrc; 30562587Sitojun dst = (struct sockaddr_in *)sc->gif_pdst; 30662587Sitojun 30762587Sitojun /* check for address match */ 308105293Sume if (src->sin_addr.s_addr != ip->ip_dst.s_addr || 309105293Sume dst->sin_addr.s_addr != ip->ip_src.s_addr) 31062587Sitojun return 0; 31162587Sitojun 31262587Sitojun /* martian filters on outer source - NOT done in ip_input! */ 313105293Sume if (IN_MULTICAST(ntohl(ip->ip_src.s_addr))) 31462587Sitojun return 0; 315105293Sume switch ((ntohl(ip->ip_src.s_addr) & 0xff000000) >> 24) { 31662587Sitojun case 0: case 127: case 255: 31762587Sitojun return 0; 31862587Sitojun } 31962587Sitojun /* reject packets with broadcast on source */ 32072012Sphk TAILQ_FOREACH(ia4, &in_ifaddrhead, ia_link) 32162587Sitojun { 32262587Sitojun if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) 32362587Sitojun continue; 324105293Sume if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) 32562587Sitojun return 0; 32662587Sitojun } 32762587Sitojun 32862587Sitojun /* ingress filters on outer source */ 329105293Sume if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) { 33062587Sitojun struct sockaddr_in sin; 33162587Sitojun struct rtentry *rt; 33262587Sitojun 33362587Sitojun bzero(&sin, sizeof(sin)); 33462587Sitojun sin.sin_family = AF_INET; 33562587Sitojun sin.sin_len = sizeof(struct sockaddr_in); 336105293Sume sin.sin_addr = ip->ip_src; 33762587Sitojun rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL); 338105293Sume if (!rt || rt->rt_ifp != ifp) { 33978064Sume#if 0 34078064Sume log(LOG_WARNING, "%s: packet from 0x%x dropped " 34178064Sume "due to ingress filter\n", if_name(&sc->gif_if), 34278064Sume (u_int32_t)ntohl(sin.sin_addr.s_addr)); 34378064Sume#endif 34478064Sume if (rt) 34578064Sume rtfree(rt); 34662587Sitojun return 0; 34762587Sitojun } 34862587Sitojun rtfree(rt); 34962587Sitojun } 35062587Sitojun 35178064Sume return 32 * 2; 35262587Sitojun} 353105293Sume 354105293Sume/* 355105293Sume * we know that we are in IFF_UP, outer address available, and outer family 356105293Sume * matched the physical addr family. see gif_encapcheck(). 357105293Sume */ 358105293Sumeint 359105293Sumegif_encapcheck4(m, off, proto, arg) 360105293Sume const struct mbuf *m; 361105293Sume int off; 362105293Sume int proto; 363105293Sume void *arg; 364105293Sume{ 365105293Sume struct ip ip; 366105293Sume struct gif_softc *sc; 367105293Sume struct ifnet *ifp; 368105293Sume 369105293Sume /* sanity check done in caller */ 370105293Sume sc = (struct gif_softc *)arg; 371105293Sume 372105293Sume /* LINTED const cast */ 373105293Sume m_copydata(m, 0, sizeof(ip), (caddr_t)&ip); 374105293Sume ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; 375105293Sume 376105293Sume return gif_validate4(&ip, sc, ifp); 377105293Sume} 378105293Sume 379105293Sumeint 380105293Sumein_gif_attach(sc) 381105293Sume struct gif_softc *sc; 382105293Sume{ 383105293Sume#ifndef USE_ENCAPCHECK 384105293Sume struct sockaddr_in mask4; 385105293Sume 386105293Sume bzero(&mask4, sizeof(mask4)); 387105293Sume mask4.sin_len = sizeof(struct sockaddr_in); 388105293Sume mask4.sin_addr.s_addr = ~0; 389105293Sume 390105293Sume if (!sc->gif_psrc || !sc->gif_pdst) 391105293Sume return EINVAL; 392105293Sume sc->encap_cookie4 = encap_attach(AF_INET, -1, sc->gif_psrc, 393105293Sume (struct sockaddr *)&mask4, sc->gif_pdst, (struct sockaddr *)&mask4, 394105293Sume (struct protosw *)&in_gif_protosw, sc); 395105293Sume#else 396105293Sume sc->encap_cookie4 = encap_attach_func(AF_INET, -1, gif_encapcheck, 397105293Sume &in_gif_protosw, sc); 398105293Sume#endif 399105293Sume if (sc->encap_cookie4 == NULL) 400105293Sume return EEXIST; 401105293Sume return 0; 402105293Sume} 403105293Sume 404105293Sumeint 405105293Sumein_gif_detach(sc) 406105293Sume struct gif_softc *sc; 407105293Sume{ 408105293Sume int error; 409105293Sume 410105293Sume error = encap_detach(sc->encap_cookie4); 411105293Sume if (error == 0) 412105293Sume sc->encap_cookie4 = NULL; 413105293Sume return error; 414105293Sume} 415