git: e32221a15f08 - main - netinet6: make IPv6 fragment TTL per-VNET configurable.

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Thu, 01 Jun 2023 12:08:16 UTC
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=e32221a15f089282e5dfe18891c5312b26cbe3ba

commit e32221a15f089282e5dfe18891c5312b26cbe3ba
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2023-06-01 12:04:49 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2023-06-01 12:04:49 +0000

    netinet6: make IPv6 fragment TTL per-VNET configurable.
    
    Having it configurable adds more flexibility, especially
     for the systems with low amount of memory.
    Additionally, it allows to speedup frag6/ tests execution.
    
    Reviewed by:    kp, markj, bz
    Differential Revision:  https://reviews.freebsd.org/D35755
    MFC after:      2 weeks
---
 sys/netinet/ip6.h                    |  2 +-
 sys/netinet6/frag6.c                 | 64 +++++++++++++++++++++++++++++++++---
 sys/netinet6/ip6_var.h               |  2 +-
 tests/sys/netinet6/frag6/frag6.subr  | 10 ++++--
 tests/sys/netinet6/frag6/frag6_01.py |  2 +-
 tests/sys/netinet6/frag6/frag6_05.py |  2 +-
 tests/sys/netinet6/frag6/frag6_07.py |  2 +-
 tests/sys/netinet6/frag6/frag6_08.py |  2 +-
 tests/sys/netinet6/frag6/frag6_09.py |  2 +-
 tests/sys/netinet6/frag6/frag6_10.py |  2 +-
 tests/sys/netinet6/frag6/frag6_11.py |  2 +-
 tests/sys/netinet6/frag6/frag6_12.py |  2 +-
 tests/sys/netinet6/frag6/frag6_13.py |  2 +-
 tests/sys/netinet6/frag6/frag6_14.py |  2 +-
 tests/sys/netinet6/frag6/frag6_20.py |  2 +-
 15 files changed, 79 insertions(+), 21 deletions(-)

diff --git a/sys/netinet/ip6.h b/sys/netinet/ip6.h
index 6617e1ca7d00..30482c6985cf 100644
--- a/sys/netinet/ip6.h
+++ b/sys/netinet/ip6.h
@@ -254,7 +254,7 @@ struct ip6_frag {
  */
 #define IPV6_MAXHLIM	255	/* maximum hoplimit */
 #define IPV6_DEFHLIM	64	/* default hlim */
-#define IPV6_FRAGTTL	120	/* ttl for fragment packets, in slowtimo tick */
+#define IPV6_DEFFRAGTTL	60000	/* Default fragment packets lifetime, in milliseconds */
 #define IPV6_HLIMDEC	1	/* subtracted when forwarding */
 
 #define IPV6_MMTU	1280	/* minimal MTU and reassembly. 1024 + 256 */
diff --git a/sys/netinet6/frag6.c b/sys/netinet6/frag6.c
index d634f869acd5..5acdc1b20981 100644
--- a/sys/netinet6/frag6.c
+++ b/sys/netinet6/frag6.c
@@ -125,6 +125,10 @@ VNET_DEFINE_STATIC(volatile u_int,	frag6_nfragpackets);
 #define	V_ip6_maxfragpackets		VNET(ip6_maxfragpackets)
 #define	V_frag6_nfragpackets		VNET(frag6_nfragpackets)
 
+/* Maximum per-VNET reassembly timeout (milliseconds) */
+VNET_DEFINE_STATIC(u_int,		ip6_fraglifetime) = IPV6_DEFFRAGTTL;
+#define	V_ip6_fraglifetime		VNET(ip6_fraglifetime)
+
 /* Maximum per-VNET reassembly queues per bucket and fragments per packet. */
 VNET_DEFINE_STATIC(int,			ip6_maxfragbucketsize);
 VNET_DEFINE_STATIC(int,			ip6_maxfragsperpacket);
@@ -159,6 +163,9 @@ VNET_DEFINE_STATIC(uint32_t,		ip6qb_hashseed);
 #define	IP6_MAXFRAGS		(nmbclusters / 32)
 #define	IP6_MAXFRAGPACKETS	(imin(IP6_MAXFRAGS, IP6REASS_NHASH * 50))
 
+/* Interval between periodic reassembly queue inspections */
+#define	IP6_CALLOUT_INTERVAL_MS	500
+
 /*
  * Sysctls and helper function.
  */
@@ -213,6 +220,53 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_MAXFRAGBUCKETSIZE, maxfragbucketsize,
 	CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_maxfragbucketsize), 0,
 	"Maximum number of reassembly queues per hash bucket");
 
