Author: ae
Date: Sun Nov 18 00:17:06 2018
New Revision: 340535
URL: https://svnweb.freebsd.org/changeset/base/340535

Log:
  MFC r339551:
    Add handling for appearing/disappearing of ingress addresses to if_gif(4).
  
    * register handler for ingress address appearing/disappearing;
    * add new srcaddr hash table for fast softc lookup by srcaddr;
    * when srcaddr disappears, clear IFF_DRV_RUNNING flag from interface,
      and set it otherwise;
    * remove the note about ingress address from BUGS section.
  
    Sponsored by:       Yandex LLC
    Differential Revision:      https://reviews.freebsd.org/D17134
  
  MFC r339552:
    Add handling for appearing/disappearing of ingress addresses to if_gre(4).
  
    * register handler for ingress address appearing/disappearing;
    * add new srcaddr hash table for fast softc lookup by srcaddr;
    * when srcaddr disappears, clear IFF_DRV_RUNNING flag from interface,
      and set it otherwise;
  
    Sponsored by:       Yandex LLC
    Differential Revision:      https://reviews.freebsd.org/D17214
  
  MFC r339553:
    Add handling for appearing/disappearing of ingress addresses to if_me(4).
  
    * register handler for ingress address appearing/disappearing;
    * add new srcaddr hash table for fast softc lookup by srcaddr;
    * when srcaddr disappears, clear IFF_DRV_RUNNING flag from interface,
      and set it otherwise;
  
    Sponsored by:       Yandex LLC
  
  MFC r339649:
    Add the check that current VNET is ready and access to srchash is allowed.
  
    This change is similar to r339646. The callback that checks for appearing
    and disappearing of tunnel ingress address can be called during VNET
    teardown. To prevent access to already freed memory, add check to the
    callback and epoch_wait() call to be sure that callback has finished its
    work.

Modified:
  stable/12/share/man/man4/gif.4
  stable/12/sys/net/if_gif.c
  stable/12/sys/net/if_gif.h
  stable/12/sys/net/if_gre.c
  stable/12/sys/net/if_gre.h
  stable/12/sys/net/if_me.c
  stable/12/sys/netinet/in_gif.c
  stable/12/sys/netinet/ip_gre.c
  stable/12/sys/netinet6/in6_gif.c
  stable/12/sys/netinet6/ip6_gre.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/share/man/man4/gif.4
==============================================================================
--- stable/12/share/man/man4/gif.4      Sun Nov 18 00:11:19 2018        
(r340534)
+++ stable/12/share/man/man4/gif.4      Sun Nov 18 00:17:06 2018        
(r340535)
@@ -29,7 +29,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 5, 2018
+.Dd October 21, 2018
 .Dt GIF 4
 .Os
 .Sh NAME
@@ -206,15 +206,6 @@ and are picky about outer header fields.
 For example, you cannot usually use
 .Nm
 to talk with IPsec devices that use IPsec tunnel mode.
-.Pp
-The current code does not check if the ingress address
-(outer source address)
-configured in the
-.Nm
-interface makes sense.
-Make sure to specify an address which belongs to your node.
-Otherwise, your node will not be able to receive packets from the peer,
-and it will generate packets with a spoofed source address.
 .Pp
 If the outer protocol is IPv4,
 .Nm

Modified: stable/12/sys/net/if_gif.c
==============================================================================
--- stable/12/sys/net/if_gif.c  Sun Nov 18 00:11:19 2018        (r340534)
+++ stable/12/sys/net/if_gif.c  Sun Nov 18 00:17:06 2018        (r340535)
@@ -284,6 +284,7 @@ gif_transmit(struct ifnet *ifp, struct mbuf *m)
        sc = ifp->if_softc;
        if ((ifp->if_flags & IFF_MONITOR) != 0 ||
            (ifp->if_flags & IFF_UP) == 0 ||
+           (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
            sc->gif_family == 0 ||
            (error = if_tunnel_check_nesting(ifp, m, MTAG_GIF,
                V_max_gif_nesting)) != 0) {
@@ -674,7 +675,6 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                    cmd == SIOCSIFPHYADDR_IN6 ||
 #endif
                    0) {
-                       ifp->if_drv_flags |= IFF_DRV_RUNNING;
                        if_link_state_change(ifp, LINK_STATE_UP);
                }
        }
@@ -689,6 +689,7 @@ gif_delete_tunnel(struct gif_softc *sc)
 
        sx_assert(&gif_ioctl_sx, SA_XLOCKED);
        if (sc->gif_family != 0) {
+               CK_LIST_REMOVE(sc, srchash);
                CK_LIST_REMOVE(sc, chain);
                /* Wait until it become safe to free gif_hdr */
                GIF_WAIT();

Modified: stable/12/sys/net/if_gif.h
==============================================================================
--- stable/12/sys/net/if_gif.h  Sun Nov 18 00:11:19 2018        (r340534)
+++ stable/12/sys/net/if_gif.h  Sun Nov 18 00:17:06 2018        (r340535)
@@ -63,6 +63,7 @@ struct gif_softc {
        } gif_uhdr;
 
        CK_LIST_ENTRY(gif_softc) chain;
+       CK_LIST_ENTRY(gif_softc) srchash;
 };
 CK_LIST_HEAD(gif_list, gif_softc);
 MALLOC_DECLARE(M_GIF);

