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: releng/12.0/sys/netinet/in_gif.c 336676 2018-07-24 16:35:52Z andrew $");
   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_list) = CK_LIST_HEAD_INITIALIZER();
   86 #define V_ipv4_hashtbl          VNET(ipv4_hashtbl)
   87 #define V_ipv4_list             VNET(ipv4_list)
   88 
   89 #define GIF_HASH(src, dst)      (V_ipv4_hashtbl[\
   90     in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
   91 #define GIF_HASH_SC(sc)         GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
   92     (sc)->gif_iphdr->ip_dst.s_addr)
   93 static uint32_t
   94 in_gif_hashval(in_addr_t src, in_addr_t dst)
   95 {
   96         uint32_t ret;
   97 
   98         ret = fnv_32_buf(&src, sizeof(src), FNV1_32_INIT);
   99         return (fnv_32_buf(&dst, sizeof(dst), ret));
  100 }
  101 
  102 static int
  103 in_gif_checkdup(const struct gif_softc *sc, in_addr_t src, in_addr_t dst)
  104 {
  105         struct gif_softc *tmp;
  106 
  107         if (sc->gif_family == AF_INET &&
  108             sc->gif_iphdr->ip_src.s_addr == src &&
  109             sc->gif_iphdr->ip_dst.s_addr == dst)
  110                 return (EEXIST);
  111 
  112         CK_LIST_FOREACH(tmp, &GIF_HASH(src, dst), chain) {
  113                 if (tmp == sc)
  114                         continue;
  115                 if (tmp->gif_iphdr->ip_src.s_addr == src &&
  116                     tmp->gif_iphdr->ip_dst.s_addr == dst)
  117                         return (EADDRNOTAVAIL);
  118         }
  119         return (0);
  120 }
  121 
  122 static void
  123 in_gif_attach(struct gif_softc *sc)
  124 {
  125 
  126         if (sc->gif_options & GIF_IGNORE_SOURCE)
  127                 CK_LIST_INSERT_HEAD(&V_ipv4_list, sc, chain);
  128         else
  129                 CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
  130 }
  131 
  132 int
  133 in_gif_setopts(struct gif_softc *sc, u_int options)
  134 {
  135 
  136         /* NOTE: we are protected with gif_ioctl_sx lock */
  137         MPASS(sc->gif_family == AF_INET);
  138         MPASS(sc->gif_options != options);
  139 
  140         if ((options & GIF_IGNORE_SOURCE) !=
  141             (sc->gif_options & GIF_IGNORE_SOURCE)) {
  142                 CK_LIST_REMOVE(sc, chain);
  143                 sc->gif_options = options;
  144                 in_gif_attach(sc);
  145         }
  146         return (0);
  147 }
  148 
  149 int
  150 in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t data)
  151 {
  152         struct ifreq *ifr = (struct ifreq *)data;
  153         struct sockaddr_in *dst, *src;
  154         struct ip *ip;
  155         int error;
  156 
  157         /* NOTE: we are protected with gif_ioctl_sx lock */
  158         error = EINVAL;
  159         switch (cmd) {
  160         case SIOCSIFPHYADDR:
  161                 src = &((struct in_aliasreq *)data)->ifra_addr;
  162                 dst = &((struct in_aliasreq *)data)->ifra_dstaddr;
  163 
  164                 /* sanity checks */
  165                 if (src->sin_family != dst->sin_family ||
  166                     src->sin_family != AF_INET ||
  167                     src->sin_len != dst->sin_len ||
  168                     src->sin_len != sizeof(*src))
  169                         break;
  170                 if (src->sin_addr.s_addr == INADDR_ANY ||
  171                     dst->sin_addr.s_addr == INADDR_ANY) {
  172                         error = EADDRNOTAVAIL;
  173                         break;
  174                 }
  175                 if (V_ipv4_hashtbl == NULL)
  176                         V_ipv4_hashtbl = gif_hashinit();
  177                 error = in_gif_checkdup(sc, src->sin_addr.s_addr,
  178                     dst->sin_addr.s_addr);
  179                 if (error == EADDRNOTAVAIL)
  180                         break;
  181                 if (error == EEXIST) {
  182                         /* Addresses are the same. Just return. */
  183                         error = 0;
  184                         break;
  185                 }
  186                 ip = malloc(sizeof(*ip), M_GIF, M_WAITOK | M_ZERO);
  187                 ip->ip_src.s_addr = src->sin_addr.s_addr;
  188                 ip->ip_dst.s_addr = dst->sin_addr.s_addr;
  189                 if (sc->gif_family != 0) {
  190                         /* Detach existing tunnel first */
  191                         CK_LIST_REMOVE(sc, chain);
  192                         GIF_WAIT();
  193                         free(sc->gif_hdr, M_GIF);
  194                         /* XXX: should we notify about link state change? */
  195                 }
  196                 sc->gif_family = AF_INET;
  197                 sc->gif_iphdr = ip;
  198                 in_gif_attach(sc);
  199                 break;
  200         case SIOCGIFPSRCADDR:
  201         case SIOCGIFPDSTADDR:
  202                 if (sc->gif_family != AF_INET) {
  203                         error = EADDRNOTAVAIL;
  204                         break;
  205                 }
  206                 src = (struct sockaddr_in *)&ifr->ifr_addr;
  207                 memset(src, 0, sizeof(*src));
  208                 src->sin_family = AF_INET;
  209                 src->sin_len = sizeof(*src);
  210                 src->sin_addr = (cmd == SIOCGIFPSRCADDR) ?
  211                     sc->gif_iphdr->ip_src: sc->gif_iphdr->ip_dst;
  212                 error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
  213                 if (error != 0)
  214                         memset(src, 0, sizeof(*src));
  215                 break;
  216         }
  217         return (error);
  218 }
  219 
  220 int
  221 in_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn)
  222 {
  223         struct gif_softc *sc = ifp->if_softc;
  224         struct ip *ip;
  225         int len;
  226 
  227         /* prepend new IP header */
  228         MPASS(in_epoch(net_epoch_preempt));
  229         len = sizeof(struct ip);
  230 #ifndef __NO_STRICT_ALIGNMENT
  231         if (proto == IPPROTO_ETHERIP)
  232                 len += ETHERIP_ALIGN;
  233 #endif
  234         M_PREPEND(m, len, M_NOWAIT);
  235         if (m == NULL)
  236                 return (ENOBUFS);
  237 #ifndef __NO_STRICT_ALIGNMENT
  238         if (proto == IPPROTO_ETHERIP) {
  239                 len = mtod(m, vm_offset_t) & 3;
  240                 KASSERT(len == 0 || len == ETHERIP_ALIGN,
  241                     ("in_gif_output: unexpected misalignment"));
  242                 m->m_data += len;
  243                 m->m_len -= ETHERIP_ALIGN;
  244         }
  245 #endif
  246         ip = mtod(m, struct ip *);
  247 
  248         MPASS(sc->gif_family == AF_INET);
  249         bcopy(sc->gif_iphdr, ip, sizeof(struct ip));
  250         ip->ip_p = proto;
  251         /* version will be set in ip_output() */
  252         ip->ip_ttl = V_ip_gif_ttl;
  253         ip->ip_len = htons(m->m_pkthdr.len);
  254         ip->ip_tos = ecn;
  255 
  256         return (ip_output(m, NULL, NULL, 0, NULL, NULL));
  257 }
  258 
  259 static int
  260 in_gif_input(struct mbuf *m, int off, int proto, void *arg)
  261 {
  262         struct gif_softc *sc = arg;
  263         struct ifnet *gifp;
  264         struct ip *ip;
  265         uint8_t ecn;
  266 
  267         MPASS(in_epoch(net_epoch_preempt));
  268         if (sc == NULL) {
  269                 m_freem(m);
  270                 KMOD_IPSTAT_INC(ips_nogif);
  271                 return (IPPROTO_DONE);
  272         }
  273         gifp = GIF2IFP(sc);
  274         if ((gifp->if_flags & IFF_UP) != 0) {
  275                 ip = mtod(m, struct ip *);
  276                 ecn = ip->ip_tos;
  277                 m_adj(m, off);
  278                 gif_input(m, gifp, proto, ecn);
  279         } else {
  280                 m_freem(m);
  281                 KMOD_IPSTAT_INC(ips_nogif);
  282         }
  283         return (IPPROTO_DONE);
  284 }
  285 
  286 static int
  287 in_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
  288 {
  289         const struct ip *ip;
  290         struct gif_softc *sc;
  291         int ret;
  292 
  293         if (V_ipv4_hashtbl == NULL)
  294                 return (0);
  295 
  296         MPASS(in_epoch(net_epoch_preempt));
  297         ip = mtod(m, const struct ip *);
  298         /*
  299          * NOTE: it is safe to iterate without any locking here, because softc
  300          * can be reclaimed only when we are not within net_epoch_preempt
  301          * section, but ip_encap lookup+input are executed in epoch section.
  302          */
  303         ret = 0;
  304         CK_LIST_FOREACH(sc, &GIF_HASH(ip->ip_dst.s_addr,
  305             ip->ip_src.s_addr), chain) {
  306                 /*
  307                  * This is an inbound packet, its ip_dst is source address
  308                  * in softc.
  309                  */
  310                 if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr &&
  311                     sc->gif_iphdr->ip_dst.s_addr == ip->ip_src.s_addr) {
  312                         ret = ENCAP_DRV_LOOKUP;
  313                         goto done;
  314                 }
  315         }
  316         /*
  317          * No exact match.
  318          * Check the list of interfaces with GIF_IGNORE_SOURCE flag.
  319          */
  320         CK_LIST_FOREACH(sc, &V_ipv4_list, chain) {
  321                 if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr) {
  322                         ret = 32 + 8; /* src + proto */
  323                         goto done;
  324                 }
  325         }
  326         return (0);
  327 done:
  328         if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
  329                 return (0);
  330         /* ingress filters on outer source */
  331         if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
  332                 struct nhop4_basic nh4;
  333                 struct in_addr dst;
  334 
  335                 dst = ip->ip_src;
  336                 if (fib4_lookup_nh_basic(sc->gif_fibnum, dst, 0, 0, &nh4) != 0)
  337                         return (0);
  338                 if (nh4.nh_ifp != m->m_pkthdr.rcvif)
  339                         return (0);
  340         }
  341         *arg = sc;
  342         return (ret);
  343 }
  344 
  345 static struct {
  346         const struct encap_config encap;
  347         const struct encaptab *cookie;
  348 } ipv4_encap_cfg[] = {
  349         {
  350                 .encap = {
  351                         .proto = IPPROTO_IPV4,
  352                         .min_length = 2 * sizeof(struct ip),
  353                         .exact_match = ENCAP_DRV_LOOKUP,
  354                         .lookup = in_gif_lookup,
  355                         .input = in_gif_input
  356                 },
  357         },
  358 #ifdef INET6
  359         {
  360                 .encap = {
  361                         .proto = IPPROTO_IPV6,
  362                         .min_length = sizeof(struct ip) +
  363                             sizeof(struct ip6_hdr),
  364                         .exact_match = ENCAP_DRV_LOOKUP,
  365                         .lookup = in_gif_lookup,
  366                         .input = in_gif_input
  367                 },
  368         },
  369 #endif
  370         {
  371                 .encap = {
  372                         .proto = IPPROTO_ETHERIP,
  373                         .min_length = sizeof(struct ip) +
  374                             sizeof(struct etherip_header) +
  375                             sizeof(struct ether_header),
  376                         .exact_match = ENCAP_DRV_LOOKUP,
  377                         .lookup = in_gif_lookup,
  378                         .input = in_gif_input
  379                 },
  380         }
  381 };
  382 
  383 void
  384 in_gif_init(void)
  385 {
  386         int i;
  387 
  388         if (!IS_DEFAULT_VNET(curvnet))
  389                 return;
  390         for (i = 0; i < nitems(ipv4_encap_cfg); i++)
  391                 ipv4_encap_cfg[i].cookie = ip_encap_attach(
  392                     &ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
  393 }
  394 
  395 void
  396 in_gif_uninit(void)
  397 {
  398         int i;
  399 
  400         if (IS_DEFAULT_VNET(curvnet)) {
  401                 for (i = 0; i < nitems(ipv4_encap_cfg); i++)
  402                         ip_encap_detach(ipv4_encap_cfg[i].cookie);
  403         }
  404         if (V_ipv4_hashtbl != NULL)
  405                 gif_hashdestroy(V_ipv4_hashtbl);
  406 }
  407 

Cache object: 6c9251fb3e6173c276a4397bcd524546


[ 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.