frag6: fix vnet teardown leak

When shutting down a VNET we did not cleanup the fragmentation hashes.
This has multiple problems: (1) leak memory but also (2) leak on the
global counters, which might eventually lead to a problem on a system
starting and stopping a lot of vnets and dealing with a lot of IPv6
fragments that the counters/limits would be exhausted and processing
would no longer take place.

Unfortunately we do not have a useable variable to indicate when
per-VNET initialization of frag6 has happened (or when destroy happened)
so introduce a boolean to flag this. This is needed here as well as
it was in r353635 for ip_reass.c in order to avoid tripping over the
already destroyed locks if interfaces go away after the frag6 destroy.

While splitting things up convert the TRY_LOCK to a LOCK operation in
now frag6_drain_one().  The try-lock was derived from a manual hand-rolled
implementation and carried forward all the time.  We no longer can afford
not to get the lock as that would mean we would continue to leak memory.

Assert that all the buckets are empty before destroying to lock to
ensure long-term stability of a clean shutdown.

Reported by:	hselasky
Reviewed by:	hselasky
MFC after:	3 weeks
Sponsored by:	Netflix
Differential Revision:	https://reviews.freebsd.org/D22054
This commit is contained in:
Bjoern A. Zeeb 2019-10-21 08:48:47 +00:00
parent 65456706c0
commit 67a10c4644
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=353793
3 changed files with 59 additions and 13 deletions

View File

@ -100,6 +100,12 @@ struct ip6asfrag {
static MALLOC_DEFINE(M_FRAG6, "frag6", "IPv6 fragment reassembly header");
#ifdef VIMAGE
/* A flag to indicate if IPv6 fragmentation is initialized. */
VNET_DEFINE_STATIC(bool, frag6_on);
#define V_frag6_on VNET(frag6_on)
#endif
/* System wide (global) maximum and count of packets in reassembly queues. */
static int ip6_maxfrags;
static volatile u_int frag6_nfrags = 0;
@ -289,6 +295,15 @@ frag6_cleanup(void *arg __unused, struct ifnet *ifp)
KASSERT(ifp != NULL, ("%s: ifp is NULL", __func__));
#ifdef VIMAGE
/*
* Skip processing if IPv6 reassembly is not initialised or
* torn down by frag6_destroy().
*/
if (!V_frag6_on)
return;
#endif
CURVNET_SET_QUIET(ifp->if_vnet);
for (i = 0; i < IP6REASS_NHASH; i++) {
IP6QB_LOCK(i);
@ -929,6 +944,9 @@ frag6_init(void)
}
V_ip6qb_hashseed = arc4random();
V_ip6_maxfragsperpacket = 64;
#ifdef VIMAGE
V_frag6_on = true;
#endif
if (!IS_DEFAULT_VNET(curvnet))
return;
@ -940,32 +958,58 @@ frag6_init(void)
/*
* Drain off all datagram fragments.
*/
static void
frag6_drain_one(void)
{
struct ip6q *head;
uint32_t bucket;
for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) {
IP6QB_LOCK(bucket);
head = IP6QB_HEAD(bucket);
while (head->ip6q_next != head) {
IP6STAT_INC(ip6s_fragdropped);
/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
frag6_freef(head->ip6q_next, bucket);
}
IP6QB_UNLOCK(bucket);
}
}
void
frag6_drain(void)
{
VNET_ITERATOR_DECL(vnet_iter);
struct ip6q *head;
uint32_t bucket;
VNET_LIST_RLOCK_NOSLEEP();
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) {
if (IP6QB_TRYLOCK(bucket) == 0)
continue;
head = IP6QB_HEAD(bucket);
while (head->ip6q_next != head) {
IP6STAT_INC(ip6s_fragdropped);
/* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */
frag6_freef(head->ip6q_next, bucket);
}
IP6QB_UNLOCK(bucket);
}
frag6_drain_one();
CURVNET_RESTORE();
}
VNET_LIST_RUNLOCK_NOSLEEP();
}
#ifdef VIMAGE
/*
* Clear up IPv6 reassembly structures.
*/
void
frag6_destroy(void)
{
uint32_t bucket;
frag6_drain_one();
V_frag6_on = false;
for (bucket = 0; bucket < IP6REASS_NHASH; bucket++) {
KASSERT(V_ip6qb[bucket].count == 0,
("%s: V_ip6qb[%d] (%p) count not 0 (%d)", __func__,
bucket, &V_ip6qb[bucket], V_ip6qb[bucket].count));
mtx_destroy(&V_ip6qb[bucket].lock);
}
}
#endif
/*
* Put an ip fragment on a reassembly chain.
* Like insque, but pointers in middle of structure.

View File

@ -393,6 +393,7 @@ ip6_destroy(void *unused __unused)
}
IFNET_RUNLOCK();
frag6_destroy();
nd6_destroy();
in6_ifattach_destroy();

View File

@ -392,6 +392,7 @@ int ip6_fragment(struct ifnet *, struct mbuf *, int, u_char, int,
int route6_input(struct mbuf **, int *, int);
void frag6_init(void);
void frag6_destroy(void);
int frag6_input(struct mbuf **, int *, int);
void frag6_slowtimo(void);
void frag6_drain(void);