Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.18-rc7 377 lines 9.7 kB view raw
1/* 2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * Development of this code funded by Astaro AG (http://www.astaro.com/) 9 */ 10 11#include <linux/kernel.h> 12#include <linux/init.h> 13#include <linux/module.h> 14#include <linux/spinlock.h> 15#include <linux/netlink.h> 16#include <linux/netfilter.h> 17#include <linux/netfilter/nf_tables.h> 18#include <net/netfilter/nf_tables.h> 19 20struct nft_limit { 21 spinlock_t lock; 22 u64 last; 23 u64 tokens; 24 u64 tokens_max; 25 u64 rate; 26 u64 nsecs; 27 u32 burst; 28 bool invert; 29}; 30 31static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost) 32{ 33 u64 now, tokens; 34 s64 delta; 35 36 spin_lock_bh(&limit->lock); 37 now = ktime_get_ns(); 38 tokens = limit->tokens + now - limit->last; 39 if (tokens > limit->tokens_max) 40 tokens = limit->tokens_max; 41 42 limit->last = now; 43 delta = tokens - cost; 44 if (delta >= 0) { 45 limit->tokens = delta; 46 spin_unlock_bh(&limit->lock); 47 return limit->invert; 48 } 49 limit->tokens = tokens; 50 spin_unlock_bh(&limit->lock); 51 return !limit->invert; 52} 53 54/* Use same default as in iptables. */ 55#define NFT_LIMIT_PKT_BURST_DEFAULT 5 56 57static int nft_limit_init(struct nft_limit *limit, 58 const struct nlattr * const tb[], bool pkts) 59{ 60 u64 unit, tokens; 61 62 if (tb[NFTA_LIMIT_RATE] == NULL || 63 tb[NFTA_LIMIT_UNIT] == NULL) 64 return -EINVAL; 65 66 limit->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); 67 unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); 68 limit->nsecs = unit * NSEC_PER_SEC; 69 if (limit->rate == 0 || limit->nsecs < unit) 70 return -EOVERFLOW; 71 72 if (tb[NFTA_LIMIT_BURST]) 73 limit->burst = ntohl(nla_get_be32(tb[NFTA_LIMIT_BURST])); 74 75 if (pkts && limit->burst == 0) 76 limit->burst = NFT_LIMIT_PKT_BURST_DEFAULT; 77 78 if (limit->rate + limit->burst < limit->rate) 79 return -EOVERFLOW; 80 81 if (pkts) { 82 tokens = div_u64(limit->nsecs, limit->rate) * limit->burst; 83 } else { 84 /* The token bucket size limits the number of tokens can be 85 * accumulated. tokens_max specifies the bucket size. 86 * tokens_max = unit * (rate + burst) / rate. 87 */ 88 tokens = div_u64(limit->nsecs * (limit->rate + limit->burst), 89 limit->rate); 90 } 91 92 limit->tokens = tokens; 93 limit->tokens_max = limit->tokens; 94 95 if (tb[NFTA_LIMIT_FLAGS]) { 96 u32 flags = ntohl(nla_get_be32(tb[NFTA_LIMIT_FLAGS])); 97 98 if (flags & NFT_LIMIT_F_INV) 99 limit->invert = true; 100 } 101 limit->last = ktime_get_ns(); 102 spin_lock_init(&limit->lock); 103 104 return 0; 105} 106 107static int nft_limit_dump(struct sk_buff *skb, const struct nft_limit *limit, 108 enum nft_limit_type type) 109{ 110 u32 flags = limit->invert ? NFT_LIMIT_F_INV : 0; 111 u64 secs = div_u64(limit->nsecs, NSEC_PER_SEC); 112 113 if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(limit->rate), 114 NFTA_LIMIT_PAD) || 115 nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs), 116 NFTA_LIMIT_PAD) || 117 nla_put_be32(skb, NFTA_LIMIT_BURST, htonl(limit->burst)) || 118 nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)) || 119 nla_put_be32(skb, NFTA_LIMIT_FLAGS, htonl(flags))) 120 goto nla_put_failure; 121 return 0; 122 123nla_put_failure: 124 return -1; 125} 126 127struct nft_limit_pkts { 128 struct nft_limit limit; 129 u64 cost; 130}; 131 132static void nft_limit_pkts_eval(const struct nft_expr *expr, 133 struct nft_regs *regs, 134 const struct nft_pktinfo *pkt) 135{ 136 struct nft_limit_pkts *priv = nft_expr_priv(expr); 137 138 if (nft_limit_eval(&priv->limit, priv->cost)) 139 regs->verdict.code = NFT_BREAK; 140} 141 142static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = { 143 [NFTA_LIMIT_RATE] = { .type = NLA_U64 }, 144 [NFTA_LIMIT_UNIT] = { .type = NLA_U64 }, 145 [NFTA_LIMIT_BURST] = { .type = NLA_U32 }, 146 [NFTA_LIMIT_TYPE] = { .type = NLA_U32 }, 147 [NFTA_LIMIT_FLAGS] = { .type = NLA_U32 }, 148}; 149 150static int nft_limit_pkts_init(const struct nft_ctx *ctx, 151 const struct nft_expr *expr, 152 const struct nlattr * const tb[]) 153{ 154 struct nft_limit_pkts *priv = nft_expr_priv(expr); 155 int err; 156 157 err = nft_limit_init(&priv->limit, tb, true); 158 if (err < 0) 159 return err; 160 161 priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate); 162 return 0; 163} 164 165static int nft_limit_pkts_dump(struct sk_buff *skb, const struct nft_expr *expr) 166{ 167 const struct nft_limit_pkts *priv = nft_expr_priv(expr); 168 169 return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS); 170} 171 172static struct nft_expr_type nft_limit_type; 173static const struct nft_expr_ops nft_limit_pkts_ops = { 174 .type = &nft_limit_type, 175 .size = NFT_EXPR_SIZE(sizeof(struct nft_limit_pkts)), 176 .eval = nft_limit_pkts_eval, 177 .init = nft_limit_pkts_init, 178 .dump = nft_limit_pkts_dump, 179}; 180 181static void nft_limit_bytes_eval(const struct nft_expr *expr, 182 struct nft_regs *regs, 183 const struct nft_pktinfo *pkt) 184{ 185 struct nft_limit *priv = nft_expr_priv(expr); 186 u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate); 187 188 if (nft_limit_eval(priv, cost)) 189 regs->verdict.code = NFT_BREAK; 190} 191 192static int nft_limit_bytes_init(const struct nft_ctx *ctx, 193 const struct nft_expr *expr, 194 const struct nlattr * const tb[]) 195{ 196 struct nft_limit *priv = nft_expr_priv(expr); 197 198 return nft_limit_init(priv, tb, false); 199} 200 201static int nft_limit_bytes_dump(struct sk_buff *skb, 202 const struct nft_expr *expr) 203{ 204 const struct nft_limit *priv = nft_expr_priv(expr); 205 206 return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES); 207} 208 209static const struct nft_expr_ops nft_limit_bytes_ops = { 210 .type = &nft_limit_type, 211 .size = NFT_EXPR_SIZE(sizeof(struct nft_limit)), 212 .eval = nft_limit_bytes_eval, 213 .init = nft_limit_bytes_init, 214 .dump = nft_limit_bytes_dump, 215}; 216 217static const struct nft_expr_ops * 218nft_limit_select_ops(const struct nft_ctx *ctx, 219 const struct nlattr * const tb[]) 220{ 221 if (tb[NFTA_LIMIT_TYPE] == NULL) 222 return &nft_limit_pkts_ops; 223 224 switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) { 225 case NFT_LIMIT_PKTS: 226 return &nft_limit_pkts_ops; 227 case NFT_LIMIT_PKT_BYTES: 228 return &nft_limit_bytes_ops; 229 } 230 return ERR_PTR(-EOPNOTSUPP); 231} 232 233static struct nft_expr_type nft_limit_type __read_mostly = { 234 .name = "limit", 235 .select_ops = nft_limit_select_ops, 236 .policy = nft_limit_policy, 237 .maxattr = NFTA_LIMIT_MAX, 238 .flags = NFT_EXPR_STATEFUL, 239 .owner = THIS_MODULE, 240}; 241 242static void nft_limit_obj_pkts_eval(struct nft_object *obj, 243 struct nft_regs *regs, 244 const struct nft_pktinfo *pkt) 245{ 246 struct nft_limit_pkts *priv = nft_obj_data(obj); 247 248 if (nft_limit_eval(&priv->limit, priv->cost)) 249 regs->verdict.code = NFT_BREAK; 250} 251 252static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx, 253 const struct nlattr * const tb[], 254 struct nft_object *obj) 255{ 256 struct nft_limit_pkts *priv = nft_obj_data(obj); 257 int err; 258 259 err = nft_limit_init(&priv->limit, tb, true); 260 if (err < 0) 261 return err; 262 263 priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate); 264 return 0; 265} 266 267static int nft_limit_obj_pkts_dump(struct sk_buff *skb, 268 struct nft_object *obj, 269 bool reset) 270{ 271 const struct nft_limit_pkts *priv = nft_obj_data(obj); 272 273 return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS); 274} 275 276static struct nft_object_type nft_limit_obj_type; 277static const struct nft_object_ops nft_limit_obj_pkts_ops = { 278 .type = &nft_limit_obj_type, 279 .size = NFT_EXPR_SIZE(sizeof(struct nft_limit_pkts)), 280 .init = nft_limit_obj_pkts_init, 281 .eval = nft_limit_obj_pkts_eval, 282 .dump = nft_limit_obj_pkts_dump, 283}; 284 285static void nft_limit_obj_bytes_eval(struct nft_object *obj, 286 struct nft_regs *regs, 287 const struct nft_pktinfo *pkt) 288{ 289 struct nft_limit *priv = nft_obj_data(obj); 290 u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate); 291 292 if (nft_limit_eval(priv, cost)) 293 regs->verdict.code = NFT_BREAK; 294} 295 296static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx, 297 const struct nlattr * const tb[], 298 struct nft_object *obj) 299{ 300 struct nft_limit *priv = nft_obj_data(obj); 301 302 return nft_limit_init(priv, tb, false); 303} 304 305static int nft_limit_obj_bytes_dump(struct sk_buff *skb, 306 struct nft_object *obj, 307 bool reset) 308{ 309 const struct nft_limit *priv = nft_obj_data(obj); 310 311 return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES); 312} 313 314static struct nft_object_type nft_limit_obj_type; 315static const struct nft_object_ops nft_limit_obj_bytes_ops = { 316 .type = &nft_limit_obj_type, 317 .size = sizeof(struct nft_limit), 318 .init = nft_limit_obj_bytes_init, 319 .eval = nft_limit_obj_bytes_eval, 320 .dump = nft_limit_obj_bytes_dump, 321}; 322 323static const struct nft_object_ops * 324nft_limit_obj_select_ops(const struct nft_ctx *ctx, 325 const struct nlattr * const tb[]) 326{ 327 if (!tb[NFTA_LIMIT_TYPE]) 328 return &nft_limit_obj_pkts_ops; 329 330 switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) { 331 case NFT_LIMIT_PKTS: 332 return &nft_limit_obj_pkts_ops; 333 case NFT_LIMIT_PKT_BYTES: 334 return &nft_limit_obj_bytes_ops; 335 } 336 return ERR_PTR(-EOPNOTSUPP); 337} 338 339static struct nft_object_type nft_limit_obj_type __read_mostly = { 340 .select_ops = nft_limit_obj_select_ops, 341 .type = NFT_OBJECT_LIMIT, 342 .maxattr = NFTA_LIMIT_MAX, 343 .policy = nft_limit_policy, 344 .owner = THIS_MODULE, 345}; 346 347static int __init nft_limit_module_init(void) 348{ 349 int err; 350 351 err = nft_register_obj(&nft_limit_obj_type); 352 if (err < 0) 353 return err; 354 355 err = nft_register_expr(&nft_limit_type); 356 if (err < 0) 357 goto err1; 358 359 return 0; 360err1: 361 nft_unregister_obj(&nft_limit_obj_type); 362 return err; 363} 364 365static void __exit nft_limit_module_exit(void) 366{ 367 nft_unregister_expr(&nft_limit_type); 368 nft_unregister_obj(&nft_limit_obj_type); 369} 370 371module_init(nft_limit_module_init); 372module_exit(nft_limit_module_exit); 373 374MODULE_LICENSE("GPL"); 375MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 376MODULE_ALIAS_NFT_EXPR("limit"); 377MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_LIMIT);