Modified: stable/12/sys/net/if_gre.c
==============================================================================
--- stable/12/sys/net/if_gre.c  Sun Nov 18 00:11:19 2018        (r340534)
+++ stable/12/sys/net/if_gre.c  Sun Nov 18 00:17:06 2018        (r340535)
@@ -326,7 +326,6 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
                    cmd == SIOCSIFPHYADDR_IN6 ||
 #endif
                    0) {
-                       ifp->if_drv_flags |= IFF_DRV_RUNNING;
                        if_link_state_change(ifp, LINK_STATE_UP);
                }
        }
@@ -342,6 +341,7 @@ gre_delete_tunnel(struct gre_softc *sc)
        sx_assert(&gre_ioctl_sx, SA_XLOCKED);
        if (sc->gre_family != 0) {
                CK_LIST_REMOVE(sc, chain);
+               CK_LIST_REMOVE(sc, srchash);
                GRE_WAIT();
                free(sc->gre_hdr, M_GRE);
                sc->gre_family = 0;
@@ -543,6 +543,7 @@ gre_setseqn(struct grehdr *gh, uint32_t seq)
 static int
 gre_transmit(struct ifnet *ifp, struct mbuf *m)
 {
+       GRE_RLOCK_TRACKER;
        struct gre_softc *sc;
        struct grehdr *gh;
        uint32_t af;
@@ -562,6 +563,7 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m)
        sc = ifp->if_softc;
        if ((ifp->if_flags & IFF_MONITOR) != 0 ||
            (ifp->if_flags & IFF_UP) == 0 ||
+           (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
            sc->gre_family == 0 ||
            (error = if_tunnel_check_nesting(ifp, m, MTAG_GRE,
                V_max_gre_nesting)) != 0) {

Modified: stable/12/sys/net/if_gre.h
==============================================================================
--- stable/12/sys/net/if_gre.h  Sun Nov 18 00:11:19 2018        (r340534)
+++ stable/12/sys/net/if_gre.h  Sun Nov 18 00:17:06 2018        (r340535)
@@ -82,6 +82,7 @@ struct gre_softc {
        } gre_uhdr;
 
        CK_LIST_ENTRY(gre_softc) chain;
+       CK_LIST_ENTRY(gre_softc) srchash;
 };
 CK_LIST_HEAD(gre_list, gre_softc);
 MALLOC_DECLARE(M_GRE);
@@ -91,7 +92,8 @@ MALLOC_DECLARE(M_GRE);
 #endif
 
 #define        GRE2IFP(sc)             ((sc)->gre_ifp)
-#define        GRE_RLOCK()             struct epoch_tracker gre_et; 
epoch_enter_preempt(net_epoch_preempt, &gre_et)
+#define        GRE_RLOCK_TRACKER       struct epoch_tracker gre_et
+#define        GRE_RLOCK()             epoch_enter_preempt(net_epoch_preempt, 
&gre_et)
 #define        GRE_RUNLOCK()           epoch_exit_preempt(net_epoch_preempt, 
&gre_et)
 #define        GRE_WAIT()              epoch_wait_preempt(net_epoch_preempt)
 

Modified: stable/12/sys/net/if_me.c
==============================================================================
--- stable/12/sys/net/if_me.c   Sun Nov 18 00:11:19 2018        (r340534)
+++ stable/12/sys/net/if_me.c   Sun Nov 18 00:17:06 2018        (r340535)
@@ -83,11 +83,13 @@ struct me_softc {
        struct in_addr          me_dst;
 
        CK_LIST_ENTRY(me_softc) chain;
+       CK_LIST_ENTRY(me_softc) srchash;
 };
 CK_LIST_HEAD(me_list, me_softc);
 #define        ME2IFP(sc)              ((sc)->me_ifp)
 #define        ME_READY(sc)            ((sc)->me_src.s_addr != 0)
-#define        ME_RLOCK()              struct epoch_tracker me_et; 
epoch_enter_preempt(net_epoch_preempt, &me_et)
+#define        ME_RLOCK_TRACKER        struct epoch_tracker me_et
+#define        ME_RLOCK()              epoch_enter_preempt(net_epoch_preempt, 
&me_et)
 #define        ME_RUNLOCK()            epoch_exit_preempt(net_epoch_preempt, 
&me_et)
 #define        ME_WAIT()               epoch_wait_preempt(net_epoch_preempt)
 
@@ -95,9 +97,13 @@ CK_LIST_HEAD(me_list, me_softc);
 #define        ME_HASH_SIZE    (1 << 4)
 #endif
 VNET_DEFINE_STATIC(struct me_list *, me_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct me_list *, me_srchashtbl) = NULL;
 #define        V_me_hashtbl            VNET(me_hashtbl)
+#define        V_me_srchashtbl         VNET(me_srchashtbl)
 #define        ME_HASH(src, dst)       (V_me_hashtbl[\
     me_hashval((src), (dst)) & (ME_HASH_SIZE - 1)])
