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/u64_stats_sync.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 #include <net/netfilter/nf_tables_core.h> 17 #include <net/netfilter/nf_tables_offload.h> 18 19 struct nft_counter { 20 u64_stats_t bytes; 21 u64_stats_t packets; 22 }; 23 24 struct nft_counter_tot { 25 s64 bytes; 26 s64 packets; 27 }; 28 29 struct nft_counter_percpu_priv { 30 struct nft_counter __percpu *counter; 31 }; 32 33 static DEFINE_PER_CPU(struct u64_stats_sync, nft_counter_sync); 34 35 static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv, 36 struct nft_regs *regs, 37 const struct nft_pktinfo *pkt) 38 { 39 struct u64_stats_sync *nft_sync; 40 struct nft_counter *this_cpu; 41 42 local_bh_disable(); 43 this_cpu = this_cpu_ptr(priv->counter); 44 nft_sync = this_cpu_ptr(&nft_counter_sync); 45 46 u64_stats_update_begin(nft_sync); 47 u64_stats_add(&this_cpu->bytes, pkt->skb->len); 48 u64_stats_inc(&this_cpu->packets); 49 u64_stats_update_end(nft_sync); 50 51 local_bh_enable(); 52 } 53 54 static inline void nft_counter_obj_eval(struct nft_object *obj, 55 struct nft_regs *regs, 56 const struct nft_pktinfo *pkt) 57 { 58 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 59 60 nft_counter_do_eval(priv, regs, pkt); 61 } 62 63 static int nft_counter_do_init(const struct nlattr * const tb[], 64 struct nft_counter_percpu_priv *priv) 65 { 66 struct nft_counter __percpu *cpu_stats; 67 struct nft_counter *this_cpu; 68 69 cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_KERNEL_ACCOUNT); 70 if (cpu_stats == NULL) 71 return -ENOMEM; 72 73 this_cpu = raw_cpu_ptr(cpu_stats); 74 if (tb[NFTA_COUNTER_PACKETS]) { 75 u64_stats_set(&this_cpu->packets, 76 be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]))); 77 } 78 if (tb[NFTA_COUNTER_BYTES]) { 79 u64_stats_set(&this_cpu->bytes, 80 be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]))); 81 } 82 83 priv->counter = cpu_stats; 84 return 0; 85 } 86 87 static int nft_counter_obj_init(const struct nft_ctx *ctx, 88 const struct nlattr * const tb[], 89 struct nft_object *obj) 90 { 91 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 92 93 return nft_counter_do_init(tb, priv); 94 } 95 96 static void nft_counter_do_destroy(struct nft_counter_percpu_priv *priv) 97 { 98 free_percpu(priv->counter); 99 } 100 101 static void nft_counter_obj_destroy(const struct nft_ctx *ctx, 102 struct nft_object *obj) 103 { 104 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 105 106 nft_counter_do_destroy(priv); 107 } 108 109 static void nft_counter_reset(struct nft_counter_percpu_priv *priv, 110 struct nft_counter_tot *total) 111 { 112 struct u64_stats_sync *nft_sync; 113 struct nft_counter *this_cpu; 114 115 local_bh_disable(); 116 this_cpu = this_cpu_ptr(priv->counter); 117 nft_sync = this_cpu_ptr(&nft_counter_sync); 118 119 u64_stats_update_begin(nft_sync); 120 u64_stats_add(&this_cpu->packets, -total->packets); 121 u64_stats_add(&this_cpu->bytes, -total->bytes); 122 u64_stats_update_end(nft_sync); 123 124 local_bh_enable(); 125 } 126 127 static void nft_counter_fetch(struct nft_counter_percpu_priv *priv, 128 struct nft_counter_tot *total) 129 { 130 struct nft_counter *this_cpu; 131 u64 bytes, packets; 132 unsigned int seq; 133 int cpu; 134 135 memset(total, 0, sizeof(*total)); 136 for_each_possible_cpu(cpu) { 137 struct u64_stats_sync *nft_sync = per_cpu_ptr(&nft_counter_sync, cpu); 138 139 this_cpu = per_cpu_ptr(priv->counter, cpu); 140 do { 141 seq = u64_stats_fetch_begin(nft_sync); 142 bytes = u64_stats_read(&this_cpu->bytes); 143 packets = u64_stats_read(&this_cpu->packets); 144 } while (u64_stats_fetch_retry(nft_sync, seq)); 145 146 total->bytes += bytes; 147 total->packets += packets; 148 } 149 } 150 151 static int nft_counter_do_dump(struct sk_buff *skb, 152 struct nft_counter_percpu_priv *priv, 153 bool reset) 154 { 155 struct nft_counter_tot total; 156 157 nft_counter_fetch(priv, &total); 158 159 if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes), 160 NFTA_COUNTER_PAD) || 161 nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.packets), 162 NFTA_COUNTER_PAD)) 163 goto nla_put_failure; 164 165 if (reset) 166 nft_counter_reset(priv, &total); 167 168 return 0; 169 170 nla_put_failure: 171 return -1; 172 } 173 174 static int nft_counter_obj_dump(struct sk_buff *skb, 175 struct nft_object *obj, bool reset) 176 { 177 struct nft_counter_percpu_priv *priv = nft_obj_data(obj); 178 179 return nft_counter_do_dump(skb, priv, reset); 180 } 181 182 static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { 183 [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, 184 [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, 185 }; 186 187 struct nft_object_type nft_counter_obj_type; 188 static const struct nft_object_ops nft_counter_obj_ops = { 189 .type = &nft_counter_obj_type, 190 .size = sizeof(struct nft_counter_percpu_priv), 191 .eval = nft_counter_obj_eval, 192 .init = nft_counter_obj_init, 193 .destroy = nft_counter_obj_destroy, 194 .dump = nft_counter_obj_dump, 195 }; 196 197 struct nft_object_type nft_counter_obj_type __read_mostly = { 198 .type = NFT_OBJECT_COUNTER, 199 .ops = &nft_counter_obj_ops, 200 .maxattr = NFTA_COUNTER_MAX, 201 .policy = nft_counter_policy, 202 .owner = THIS_MODULE, 203 }; 204 205 void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs, 206 const struct nft_pktinfo *pkt) 207 { 208 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 209 210 nft_counter_do_eval(priv, regs, pkt); 211 } 212 213 static int nft_counter_dump(struct sk_buff *skb, 214 const struct nft_expr *expr, bool reset) 215 { 216 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 217 218 return nft_counter_do_dump(skb, priv, reset); 219 } 220 221 static int nft_counter_init(const struct nft_ctx *ctx, 222 const struct nft_expr *expr, 223 const struct nlattr * const tb[]) 224 { 225 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 226 227 return nft_counter_do_init(tb, priv); 228 } 229 230 static void nft_counter_destroy(const struct nft_ctx *ctx, 231 const struct nft_expr *expr) 232 { 233 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 234 235 nft_counter_do_destroy(priv); 236 } 237 238 static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src, gfp_t gfp) 239 { 240 struct nft_counter_percpu_priv *priv = nft_expr_priv(src); 241 struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst); 242 struct nft_counter __percpu *cpu_stats; 243 struct nft_counter *this_cpu; 244 struct nft_counter_tot total; 245 246 nft_counter_fetch(priv, &total); 247 248 cpu_stats = alloc_percpu_gfp(struct nft_counter, gfp); 249 if (cpu_stats == NULL) 250 return -ENOMEM; 251 252 this_cpu = raw_cpu_ptr(cpu_stats); 253 u64_stats_set(&this_cpu->packets, total.packets); 254 u64_stats_set(&this_cpu->bytes, total.bytes); 255 256 priv_clone->counter = cpu_stats; 257 return 0; 258 } 259 260 static int nft_counter_offload(struct nft_offload_ctx *ctx, 261 struct nft_flow_rule *flow, 262 const struct nft_expr *expr) 263 { 264 /* No specific offload action is needed, but report success. */ 265 return 0; 266 } 267 268 static void nft_counter_offload_stats(struct nft_expr *expr, 269 const struct flow_stats *stats) 270 { 271 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr); 272 struct u64_stats_sync *nft_sync; 273 struct nft_counter *this_cpu; 274 275 local_bh_disable(); 276 this_cpu = this_cpu_ptr(priv->counter); 277 nft_sync = this_cpu_ptr(&nft_counter_sync); 278 279 u64_stats_update_begin(nft_sync); 280 u64_stats_add(&this_cpu->packets, stats->pkts); 281 u64_stats_add(&this_cpu->bytes, stats->bytes); 282 u64_stats_update_end(nft_sync); 283 local_bh_enable(); 284 } 285 286 void nft_counter_init_seqcount(void) 287 { 288 int cpu; 289 290 for_each_possible_cpu(cpu) 291 u64_stats_init(per_cpu_ptr(&nft_counter_sync, cpu)); 292 } 293 294 struct nft_expr_type nft_counter_type; 295 static const struct nft_expr_ops nft_counter_ops = { 296 .type = &nft_counter_type, 297 .size = NFT_EXPR_SIZE(sizeof(struct nft_counter_percpu_priv)), 298 .eval = nft_counter_eval, 299 .init = nft_counter_init, 300 .destroy = nft_counter_destroy, 301 .destroy_clone = nft_counter_destroy, 302 .dump = nft_counter_dump, 303 .clone = nft_counter_clone, 304 .reduce = NFT_REDUCE_READONLY, 305 .offload = nft_counter_offload, 306 .offload_stats = nft_counter_offload_stats, 307 }; 308 309 struct nft_expr_type nft_counter_type __read_mostly = { 310 .name = "counter", 311 .ops = &nft_counter_ops, 312 .policy = nft_counter_policy, 313 .maxattr = NFTA_COUNTER_MAX, 314 .flags = NFT_EXPR_STATEFUL, 315 .owner = THIS_MODULE, 316 }; 317