+static int
+frag6_milli_to_callout_ticks(int ms)
+{
+	return (ms / IP6_CALLOUT_INTERVAL_MS);
+}
+
+static int
+frag6_callout_ticks_to_milli(int ms)
+{
+	return (ms * IP6_CALLOUT_INTERVAL_MS);
+}
+
+_Static_assert(sizeof(((struct ip6q *)NULL)->ip6q_ttl) >= 2,
+    "ip6q_ttl field is not large enough");
+
+static int
+sysctl_ip6_fraglifetime(SYSCTL_HANDLER_ARGS)
+{
+	int error, val;
+
+	val = V_ip6_fraglifetime;
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	if (error != 0 || !req->newptr)
+		return (error);
+	if (val <= 0)
+		val = IPV6_DEFFRAGTTL;
+
+	if (frag6_milli_to_callout_ticks(val) >= 65536)
+		val = frag6_callout_ticks_to_milli(65535);
+#ifdef VIMAGE
+	if (!IS_DEFAULT_VNET(curvnet)) {
+		CURVNET_SET(vnet0);
+		int host_val = V_ip6_fraglifetime;
+		CURVNET_RESTORE();
+
+		if (val > host_val)
+			val = host_val;
+	}
+#endif
+	V_ip6_fraglifetime = val;
+	return (0);
+}
+SYSCTL_PROC(_net_inet6_ip6, OID_AUTO, fraglifetime_ms,
+	CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+	NULL, 0, sysctl_ip6_fraglifetime, "I",
+	"Fragment lifetime, in milliseconds");
+
 /*
  * Remove the IPv6 fragmentation header from the mbuf.
  */
@@ -552,7 +606,7 @@ frag6_input(struct mbuf **mp, int *offp, int proto)
 		/* ip6q_nxt will be filled afterwards, from 1st fragment. */
 		TAILQ_INIT(&q6->ip6q_frags);
 		q6->ip6q_ident	= ip6f->ip6f_ident;
-		q6->ip6q_ttl	= IPV6_FRAGTTL;
+		q6->ip6q_ttl	= frag6_milli_to_callout_ticks(V_ip6_fraglifetime);
 		q6->ip6q_src	= ip6->ip6_src;
 		q6->ip6q_dst	= ip6->ip6_dst;
 		q6->ip6q_ecn	= IPV6_ECN(ip6);
@@ -952,8 +1006,8 @@ frag6_slowtimo(void *arg __unused)
 	}
 	VNET_LIST_RUNLOCK_NOSLEEP();
 done:
-	callout_reset_sbt(&frag6_callout, SBT_1MS * 500, SBT_1MS * 10,
-	    frag6_slowtimo, NULL, 0);
+	callout_reset_sbt(&frag6_callout, SBT_1MS * IP6_CALLOUT_INTERVAL_MS,
+	    SBT_1MS * 10, frag6_slowtimo, NULL, 0);
 }
 
 static void
