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         NET_EPOCH_ASSERT();
  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 epoch_tracker et;
  200         struct sockaddr_in *dst, *src;
  201         struct ip *ip;
  202         int error;
  203 
  204         /* NOTE: we are protected with gif_ioctl_sx lock */
  205         error = EINVAL;
  206         switch (cmd) {
  207         case SIOCSIFPHYADDR:
  208                 src = &((struct in_aliasreq *)data)->ifra_addr;
  209                 dst = &((struct in_aliasreq *)data)->ifra_dstaddr;
  210 
  211                 /* sanity checks */
  212                 if (src->sin_family != dst->sin_family ||
  213                     src->sin_family != AF_INET ||
  214                     src->sin_len != dst->sin_len ||
  215                     src->sin_len != sizeof(*src))
  216                         break;
  217                 if (src->sin_addr.s_addr == INADDR_ANY ||
  218                     dst->sin_addr.s_addr == INADDR_ANY) {
  219                         error = EADDRNOTAVAIL;
  220                         break;
  221                 }
  222                 if (V_ipv4_hashtbl == NULL) {
  223                         V_ipv4_hashtbl = gif_hashinit();
  224                         V_ipv4_srchashtbl = gif_hashinit();
  225                 }
  226                 error = in_gif_checkdup(sc, src->sin_addr.s_addr,
  227                     dst->sin_addr.s_addr);
  228                 if (error == EADDRNOTAVAIL)
  229                         break;
  230                 if (error == EEXIST) {
  231                         /* Addresses are the same. Just return. */
  232                         error = 0;
  233                         break;
  234                 }
  235                 ip = malloc(sizeof(*ip), M_GIF, M_WAITOK | M_ZERO);
  236                 ip->ip_src.s_addr = src->sin_addr.s_addr;
  237                 ip->ip_dst.s_addr = dst->sin_addr.s_addr;
  238                 if (sc->gif_family != 0) {
  239                         /* Detach existing tunnel first */
  240                         CK_LIST_REMOVE(sc, srchash);
  241                         CK_LIST_REMOVE(sc, chain);
  242                         GIF_WAIT();
  243                         free(sc->gif_hdr, M_GIF);
  244                         /* XXX: should we notify about link state change? */
  245                 }
  246                 sc->gif_family = AF_INET;
  247                 sc->gif_iphdr = ip;
  248                 in_gif_attach(sc);
  249                 NET_EPOCH_ENTER(et);
  250                 in_gif_set_running(sc);
  251                 NET_EPOCH_EXIT(et);
  252                 break;
  253         case SIOCGIFPSRCADDR:
  254         case SIOCGIFPDSTADDR:
  255                 if (sc->gif_family != AF_INET) {
  256                         error = EADDRNOTAVAIL;
  257                         break;
  258                 }
  259                 src = (struct sockaddr_in *)&ifr->ifr_addr;
  260                 memset(src, 0, sizeof(*src));
  261                 src->sin_family = AF_INET;
  262                 src->sin_len = sizeof(*src);
  263                 src->sin_addr = (cmd == SIOCGIFPSRCADDR) ?
  264                     sc->gif_iphdr->ip_src: sc->gif_iphdr->ip_dst;
  265                 error = prison_if(curthread->td_ucred, (struct sockaddr *)src);
  266                 if (error != 0)
  267                         memset(src, 0, sizeof(*src));
  268                 break;
  269         }
  270         return (error);
  271 }
  272 
  273 int
  274 in_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn)
  275 {
  276         struct gif_softc *sc = ifp->if_softc;
  277         struct ip *ip;
  278         int len;
  279 
  280         /* prepend new IP header */
  281         NET_EPOCH_ASSERT();
  282         len = sizeof(struct ip);
  283 #ifndef __NO_STRICT_ALIGNMENT
  284         if (proto == IPPROTO_ETHERIP)
  285                 len += ETHERIP_ALIGN;
  286 #endif
  287         M_PREPEND(m, len, M_NOWAIT);
  288         if (m == NULL)
  289                 return (ENOBUFS);
  290 #ifndef __NO_STRICT_ALIGNMENT
  291         if (proto == IPPROTO_ETHERIP) {
  292                 len = mtod(m, vm_offset_t) & 3;
  293                 KASSERT(len == 0 || len == ETHERIP_ALIGN,
  294                     ("in_gif_output: unexpected misalignment"));
  295                 m->m_data += len;
  296                 m->m_len -= ETHERIP_ALIGN;
  297         }
  298 #endif
  299         ip = mtod(m, struct ip *);
  300 
  301         MPASS(sc->gif_family == AF_INET);
  302         bcopy(sc->gif_iphdr, ip, sizeof(struct ip));
  303         ip->ip_p = proto;
  304         /* version will be set in ip_output() */
  305         ip->ip_ttl = V_ip_gif_ttl;
  306         ip->ip_len = htons(m->m_pkthdr.len);
  307         ip->ip_tos = ecn;
  308 
  309         return (ip_output(m, NULL, NULL, 0, NULL, NULL));
  310 }
  311 
  312 static int
  313 in_gif_input(struct mbuf *m, int off, int proto, void *arg)
  314 {
  315         struct gif_softc *sc = arg;
  316         struct ifnet *gifp;
  317         struct ip *ip;
  318         uint8_t ecn;
  319 
  320         NET_EPOCH_ASSERT();
  321         if (sc == NULL) {
  322                 m_freem(m);
  323                 KMOD_IPSTAT_INC(ips_nogif);
  324                 return (IPPROTO_DONE);
  325         }
  326         gifp = GIF2IFP(sc);
  327         if ((gifp->if_flags & IFF_UP) != 0) {
  328                 ip = mtod(m, struct ip *);
  329                 ecn = ip->ip_tos;
  330                 m_adj(m, off);
  331                 gif_input(m, gifp, proto, ecn);
  332         } else {
  333                 m_freem(m);
  334                 KMOD_IPSTAT_INC(ips_nogif);
  335         }
  336         return (IPPROTO_DONE);
  337 }
  338 
  339 static int
  340 in_gif_lookup(const struct mbuf *m, int off, int proto, void **arg)
  341 {
  342         const struct ip *ip;
  343         struct gif_softc *sc;
  344         int ret;
  345 
  346         if (V_ipv4_hashtbl == NULL)
  347                 return (0);
  348 
  349         NET_EPOCH_ASSERT();
  350         ip = mtod(m, const struct ip *);
  351         /*
  352          * NOTE: it is safe to iterate without any locking here, because softc
  353          * can be reclaimed only when we are not within net_epoch_preempt
  354          * section, but ip_encap lookup+input are executed in epoch section.
  355          */
  356         ret = 0;
  357         CK_LIST_FOREACH(sc, &GIF_HASH(ip->ip_dst.s_addr,
  358             ip->ip_src.s_addr), chain) {
  359                 /*
  360                  * This is an inbound packet, its ip_dst is source address
  361                  * in softc.
  362                  */
  363                 if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr &&
  364                     sc->gif_iphdr->ip_dst.s_addr == ip->ip_src.s_addr) {
  365                         ret = ENCAP_DRV_LOOKUP;
  366                         goto done;
  367                 }
  368         }
  369         /*
  370          * No exact match.
  371          * Check the list of interfaces with GIF_IGNORE_SOURCE flag.
  372          */
  373         CK_LIST_FOREACH(sc, &V_ipv4_list, chain) {
  374                 if (sc->gif_iphdr->ip_src.s_addr == ip->ip_dst.s_addr) {
  375                         ret = 32 + 8; /* src + proto */
  376                         goto done;
  377                 }
  378         }
  379         return (0);
  380 done:
  381         if ((GIF2IFP(sc)->if_flags & IFF_UP) == 0)
  382                 return (0);
  383         /* ingress filters on outer source */
  384         if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0) {
  385                 if (fib4_check_urpf(sc->gif_fibnum, ip->ip_src, 0, NHR_NONE,
  386                                         m->m_pkthdr.rcvif) == 0)
  387                         return (0);
  388         }
  389         *arg = sc;
  390         return (ret);
  391 }
  392 
  393 static const struct srcaddrtab *ipv4_srcaddrtab;
  394 static struct {
  395         const struct encap_config encap;
  396         const struct encaptab *cookie;
  397 } ipv4_encap_cfg[] = {
  398         {
  399                 .encap = {
  400                         .proto = IPPROTO_IPV4,
  401                         .min_length = 2 * sizeof(struct ip),
  402                         .exact_match = ENCAP_DRV_LOOKUP,
  403                         .lookup = in_gif_lookup,
  404                         .input = in_gif_input
  405                 },
  406         },
  407 #ifdef INET6
  408         {
  409                 .encap = {
  410                         .proto = IPPROTO_IPV6,
  411                         .min_length = sizeof(struct ip) +
  412                             sizeof(struct ip6_hdr),
  413                         .exact_match = ENCAP_DRV_LOOKUP,
  414                         .lookup = in_gif_lookup,
  415                         .input = in_gif_input
  416                 },
  417         },
  418 #endif
  419         {
  420                 .encap = {
  421                         .proto = IPPROTO_ETHERIP,
  422                         .min_length = sizeof(struct ip) +
  423                             sizeof(struct etherip_header) +
  424                             sizeof(struct ether_header),
  425                         .exact_match = ENCAP_DRV_LOOKUP,
  426                         .lookup = in_gif_lookup,
  427                         .input = in_gif_input
  428                 },
  429         }
  430 };
  431 
  432 void
  433 in_gif_init(void)
  434 {
  435         int i;
  436 
  437         if (!IS_DEFAULT_VNET(curvnet))
  438                 return;
  439 
  440         ipv4_srcaddrtab = ip_encap_register_srcaddr(in_gif_srcaddr,
  441             NULL, M_WAITOK);
  442         for (i = 0; i < nitems(ipv4_encap_cfg); i++)
  443                 ipv4_encap_cfg[i].cookie = ip_encap_attach(
  444                     &ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
  445 }
  446 
  447 void
  448 in_gif_uninit(void)
  449 {
  450         int i;
  451 
  452         if (IS_DEFAULT_VNET(curvnet)) {
  453                 for (i = 0; i < nitems(ipv4_encap_cfg); i++)
  454                         ip_encap_detach(ipv4_encap_cfg[i].cookie);
  455                 ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
  456         }
  457         if (V_ipv4_hashtbl != NULL) {
  458                 gif_hashdestroy(V_ipv4_hashtbl);
  459                 V_ipv4_hashtbl = NULL;
  460                 GIF_WAIT();
  461                 gif_hashdestroy(V_ipv4_srchashtbl);
  462         }
  463 }

Cache object: bae0ca8ab7665678dff9b4f3028142b1


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