/* Copyright (C) 2015-2016 Jason A. Donenfeld . All Rights Reserved. */ #include "ratelimiter.h" #include "peer.h" #include "device.h" #include #include #include static struct xt_match *v4_match; #if IS_ENABLED(CONFIG_IPV6) static struct xt_match *v6_match; #endif 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; /* 75 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); memcpy(ratelimiter->v4_info.name, dev->name, IFNAMSIZ); chk.matchinfo = &ratelimiter->v4_info; chk.match = v4_match; chk.family = NFPROTO_IPV4; ret = v4_match->checkentry(&chk); if (ret < 0) return ret; #if IS_ENABLED(CONFIG_IPV6) cfg_init(&ratelimiter->v6_info.cfg, NFPROTO_IPV6); memcpy(ratelimiter->v6_info.name, dev->name, IFNAMSIZ); chk.matchinfo = &ratelimiter->v6_info; chk.match = v6_match; chk.family = NFPROTO_IPV6; ret = v6_match->checkentry(&chk); if (ret < 0) { struct xt_mtdtor_param dtor_v4 = { .net = wg->creating_net, .match = v4_match, .matchinfo = &ratelimiter->v4_info, .family = NFPROTO_IPV4 }; v4_match->destroy(&dtor_v4); return ret; } #endif ratelimiter->net = wg->creating_net; return 0; } void ratelimiter_uninit(struct ratelimiter *ratelimiter) { struct xt_mtdtor_param dtor = { .net = ratelimiter->net }; dtor.match = v4_match; dtor.matchinfo = &ratelimiter->v4_info; dtor.family = NFPROTO_IPV4; v4_match->destroy(&dtor); #if IS_ENABLED(CONFIG_IPV6) dtor.match = v6_match; dtor.matchinfo = &ratelimiter->v6_info; dtor.family = NFPROTO_IPV6; v6_match->destroy(&dtor); #endif } 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 = v4_match; action.matchinfo = &ratelimiter->v4_info; action.thoff = ip_hdrlen(skb); } #if IS_ENABLED(CONFIG_IPV6) else if (ip_hdr(skb)->version == 6) { action.match = v6_match; action.matchinfo = &ratelimiter->v6_info; } #endif else return false; return action.match->match(skb, &action); } int ratelimiter_module_init(void) { v4_match = xt_request_find_match(NFPROTO_IPV4, "hashlimit", 1); if (IS_ERR(v4_match)) { pr_err("The xt_hashlimit module for IPv4 is required"); return PTR_ERR(v4_match); } #if IS_ENABLED(CONFIG_IPV6) v6_match = xt_request_find_match(NFPROTO_IPV6, "hashlimit", 1); if (IS_ERR(v6_match)) { pr_err("The xt_hashlimit module for IPv6 is required"); module_put(v4_match->me); return PTR_ERR(v6_match); } #endif return 0; } void ratelimiter_module_deinit(void) { module_put(v4_match->me); #if IS_ENABLED(CONFIG_IPV6) module_put(v6_match->me); #endif }