@@ -961,8 +1015,8 @@ frag6_slowtimo_init(void *arg __unused)
 {
 
 	callout_init(&frag6_callout, 1);
-	callout_reset_sbt(&frag6_callout, SBT_1MS * 500, SBT_1MS * 10,
-	    frag6_slowtimo, NULL, 0);
+	callout_reset_sbt(&frag6_callout, SBT_1MS * IP6_CALLOUT_INTERVAL_MS,
+	    SBT_1MS * 10, frag6_slowtimo, NULL, 0);
 }
 SYSINIT(frag6, SI_SUB_VNET_DONE, SI_ORDER_ANY, frag6_slowtimo_init, NULL);
 
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index c267503d7151..bc409780feec 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -81,7 +81,7 @@ struct	ip6q {
 	u_int32_t	ip6q_ident;
 	u_int8_t	ip6q_nxt;
 	u_int8_t	ip6q_ecn;
-	u_int8_t	ip6q_ttl;
+	u_int16_t	ip6q_ttl;
 	struct in6_addr ip6q_src, ip6q_dst;
 	TAILQ_ENTRY(ip6q) ip6q_tq;
 	int		ip6q_unfrglen;	/* len of unfragmentable part */
diff --git a/tests/sys/netinet6/frag6/frag6.subr b/tests/sys/netinet6/frag6/frag6.subr
index 1ea947c72de5..6f7d0799642c 100644
--- a/tests/sys/netinet6/frag6/frag6.subr
+++ b/tests/sys/netinet6/frag6/frag6.subr
@@ -59,13 +59,17 @@ frag6_body()
 
 	jname="v6t-${id}-${yl}-${xl}"
 	vnet_mkjail ${jname} ${epair}b
+	jexec ${jname} sysctl net.inet6.ip6.dad_count=0
 	jexec ${jname} ifconfig ${epair}b up
 	jexec ${jname} ifconfig ${epair}b inet6 ${ip6b}/64
 
+	# Set max fragment reassembly time to 2 seconds
+	jexec ${jname} sysctl net.inet6.ip6.fraglifetime_ms=2000
+
 	# Let IPv6 ND do its thing.
-	#ping6 -q -c 1 ff02::1%${epair}a
-	#ping6 -q -c 1 ${ip6b}
-	sleep 3
+	while [ `ifconfig ${epair}a inet6 | grep -c tentative` != "0" ]; do
+		sleep 0.1
+	done
 
 	# We need to try to make sure all expiry happened, otherwise there might
 	# be global fragments queued.  (This still does not rule out that there
diff --git a/tests/sys/netinet6/frag6/frag6_01.py b/tests/sys/netinet6/frag6/frag6_01.py
index efa99ce65759..db6cabc1a84a 100644
--- a/tests/sys/netinet6/frag6/frag6_01.py
+++ b/tests/sys/netinet6/frag6/frag6_01.py
@@ -105,7 +105,7 @@ def main():
 
 	# We should only need to sleep 0.10 but it seems scapy
 	# takes time for this one.
-	sleep(75)
+	sleep(3)
 	sniffer.setEnd()
 	sniffer.join()
 	if not sniffer.foundCorrectPacket:
diff --git a/tests/sys/netinet6/frag6/frag6_05.py b/tests/sys/netinet6/frag6/frag6_05.py
index f9bc947d5465..9c98ea1aaf2e 100644
--- a/tests/sys/netinet6/frag6/frag6_05.py
+++ b/tests/sys/netinet6/frag6/frag6_05.py
@@ -79,7 +79,7 @@ def main():
 		sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
 
 	# Wait for possible expiry to happen.
-	sleep(75)
+	sleep(3)
 	sys.exit(0)
 
 if __name__ == '__main__':
diff --git a/tests/sys/netinet6/frag6/frag6_07.py b/tests/sys/netinet6/frag6/frag6_07.py
index 231f49eac2e0..540c5012e524 100644
--- a/tests/sys/netinet6/frag6/frag6_07.py
+++ b/tests/sys/netinet6/frag6/frag6_07.py
@@ -168,7 +168,7 @@ def main():
 		sys.exit(1)
 
 	# Wait for expiry from first test run.
-	sleep(75)
+	sleep(3)
 	sniffer2.setEnd()
 	sniffer2.join()
 	if not sniffer2.foundCorrectPacket:
diff --git a/tests/sys/netinet6/frag6/frag6_08.py b/tests/sys/netinet6/frag6/frag6_08.py
index 25f57f702e71..74d7495f43cb 100644
--- a/tests/sys/netinet6/frag6/frag6_08.py
+++ b/tests/sys/netinet6/frag6/frag6_08.py
@@ -142,7 +142,7 @@ def main():
 	sniffer.join()
 	if not sniffer.foundCorrectPacket:
 		sys.exit(1)
-	sleep(75)
+	sleep(3)
 	sniffer2.setEnd()
 	sniffer2.join()
 	if not sniffer2.foundCorrectPacket:
diff --git a/tests/sys/netinet6/frag6/frag6_09.py b/tests/sys/netinet6/frag6/frag6_09.py
index 63ec646e1175..b5c519040292 100644
--- a/tests/sys/netinet6/frag6/frag6_09.py
+++ b/tests/sys/netinet6/frag6/frag6_09.py
@@ -99,7 +99,7 @@ def main():
 	sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
 
 	# Wait for ICMPv6 error generation on timeout.
-	sleep(75)
+	sleep(3)
 	sniffer.setEnd()
 	sniffer.join()
 	if not sniffer.foundCorrectPacket:
diff --git a/tests/sys/netinet6/frag6/frag6_10.py b/tests/sys/netinet6/frag6/frag6_10.py
index fcd331190c02..191e3fc3e075 100644
--- a/tests/sys/netinet6/frag6/frag6_10.py
+++ b/tests/sys/netinet6/frag6/frag6_10.py
@@ -75,7 +75,7 @@ def main():
 
 	# We do not generate ICMPv6 for non-off=0-segments.
 	# Wait for expiry.
-	sleep(75)
+	sleep(3)
 
 	sys.exit(0)
 
diff --git a/tests/sys/netinet6/frag6/frag6_11.py b/tests/sys/netinet6/frag6/frag6_11.py
index 6b9643337597..be50a766ab1a 100644
--- a/tests/sys/netinet6/frag6/frag6_11.py
+++ b/tests/sys/netinet6/frag6/frag6_11.py
@@ -75,7 +75,7 @@ def main():
 
 	# Wait for expiration to happen.  We will not see an ICMPv6 as there
 	# is no frag with offset=0.
-	sleep(75)
+	sleep(3)
 
 	sys.exit(0)
 
diff --git a/tests/sys/netinet6/frag6/frag6_12.py b/tests/sys/netinet6/frag6/frag6_12.py
index a683782f2b69..d82610a0f75b 100644
--- a/tests/sys/netinet6/frag6/frag6_12.py
+++ b/tests/sys/netinet6/frag6/frag6_12.py
@@ -101,7 +101,7 @@ def main():
 	sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
 
 	# Wait for ICMPv6 error generation on timeout.
-	sleep(75)
+	sleep(3)
 	sniffer.setEnd()
 	sniffer.join()
 	if not sniffer.foundCorrectPacket:
diff --git a/tests/sys/netinet6/frag6/frag6_13.py b/tests/sys/netinet6/frag6/frag6_13.py
index e377a4272fa1..a8717c2c795c 100644
--- a/tests/sys/netinet6/frag6/frag6_13.py
+++ b/tests/sys/netinet6/frag6/frag6_13.py
@@ -117,7 +117,7 @@ def main():
 	sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
 
 	# Wait for expiry.
-	sleep(75)
+	sleep(3)
 	sys.exit(0)
 
 if __name__ == '__main__':
diff --git a/tests/sys/netinet6/frag6/frag6_14.py b/tests/sys/netinet6/frag6/frag6_14.py
index b53a65e67529..49fae50f4bae 100644
--- a/tests/sys/netinet6/frag6/frag6_14.py
+++ b/tests/sys/netinet6/frag6/frag6_14.py
@@ -132,7 +132,7 @@ def main():
 	sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
 
 	# Wait for expiry.
-	sleep(75)
+	sleep(3)
 	sys.exit(0)
 
 if __name__ == '__main__':
diff --git a/tests/sys/netinet6/frag6/frag6_20.py b/tests/sys/netinet6/frag6/frag6_20.py
index 6dd4c2379357..4a68b242a23e 100755
--- a/tests/sys/netinet6/frag6/frag6_20.py
+++ b/tests/sys/netinet6/frag6/frag6_20.py
@@ -127,7 +127,7 @@ def main():
 	sp.sendp(ip6f01, iface=args.sendif[0], verbose=False)
 	sp.sendp(ip6f02, iface=args.sendif[0], verbose=False)
 
-	sleep(75)
+	sleep(3)
 	sniffer.setEnd()
 	sniffer.join()
 	if not sniffer.foundCorrectPacket: