Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.6-rc5 305 lines 7.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> 4 * 5 * Development of this code funded by Astaro AG (http://www.astaro.com/) 6 */ 7 8#include <linux/kernel.h> 9#include <linux/init.h> 10#include <linux/module.h> 11#include <linux/seqlock.h> 12#include <linux/netlink.h> 13#include <linux/netfilter.h> 14#include <linux/netfilter/nf_tables.h> 15#include <net/netfilter/nf_tables.h> 16 17struct nft_counter { 18 s64 bytes; 19 s64 packets; 20}; 21 22struct nft_counter_percpu_priv { 23 struct nft_counter __percpu *counter; 24}; 25 26static DEFINE_PER_CPU(seqcount_t, nft_counter_seq); 27 28static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv, 29 struct nft_regs *regs, 30 const struct nft_pktinfo *pkt) 31{ 32 struct nft_counter *this_cpu; 33 seqcount_t *myseq; 34 35 local_bh_disable(); 36 this_cpu = this_cpu_ptr(priv->counter); 37 myseq = this_cpu_ptr(&nft_counter_seq); 38 39 write_seqcount_begin(myseq); 40 41 this_cpu->bytes += pkt->skb->len; 42 this_cpu->packets++; 43 44 write_seqcount_end(myseq); 45 local_bh_enable(); 46} 47 48static inline void nft_counter_obj_eval(struct nft_object *obj, 49 struct nft_regs *regs, 50 const struct nft_pktinfo *pkt) 51{ 52 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 53 54 nft_counter_do_eval(priv, regs, pkt); 55} 56 57static int nft_counter_do_init(const struct nlattr * const tb[], 58 struct nft_counter_percpu_priv *priv) 59{ 60 struct nft_counter __percpu *cpu_stats; 61 struct nft_counter *this_cpu; 62 63 cpu_stats = alloc_percpu(struct nft_counter); 64 if (cpu_stats == NULL) 65 return -ENOMEM; 66 67 preempt_disable(); 68 this_cpu = this_cpu_ptr(cpu_stats); 69 if (tb[NFTA_COUNTER_PACKETS]) { 70 this_cpu->packets = 71 be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); 72 } 73 if (tb[NFTA_COUNTER_BYTES]) { 74 this_cpu->bytes = 75 be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); 76 } 77 preempt_enable(); 78 priv->counter = cpu_stats; 79 return 0; 80} 81 82static int nft_counter_obj_init(const struct nft_ctx *ctx, 83 const struct nlattr * const tb[], 84 struct nft_object *obj) 85{ 86 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 87 88 return nft_counter_do_init(tb, priv); 89} 90 91static void nft_counter_do_destroy(struct nft_counter_percpu_priv *priv) 92{ 93 free_percpu(priv->counter); 94} 95 96static void nft_counter_obj_destroy(const struct nft_ctx *ctx, 97 struct nft_object *obj) 98{ 99 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 100 101 nft_counter_do_destroy(priv); 102} 103 104static void nft_counter_reset(struct nft_counter_percpu_priv *priv, 105 struct nft_counter *total) 106{ 107 struct nft_counter *this_cpu; 108 109 local_bh_disable(); 110 this_cpu = this_cpu_ptr(priv->counter); 111 this_cpu->packets -= total->packets; 112 this_cpu->bytes -= total->bytes; 113 local_bh_enable(); 114} 115 116static void nft_counter_fetch(struct nft_counter_percpu_priv *priv, 117 struct nft_counter *total) 118{ 119 struct nft_counter *this_cpu; 120 const seqcount_t *myseq; 121 u64 bytes, packets; 122 unsigned int seq; 123 int cpu; 124 125 memset(total, 0, sizeof(*total)); 126 for_each_possible_cpu(cpu) { 127 myseq = per_cpu_ptr(&nft_counter_seq, cpu); 128 this_cpu = per_cpu_ptr(priv->counter, cpu); 129 do { 130 seq = read_seqcount_begin(myseq); 131 bytes = this_cpu->bytes; 132 packets = this_cpu->packets; 133 } while (read_seqcount_retry(myseq, seq)); 134 135 total->bytes += bytes; 136 total->packets += packets; 137 } 138} 139 140static int nft_counter_do_dump(struct sk_buff *skb, 141 struct nft_counter_percpu_priv *priv, 142 bool reset) 143{ 144 struct nft_counter total; 145 146 nft_counter_fetch(priv, &total); 147 148 if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes), 149 NFTA_COUNTER_PAD) || 150 nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.packets), 151 NFTA_COUNTER_PAD)) 152 goto nla_put_failure; 153 154 if (reset) 155 nft_counter_reset(priv, &total); 156 157 return 0; 158 159nla_put_failure: 160 return -1; 161} 162 163static int nft_counter_obj_dump(struct sk_buff *skb, 164 struct nft_object *obj, bool reset) 165{ 166 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 167 168 return nft_counter_do_dump(skb, priv, reset); 169} 170 171static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { 172 [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, 173 [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, 174}; 175 176static struct nft_object_type nft_counter_obj_type; 177static const struct nft_object_ops nft_counter_obj_ops = { 178 .type = &nft_counter_obj_type, 179 .size = sizeof(struct nft_counter_percpu_priv), 180 .eval = nft_counter_obj_eval, 181 .init = nft_counter_obj_init, 182 .destroy = nft_counter_obj_destroy, 183 .dump = nft_counter_obj_dump, 184}; 185 186static struct nft_object_type nft_counter_obj_type __read_mostly = { 187 .type = NFT_OBJECT_COUNTER, 188 .ops = &nft_counter_obj_ops, 189 .maxattr = NFTA_COUNTER_MAX, 190 .policy = nft_counter_policy, 191 .owner = THIS_MODULE, 192}; 193 194static void nft_counter_eval(const struct nft_expr *expr, 195 struct nft_regs *regs, 196 const struct nft_pktinfo *pkt) 197{ 198 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 199 200 nft_counter_do_eval(priv, regs, pkt); 201} 202 203static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) 204{ 205 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 206 207 return nft_counter_do_dump(skb, priv, false); 208} 209 210static int nft_counter_init(const struct nft_ctx *ctx, 211 const struct nft_expr *expr, 212 const struct nlattr * const tb[]) 213{ 214 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 215 216 return nft_counter_do_init(tb, priv); 217} 218 219static void nft_counter_destroy(const struct nft_ctx *ctx, 220 const struct nft_expr *expr) 221{ 222 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 223 224 nft_counter_do_destroy(priv); 225} 226 227static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src) 228{ 229 struct nft_counter_percpu_priv *priv = nft_expr_priv(src); 230 struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst); 231 struct nft_counter __percpu *cpu_stats; 232 struct nft_counter *this_cpu; 233 struct nft_counter total; 234 235 nft_counter_fetch(priv, &total); 236 237 cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_ATOMIC); 238 if (cpu_stats == NULL) 239 return -ENOMEM; 240 241 preempt_disable(); 242 this_cpu = this_cpu_ptr(cpu_stats); 243 this_cpu->packets = total.packets; 244 this_cpu->bytes = total.bytes; 245 preempt_enable(); 246 247 priv_clone->counter = cpu_stats; 248 return 0; 249} 250 251static struct nft_expr_type nft_counter_type; 252static const struct nft_expr_ops nft_counter_ops = { 253 .type = &nft_counter_type, 254 .size = NFT_EXPR_SIZE(sizeof(struct nft_counter_percpu_priv)), 255 .eval = nft_counter_eval, 256 .init = nft_counter_init, 257 .destroy = nft_counter_destroy, 258 .destroy_clone = nft_counter_destroy, 259 .dump = nft_counter_dump, 260 .clone = nft_counter_clone, 261}; 262 263static struct nft_expr_type nft_counter_type __read_mostly = { 264 .name = "counter", 265 .ops = &nft_counter_ops, 266 .policy = nft_counter_policy, 267 .maxattr = NFTA_COUNTER_MAX, 268 .flags = NFT_EXPR_STATEFUL, 269 .owner = THIS_MODULE, 270}; 271 272static int __init nft_counter_module_init(void) 273{ 274 int cpu, err; 275 276 for_each_possible_cpu(cpu) 277 seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu)); 278 279 err = nft_register_obj(&nft_counter_obj_type); 280 if (err < 0) 281 return err; 282 283 err = nft_register_expr(&nft_counter_type); 284 if (err < 0) 285 goto err1; 286 287 return 0; 288err1: 289 nft_unregister_obj(&nft_counter_obj_type); 290 return err; 291} 292 293static void __exit nft_counter_module_exit(void) 294{ 295 nft_unregister_expr(&nft_counter_type); 296 nft_unregister_obj(&nft_counter_obj_type); 297} 298 299module_init(nft_counter_module_init); 300module_exit(nft_counter_module_exit); 301 302MODULE_LICENSE("GPL"); 303MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 304MODULE_ALIAS_NFT_EXPR("counter"); 305MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_COUNTER);