+#define        ME_SRCHASH(src)         (V_me_srchashtbl[\
+    fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (ME_HASH_SIZE - 1)])
 
 static struct sx me_ioctl_sx;
 SX_SYSINIT(me_ioctl_sx, &me_ioctl_sx, "me_ioctl");
@@ -155,6 +161,7 @@ me_hashinit(void)
 static void
 vnet_me_init(const void *unused __unused)
 {
+
        V_me_cloner = if_clone_simple(mename, me_clone_create,
            me_clone_destroy, 0);
 }
@@ -165,8 +172,12 @@ static void
 vnet_me_uninit(const void *unused __unused)
 {
 
-       if (V_me_hashtbl != NULL)
+       if (V_me_hashtbl != NULL) {
                free(V_me_hashtbl, M_IFME);
+               V_me_hashtbl = NULL;
+               ME_WAIT();
+               free(V_me_srchashtbl, M_IFME);
+       }
        if_clone_detach(V_me_cloner);
 }
 VNET_SYSUNINIT(vnet_me_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
@@ -330,6 +341,44 @@ me_lookup(const struct mbuf *m, int off, int proto, vo
        return (0);
 }
 
+/*
+ * Check that ingress address belongs to local host.
+ */
+static void
+me_set_running(struct me_softc *sc)
+{
+
+       if (in_localip(sc->me_src))
+               ME2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+       else
+               ME2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+/*
+ * ifaddr_event handler.
+ * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
+ * source address spoofing.
+ */
+static void
+me_srcaddr(void *arg __unused, const struct sockaddr *sa,
+    int event __unused)
+{
+       const struct sockaddr_in *sin;
+       struct me_softc *sc;
+
+       /* Check that VNET is ready */
+       if (V_me_hashtbl == NULL)
+               return;
+
+       MPASS(in_epoch(net_epoch_preempt));
+       sin = (const struct sockaddr_in *)sa;
+       CK_LIST_FOREACH(sc, &ME_SRCHASH(sin->sin_addr.s_addr), srchash) {
+               if (sc->me_src.s_addr != sin->sin_addr.s_addr)
+                       continue;
+               me_set_running(sc);
+       }
+}
+
 static int
 me_set_tunnel(struct me_softc *sc, in_addr_t src, in_addr_t dst)
 {
@@ -337,8 +386,10 @@ me_set_tunnel(struct me_softc *sc, in_addr_t src, in_a
 
        sx_assert(&me_ioctl_sx, SA_XLOCKED);
 
-       if (V_me_hashtbl == NULL)
+       if (V_me_hashtbl == NULL) {
                V_me_hashtbl = me_hashinit();
+               V_me_srchashtbl = me_hashinit();
+       }
 
        if (sc->me_src.s_addr == src && sc->me_dst.s_addr == dst)
                return (0);
@@ -355,8 +406,9 @@ me_set_tunnel(struct me_softc *sc, in_addr_t src, in_a
        sc->me_dst.s_addr = dst;
        sc->me_src.s_addr = src;
        CK_LIST_INSERT_HEAD(&ME_HASH(src, dst), sc, chain);
+       CK_LIST_INSERT_HEAD(&ME_SRCHASH(src), sc, srchash);
 
-       ME2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+       me_set_running(sc);
        if_link_state_change(ME2IFP(sc), LINK_STATE_UP);
        return (0);
 }
@@ -368,6 +420,7 @@ me_delete_tunnel(struct me_softc *sc)
        sx_assert(&me_ioctl_sx, SA_XLOCKED);
        if (ME_READY(sc)) {
                CK_LIST_REMOVE(sc, chain);
+               CK_LIST_REMOVE(sc, srchash);
                ME_WAIT();
 
                sc->me_src.s_addr = 0;
@@ -473,6 +526,7 @@ me_output(struct ifnet *ifp, struct mbuf *m, const str
 static int
 me_transmit(struct ifnet *ifp, struct mbuf *m)
 {
+       ME_RLOCK_TRACKER;
        struct mobhdr mh;
        struct me_softc *sc;
        struct ip *ip;
@@ -490,6 +544,7 @@ me_transmit(struct ifnet *ifp, struct mbuf *m)
        if (sc == NULL || !ME_READY(sc) ||
            (ifp->if_flags & IFF_MONITOR) != 0 ||
            (ifp->if_flags & IFF_UP) == 0 ||
+           (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
            (error = if_tunnel_check_nesting(ifp, m, MTAG_ME,
                V_max_me_nesting)) != 0) {
                m_freem(m);
@@ -567,6 +622,7 @@ me_qflush(struct ifnet *ifp __unused)
 
 }
 
+static const struct srcaddrtab *me_srcaddrtab = NULL;
 static const struct encaptab *ecookie = NULL;
 static const struct encap_config me_encap_cfg = {
        .proto = IPPROTO_MOBILE,
@@ -583,10 +639,13 @@ memodevent(module_t mod, int type, void *data)
 
        switch (type) {
        case MOD_LOAD:
+               me_srcaddrtab = ip_encap_register_srcaddr(me_srcaddr,
+                   NULL, M_WAITOK);
                ecookie = ip_encap_attach(&me_encap_cfg, NULL, M_WAITOK);
                break;
        case MOD_UNLOAD:
                ip_encap_detach(ecookie);
+               ip_encap_unregister_srcaddr(me_srcaddrtab);
                break;
        default:
                return (EOPNOTSUPP);

Modified: stable/12/sys/netinet/in_gif.c
==============================================================================
--- stable/12/sys/netinet/in_gif.c      Sun Nov 18 00:11:19 2018        
(r340534)
+++ stable/12/sys/netinet/in_gif.c      Sun Nov 18 00:17:06 2018        
(r340535)
@@ -82,12 +82,16 @@ SYSCTL_INT(_net_inet_ip, IPCTL_GIF_TTL, gifttl, CTLFLA
  * Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
  */
 VNET_DEFINE_STATIC(struct gif_list *, ipv4_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gif_list *, ipv4_srchashtbl) = NULL;
 VNET_DEFINE_STATIC(struct gif_list, ipv4_list) = CK_LIST_HEAD_INITIALIZER();
 #define        V_ipv4_hashtbl          VNET(ipv4_hashtbl)
+#define        V_ipv4_srchashtbl       VNET(ipv4_srchashtbl)
 #define        V_ipv4_list             VNET(ipv4_list)
 
 #define        GIF_HASH(src, dst)      (V_ipv4_hashtbl[\
     in_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
+#define        GIF_SRCHASH(src)        (V_ipv4_srchashtbl[\
+    fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
 #define        GIF_HASH_SC(sc)         
GIF_HASH((sc)->gif_iphdr->ip_src.s_addr,\
     (sc)->gif_iphdr->ip_dst.s_addr)
 static uint32_t
@@ -119,7 +123,45 @@ in_gif_checkdup(const struct gif_softc *sc, in_addr_t 
        return (0);
 }
 
+/*
+ * Check that ingress address belongs to local host.
+ */
 static void
+in_gif_set_running(struct gif_softc *sc)
+{
+
+       if (in_localip(sc->gif_iphdr->ip_src))
+               GIF2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+       else
+               GIF2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+/*
+ * ifaddr_event handler.
+ * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
+ * source address spoofing.
+ */
+static void
+in_gif_srcaddr(void *arg __unused, const struct sockaddr *sa,
+    int event __unused)
+{
+       const struct sockaddr_in *sin;
+       struct gif_softc *sc;
+
+       /* Check that VNET is ready */
+       if (V_ipv4_hashtbl == NULL)
+               return;
+
+       MPASS(in_epoch(net_epoch_preempt));
+       sin = (const struct sockaddr_in *)sa;
+       CK_LIST_FOREACH(sc, &GIF_SRCHASH(sin->sin_addr.s_addr), srchash) {
+               if (sc->gif_iphdr->ip_src.s_addr != sin->sin_addr.s_addr)
+                       continue;
+               in_gif_set_running(sc);
+       }
+}
+
+static void
 in_gif_attach(struct gif_softc *sc)
 {
 
@@ -127,6 +169,9 @@ in_gif_attach(struct gif_softc *sc)
                CK_LIST_INSERT_HEAD(&V_ipv4_list, sc, chain);
        else
                CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
+
+       CK_LIST_INSERT_HEAD(&GIF_SRCHASH(sc->gif_iphdr->ip_src.s_addr),
+           sc, srchash);
 }
 
 int
@@ -139,6 +184,7 @@ in_gif_setopts(struct gif_softc *sc, u_int options)
 
        if ((options & GIF_IGNORE_SOURCE) !=
            (sc->gif_options & GIF_IGNORE_SOURCE)) {
+               CK_LIST_REMOVE(sc, srchash);
                CK_LIST_REMOVE(sc, chain);
                sc->gif_options = options;
                in_gif_attach(sc);
@@ -172,8 +218,10 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t
                        error = EADDRNOTAVAIL;
                        break;
                }
-               if (V_ipv4_hashtbl == NULL)
+               if (V_ipv4_hashtbl == NULL) {
                        V_ipv4_hashtbl = gif_hashinit();
+                       V_ipv4_srchashtbl = gif_hashinit();
+               }
                error = in_gif_checkdup(sc, src->sin_addr.s_addr,
                    dst->sin_addr.s_addr);
                if (error == EADDRNOTAVAIL)
@@ -188,6 +236,7 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t
                ip->ip_dst.s_addr = dst->sin_addr.s_addr;
                if (sc->gif_family != 0) {
                        /* Detach existing tunnel first */
+                       CK_LIST_REMOVE(sc, srchash);
                        CK_LIST_REMOVE(sc, chain);
                        GIF_WAIT();
                        free(sc->gif_hdr, M_GIF);
@@ -196,6 +245,7 @@ in_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_t
                sc->gif_family = AF_INET;
                sc->gif_iphdr = ip;
                in_gif_attach(sc);
+               in_gif_set_running(sc);
                break;
        case SIOCGIFPSRCADDR:
        case SIOCGIFPDSTADDR:
@@ -342,6 +392,7 @@ done:
        return (ret);
 }
 
+static const struct srcaddrtab *ipv4_srcaddrtab;
 static struct {
        const struct encap_config encap;
        const struct encaptab *cookie;
@@ -387,6 +438,9 @@ in_gif_init(void)
 
        if (!IS_DEFAULT_VNET(curvnet))
                return;
+
+       ipv4_srcaddrtab = ip_encap_register_srcaddr(in_gif_srcaddr,
+           NULL, M_WAITOK);
        for (i = 0; i < nitems(ipv4_encap_cfg); i++)
                ipv4_encap_cfg[i].cookie = ip_encap_attach(
                    &ipv4_encap_cfg[i].encap, NULL, M_WAITOK);
@@ -400,8 +454,13 @@ in_gif_uninit(void)
        if (IS_DEFAULT_VNET(curvnet)) {
                for (i = 0; i < nitems(ipv4_encap_cfg); i++)
                        ip_encap_detach(ipv4_encap_cfg[i].cookie);
+               ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
        }
-       if (V_ipv4_hashtbl != NULL)
+       if (V_ipv4_hashtbl != NULL) {
                gif_hashdestroy(V_ipv4_hashtbl);
+               V_ipv4_hashtbl = NULL;
+               GIF_WAIT();
+               gif_hashdestroy(V_ipv4_srchashtbl);
+       }
 }
 

Modified: stable/12/sys/netinet/ip_gre.c
==============================================================================
--- stable/12/sys/netinet/ip_gre.c      Sun Nov 18 00:11:19 2018        
(r340534)
+++ stable/12/sys/netinet/ip_gre.c      Sun Nov 18 00:17:06 2018        
(r340535)
@@ -75,9 +75,13 @@ SYSCTL_INT(_net_inet_ip, OID_AUTO, grettl, CTLFLAG_VNE
     &VNET_NAME(ip_gre_ttl), 0, "Default TTL value for encapsulated packets");
 
 VNET_DEFINE_STATIC(struct gre_list *, ipv4_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gre_list *, ipv4_srchashtbl) = NULL;
 #define        V_ipv4_hashtbl          VNET(ipv4_hashtbl)
+#define        V_ipv4_srchashtbl       VNET(ipv4_srchashtbl)
 #define        GRE_HASH(src, dst)      (V_ipv4_hashtbl[\
     in_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)])
+#define        GRE_SRCHASH(src)        (V_ipv4_srchashtbl[\
+    fnv_32_buf(&(src), sizeof(src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
 #define        GRE_HASH_SC(sc)         GRE_HASH((sc)->gre_oip.ip_src.s_addr,\
     (sc)->gre_oip.ip_dst.s_addr)
 
@@ -138,7 +142,45 @@ in_gre_lookup(const struct mbuf *m, int off, int proto
        return (0);
 }
 
+/*
+ * Check that ingress address belongs to local host.
+ */
 static void
+in_gre_set_running(struct gre_softc *sc)
+{
+
+       if (in_localip(sc->gre_oip.ip_src))
+               GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+       else
+               GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+/*
+ * ifaddr_event handler.
+ * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
+ * source address spoofing.
+ */
+static void
+in_gre_srcaddr(void *arg __unused, const struct sockaddr *sa,
+    int event __unused)
+{
+       const struct sockaddr_in *sin;
+       struct gre_softc *sc;
+
+       /* Check that VNET is ready */
+       if (V_ipv4_hashtbl == NULL)
+               return;
+
+       MPASS(in_epoch(net_epoch_preempt));
+       sin = (const struct sockaddr_in *)sa;
+       CK_LIST_FOREACH(sc, &GRE_SRCHASH(sin->sin_addr.s_addr), srchash) {
+               if (sc->gre_oip.ip_src.s_addr != sin->sin_addr.s_addr)
+                       continue;
+               in_gre_set_running(sc);
+       }
+}
+
+static void
 in_gre_attach(struct gre_softc *sc)
 {
 
@@ -148,6 +190,8 @@ in_gre_attach(struct gre_softc *sc)
        sc->gre_oip.ip_p = IPPROTO_GRE;
        gre_updatehdr(sc, &sc->gre_gihdr->gi_gre);
        CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain);
+       CK_LIST_INSERT_HEAD(&GRE_SRCHASH(sc->gre_oip.ip_src.s_addr),
+           sc, srchash);
 }
 
 void
@@ -159,6 +203,7 @@ in_gre_setopts(struct gre_softc *sc, u_long cmd, uint3
        /* NOTE: we are protected with gre_ioctl_sx lock */
        MPASS(sc->gre_family == AF_INET);
        CK_LIST_REMOVE(sc, chain);
+       CK_LIST_REMOVE(sc, srchash);
        GRE_WAIT();
        if (cmd == GRESKEY)
                sc->gre_key = value;
@@ -193,8 +238,10 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t
                        error = EADDRNOTAVAIL;
                        break;
                }
-               if (V_ipv4_hashtbl == NULL)
+               if (V_ipv4_hashtbl == NULL) {
                        V_ipv4_hashtbl = gre_hashinit();
+                       V_ipv4_srchashtbl = gre_hashinit();
+               }
                error = in_gre_checkdup(sc, src->sin_addr.s_addr,
                    dst->sin_addr.s_addr);
                if (error == EADDRNOTAVAIL)
@@ -211,6 +258,7 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t
                if (sc->gre_family != 0) {
                        /* Detach existing tunnel first */
                        CK_LIST_REMOVE(sc, chain);
+                       CK_LIST_REMOVE(sc, srchash);
                        GRE_WAIT();
                        free(sc->gre_hdr, M_GRE);
                        /* XXX: should we notify about link state change? */
@@ -220,6 +268,7 @@ in_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_t
                sc->gre_oseq = 0;
                sc->gre_iseq = UINT32_MAX;
                in_gre_attach(sc);
+               in_gre_set_running(sc);
                break;
        case SIOCGIFPSRCADDR:
        case SIOCGIFPDSTADDR:
@@ -271,6 +320,7 @@ in_gre_output(struct mbuf *m, int af, int hlen)
        return (ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL));
 }
 
+static const struct srcaddrtab *ipv4_srcaddrtab = NULL;
 static const struct encaptab *ecookie = NULL;
 static const struct encap_config ipv4_encap_cfg = {
        .proto = IPPROTO_GRE,
@@ -286,6 +336,8 @@ in_gre_init(void)
 
        if (!IS_DEFAULT_VNET(curvnet))
                return;
+       ipv4_srcaddrtab = ip_encap_register_srcaddr(in_gre_srcaddr,
+           NULL, M_WAITOK);
        ecookie = ip_encap_attach(&ipv4_encap_cfg, NULL, M_WAITOK);
 }
 
@@ -293,8 +345,14 @@ void
 in_gre_uninit(void)
 {
 
-       if (IS_DEFAULT_VNET(curvnet))
+       if (IS_DEFAULT_VNET(curvnet)) {
                ip_encap_detach(ecookie);
-       if (V_ipv4_hashtbl != NULL)
+               ip_encap_unregister_srcaddr(ipv4_srcaddrtab);
+       }
+       if (V_ipv4_hashtbl != NULL) {
                gre_hashdestroy(V_ipv4_hashtbl);
+               V_ipv4_hashtbl = NULL;
+               GRE_WAIT();
+               gre_hashdestroy(V_ipv4_srchashtbl);
+       }
 }

Modified: stable/12/sys/netinet6/in6_gif.c
==============================================================================
--- stable/12/sys/netinet6/in6_gif.c    Sun Nov 18 00:11:19 2018        
(r340534)
+++ stable/12/sys/netinet6/in6_gif.c    Sun Nov 18 00:17:06 2018        
(r340535)
@@ -87,12 +87,16 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim,
  * Interfaces with GIF_IGNORE_SOURCE flag are linked into plain list.
  */
 VNET_DEFINE_STATIC(struct gif_list *, ipv6_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gif_list *, ipv6_srchashtbl) = NULL;
 VNET_DEFINE_STATIC(struct gif_list, ipv6_list) = CK_LIST_HEAD_INITIALIZER();
 #define        V_ipv6_hashtbl          VNET(ipv6_hashtbl)
+#define        V_ipv6_srchashtbl       VNET(ipv6_srchashtbl)
 #define        V_ipv6_list             VNET(ipv6_list)
 
 #define        GIF_HASH(src, dst)      (V_ipv6_hashtbl[\
     in6_gif_hashval((src), (dst)) & (GIF_HASH_SIZE - 1)])
+#define        GIF_SRCHASH(src)        (V_ipv6_srchashtbl[\
+    fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GIF_HASH_SIZE - 1)])
 #define        GIF_HASH_SC(sc)         GIF_HASH(&(sc)->gif_ip6hdr->ip6_src,\
     &(sc)->gif_ip6hdr->ip6_dst)
 static uint32_t
@@ -125,7 +129,45 @@ in6_gif_checkdup(const struct gif_softc *sc, const str
        return (0);
 }
 
+/*
+ * Check that ingress address belongs to local host.
+ */
 static void
+in6_gif_set_running(struct gif_softc *sc)
+{
+
+       if (in6_localip(&sc->gif_ip6hdr->ip6_src))
+               GIF2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+       else
+               GIF2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+/*
+ * ifaddr_event handler.
+ * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
+ * source address spoofing.
+ */
+static void
+in6_gif_srcaddr(void *arg __unused, const struct sockaddr *sa, int event)
+{
+       const struct sockaddr_in6 *sin;
+       struct gif_softc *sc;
+
+       /* Check that VNET is ready */
+       if (V_ipv6_hashtbl == NULL)
+               return;
+
+       MPASS(in_epoch(net_epoch_preempt));
+       sin = (const struct sockaddr_in6 *)sa;
+       CK_LIST_FOREACH(sc, &GIF_SRCHASH(&sin->sin6_addr), srchash) {
+               if (IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_src,
+                   &sin->sin6_addr) == 0)
+                       continue;
+               in6_gif_set_running(sc);
+       }
+}
+
+static void
 in6_gif_attach(struct gif_softc *sc)
 {
 
@@ -133,6 +175,9 @@ in6_gif_attach(struct gif_softc *sc)
                CK_LIST_INSERT_HEAD(&V_ipv6_list, sc, chain);
        else
                CK_LIST_INSERT_HEAD(&GIF_HASH_SC(sc), sc, chain);
+
+       CK_LIST_INSERT_HEAD(&GIF_SRCHASH(&sc->gif_ip6hdr->ip6_src),
+           sc, srchash);
 }
 
 int
@@ -145,6 +190,7 @@ in6_gif_setopts(struct gif_softc *sc, u_int options)
 
        if ((options & GIF_IGNORE_SOURCE) !=
            (sc->gif_options & GIF_IGNORE_SOURCE)) {
+               CK_LIST_REMOVE(sc, srchash);
                CK_LIST_REMOVE(sc, chain);
                sc->gif_options = options;
                in6_gif_attach(sc);
@@ -187,8 +233,10 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_
                    (error = sa6_embedscope(dst, 0)) != 0)
                        break;
 
-               if (V_ipv6_hashtbl == NULL)
+               if (V_ipv6_hashtbl == NULL) {
                        V_ipv6_hashtbl = gif_hashinit();
+                       V_ipv6_srchashtbl = gif_hashinit();
+               }
                error = in6_gif_checkdup(sc, &src->sin6_addr,
                    &dst->sin6_addr);
                if (error == EADDRNOTAVAIL)
@@ -204,6 +252,7 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_
                ip6->ip6_vfc = IPV6_VERSION;
                if (sc->gif_family != 0) {
                        /* Detach existing tunnel first */
+                       CK_LIST_REMOVE(sc, srchash);
                        CK_LIST_REMOVE(sc, chain);
                        GIF_WAIT();
                        free(sc->gif_hdr, M_GIF);
@@ -212,6 +261,7 @@ in6_gif_ioctl(struct gif_softc *sc, u_long cmd, caddr_
                sc->gif_family = AF_INET6;
                sc->gif_ip6hdr = ip6;
                in6_gif_attach(sc);
+               in6_gif_set_running(sc);
                break;
        case SIOCGIFPSRCADDR_IN6:
        case SIOCGIFPDSTADDR_IN6:
@@ -365,6 +415,7 @@ done:
        return (ret);
 }
 
+static const struct srcaddrtab *ipv6_srcaddrtab;
 static struct {
        const struct encap_config encap;
        const struct encaptab *cookie;
@@ -410,6 +461,9 @@ in6_gif_init(void)
 
        if (!IS_DEFAULT_VNET(curvnet))
                return;
+
+       ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gif_srcaddr,
+           NULL, M_WAITOK);
        for (i = 0; i < nitems(ipv6_encap_cfg); i++)
                ipv6_encap_cfg[i].cookie = ip6_encap_attach(
                    &ipv6_encap_cfg[i].encap, NULL, M_WAITOK);
@@ -423,7 +477,12 @@ in6_gif_uninit(void)
        if (IS_DEFAULT_VNET(curvnet)) {
                for (i = 0; i < nitems(ipv6_encap_cfg); i++)
                        ip6_encap_detach(ipv6_encap_cfg[i].cookie);
+               ip6_encap_unregister_srcaddr(ipv6_srcaddrtab);
        }
-       if (V_ipv6_hashtbl != NULL)
+       if (V_ipv6_hashtbl != NULL) {
                gif_hashdestroy(V_ipv6_hashtbl);
+               V_ipv6_hashtbl = NULL;
+               GIF_WAIT();
+               gif_hashdestroy(V_ipv6_srchashtbl);
+       }
 }

Modified: stable/12/sys/netinet6/ip6_gre.c
==============================================================================
--- stable/12/sys/netinet6/ip6_gre.c    Sun Nov 18 00:11:19 2018        
(r340534)
+++ stable/12/sys/netinet6/ip6_gre.c    Sun Nov 18 00:17:06 2018        
(r340535)
@@ -66,9 +66,13 @@ SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_
     &VNET_NAME(ip6_gre_hlim), 0, "Default hop limit for encapsulated packets");
 
 VNET_DEFINE_STATIC(struct gre_list *, ipv6_hashtbl) = NULL;
+VNET_DEFINE_STATIC(struct gre_list *, ipv6_srchashtbl) = NULL;
 #define        V_ipv6_hashtbl          VNET(ipv6_hashtbl)
+#define        V_ipv6_srchashtbl       VNET(ipv6_srchashtbl)
 #define        GRE_HASH(src, dst)      (V_ipv6_hashtbl[\
     in6_gre_hashval((src), (dst)) & (GRE_HASH_SIZE - 1)])
