The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/in_gif.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-3-Clause
    3  *
    4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
    5  * Copyright (c) 2018 Andrey V. Elsukov <ae@FreeBSD.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the project nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      $KAME: in_gif.c,v 1.54 2001/05/14 14:02:16 itojun Exp $
   33  */
   34 
   35 #include <sys/cdefs.h>
   36 __FBSDID("$FreeBSD$");
   37 
   38 #include "opt_inet.h"
   39 #include "opt_inet6.h"
   40 
   41 #include <sys/param.h>
   42 #include <sys/systm.h>
   43 #include <sys/jail.h>
   44 #include <sys/socket.h>
   45 #include <sys/sockio.h>
   46 #include <sys/mbuf.h>
   47 #include <sys/errno.h>
   48 #include <sys/kernel.h>
   49 #include <sys/sysctl.h>
   50 #include <sys/malloc.h>
   51 #include <sys/proc.h>
   52 
   53 #include <net/ethernet.h>
   54 #include <net/if.h>
   55 #include <net/if_var.h>
   56 #include <net/route.h>
   57 #include <net/vnet.h>
   58 
   59 #include <netinet/in.h>
   60 #include <netinet/in_systm.h>
   61 #include <netinet/ip.h>
   62 #include <netinet/ip_var.h>
   63 #include <netinet/in_var.h>
   64 #include <netinet/ip_encap.h>
   65 #include <netinet/ip_ecn.h>
   66 #include <netinet/in_fib.h>
   67 
   68 #ifdef INET6
   69 #include <netinet/ip6.h>
   70 #endif
   71 
   72 #include <net/if_gif.h>
   73 
   74 #define GIF_TTL         30
   75 VNET_DEFINE_STATIC(int, ip_gif_ttl) = GIF_TTL;
   76 #define V_ip_gif_ttl            VNET(ip_gif_ttl)
   77 SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLAG_VNET | CTLFLAG_RW,
   78     &VNET_NAME(ip_gif_ttl), 0, "Default TTL value for encapsulated packets");
   79 
   80 /*
   81  * We keep interfaces in a hash table using src+dst as key.
   82  * Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
   83  */
   84 VNET_DEFINE_STATIC(struct gif_list *, ipv4_hashtbl) = NULL;
   85 VNET_DEFINE_STATIC(struct gif_list *, ipv4_srchashtbl) = NULL;
   86 VNET_DEFINE_STATIC(struct gif_list, ipv4_list) = CK_LIST_HEAD_INITIALIZER();
   87 #define V_ipv4_hashtbl          VNET(ipv4_hashtbl)
   88 #define V_ipv4_srchashtbl       VNET(ipv4_srchashtbl)
   89 #define V_ipv4_list             VNET(ipv4_list)
   90 
   91 #define GIF_HASH(src, dst)      (V_ipv4_hashtbl[\
   92     in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
   93 #define GIF_SRCHASH(src)        (V_ipv4_srchashtbl[\
   94     fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
   95 #define GIF_HASH_SC(sc)         GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
   96     (sc)->gif_iphdr->ip_dst.s_addr)
   97 static uint32_t
   98 in_gif_hashval(in_addr_t src, in_addr_t dst)
   99 {
  100         uint32_t ret;
  101 
  102         ret = fnv_32_buf(&src, sizeof(src), FNV1_32_INIT);
  103         return (fnv_32_buf(&dst, sizeof(dst), ret));
  104 }
  105 
  106 static int
  107 in_gif_checkdup(const struct gif_softc *sc, in_addr_t src, in_addr_t dst)
  108 {
  109         struct gif_softc *tmp;
  110 
  111         if (sc->gif_family == AF_INET &&
  112             sc->gif_iphdr->ip_src.s_addr == src &&
  113             sc->gif_iphdr->ip_dst.s_addr == dst)
  114                 return (EEXIST);
  115 
  116         CK_LIST_FOREACH(tmp, &GIF_HASH(src, dst), chain) {
  117                 if (tmp == sc)
  118                         continue;
  119                 if (tmp->gif_iphdr->ip_src.s_addr == src &&
  120                     tmp->gif_iphdr->ip_dst.s_addr == dst)
  121                         return (EADDRNOTAVAIL);
  122         }
  123         return (0);
  124 }
  125 
  126 /*
  127  * Check that ingress address belongs to local host.
  128  */
  129 static void
  130 in_gif_set_running(struct gif_softc *sc)
  131 {
  132 
  133         if (in_localip(sc->gif_iphdr->ip_src))
  134                 GIF2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
  135         else
  136                 GIF2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
  137 }
  138 
  139 /*
  140  * ifaddr_event handler.
  141  * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
  142  * source address spoofing.
  143  */
  144 static void
  145 in_gif_srcaddr(void *arg __unused, const struct sockaddr *sa,
  146     int event __unused)
  147 {
  148         const struct sockaddr_in *sin;
  149         struct gif_softc *sc;
  150 
  151         /* Check that VNET is ready */
  152         if (V_ipv4_hashtbl == NULL)
  153                 return;
  154 
  155         MPASS(in_epoch(net_epoch_preempt));
  156         sin = (const struct sockaddr_in *)sa;
  157         CK_LIST_FOREACH(sc, &GIF_SRCHASH(sin->sin_addr.s_addr), srchash) {
  158                 if (sc->gif_iphdr->ip_src.s_addr != sin->sin_addr.s_addr)
  159                         continue;
  160                 in_gif_set_running(sc);
  161         }
  162 }
  163 
  164 static void
  165 in_gif_attach(struct gif_softc *sc)
  166 {
  167 
  168         if (sc->gif_options & GIF_IGNORE_SOURCE)
  169                 CK_LIST_INSERT_HEAD(&V_ipv4_list, sc, chain);
  170         else
  171                 CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
  172 
  173         CK_LIST_INSERT_HEAD(&GIF_SRCHASH(sc->gif_iphdr->ip_src.s_addr),
  174             sc, srchash);
  175 }
  176 
  177 int
  178 in_gif_setopts(struct gif_softc *sc, u_int options)
  179 {
  180 
  181         /* NOTE: we are protected with gif_ioctl_sx lock */
  182         MPASS(sc->gif_family == AF_INET);
  183         MPASS(sc->gif_options != options);
  184 
  185         if ((options & GIF_IGNORE_SOURCE) !=
  186             (sc->gif_options & GIF_IGNORE_SOURCE)) {
  187                 CK_LIST_REMOVE(sc, srchash);
  188                 CK_LIST_REMOVE(sc, chain);
  189                 sc->gif_options = options;
  190                 in_gif_attach(sc);
  191         }
  192         return (0);
  193 }
  194 
  195 int
  196 in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
  197 {
  198         struct ifreq *ifr = (struct ifreq *)data;
  199         struct sockaddr_in *dst, *src;
  200         struct ip *ip;
  201         int error;
  202 
  203         /* NOTE: we are protected with gif_ioctl_sx lock */
  204         error = EINVAL;
  205         switch (cmd) {
  206         case SIOCSIFPHYADDR:
  207                 src = &((struct in_aliasreq *)data)->ifra_addr;
  208                 dst = &((struct in_aliasreq *)data)->ifra_dstaddr;
  209 
  210                 /* sanity checks */
  211                 if (src->sin_family != dst->sin_family ||
  212                     src->sin_family != AF_INET ||
  213                     src->sin_len != dst->sin_len ||
  214                     src->sin_len != sizeof(*src))
  215                         break;
  216                 if (src->sin_addr.s_addr == INADDR_ANY ||
  217                     dst->sin_addr.s_addr == INADDR_ANY) {
  218                         error = EADDRNOTAVAIL;
  219                         break;
  220                 }
  221                 if (V_ipv4_hashtbl == NULL) {
  222                         V_ipv4_hashtbl = gif_hashinit();
  223                         V_ipv4_srchashtbl = gif_hashinit();
  224                 }
  225                 error = in_gif_checkdup(sc, src->sin_addr.s_addr,
  226                     dst->sin_addr.s_addr);
  227                 if (error == EADDRNOTAVAIL)
  228                         break;
  229                 if (error == EEXIST) {
  230                         /* Addresses are the same. Just return. */
  231                         error = 0;
  232                         break;
  233                 }
  234                 ip = malloc(sizeof(*ip), M_GIF, M_WAITOK | M_ZERO);
  235                 ip->ip_src.s_addr = src->sin_addr.s_addr;
  236                 ip->ip_dst.s_addr = dst->sin_addr.s_addr;
  237                 if (sc->gif_family != 0) {
  238                         /* Detach existing tunnel first */
  239                         CK_LIST_REMOVE(sc, srchash);
  240                         CK_LIST_REMOVE(sc, chain);
  241                         GIF_WAIT();
  242                         free(sc->gif_hdr, M_GIF);
  243                         /* XXX: should we notify about link state change? */
  244                 }
  245                 sc->gif_family = AF_INET;
  246                 sc->gif_iphdr = ip;
  247                 in_gif_attach(sc);
  248                 in_gif_set_running(sc);
  249                 break;
  250         case SIOCGIFPSRCADDR:
  251         case SIOCGIFPDSTADDR:
  252                 if (sc->gif_family != AF_INET) {
  253                         error = EADDRNOTAVAIL;
  254                         break;
  255                 }
  256                 src = (struct sockaddr_in *)&ifr->ifr_addr;
  257                 memset(src, 0, sizeof(*src));
  258                 src->sin_family = AF_INET;
  259                 src->sin_len = sizeof(*src);
  260                 src->sin_addr = (cmd == SIOCGIFPSRCADDR) ?
  261                     sc->gif_iphdr->ip_src: sc->gif_iphdr->ip_dst;
  262                 error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
  263                 if (error != 0)
  264                         memset(src, 0, sizeof(*src));
  265                 break;
  266         }
  267         return (error);
  268 }
  269 
  270 int
  271 in_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn)
  272 {
  273         struct gif_softc *sc = ifp->if_softc;
  274         struct ip *ip;
  275         int len;
  276 
  277         /* prepend new IP header */
  278         MPASS(in_epoch(net_epoch_preempt));
  279         len = sizeof(struct ip);
  280 #ifndef __NO_STRICT_ALIGNMENT
  281         if (proto == IPPROTO_ETHERIP)
  282                 len += ETHERIP_ALIGN;
  283 #endif
  284         M_PREPEND(m, len, M_NOWAIT);
  285         if (m == NULL)
  286                 return (ENOBUFS);
  287 #ifndef __NO_STRICT_ALIGNMENT
  288         if (proto == IPPROTO_ETHERIP) {
  289                 len = mtod(m, vm_offset_t) & 3;
  290                 KASSERT(len == 0 || len == ETHERIP_ALIGN,
  291                     ("in_gif_output: unexpected misalignment"));
  292                 m->m_data += len;
  293                 m->m_len -= ETHERIP_ALIGN;
  294         }
  295 #endif
  296         ip = mtod(m, struct ip *);
  297 
  298         MPASS(sc->gif_family == AF_INET);
  299         bcopy(sc->gif_iphdr, ip, sizeof(struct ip));
  300         ip->ip_p = proto;
  301         /* version will be set in ip_output() */
  302         ip->ip_ttl = V_ip_gif_ttl;
  303         ip->ip_len = htons(m->m_pkthdr.len);
  304         ip->ip_tos = ecn;
  305 
  306         return (ip_output(m, NULL, NULL, 0, NULL, NULL));
  307 }
  308 
  309 static int
  310 in_gif_input(struct mbuf *m, int off, int proto, void *arg)
  311 {
  312         struct gif_softc *sc = arg;
  313         struct ifnet *gifp;
  314         struct ip *ip;
  315         uint8_t ecn;
  316 
  317         MPASS(in_epoch(net_epoch_preempt));
  318         if (sc == NULL) {
  319                 m_freem(m);
  320                 KMOD_IPSTAT_INC(ips_nogif);
  321                 return (IPPROTO_DONE);
  322         }
  323         gifp = GIF2IFP(sc);
  324         if ((gifp->if_flags & IFF_UP) != 0) {
  325                 ip = mtod(m, struct ip *);
  326                 ecn = ip->ip_tos;
  327                 m_adj(m, off);
  328                 gif_input(m, gifp, proto, ecn);
  329         } else {
  330                 m_freem(m);
  331                 KMOD_IPSTAT_INC(ips_nogif);
  332         }
  333         return (IPPROTO_DONE);
  334 }
  335 
  336 static int
  337 in_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
  338 {
  339         const struct ip *ip;
  340         struct gif_softc *sc;
  341         int ret;
  342 
  343         if (V_ipv4_hashtbl == NULL)
  344                 return (0);
  345 
  346         MPASS(in_epoch(net_epoch_preempt));
  347         ip = mtod(m, const struct ip *);
  348         /*
  349          * NOTE: it is safe to iterate without any locking here, because softc
  350          * can be reclaimed only when we are not within net_epoch_preempt
  351          * section, but ip_encap lookup+input are executed in epoch section.
  352          */
  353         ret = 0;
  354         CK_LIST_FOREACH(sc, &GIF_HASH(ip->ip_dst.s_addr,
  355             ip->ip_src.s_addr), chain) {
  356                 /*
  357                  * This is an inbound packet, its ip_dst is source address
  358                  * in softc.
  359                  */
  360                 if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr &&
  361                     sc->gif_iphdr->ip_dst.s_addr == ip->ip_src.s_addr) {
  362                         ret = ENCAP_DRV_LOOKUP;
  363                         goto done;
  364                 }
  365         }
  366         /*
  367          * No exact match.
  368          * Check the list of interfaces with GIF_IGNORE_SOURCE flag.
  369          */
  370         CK_LIST_FOREACH(sc, &V_ipv4_list, chain) {
  371                 if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr) {
  372                         ret = 32 + 8; /* src + proto */
  373                         goto done;
  374                 }
  375         }
  376         return (0);
  377 done:
  378         if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
  379                 return (0);
  380         /* ingress filters on outer source */
  381         if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
  382                 struct nhop4_basic nh4;
  383                 struct in_addr dst;
  384 
  385                 dst = ip->ip_src;
  386                 if (fib4_lookup_nh_basic(sc->gif_fibnum, dst, 0, 0, &nh4) != 0)
  387                         return (0);
  388                 if (nh4.nh_ifp != m->m_pkthdr.rcvif)
  389                         return (0);
  390         }
  391         *arg = sc;
  392         return (ret);
  393 }
  394 
  395 static const struct srcaddrtab *ipv4_srcaddrtab;
  396 static struct {
  397         const struct encap_config encap;
  398         const struct encaptab *cookie;
  399 } ipv4_encap_cfg[] = {
  400         {
  401                 .encap = {
  402                         .proto = IPPROTO_IPV4,
  403                         .min_length = 2 * sizeof(struct ip),
  404                         .exact_match = ENCAP_DRV_LOOKUP,
  405                         .lookup = in_gif_lookup,
  406                         .input = in_gif_input
  407                 },
  408         },
  409 #ifdef INET6
  410         {
  411                 .encap = {
  412                         .proto = IPPROTO_IPV6,
  413                         .min_length = sizeof(struct ip) +
  414                             sizeof(struct ip6_hdr),
  415                         .exact_match = ENCAP_DRV_LOOKUP,
  416                         .lookup = in_gif_lookup,
  417                         .input = in_gif_input
  418                 },
  419         },
  420 #endif
  421         {
  422                 .encap = {
  423                         .proto = IPPROTO_ETHERIP,
  424                         .min_length = sizeof(struct ip) +
  425                             sizeof(struct etherip_header) +
  426                             sizeof(struct ether_header),
  427                         .exact_match = ENCAP_DRV_LOOKUP,
  428                         .lookup = in_gif_lookup,
  429                         .input = in_gif_input
  430                 },
  431         }
  432 };
  433 
  434 void
  435 in_gif_init(void)
  436 {
  437         int i;
  438 
  439         if (!IS_DEFAULT_VNET(curvnet))
  440                 return;
  441 
  442         ipv4_srcaddrtab = ip_encap_register_srcaddr(in_gif_srcaddr,
  443             NULL, M_WAITOK);
  444         for (i = 0; i < nitems(ipv4_encap_cfg); i++)
  445                 ipv4_encap_cfg[i].cookie = ip_encap_attach(
  446                     &ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
  447 }
  448 
  449 void
  450 in_gif_uninit(void)
  451 {
  452         int i;
  453 
  454         if (IS_DEFAULT_VNET(curvnet)) {
  455                 for (i = 0; i < nitems(ipv4_encap_cfg); i++)
  456                         ip_encap_detach(ipv4_encap_cfg[i].cookie);
  457                 ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
  458         }
  459         if (V_ipv4_hashtbl != NULL) {
  460                 gif_hashdestroy(V_ipv4_hashtbl);
  461                 V_ipv4_hashtbl = NULL;
  462                 GIF_WAIT();
  463                 gif_hashdestroy(V_ipv4_srchashtbl);
  464         }
  465 }
  466 

Cache object: 6b319ac73a6fa2ecaadfb381f8891657


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.