0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/kernel.h>
0009 #include <linux/init.h>
0010 #include <linux/module.h>
0011 #include <linux/seqlock.h>
0012 #include <linux/netlink.h>
0013 #include <linux/netfilter.h>
0014 #include <linux/netfilter/nf_tables.h>
0015 #include <net/netfilter/nf_tables.h>
0016 #include <net/netfilter/nf_tables_core.h>
0017 #include <net/netfilter/nf_tables_offload.h>
0018
0019 struct nft_counter {
0020 s64 bytes;
0021 s64 packets;
0022 };
0023
0024 struct nft_counter_percpu_priv {
0025 struct nft_counter __percpu *counter;
0026 };
0027
0028 static DEFINE_PER_CPU(seqcount_t, nft_counter_seq);
0029
0030 static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv,
0031 struct nft_regs *regs,
0032 const struct nft_pktinfo *pkt)
0033 {
0034 struct nft_counter *this_cpu;
0035 seqcount_t *myseq;
0036
0037 local_bh_disable();
0038 this_cpu = this_cpu_ptr(priv->counter);
0039 myseq = this_cpu_ptr(&nft_counter_seq);
0040
0041 write_seqcount_begin(myseq);
0042
0043 this_cpu->bytes += pkt->skb->len;
0044 this_cpu->packets++;
0045
0046 write_seqcount_end(myseq);
0047 local_bh_enable();
0048 }
0049
0050 static inline void nft_counter_obj_eval(struct nft_object *obj,
0051 struct nft_regs *regs,
0052 const struct nft_pktinfo *pkt)
0053 {
0054 struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
0055
0056 nft_counter_do_eval(priv, regs, pkt);
0057 }
0058
0059 static int nft_counter_do_init(const struct nlattr * const tb[],
0060 struct nft_counter_percpu_priv *priv)
0061 {
0062 struct nft_counter __percpu *cpu_stats;
0063 struct nft_counter *this_cpu;
0064
0065 cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_KERNEL_ACCOUNT);
0066 if (cpu_stats == NULL)
0067 return -ENOMEM;
0068
0069 preempt_disable();
0070 this_cpu = this_cpu_ptr(cpu_stats);
0071 if (tb[NFTA_COUNTER_PACKETS]) {
0072 this_cpu->packets =
0073 be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
0074 }
0075 if (tb[NFTA_COUNTER_BYTES]) {
0076 this_cpu->bytes =
0077 be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
0078 }
0079 preempt_enable();
0080 priv->counter = cpu_stats;
0081 return 0;
0082 }
0083
0084 static int nft_counter_obj_init(const struct nft_ctx *ctx,
0085 const struct nlattr * const tb[],
0086 struct nft_object *obj)
0087 {
0088 struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
0089
0090 return nft_counter_do_init(tb, priv);
0091 }
0092
0093 static void nft_counter_do_destroy(struct nft_counter_percpu_priv *priv)
0094 {
0095 free_percpu(priv->counter);
0096 }
0097
0098 static void nft_counter_obj_destroy(const struct nft_ctx *ctx,
0099 struct nft_object *obj)
0100 {
0101 struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
0102
0103 nft_counter_do_destroy(priv);
0104 }
0105
0106 static void nft_counter_reset(struct nft_counter_percpu_priv *priv,
0107 struct nft_counter *total)
0108 {
0109 struct nft_counter *this_cpu;
0110
0111 local_bh_disable();
0112 this_cpu = this_cpu_ptr(priv->counter);
0113 this_cpu->packets -= total->packets;
0114 this_cpu->bytes -= total->bytes;
0115 local_bh_enable();
0116 }
0117
0118 static void nft_counter_fetch(struct nft_counter_percpu_priv *priv,
0119 struct nft_counter *total)
0120 {
0121 struct nft_counter *this_cpu;
0122 const seqcount_t *myseq;
0123 u64 bytes, packets;
0124 unsigned int seq;
0125 int cpu;
0126
0127 memset(total, 0, sizeof(*total));
0128 for_each_possible_cpu(cpu) {
0129 myseq = per_cpu_ptr(&nft_counter_seq, cpu);
0130 this_cpu = per_cpu_ptr(priv->counter, cpu);
0131 do {
0132 seq = read_seqcount_begin(myseq);
0133 bytes = this_cpu->bytes;
0134 packets = this_cpu->packets;
0135 } while (read_seqcount_retry(myseq, seq));
0136
0137 total->bytes += bytes;
0138 total->packets += packets;
0139 }
0140 }
0141
0142 static int nft_counter_do_dump(struct sk_buff *skb,
0143 struct nft_counter_percpu_priv *priv,
0144 bool reset)
0145 {
0146 struct nft_counter total;
0147
0148 nft_counter_fetch(priv, &total);
0149
0150 if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes),
0151 NFTA_COUNTER_PAD) ||
0152 nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.packets),
0153 NFTA_COUNTER_PAD))
0154 goto nla_put_failure;
0155
0156 if (reset)
0157 nft_counter_reset(priv, &total);
0158
0159 return 0;
0160
0161 nla_put_failure:
0162 return -1;
0163 }
0164
0165 static int nft_counter_obj_dump(struct sk_buff *skb,
0166 struct nft_object *obj, bool reset)
0167 {
0168 struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
0169
0170 return nft_counter_do_dump(skb, priv, reset);
0171 }
0172
0173 static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
0174 [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
0175 [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
0176 };
0177
0178 struct nft_object_type nft_counter_obj_type;
0179 static const struct nft_object_ops nft_counter_obj_ops = {
0180 .type = &nft_counter_obj_type,
0181 .size = sizeof(struct nft_counter_percpu_priv),
0182 .eval = nft_counter_obj_eval,
0183 .init = nft_counter_obj_init,
0184 .destroy = nft_counter_obj_destroy,
0185 .dump = nft_counter_obj_dump,
0186 };
0187
0188 struct nft_object_type nft_counter_obj_type __read_mostly = {
0189 .type = NFT_OBJECT_COUNTER,
0190 .ops = &nft_counter_obj_ops,
0191 .maxattr = NFTA_COUNTER_MAX,
0192 .policy = nft_counter_policy,
0193 .owner = THIS_MODULE,
0194 };
0195
0196 void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs,
0197 const struct nft_pktinfo *pkt)
0198 {
0199 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
0200
0201 nft_counter_do_eval(priv, regs, pkt);
0202 }
0203
0204 static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
0205 {
0206 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
0207
0208 return nft_counter_do_dump(skb, priv, false);
0209 }
0210
0211 static int nft_counter_init(const struct nft_ctx *ctx,
0212 const struct nft_expr *expr,
0213 const struct nlattr * const tb[])
0214 {
0215 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
0216
0217 return nft_counter_do_init(tb, priv);
0218 }
0219
0220 static void nft_counter_destroy(const struct nft_ctx *ctx,
0221 const struct nft_expr *expr)
0222 {
0223 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
0224
0225 nft_counter_do_destroy(priv);
0226 }
0227
0228 static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src)
0229 {
0230 struct nft_counter_percpu_priv *priv = nft_expr_priv(src);
0231 struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst);
0232 struct nft_counter __percpu *cpu_stats;
0233 struct nft_counter *this_cpu;
0234 struct nft_counter total;
0235
0236 nft_counter_fetch(priv, &total);
0237
0238 cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_ATOMIC);
0239 if (cpu_stats == NULL)
0240 return -ENOMEM;
0241
0242 preempt_disable();
0243 this_cpu = this_cpu_ptr(cpu_stats);
0244 this_cpu->packets = total.packets;
0245 this_cpu->bytes = total.bytes;
0246 preempt_enable();
0247
0248 priv_clone->counter = cpu_stats;
0249 return 0;
0250 }
0251
0252 static int nft_counter_offload(struct nft_offload_ctx *ctx,
0253 struct nft_flow_rule *flow,
0254 const struct nft_expr *expr)
0255 {
0256
0257 return 0;
0258 }
0259
0260 static void nft_counter_offload_stats(struct nft_expr *expr,
0261 const struct flow_stats *stats)
0262 {
0263 struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
0264 struct nft_counter *this_cpu;
0265 seqcount_t *myseq;
0266
0267 preempt_disable();
0268 this_cpu = this_cpu_ptr(priv->counter);
0269 myseq = this_cpu_ptr(&nft_counter_seq);
0270
0271 write_seqcount_begin(myseq);
0272 this_cpu->packets += stats->pkts;
0273 this_cpu->bytes += stats->bytes;
0274 write_seqcount_end(myseq);
0275 preempt_enable();
0276 }
0277
0278 void nft_counter_init_seqcount(void)
0279 {
0280 int cpu;
0281
0282 for_each_possible_cpu(cpu)
0283 seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu));
0284 }
0285
0286 struct nft_expr_type nft_counter_type;
0287 static const struct nft_expr_ops nft_counter_ops = {
0288 .type = &nft_counter_type,
0289 .size = NFT_EXPR_SIZE(sizeof(struct nft_counter_percpu_priv)),
0290 .eval = nft_counter_eval,
0291 .init = nft_counter_init,
0292 .destroy = nft_counter_destroy,
0293 .destroy_clone = nft_counter_destroy,
0294 .dump = nft_counter_dump,
0295 .clone = nft_counter_clone,
0296 .reduce = NFT_REDUCE_READONLY,
0297 .offload = nft_counter_offload,
0298 .offload_stats = nft_counter_offload_stats,
0299 };
0300
0301 struct nft_expr_type nft_counter_type __read_mostly = {
0302 .name = "counter",
0303 .ops = &nft_counter_ops,
0304 .policy = nft_counter_policy,
0305 .maxattr = NFTA_COUNTER_MAX,
0306 .flags = NFT_EXPR_STATEFUL,
0307 .owner = THIS_MODULE,
0308 };