Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 21 Oct 2018 18:13:45 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r339552 - in head/sys: net netinet netinet6
Message-ID:  <201810211813.w9LIDjW1088848@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Sun Oct 21 18:13:45 2018
New Revision: 339552
URL: https://svnweb.freebsd.org/changeset/base/339552

Log:
  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;
  
  MFC after:	1 month
  Sponsored by:	Yandex LLC
  Differential Revision:	https://reviews.freebsd.org/D17214

Modified:
  head/sys/net/if_gre.c
  head/sys/net/if_gre.h
  head/sys/netinet/ip_gre.c
  head/sys/netinet6/ip6_gre.c

Modified: head/sys/net/if_gre.c
==============================================================================
--- head/sys/net/if_gre.c	Sun Oct 21 18:06:15 2018	(r339551)
+++ head/sys/net/if_gre.c	Sun Oct 21 18:13:45 2018	(r339552)
@@ -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: head/sys/net/if_gre.h
==============================================================================
--- head/sys/net/if_gre.h	Sun Oct 21 18:06:15 2018	(r339551)
+++ head/sys/net/if_gre.h	Sun Oct 21 18:13:45 2018	(r339552)
@@ -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: head/sys/netinet/ip_gre.c
==============================================================================
--- head/sys/netinet/ip_gre.c	Sun Oct 21 18:06:15 2018	(r339551)
+++ head/sys/netinet/ip_gre.c	Sun Oct 21 18:13:45 2018	(r339552)
@@ -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,44 @@ 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;
+
+	if (V_ipv4_srchashtbl == 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 +189,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 +202,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 +237,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 +257,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 +267,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 +319,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 +335,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 +344,12 @@ 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);
+		gre_hashdestroy(V_ipv4_srchashtbl);
+	}
 }

Modified: head/sys/netinet6/ip6_gre.c
==============================================================================
--- head/sys/netinet6/ip6_gre.c	Sun Oct 21 18:06:15 2018	(r339551)
+++ head/sys/netinet6/ip6_gre.c	Sun Oct 21 18:13:45 2018	(r339552)
@@ -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,45 @@ 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;
+
+	if (V_ipv6_srchashtbl == 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 +182,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 +194,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 +238,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 +258,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 +268,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 +302,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 +323,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 +332,12 @@ 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);
+		gre_hashdestroy(V_ipv6_srchashtbl);
+	}
 }



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