+#define        GRE_SRCHASH(src)        (V_ipv6_srchashtbl[\
+    fnv_32_buf((src), sizeof(*src), FNV1_32_INIT) & (GRE_HASH_SIZE - 1)])
 #define        GRE_HASH_SC(sc)         GRE_HASH(&(sc)->gre_oip6.ip6_src,\
     &(sc)->gre_oip6.ip6_dst)
 
@@ -131,7 +135,46 @@ in6_gre_lookup(const struct mbuf *m, int off, int prot
        return (0);
 }
 
+/*
+ * Check that ingress address belongs to local host.
+ */
 static void
+in6_gre_set_running(struct gre_softc *sc)
+{
+
+       if (in6_localip(&sc->gre_oip6.ip6_src))
+               GRE2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
+       else
+               GRE2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+/*
+ * ifaddr_event handler.
+ * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
+ * source address spoofing.
+ */
+static void
+in6_gre_srcaddr(void *arg __unused, const struct sockaddr *sa,
+    int event __unused)
+{
+       const struct sockaddr_in6 *sin;
+       struct gre_softc *sc;
+
+       /* Check that VNET is ready */
+       if (V_ipv6_hashtbl == NULL)
+               return;
+
+       MPASS(in_epoch(net_epoch_preempt));
+       sin = (const struct sockaddr_in6 *)sa;
+       CK_LIST_FOREACH(sc, &GRE_SRCHASH(&sin->sin6_addr), srchash) {
+               if (IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src,
+                   &sin->sin6_addr) == 0)
+                       continue;
+               in6_gre_set_running(sc);
+       }
+}
+
+static void
 in6_gre_attach(struct gre_softc *sc)
 {
 
@@ -140,6 +183,7 @@ in6_gre_attach(struct gre_softc *sc)
        sc->gre_oip6.ip6_nxt = IPPROTO_GRE;
        gre_updatehdr(sc, &sc->gre_gi6hdr->gi6_gre);
        CK_LIST_INSERT_HEAD(&GRE_HASH_SC(sc), sc, chain);
+       CK_LIST_INSERT_HEAD(&GRE_SRCHASH(&sc->gre_oip6.ip6_src), sc, srchash);
 }
 
 void
