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 17 struct nft_counter { 18 s64 bytes; 19 s64 packets; 20 }; 21 22 struct nft_counter_percpu_priv { 23 struct nft_counter __percpu *counter; 24 }; 25 26 static DEFINE_PER_CPU(seqcount_t, nft_counter_seq); 27 28 static 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 48 static 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 57 static 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 82 static 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 91 static void nft_counter_do_destroy(struct nft_counter_percpu_priv *priv) 92 { 93 free_percpu(priv->counter); 94 } 95 96 static 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 104 static 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 116 static 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 140 static 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 159 nla_put_failure: 160 return -1; 161 } 162 163 static 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 171 static 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 176 static struct nft_object_type nft_counter_obj_type; 177 static 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 186 static 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 194 static 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 203 static 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 210 static 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 219 static 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 227 static 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 251 static struct nft_expr_type nft_counter_type; 252 static 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 263 static 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 272 static 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; 288 err1: 289 nft_unregister_obj(&nft_counter_obj_type); 290 return err; 291 } 292 293 static 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 299 module_init(nft_counter_module_init); 300 module_exit(nft_counter_module_exit); 301 302 MODULE_LICENSE("GPL"); 303 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 304 MODULE_ALIAS_NFT_EXPR("counter"); 305 MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_COUNTER); 306 MODULE_DESCRIPTION("nftables counter rule support"); 307