WireGuard/src/ratelimiter.c

120 lines
3.7 KiB
C

/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
#include "wireguard.h"
#include "ratelimiter.h"
#include <linux/netfilter/x_tables.h>
#include <net/ip.h>
#include <linux/module.h>
enum {
RATELIMITER_PACKETS_PER_SECOND = 75,
RATELIMITER_PACKETS_BURSTABLE = 5
};
static inline void cfg_init(struct hashlimit_cfg1 *cfg, int family)
{
memset(cfg, 0, sizeof(struct hashlimit_cfg1));
if (family == NFPROTO_IPV4)
cfg->srcmask = 32;
else if (family == NFPROTO_IPV6)
cfg->srcmask = 96;
cfg->mode = XT_HASHLIMIT_HASH_SIP; /* source IP only -- we could also do source port by ORing this with XT_HASHLIMIT_HASH_SPT */
cfg->avg = XT_HASHLIMIT_SCALE / RATELIMITER_PACKETS_PER_SECOND; /* 50 per second per IP */
cfg->burst = RATELIMITER_PACKETS_BURSTABLE; /* Allow bursts of 5 at a time */
cfg->gc_interval = 1000; /* same as expiration date */
cfg->expire = 1000; /* Units of avg (seconds = 1) times 1000 */
/* cfg->size and cfg->max are computed based on the memory size of left to zero */
}
int ratelimiter_init(struct ratelimiter *ratelimiter, struct wireguard_device *wg)
{
struct net_device *dev = netdev_pub(wg);
struct xt_mtchk_param chk = { .net = wg->creating_net };
int ret;
memset(ratelimiter, 0, sizeof(struct ratelimiter));
cfg_init(&ratelimiter->v4_info.cfg, NFPROTO_IPV4);
cfg_init(&ratelimiter->v6_info.cfg, NFPROTO_IPV6);
memcpy(ratelimiter->v4_info.name, dev->name, IFNAMSIZ);
memcpy(ratelimiter->v6_info.name, dev->name, IFNAMSIZ);
ratelimiter->v4_match = xt_request_find_match(NFPROTO_IPV4, "hashlimit", 1);
if (IS_ERR(ratelimiter->v4_match)) {
pr_err("The xt_hashlimit module is required");
return PTR_ERR(ratelimiter->v4_match);
}
chk.matchinfo = &ratelimiter->v4_info;
chk.match = ratelimiter->v4_match;
chk.family = NFPROTO_IPV4;
ret = ratelimiter->v4_match->checkentry(&chk);
if (ret < 0) {
module_put(ratelimiter->v4_match->me);
return ret;
}
ratelimiter->v6_match = xt_request_find_match(NFPROTO_IPV6, "hashlimit", 1);
if (IS_ERR(ratelimiter->v6_match)) {
pr_err("The xt_hashlimit module is required");
module_put(ratelimiter->v4_match->me);
return PTR_ERR(ratelimiter->v6_match);
}
chk.matchinfo = &ratelimiter->v6_info;
chk.match = ratelimiter->v6_match;
chk.family = NFPROTO_IPV6;
ret = ratelimiter->v6_match->checkentry(&chk);
if (ret < 0) {
struct xt_mtdtor_param dtor_v4 = {
.net = wg->creating_net,
.match = ratelimiter->v4_match,
.matchinfo = &ratelimiter->v4_info,
.family = NFPROTO_IPV4
};
ratelimiter->v4_match->destroy(&dtor_v4);
module_put(ratelimiter->v4_match->me);
module_put(ratelimiter->v6_match->me);
return ret;
}
ratelimiter->net = wg->creating_net;
return 0;
}
void ratelimiter_uninit(struct ratelimiter *ratelimiter)
{
struct xt_mtdtor_param dtor = { .net = ratelimiter->net };
dtor.match = ratelimiter->v4_match;
dtor.matchinfo = &ratelimiter->v4_info;
dtor.family = NFPROTO_IPV4;
ratelimiter->v4_match->destroy(&dtor);
module_put(ratelimiter->v4_match->me);
dtor.match = ratelimiter->v6_match;
dtor.matchinfo = &ratelimiter->v6_info;
dtor.family = NFPROTO_IPV6;
ratelimiter->v6_match->destroy(&dtor);
module_put(ratelimiter->v6_match->me);
}
bool ratelimiter_allow(struct ratelimiter *ratelimiter, struct sk_buff *skb)
{
struct xt_action_param action = { { NULL } };
if (unlikely(skb->len < sizeof(struct iphdr)))
return false;
if (ip_hdr(skb)->version == 4) {
action.match = ratelimiter->v4_match;
action.matchinfo = &ratelimiter->v4_info;
action.thoff = ip_hdrlen(skb);
action.family = NFPROTO_IPV4;
} else if (ip_hdr(skb)->version == 6) {
action.match = ratelimiter->v6_match;
action.matchinfo = &ratelimiter->v6_info;
action.family = NFPROTO_IPV6;
} else
return false;
return action.match->match(skb, &action);
}