bf7b042dc6
When removing single nodes, it's possible that that node's parent is an
empty intermediate node, in which case, it too should be removed.
Otherwise the trie fills up and never is fully emptied, leading to
gradual memory leaks over time for tries that are modified often. There
was originally code to do this, but was removed during refactoring in
2016 and never reworked. Now that we have proper parent pointers from
the previous commits, we can implement this properly.
In order to reduce branching and expensive comparisons, we want to keep
the double pointer for parent assignment (which lets us easily chain up
to the root), but we still need to actually get the parent's base
address. So encode the bit number into the last two bits of the pointer,
and pack and unpack it as needed. This is a little bit clumsy but is the
fastest and less memory wasteful of the compromises. Note that we align
the root struct here to a minimum of 4, because it's embedded into a
larger struct, and we're relying on having the bottom two bits for our
flag, which would only be 16-bit aligned on m68k.
The existing macro-based helpers were a bit unwieldy for adding the bit
packing to, so this commit replaces them with safer and clearer ordinary
functions.
We add a test to the randomized/fuzzer part of the selftests, to free
the randomized tries by-peer, refuzz it, and repeat, until it's supposed
to be empty, and then then see if that actually resulted in the whole
thing being emptied. That combined with kmemcheck should hopefully make
sure this commit is doing what it should. Along the way this resulted in
various other cleanups of the tests and fixes for recent graphviz.
Fixes: e7096c131e
("net: WireGuard secure network tunnel")
Cc: stable@vger.kernel.org
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
60 lines
1.8 KiB
C
60 lines
1.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
|
*/
|
|
|
|
#ifndef _WG_ALLOWEDIPS_H
|
|
#define _WG_ALLOWEDIPS_H
|
|
|
|
#include <linux/mutex.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/ipv6.h>
|
|
|
|
struct wg_peer;
|
|
|
|
struct allowedips_node {
|
|
struct wg_peer __rcu *peer;
|
|
struct allowedips_node __rcu *bit[2];
|
|
u8 cidr, bit_at_a, bit_at_b, bitlen;
|
|
u8 bits[16] __aligned(__alignof(u64));
|
|
|
|
/* Keep rarely used members at bottom to be beyond cache line. */
|
|
unsigned long parent_bit_packed;
|
|
union {
|
|
struct list_head peer_list;
|
|
struct rcu_head rcu;
|
|
};
|
|
};
|
|
|
|
struct allowedips {
|
|
struct allowedips_node __rcu *root4;
|
|
struct allowedips_node __rcu *root6;
|
|
u64 seq;
|
|
} __aligned(4); /* We pack the lower 2 bits of &root, but m68k only gives 16-bit alignment. */
|
|
|
|
void wg_allowedips_init(struct allowedips *table);
|
|
void wg_allowedips_free(struct allowedips *table, struct mutex *mutex);
|
|
int wg_allowedips_insert_v4(struct allowedips *table, const struct in_addr *ip,
|
|
u8 cidr, struct wg_peer *peer, struct mutex *lock);
|
|
int wg_allowedips_insert_v6(struct allowedips *table, const struct in6_addr *ip,
|
|
u8 cidr, struct wg_peer *peer, struct mutex *lock);
|
|
void wg_allowedips_remove_by_peer(struct allowedips *table,
|
|
struct wg_peer *peer, struct mutex *lock);
|
|
/* The ip input pointer should be __aligned(__alignof(u64))) */
|
|
int wg_allowedips_read_node(struct allowedips_node *node, u8 ip[16], u8 *cidr);
|
|
|
|
/* These return a strong reference to a peer: */
|
|
struct wg_peer *wg_allowedips_lookup_dst(struct allowedips *table,
|
|
struct sk_buff *skb);
|
|
struct wg_peer *wg_allowedips_lookup_src(struct allowedips *table,
|
|
struct sk_buff *skb);
|
|
|
|
#ifdef DEBUG
|
|
bool wg_allowedips_selftest(void);
|
|
#endif
|
|
|
|
int wg_allowedips_slab_init(void);
|
|
void wg_allowedips_slab_uninit(void);
|
|
|
|
#endif /* _WG_ALLOWEDIPS_H */
|