Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 #include <linux/kernel.h>
0003 #include <linux/init.h>
0004 #include <linux/module.h>
0005 #include <linux/spinlock.h>
0006 #include <linux/netlink.h>
0007 #include <linux/netfilter.h>
0008 #include <linux/netfilter/nf_tables.h>
0009 #include <net/netfilter/nf_tables.h>
0010 #include <net/netfilter/nf_conntrack.h>
0011 #include <net/netfilter/nf_conntrack_count.h>
0012 #include <net/netfilter/nf_conntrack_core.h>
0013 #include <net/netfilter/nf_conntrack_tuple.h>
0014 #include <net/netfilter/nf_conntrack_zones.h>
0015 
0016 struct nft_connlimit {
0017     struct nf_conncount_list    *list;
0018     u32             limit;
0019     bool                invert;
0020 };
0021 
0022 static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
0023                      struct nft_regs *regs,
0024                      const struct nft_pktinfo *pkt,
0025                      const struct nft_set_ext *ext)
0026 {
0027     const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
0028     const struct nf_conntrack_tuple *tuple_ptr;
0029     struct nf_conntrack_tuple tuple;
0030     enum ip_conntrack_info ctinfo;
0031     const struct nf_conn *ct;
0032     unsigned int count;
0033 
0034     tuple_ptr = &tuple;
0035 
0036     ct = nf_ct_get(pkt->skb, &ctinfo);
0037     if (ct != NULL) {
0038         tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
0039         zone = nf_ct_zone(ct);
0040     } else if (!nf_ct_get_tuplepr(pkt->skb, skb_network_offset(pkt->skb),
0041                       nft_pf(pkt), nft_net(pkt), &tuple)) {
0042         regs->verdict.code = NF_DROP;
0043         return;
0044     }
0045 
0046     if (nf_conncount_add(nft_net(pkt), priv->list, tuple_ptr, zone)) {
0047         regs->verdict.code = NF_DROP;
0048         return;
0049     }
0050 
0051     count = priv->list->count;
0052 
0053     if ((count > priv->limit) ^ priv->invert) {
0054         regs->verdict.code = NFT_BREAK;
0055         return;
0056     }
0057 }
0058 
0059 static int nft_connlimit_do_init(const struct nft_ctx *ctx,
0060                  const struct nlattr * const tb[],
0061                  struct nft_connlimit *priv)
0062 {
0063     bool invert = false;
0064     u32 flags, limit;
0065     int err;
0066 
0067     if (!tb[NFTA_CONNLIMIT_COUNT])
0068         return -EINVAL;
0069 
0070     limit = ntohl(nla_get_be32(tb[NFTA_CONNLIMIT_COUNT]));
0071 
0072     if (tb[NFTA_CONNLIMIT_FLAGS]) {
0073         flags = ntohl(nla_get_be32(tb[NFTA_CONNLIMIT_FLAGS]));
0074         if (flags & ~NFT_CONNLIMIT_F_INV)
0075             return -EOPNOTSUPP;
0076         if (flags & NFT_CONNLIMIT_F_INV)
0077             invert = true;
0078     }
0079 
0080     priv->list = kmalloc(sizeof(*priv->list), GFP_KERNEL_ACCOUNT);
0081     if (!priv->list)
0082         return -ENOMEM;
0083 
0084     nf_conncount_list_init(priv->list);
0085     priv->limit = limit;
0086     priv->invert    = invert;
0087 
0088     err = nf_ct_netns_get(ctx->net, ctx->family);
0089     if (err < 0)
0090         goto err_netns;
0091 
0092     return 0;
0093 err_netns:
0094     kfree(priv->list);
0095 
0096     return err;
0097 }
0098 
0099 static void nft_connlimit_do_destroy(const struct nft_ctx *ctx,
0100                      struct nft_connlimit *priv)
0101 {
0102     nf_ct_netns_put(ctx->net, ctx->family);
0103     nf_conncount_cache_free(priv->list);
0104     kfree(priv->list);
0105 }
0106 
0107 static int nft_connlimit_do_dump(struct sk_buff *skb,
0108                  struct nft_connlimit *priv)
0109 {
0110     if (nla_put_be32(skb, NFTA_CONNLIMIT_COUNT, htonl(priv->limit)))
0111         goto nla_put_failure;
0112     if (priv->invert &&
0113         nla_put_be32(skb, NFTA_CONNLIMIT_FLAGS, htonl(NFT_CONNLIMIT_F_INV)))
0114         goto nla_put_failure;
0115 
0116     return 0;
0117 
0118 nla_put_failure:
0119     return -1;
0120 }
0121 
0122 static inline void nft_connlimit_obj_eval(struct nft_object *obj,
0123                     struct nft_regs *regs,
0124                     const struct nft_pktinfo *pkt)
0125 {
0126     struct nft_connlimit *priv = nft_obj_data(obj);
0127 
0128     nft_connlimit_do_eval(priv, regs, pkt, NULL);
0129 }
0130 
0131 static int nft_connlimit_obj_init(const struct nft_ctx *ctx,
0132                 const struct nlattr * const tb[],
0133                 struct nft_object *obj)
0134 {
0135     struct nft_connlimit *priv = nft_obj_data(obj);
0136 
0137     return nft_connlimit_do_init(ctx, tb, priv);
0138 }
0139 
0140 static void nft_connlimit_obj_destroy(const struct nft_ctx *ctx,
0141                       struct nft_object *obj)
0142 {
0143     struct nft_connlimit *priv = nft_obj_data(obj);
0144 
0145     nft_connlimit_do_destroy(ctx, priv);
0146 }
0147 
0148 static int nft_connlimit_obj_dump(struct sk_buff *skb,
0149                   struct nft_object *obj, bool reset)
0150 {
0151     struct nft_connlimit *priv = nft_obj_data(obj);
0152 
0153     return nft_connlimit_do_dump(skb, priv);
0154 }
0155 
0156 static const struct nla_policy nft_connlimit_policy[NFTA_CONNLIMIT_MAX + 1] = {
0157     [NFTA_CONNLIMIT_COUNT]  = { .type = NLA_U32 },
0158     [NFTA_CONNLIMIT_FLAGS]  = { .type = NLA_U32 },
0159 };
0160 
0161 static struct nft_object_type nft_connlimit_obj_type;
0162 static const struct nft_object_ops nft_connlimit_obj_ops = {
0163     .type       = &nft_connlimit_obj_type,
0164     .size       = sizeof(struct nft_connlimit),
0165     .eval       = nft_connlimit_obj_eval,
0166     .init       = nft_connlimit_obj_init,
0167     .destroy    = nft_connlimit_obj_destroy,
0168     .dump       = nft_connlimit_obj_dump,
0169 };
0170 
0171 static struct nft_object_type nft_connlimit_obj_type __read_mostly = {
0172     .type       = NFT_OBJECT_CONNLIMIT,
0173     .ops        = &nft_connlimit_obj_ops,
0174     .maxattr    = NFTA_CONNLIMIT_MAX,
0175     .policy     = nft_connlimit_policy,
0176     .owner      = THIS_MODULE,
0177 };
0178 
0179 static void nft_connlimit_eval(const struct nft_expr *expr,
0180                    struct nft_regs *regs,
0181                    const struct nft_pktinfo *pkt)
0182 {
0183     struct nft_connlimit *priv = nft_expr_priv(expr);
0184 
0185     nft_connlimit_do_eval(priv, regs, pkt, NULL);
0186 }
0187 
0188 static int nft_connlimit_dump(struct sk_buff *skb, const struct nft_expr *expr)
0189 {
0190     struct nft_connlimit *priv = nft_expr_priv(expr);
0191 
0192     return nft_connlimit_do_dump(skb, priv);
0193 }
0194 
0195 static int nft_connlimit_init(const struct nft_ctx *ctx,
0196                   const struct nft_expr *expr,
0197                   const struct nlattr * const tb[])
0198 {
0199     struct nft_connlimit *priv = nft_expr_priv(expr);
0200 
0201     return nft_connlimit_do_init(ctx, tb, priv);
0202 }
0203 
0204 static void nft_connlimit_destroy(const struct nft_ctx *ctx,
0205                 const struct nft_expr *expr)
0206 {
0207     struct nft_connlimit *priv = nft_expr_priv(expr);
0208 
0209     nft_connlimit_do_destroy(ctx, priv);
0210 }
0211 
0212 static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src)
0213 {
0214     struct nft_connlimit *priv_dst = nft_expr_priv(dst);
0215     struct nft_connlimit *priv_src = nft_expr_priv(src);
0216 
0217     priv_dst->list = kmalloc(sizeof(*priv_dst->list), GFP_ATOMIC);
0218     if (!priv_dst->list)
0219         return -ENOMEM;
0220 
0221     nf_conncount_list_init(priv_dst->list);
0222     priv_dst->limit  = priv_src->limit;
0223     priv_dst->invert = priv_src->invert;
0224 
0225     return 0;
0226 }
0227 
0228 static void nft_connlimit_destroy_clone(const struct nft_ctx *ctx,
0229                     const struct nft_expr *expr)
0230 {
0231     struct nft_connlimit *priv = nft_expr_priv(expr);
0232 
0233     nf_conncount_cache_free(priv->list);
0234     kfree(priv->list);
0235 }
0236 
0237 static bool nft_connlimit_gc(struct net *net, const struct nft_expr *expr)
0238 {
0239     struct nft_connlimit *priv = nft_expr_priv(expr);
0240     bool ret;
0241 
0242     local_bh_disable();
0243     ret = nf_conncount_gc_list(net, priv->list);
0244     local_bh_enable();
0245 
0246     return ret;
0247 }
0248 
0249 static struct nft_expr_type nft_connlimit_type;
0250 static const struct nft_expr_ops nft_connlimit_ops = {
0251     .type       = &nft_connlimit_type,
0252     .size       = NFT_EXPR_SIZE(sizeof(struct nft_connlimit)),
0253     .eval       = nft_connlimit_eval,
0254     .init       = nft_connlimit_init,
0255     .destroy    = nft_connlimit_destroy,
0256     .clone      = nft_connlimit_clone,
0257     .destroy_clone  = nft_connlimit_destroy_clone,
0258     .dump       = nft_connlimit_dump,
0259     .gc     = nft_connlimit_gc,
0260     .reduce     = NFT_REDUCE_READONLY,
0261 };
0262 
0263 static struct nft_expr_type nft_connlimit_type __read_mostly = {
0264     .name       = "connlimit",
0265     .ops        = &nft_connlimit_ops,
0266     .policy     = nft_connlimit_policy,
0267     .maxattr    = NFTA_CONNLIMIT_MAX,
0268     .flags      = NFT_EXPR_STATEFUL | NFT_EXPR_GC,
0269     .owner      = THIS_MODULE,
0270 };
0271 
0272 static int __init nft_connlimit_module_init(void)
0273 {
0274     int err;
0275 
0276     err = nft_register_obj(&nft_connlimit_obj_type);
0277     if (err < 0)
0278         return err;
0279 
0280     err = nft_register_expr(&nft_connlimit_type);
0281     if (err < 0)
0282         goto err1;
0283 
0284     return 0;
0285 err1:
0286     nft_unregister_obj(&nft_connlimit_obj_type);
0287     return err;
0288 }
0289 
0290 static void __exit nft_connlimit_module_exit(void)
0291 {
0292     nft_unregister_expr(&nft_connlimit_type);
0293     nft_unregister_obj(&nft_connlimit_obj_type);
0294 }
0295 
0296 module_init(nft_connlimit_module_init);
0297 module_exit(nft_connlimit_module_exit);
0298 
0299 MODULE_LICENSE("GPL");
0300 MODULE_AUTHOR("Pablo Neira Ayuso");
0301 MODULE_ALIAS_NFT_EXPR("connlimit");
0302 MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CONNLIMIT);
0303 MODULE_DESCRIPTION("nftables connlimit rule support");