From e1f203488cc652bbb6b062bf1eb454982e95cca6 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 25 Nov 2016 21:14:50 +0100 Subject: ratelimiter: load hashlimit at modinsert time This fixes a potential race with net_lock and rtnl_lock. --- src/ratelimiter.c | 81 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 35 deletions(-) (limited to 'src/ratelimiter.c') diff --git a/src/ratelimiter.c b/src/ratelimiter.c index 8bfe46b..7c23d55 100644 --- a/src/ratelimiter.c +++ b/src/ratelimiter.c @@ -15,6 +15,11 @@ #error "WireGuard requires CONFIG_IP6_NF_IPTABLES when using CONFIG_IPV6." #endif +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 @@ -44,47 +49,29 @@ int ratelimiter_init(struct ratelimiter *ratelimiter, struct wireguard_device *w 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 for IPv4 is required"); - return PTR_ERR(ratelimiter->v4_match); - } - chk.matchinfo = &ratelimiter->v4_info; - chk.match = ratelimiter->v4_match; + chk.match = v4_match; chk.family = NFPROTO_IPV4; - ret = ratelimiter->v4_match->checkentry(&chk); - if (ret < 0) { - module_put(ratelimiter->v4_match->me); + ret = v4_match->checkentry(&chk); + if (ret < 0) return ret; - } #if IS_ENABLED(CONFIG_IPV6) - ratelimiter->v6_match = xt_request_find_match(NFPROTO_IPV6, "hashlimit", 1); - if (IS_ERR(ratelimiter->v6_match)) { - pr_err("The xt_hashlimit module for IPv6 is required"); - module_put(ratelimiter->v4_match->me); - return PTR_ERR(ratelimiter->v6_match); - } - + cfg_init(&ratelimiter->v6_info.cfg, NFPROTO_IPV6); + memcpy(ratelimiter->v6_info.name, dev->name, IFNAMSIZ); chk.matchinfo = &ratelimiter->v6_info; - chk.match = ratelimiter->v6_match; + chk.match = v6_match; chk.family = NFPROTO_IPV6; - ret = ratelimiter->v6_match->checkentry(&chk); + ret = v6_match->checkentry(&chk); if (ret < 0) { struct xt_mtdtor_param dtor_v4 = { .net = wg->creating_net, - .match = ratelimiter->v4_match, + .match = 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); + v4_match->destroy(&dtor_v4); return ret; } #endif @@ -97,18 +84,16 @@ void ratelimiter_uninit(struct ratelimiter *ratelimiter) { struct xt_mtdtor_param dtor = { .net = ratelimiter->net }; - dtor.match = ratelimiter->v4_match; + dtor.match = v4_match; dtor.matchinfo = &ratelimiter->v4_info; dtor.family = NFPROTO_IPV4; - ratelimiter->v4_match->destroy(&dtor); - module_put(ratelimiter->v4_match->me); + v4_match->destroy(&dtor); #if IS_ENABLED(CONFIG_IPV6) - dtor.match = ratelimiter->v6_match; + dtor.match = v6_match; dtor.matchinfo = &ratelimiter->v6_info; dtor.family = NFPROTO_IPV6; - ratelimiter->v6_match->destroy(&dtor); - module_put(ratelimiter->v6_match->me); + v6_match->destroy(&dtor); #endif } @@ -118,14 +103,14 @@ bool ratelimiter_allow(struct ratelimiter *ratelimiter, struct sk_buff *skb) if (unlikely(skb->len < sizeof(struct iphdr))) return false; if (ip_hdr(skb)->version == 4) { - action.match = ratelimiter->v4_match; + action.match = v4_match; action.matchinfo = &ratelimiter->v4_info; action.thoff = ip_hdrlen(skb); action.family = NFPROTO_IPV4; } #if IS_ENABLED(CONFIG_IPV6) else if (ip_hdr(skb)->version == 6) { - action.match = ratelimiter->v6_match; + action.match = v6_match; action.matchinfo = &ratelimiter->v6_info; action.family = NFPROTO_IPV6; } @@ -134,3 +119,29 @@ bool ratelimiter_allow(struct ratelimiter *ratelimiter, struct sk_buff *skb) 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 +} -- cgit v1.2.3-59-g8ed1b