svn commit: r339551 - in head: share/man/man4 sys/net sys/netinet sys/netinet6

Andrey V. Elsukov ae at FreeBSD.org
Sun Oct 21 18:06:17 UTC 2018


Author: ae
Date: Sun Oct 21 18:06:15 2018
New Revision: 339551
URL: https://svnweb.freebsd.org/changeset/base/339551

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

Modified:
  head/share/man/man4/gif.4
  head/sys/net/if_gif.c
  head/sys/net/if_gif.h
  head/sys/netinet/in_gif.c
  head/sys/netinet6/in6_gif.c

Modified: head/share/man/man4/gif.4
==============================================================================
--- head/share/man/man4/gif.4	Sun Oct 21 17:55:26 2018	(r339550)
+++ head/share/man/man4/gif.4	Sun Oct 21 18:06:15 2018	(r339551)
@@ -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: head/sys/net/if_gif.c
==============================================================================
--- head/sys/net/if_gif.c	Sun Oct 21 17:55:26 2018	(r339550)
+++ head/sys/net/if_gif.c	Sun Oct 21 18:06:15 2018	(r339551)
@@ -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: head/sys/net/if_gif.h
==============================================================================
--- head/sys/net/if_gif.h	Sun Oct 21 17:55:26 2018	(r339550)
+++ head/sys/net/if_gif.h	Sun Oct 21 18:06:15 2018	(r339551)
@@ -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: head/sys/netinet/in_gif.c
==============================================================================
--- head/sys/netinet/in_gif.c	Sun Oct 21 17:55:26 2018	(r339550)
+++ head/sys/netinet/in_gif.c	Sun Oct 21 18:06:15 2018	(r339551)
@@ -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,44 @@ 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;
+
+	if (V_ipv4_srchashtbl == 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 +168,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 +183,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 +217,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 +235,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 +244,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 +391,7 @@ done:
 	return (ret);
 }
 
+static const struct srcaddrtab *ipv4_srcaddrtab;
 static struct {
 	const struct encap_config encap;
 	const struct encaptab *cookie;
@@ -387,6 +437,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 +453,11 @@ 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);
+		gif_hashdestroy(V_ipv4_srchashtbl);
+	}
 }
 

Modified: head/sys/netinet6/in6_gif.c
==============================================================================
--- head/sys/netinet6/in6_gif.c	Sun Oct 21 17:55:26 2018	(r339550)
+++ head/sys/netinet6/in6_gif.c	Sun Oct 21 18:06:15 2018	(r339551)
@@ -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,44 @@ 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;
+
+	if (V_ipv6_srchashtbl == 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 +174,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 +189,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 +232,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 +251,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 +260,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 +414,7 @@ done:
 	return (ret);
 }
 
+static const struct srcaddrtab *ipv6_srcaddrtab;
 static struct {
 	const struct encap_config encap;
 	const struct encaptab *cookie;
@@ -410,6 +460,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 +476,10 @@ 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);
+		gif_hashdestroy(V_ipv6_srchashtbl);
+	}
 }


More information about the svn-src-head mailing list