@@ -151,6 +195,7 @@ in6_gre_setopts(struct gre_softc *sc, u_long cmd, uint
        /* NOTE: we are protected with gre_ioctl_sx lock */
        MPASS(sc->gre_family == AF_INET6);
        CK_LIST_REMOVE(sc, chain);
+       CK_LIST_REMOVE(sc, srchash);
        GRE_WAIT();
        if (cmd == GRESKEY)
                sc->gre_key = value;
@@ -194,8 +239,10 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_
                    (error = sa6_embedscope(dst, 0)) != 0)
                        break;
 
-               if (V_ipv6_hashtbl == NULL)
+               if (V_ipv6_hashtbl == NULL) {
                        V_ipv6_hashtbl = gre_hashinit();
+                       V_ipv6_srchashtbl = gre_hashinit();
+               }
                error = in6_gre_checkdup(sc, &src->sin6_addr,
                    &dst->sin6_addr);
                if (error == EADDRNOTAVAIL)
@@ -212,6 +259,7 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_
                if (sc->gre_family != 0) {
                        /* Detach existing tunnel first */
                        CK_LIST_REMOVE(sc, chain);
+                       CK_LIST_REMOVE(sc, srchash);
                        GRE_WAIT();
                        free(sc->gre_hdr, M_GRE);
                        /* XXX: should we notify about link state change? */
@@ -221,6 +269,7 @@ in6_gre_ioctl(struct gre_softc *sc, u_long cmd, caddr_
                sc->gre_oseq = 0;
                sc->gre_iseq = UINT32_MAX;
                in6_gre_attach(sc);
+               in6_gre_set_running(sc);
                break;
        case SIOCGIFPSRCADDR_IN6:
        case SIOCGIFPDSTADDR_IN6:
@@ -254,6 +303,7 @@ in6_gre_output(struct mbuf *m, int af __unused, int hl
        return (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, NULL));
 }
 
+static const struct srcaddrtab *ipv6_srcaddrtab = NULL;
 static const struct encaptab *ecookie = NULL;
 static const struct encap_config ipv6_encap_cfg = {
        .proto = IPPROTO_GRE,
@@ -274,6 +324,8 @@ in6_gre_init(void)
 
        if (!IS_DEFAULT_VNET(curvnet))
                return;
+       ipv6_srcaddrtab = ip6_encap_register_srcaddr(in6_gre_srcaddr,
+           NULL, M_WAITOK);
        ecookie = ip6_encap_attach(&ipv6_encap_cfg, NULL, M_WAITOK);
 }
 
@@ -281,8 +333,14 @@ void
 in6_gre_uninit(void)
 {
 
-       if (IS_DEFAULT_VNET(curvnet))
+       if (IS_DEFAULT_VNET(curvnet)) {
                ip6_encap_detach(ecookie);
-       if (V_ipv6_hashtbl != NULL)
+               ip6_encap_unregister_srcaddr(ipv6_srcaddrtab);
+       }
+       if (V_ipv6_hashtbl != NULL) {
                gre_hashdestroy(V_ipv6_hashtbl);
+               V_ipv6_hashtbl = NULL;
+               GRE_WAIT();
+               gre_hashdestroy(V_ipv6_srchashtbl);
+       }
 }
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to