Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
0004  *
0005  * Development of this code funded by Astaro AG (http://www.astaro.com/)
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     /* No specific offload action is needed, but report success. */
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 };