aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2017-08-03 23:45:26 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2017-08-03 23:45:26 +0200
commita0a962428b430f97b831fb167c9adbebe777beb7 (patch)
tree833dd24463863e3a3b739be1a7d051e96bdbd805
parentnetns: work around linux 3.10 issues (diff)
downloadwireguard-monolithic-historical-a0a962428b430f97b831fb167c9adbebe777beb7.tar.xz
wireguard-monolithic-historical-a0a962428b430f97b831fb167c9adbebe777beb7.zip
ratelimiter: remove and use in-kernel functionalityjd/builtin-ratelimiter
-rw-r--r--src/Kbuild2
-rw-r--r--src/cookie.c20
-rw-r--r--src/device.c10
-rw-r--r--src/main.c3
-rw-r--r--src/ratelimiter.c194
-rw-r--r--src/ratelimiter.h16
-rw-r--r--src/selftest/ratelimiter.h143
7 files changed, 22 insertions, 366 deletions
diff --git a/src/Kbuild b/src/Kbuild
index c5b8718..f5ec49c 100644
--- a/src/Kbuild
+++ b/src/Kbuild
@@ -2,7 +2,7 @@ ccflags-y := -O3 -fvisibility=hidden
ccflags-$(CONFIG_WIREGUARD_DEBUG) += -DDEBUG -g
ccflags-y += -Wframe-larger-than=8192
ccflags-y += -D'pr_fmt(fmt)=KBUILD_MODNAME ": " fmt'
-wireguard-y := main.o noise.o device.o peer.o timers.o data.o send.o receive.o socket.o config.o hashtables.o routingtable.o ratelimiter.o cookie.o
+wireguard-y := main.o noise.o device.o peer.o timers.o data.o send.o receive.o socket.o config.o hashtables.o routingtable.o cookie.o
wireguard-y += crypto/curve25519.o crypto/chacha20poly1305.o crypto/blake2s.o
ifeq ($(CONFIG_X86_64),y)
diff --git a/src/cookie.c b/src/cookie.c
index ec58903..323f046 100644
--- a/src/cookie.c
+++ b/src/cookie.c
@@ -4,7 +4,6 @@
#include "peer.h"
#include "device.h"
#include "messages.h"
-#include "ratelimiter.h"
#include "crypto/blake2s.h"
#include "crypto/chacha20poly1305.h"
@@ -95,6 +94,25 @@ static void make_cookie(u8 cookie[COOKIE_LEN], struct sk_buff *skb, struct cooki
up_read(&checker->secret_lock);
}
+enum { PACKETS_PER_SECOND = 20 };
+static bool ratelimiter_allow(struct sk_buff *skb, struct net *net)
+{
+ struct inet_peer *peer = NULL;
+ bool ret;
+
+ if (skb->protocol == htons(ETH_P_IP))
+ peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, l3mdev_master_ifindex(skb->dev), true);
+#if IS_ENABLED(CONFIG_IPV6)
+ else if (skb->protocol == htons(ETH_P_IPV6))
+ peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr, true);
+#endif
+ if (unlikely(!peer))
+ return false;
+ ret = inet_peer_xrlim_allow(peer, PACKETS_PER_SECOND);
+ inet_putpeer(peer);
+ return ret;
+}
+
enum cookie_mac_state cookie_validate_packet(struct cookie_checker *checker, struct sk_buff *skb, bool check_cookie)
{
u8 computed_mac[COOKIE_LEN];
diff --git a/src/device.c b/src/device.c
index cb5517e..c079f2b 100644
--- a/src/device.c
+++ b/src/device.c
@@ -5,7 +5,6 @@
#include "timers.h"
#include "device.h"
#include "config.h"
-#include "ratelimiter.h"
#include "peer.h"
#include "uapi.h"
#include "messages.h"
@@ -230,7 +229,6 @@ static void destruct(struct net_device *dev)
destroy_workqueue(wg->crypt_wq);
#endif
routing_table_free(&wg->peer_routing_table);
- ratelimiter_uninit();
memzero_explicit(&wg->static_identity, sizeof(struct noise_static_identity));
skb_queue_purge(&wg->incoming_handshakes);
socket_uninit(wg);
@@ -326,13 +324,9 @@ static int newlink(struct net *src_net, struct net_device *dev, struct nlattr *t
padata_start(wg->decrypt_pd);
#endif
- ret = ratelimiter_init();
- if (ret < 0)
- goto error_8;
-
ret = register_netdevice(dev);
if (ret < 0)
- goto error_9;
+ goto error_8;
list_add(&wg->device_list, &device_list);
@@ -343,8 +337,6 @@ static int newlink(struct net *src_net, struct net_device *dev, struct nlattr *t
pr_debug("%s: Interface created\n", dev->name);
return ret;
-error_9:
- ratelimiter_uninit();
error_8:
#ifdef CONFIG_WIREGUARD_PARALLEL
padata_free(wg->decrypt_pd);
diff --git a/src/main.c b/src/main.c
index 0697741..a7064ea 100644
--- a/src/main.c
+++ b/src/main.c
@@ -4,7 +4,6 @@
#include "device.h"
#include "noise.h"
#include "packets.h"
-#include "ratelimiter.h"
#include "crypto/chacha20poly1305.h"
#include "crypto/blake2s.h"
#include "crypto/curve25519.h"
@@ -22,7 +21,7 @@ static int __init mod_init(void)
blake2s_fpu_init();
curve25519_fpu_init();
#ifdef DEBUG
- if (!routing_table_selftest() || !packet_counter_selftest() || !curve25519_selftest() || !chacha20poly1305_selftest() || !blake2s_selftest() || !ratelimiter_selftest())
+ if (!routing_table_selftest() || !packet_counter_selftest() || !curve25519_selftest() || !chacha20poly1305_selftest() || !blake2s_selftest())
return -ENOTRECOVERABLE;
#endif
noise_init();
diff --git a/src/ratelimiter.c b/src/ratelimiter.c
deleted file mode 100644
index 9ad451e..0000000
--- a/src/ratelimiter.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
-
-#include "ratelimiter.h"
-#include <linux/siphash.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <net/ip.h>
-
-static struct kmem_cache *entry_cache;
-static hsiphash_key_t key;
-static spinlock_t table_lock = __SPIN_LOCK_UNLOCKED("ratelimiter_table_lock");
-static atomic64_t refcnt = ATOMIC64_INIT(0);
-static atomic_t total_entries = ATOMIC_INIT(0);
-static unsigned int max_entries, table_size;
-static void gc_entries(struct work_struct *);
-static DECLARE_DEFERRABLE_WORK(gc_work, gc_entries);
-static struct hlist_head *table_v4;
-#if IS_ENABLED(CONFIG_IPV6)
-static struct hlist_head *table_v6;
-#endif
-
-struct ratelimiter_entry {
- u64 last_time_ns, tokens;
- __be64 ip;
- void *net;
- spinlock_t lock;
- struct hlist_node hash;
- struct rcu_head rcu;
-};
-
-enum {
- PACKETS_PER_SECOND = 20,
- PACKETS_BURSTABLE = 5,
- PACKET_COST = NSEC_PER_SEC / PACKETS_PER_SECOND,
- TOKEN_MAX = PACKET_COST * PACKETS_BURSTABLE
-};
-
-static void entry_free(struct rcu_head *rcu)
-{
- kmem_cache_free(entry_cache, container_of(rcu, struct ratelimiter_entry, rcu));
- atomic_dec(&total_entries);
-}
-
-static void entry_uninit(struct ratelimiter_entry *entry)
-{
- hlist_del_rcu(&entry->hash);
- call_rcu(&entry->rcu, entry_free);
-}
-
-/* Calling this function with a NULL work uninits all entries. */
-static void gc_entries(struct work_struct *work)
-{
- unsigned int i;
- struct ratelimiter_entry *entry;
- struct hlist_node *temp;
- const u64 now = ktime_get_ns();
-
- for (i = 0; i < table_size; ++i) {
- spin_lock(&table_lock);
- hlist_for_each_entry_safe (entry, temp, &table_v4[i], hash) {
- if (unlikely(!work) || now - entry->last_time_ns > NSEC_PER_SEC)
- entry_uninit(entry);
- }
-#if IS_ENABLED(CONFIG_IPV6)
- hlist_for_each_entry_safe (entry, temp, &table_v6[i], hash) {
- if (unlikely(!work) || now - entry->last_time_ns > NSEC_PER_SEC)
- entry_uninit(entry);
- }
-#endif
- spin_unlock(&table_lock);
- if (likely(work))
- cond_resched();
- }
- if (likely(work))
- queue_delayed_work(system_power_efficient_wq, &gc_work, HZ);
-}
-
-bool ratelimiter_allow(struct sk_buff *skb, struct net *net)
-{
- struct ratelimiter_entry *entry;
- struct hlist_head *bucket;
- struct { __be64 ip; u32 net; } data = { .net = (unsigned long)net & 0xffffffff };
-
- if (skb->protocol == htons(ETH_P_IP)) {
- data.ip = (__force __be64)ip_hdr(skb)->saddr;
- bucket = &table_v4[hsiphash(&data, sizeof(u32) * 3, &key) & (table_size - 1)];
- }
-#if IS_ENABLED(CONFIG_IPV6)
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- memcpy(&data.ip, &ipv6_hdr(skb)->saddr, sizeof(__be64)); /* Only 64 bits */
- bucket = &table_v6[hsiphash(&data, sizeof(u32) * 3, &key) & (table_size - 1)];
- }
-#endif
- else
- return false;
- rcu_read_lock();
- hlist_for_each_entry_rcu (entry, bucket, hash) {
- if (entry->net == net && entry->ip == data.ip) {
- u64 now, tokens;
- bool ret;
- /* Inspired by nft_limit.c, but this is actually a slightly different
- * algorithm. Namely, we incorporate the burst as part of the maximum
- * tokens, rather than as part of the rate. */
- spin_lock(&entry->lock);
- now = ktime_get_ns();
- tokens = min_t(u64, TOKEN_MAX, entry->tokens + now - entry->last_time_ns);
- entry->last_time_ns = now;
- ret = tokens >= PACKET_COST;
- entry->tokens = ret ? tokens - PACKET_COST : tokens;
- spin_unlock(&entry->lock);
- rcu_read_unlock();
- return ret;
- }
- }
- rcu_read_unlock();
-
- if (atomic_inc_return(&total_entries) > max_entries)
- goto err_oom;
-
- entry = kmem_cache_alloc(entry_cache, GFP_KERNEL);
- if (!entry)
- goto err_oom;
-
- entry->net = net;
- entry->ip = data.ip;
- INIT_HLIST_NODE(&entry->hash);
- spin_lock_init(&entry->lock);
- entry->last_time_ns = ktime_get_ns();
- entry->tokens = TOKEN_MAX - PACKET_COST;
- spin_lock(&table_lock);
- hlist_add_head_rcu(&entry->hash, bucket);
- spin_unlock(&table_lock);
- return true;
-
-err_oom:
- atomic_dec(&total_entries);
- return false;
-}
-
-int ratelimiter_init(void)
-{
- if (atomic64_inc_return(&refcnt) != 1)
- return 0;
-
- entry_cache = KMEM_CACHE(ratelimiter_entry, 0);
- if (!entry_cache)
- goto err;
-
- /* xt_hashlimit.c uses a slightly different algorithm for ratelimiting,
- * but what it shares in common is that it uses a massive hashtable. So,
- * we borrow their wisdom about good table sizes on different systems
- * dependent on RAM. This calculation here comes from there. */
- table_size = (totalram_pages > (1 << 30) / PAGE_SIZE) ? 8192 : max_t(unsigned long, 16, roundup_pow_of_two((totalram_pages << PAGE_SHIFT) / (1 << 14) / sizeof(struct hlist_head)));
- max_entries = table_size * 8;
-
- table_v4 = kvzalloc(table_size * sizeof(struct hlist_head), GFP_KERNEL);
- if (!table_v4)
- goto err_kmemcache;
-
-#if IS_ENABLED(CONFIG_IPV6)
- table_v6 = kvzalloc(table_size * sizeof(struct hlist_head), GFP_KERNEL);
- if (!table_v6) {
- kvfree(table_v4);
- goto err_kmemcache;
- }
-#endif
-
- queue_delayed_work(system_power_efficient_wq, &gc_work, HZ);
- get_random_bytes(&key, sizeof(key));
- return 0;
-
-err_kmemcache:
- kmem_cache_destroy(entry_cache);
-err:
- atomic64_dec(&refcnt);
- return -ENOMEM;
-}
-
-void ratelimiter_uninit(void)
-{
- if (atomic64_dec_return(&refcnt))
- return;
-
- cancel_delayed_work_sync(&gc_work);
- gc_entries(NULL);
- synchronize_rcu();
- kvfree(table_v4);
-#if IS_ENABLED(CONFIG_IPV6)
- kvfree(table_v6);
-#endif
- kmem_cache_destroy(entry_cache);
-}
-
-#include "selftest/ratelimiter.h"
diff --git a/src/ratelimiter.h b/src/ratelimiter.h
deleted file mode 100644
index cb0cdbf..0000000
--- a/src/ratelimiter.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
-
-#ifndef RATELIMITER_H
-#define RATELIMITER_H
-
-#include <linux/skbuff.h>
-
-int ratelimiter_init(void);
-void ratelimiter_uninit(void);
-bool ratelimiter_allow(struct sk_buff *skb, struct net *net);
-
-#ifdef DEBUG
-bool ratelimiter_selftest(void);
-#endif
-
-#endif
diff --git a/src/selftest/ratelimiter.h b/src/selftest/ratelimiter.h
deleted file mode 100644
index bf6b9b8..0000000
--- a/src/selftest/ratelimiter.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
-
-#ifdef DEBUG
-
-#include <linux/jiffies.h>
-
-static const struct { bool result; unsigned int msec_to_sleep_before; } expected_results[] __initconst = {
- [0 ... PACKETS_BURSTABLE - 1] = { true, 0 },
- [PACKETS_BURSTABLE] = { false, 0 },
- [PACKETS_BURSTABLE + 1] = { true, MSEC_PER_SEC / PACKETS_PER_SECOND },
- [PACKETS_BURSTABLE + 2] = { false, 0 },
- [PACKETS_BURSTABLE + 3] = { true, (MSEC_PER_SEC / PACKETS_PER_SECOND) * 2 },
- [PACKETS_BURSTABLE + 4] = { true, 0 },
- [PACKETS_BURSTABLE + 5] = { false, 0 }
-};
-
-static __init unsigned int maximum_jiffies_at_index(int index)
-{
- unsigned int total_msecs = 2 * MSEC_PER_SEC / PACKETS_PER_SECOND / 3;
- int i;
-
- for (i = 0; i <= index; ++i)
- total_msecs += expected_results[i].msec_to_sleep_before;
- return msecs_to_jiffies(total_msecs);
-}
-
-bool __init ratelimiter_selftest(void)
-{
- struct sk_buff *skb4;
- struct iphdr *hdr4;
-#if IS_ENABLED(CONFIG_IPV6)
- struct sk_buff *skb6;
- struct ipv6hdr *hdr6;
-#endif
- int i = -1, tries = 0, ret = false;
- unsigned long loop_start_time;
-
- BUILD_BUG_ON(MSEC_PER_SEC % PACKETS_PER_SECOND != 0);
-
- if (ratelimiter_init())
- goto out;
- if (ratelimiter_init()) {
- ratelimiter_uninit();
- goto out;
- }
- if (ratelimiter_init()) {
- ratelimiter_uninit();
- ratelimiter_uninit();
- goto out;
- }
-
- skb4 = alloc_skb(sizeof(struct iphdr), GFP_KERNEL);
- if (!skb4)
- goto err_nofree;
- skb4->protocol = htons(ETH_P_IP);
- hdr4 = (struct iphdr *)skb_put(skb4, sizeof(struct iphdr));
- hdr4->saddr = htonl(8182);
- skb_reset_network_header(skb4);
-
-#if IS_ENABLED(CONFIG_IPV6)
- skb6 = alloc_skb(sizeof(struct ipv6hdr), GFP_KERNEL);
- if (!skb6) {
- kfree_skb(skb4);
- goto err_nofree;
- }
- skb6->protocol = htons(ETH_P_IPV6);
- hdr6 = (struct ipv6hdr *)skb_put(skb6, sizeof(struct ipv6hdr));
- hdr6->saddr.in6_u.u6_addr32[0] = htonl(1212);
- hdr6->saddr.in6_u.u6_addr32[1] = htonl(289188);
- skb_reset_network_header(skb6);
-#endif
-
-restart:
- loop_start_time = jiffies;
- for (i = 0; i < ARRAY_SIZE(expected_results); ++i) {
-#define ensure_time do {\
- if (time_is_before_jiffies(loop_start_time + maximum_jiffies_at_index(i))) { \
- if (++tries >= 5000) \
- goto err; \
- gc_entries(NULL); \
- rcu_barrier(); \
- msleep(500); \
- goto restart; \
- }} while (0)
-
- if (expected_results[i].msec_to_sleep_before)
- msleep(expected_results[i].msec_to_sleep_before);
-
- ensure_time;
- if (ratelimiter_allow(skb4, &init_net) != expected_results[i].result)
- goto err;
- hdr4->saddr = htonl(ntohl(hdr4->saddr) + i + 1);
- ensure_time;
- if (!ratelimiter_allow(skb4, &init_net))
- goto err;
- hdr4->saddr = htonl(ntohl(hdr4->saddr) - i - 1);
-
-#if IS_ENABLED(CONFIG_IPV6)
- hdr6->saddr.in6_u.u6_addr32[2] = hdr6->saddr.in6_u.u6_addr32[3] = htonl(i);
- ensure_time;
- if (ratelimiter_allow(skb6, &init_net) != expected_results[i].result)
- goto err;
- hdr6->saddr.in6_u.u6_addr32[0] = htonl(ntohl(hdr6->saddr.in6_u.u6_addr32[0]) + i + 1);
- ensure_time;
- if (!ratelimiter_allow(skb6, &init_net))
- goto err;
- hdr6->saddr.in6_u.u6_addr32[0] = htonl(ntohl(hdr6->saddr.in6_u.u6_addr32[0]) - i - 1);
- ensure_time;
-#endif
- }
-
- gc_entries(NULL);
- rcu_barrier();
-
- if (atomic_read(&total_entries))
- goto err;
-
- for (i = 0; i <= max_entries; ++i) {
- hdr4->saddr = htonl(i);
- if (ratelimiter_allow(skb4, &init_net) != (i != max_entries))
- goto err;
- }
-
- ret = true;
-
-err:
- kfree_skb(skb4);
-#if IS_ENABLED(CONFIG_IPV6)
- kfree_skb(skb6);
-#endif
-err_nofree:
- ratelimiter_uninit();
- ratelimiter_uninit();
- ratelimiter_uninit();
-out:
- if (ret)
- pr_info("ratelimiter self-tests: pass\n");
- else
- pr_info("ratelimiter self-test %d: fail\n", i);
-
- return ret;
-}
-#endif