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
This commit is contained in:
Alexander V. Chernikov 2023-06-01 12:04:49 +00:00
parent 0c2beef72d
commit e32221a15f
15 changed files with 79 additions and 21 deletions

View file

@ -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 */

View file

@ -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);

View file

@ -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 */

View file

@ -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

View file

@ -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:

View file

@ -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__':

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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)

View file

@ -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)

View file

@ -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:

View file

@ -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__':

View file

@ -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__':

View file

@ -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: