Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 18 Nov 2018 00:17:06 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r340535 - in stable/12: share/man/man4 sys/net sys/netinet sys/netinet6
Message-ID:  <201811180017.wAI0H6cK083038@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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);
+	}
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811180017.wAI0H6cK083038>