Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2017 Pablo Neira Ayuso <pablo@netfilter.org>
0004  */
0005 
0006 #include <linux/kernel.h>
0007 #include <linux/init.h>
0008 #include <linux/module.h>
0009 #include <linux/list.h>
0010 #include <linux/netlink.h>
0011 #include <linux/netfilter.h>
0012 #include <linux/netfilter/nf_tables.h>
0013 #include <net/netfilter/nf_tables_core.h>
0014 
0015 struct nft_bitmap_elem {
0016     struct list_head    head;
0017     struct nft_set_ext  ext;
0018 };
0019 
0020 /* This bitmap uses two bits to represent one element. These two bits determine
0021  * the element state in the current and the future generation.
0022  *
0023  * An element can be in three states. The generation cursor is represented using
0024  * the ^ character, note that this cursor shifts on every successful transaction.
0025  * If no transaction is going on, we observe all elements are in the following
0026  * state:
0027  *
0028  * 11 = this element is active in the current generation. In case of no updates,
0029  * ^    it stays active in the next generation.
0030  * 00 = this element is inactive in the current generation. In case of no
0031  * ^    updates, it stays inactive in the next generation.
0032  *
0033  * On transaction handling, we observe these two temporary states:
0034  *
0035  * 01 = this element is inactive in the current generation and it becomes active
0036  * ^    in the next one. This happens when the element is inserted but commit
0037  *      path has not yet been executed yet, so activation is still pending. On
0038  *      transaction abortion, the element is removed.
0039  * 10 = this element is active in the current generation and it becomes inactive
0040  * ^    in the next one. This happens when the element is deactivated but commit
0041  *      path has not yet been executed yet, so removal is still pending. On
0042  *      transaction abortion, the next generation bit is reset to go back to
0043  *      restore its previous state.
0044  */
0045 struct nft_bitmap {
0046     struct  list_head   list;
0047     u16         bitmap_size;
0048     u8          bitmap[];
0049 };
0050 
0051 static inline void nft_bitmap_location(const struct nft_set *set,
0052                        const void *key,
0053                        u32 *idx, u32 *off)
0054 {
0055     u32 k;
0056 
0057     if (set->klen == 2)
0058         k = *(u16 *)key;
0059     else
0060         k = *(u8 *)key;
0061     k <<= 1;
0062 
0063     *idx = k / BITS_PER_BYTE;
0064     *off = k % BITS_PER_BYTE;
0065 }
0066 
0067 /* Fetch the two bits that represent the element and check if it is active based
0068  * on the generation mask.
0069  */
0070 static inline bool
0071 nft_bitmap_active(const u8 *bitmap, u32 idx, u32 off, u8 genmask)
0072 {
0073     return (bitmap[idx] & (0x3 << off)) & (genmask << off);
0074 }
0075 
0076 INDIRECT_CALLABLE_SCOPE
0077 bool nft_bitmap_lookup(const struct net *net, const struct nft_set *set,
0078                const u32 *key, const struct nft_set_ext **ext)
0079 {
0080     const struct nft_bitmap *priv = nft_set_priv(set);
0081     u8 genmask = nft_genmask_cur(net);
0082     u32 idx, off;
0083 
0084     nft_bitmap_location(set, key, &idx, &off);
0085 
0086     return nft_bitmap_active(priv->bitmap, idx, off, genmask);
0087 }
0088 
0089 static struct nft_bitmap_elem *
0090 nft_bitmap_elem_find(const struct nft_set *set, struct nft_bitmap_elem *this,
0091              u8 genmask)
0092 {
0093     const struct nft_bitmap *priv = nft_set_priv(set);
0094     struct nft_bitmap_elem *be;
0095 
0096     list_for_each_entry_rcu(be, &priv->list, head) {
0097         if (memcmp(nft_set_ext_key(&be->ext),
0098                nft_set_ext_key(&this->ext), set->klen) ||
0099             !nft_set_elem_active(&be->ext, genmask))
0100             continue;
0101 
0102         return be;
0103     }
0104     return NULL;
0105 }
0106 
0107 static void *nft_bitmap_get(const struct net *net, const struct nft_set *set,
0108                 const struct nft_set_elem *elem, unsigned int flags)
0109 {
0110     const struct nft_bitmap *priv = nft_set_priv(set);
0111     u8 genmask = nft_genmask_cur(net);
0112     struct nft_bitmap_elem *be;
0113 
0114     list_for_each_entry_rcu(be, &priv->list, head) {
0115         if (memcmp(nft_set_ext_key(&be->ext), elem->key.val.data, set->klen) ||
0116             !nft_set_elem_active(&be->ext, genmask))
0117             continue;
0118 
0119         return be;
0120     }
0121     return ERR_PTR(-ENOENT);
0122 }
0123 
0124 static int nft_bitmap_insert(const struct net *net, const struct nft_set *set,
0125                  const struct nft_set_elem *elem,
0126                  struct nft_set_ext **ext)
0127 {
0128     struct nft_bitmap *priv = nft_set_priv(set);
0129     struct nft_bitmap_elem *new = elem->priv, *be;
0130     u8 genmask = nft_genmask_next(net);
0131     u32 idx, off;
0132 
0133     be = nft_bitmap_elem_find(set, new, genmask);
0134     if (be) {
0135         *ext = &be->ext;
0136         return -EEXIST;
0137     }
0138 
0139     nft_bitmap_location(set, nft_set_ext_key(&new->ext), &idx, &off);
0140     /* Enter 01 state. */
0141     priv->bitmap[idx] |= (genmask << off);
0142     list_add_tail_rcu(&new->head, &priv->list);
0143 
0144     return 0;
0145 }
0146 
0147 static void nft_bitmap_remove(const struct net *net,
0148                   const struct nft_set *set,
0149                   const struct nft_set_elem *elem)
0150 {
0151     struct nft_bitmap *priv = nft_set_priv(set);
0152     struct nft_bitmap_elem *be = elem->priv;
0153     u8 genmask = nft_genmask_next(net);
0154     u32 idx, off;
0155 
0156     nft_bitmap_location(set, nft_set_ext_key(&be->ext), &idx, &off);
0157     /* Enter 00 state. */
0158     priv->bitmap[idx] &= ~(genmask << off);
0159     list_del_rcu(&be->head);
0160 }
0161 
0162 static void nft_bitmap_activate(const struct net *net,
0163                 const struct nft_set *set,
0164                 const struct nft_set_elem *elem)
0165 {
0166     struct nft_bitmap *priv = nft_set_priv(set);
0167     struct nft_bitmap_elem *be = elem->priv;
0168     u8 genmask = nft_genmask_next(net);
0169     u32 idx, off;
0170 
0171     nft_bitmap_location(set, nft_set_ext_key(&be->ext), &idx, &off);
0172     /* Enter 11 state. */
0173     priv->bitmap[idx] |= (genmask << off);
0174     nft_set_elem_change_active(net, set, &be->ext);
0175 }
0176 
0177 static bool nft_bitmap_flush(const struct net *net,
0178                  const struct nft_set *set, void *_be)
0179 {
0180     struct nft_bitmap *priv = nft_set_priv(set);
0181     u8 genmask = nft_genmask_next(net);
0182     struct nft_bitmap_elem *be = _be;
0183     u32 idx, off;
0184 
0185     nft_bitmap_location(set, nft_set_ext_key(&be->ext), &idx, &off);
0186     /* Enter 10 state, similar to deactivation. */
0187     priv->bitmap[idx] &= ~(genmask << off);
0188     nft_set_elem_change_active(net, set, &be->ext);
0189 
0190     return true;
0191 }
0192 
0193 static void *nft_bitmap_deactivate(const struct net *net,
0194                    const struct nft_set *set,
0195                    const struct nft_set_elem *elem)
0196 {
0197     struct nft_bitmap *priv = nft_set_priv(set);
0198     struct nft_bitmap_elem *this = elem->priv, *be;
0199     u8 genmask = nft_genmask_next(net);
0200     u32 idx, off;
0201 
0202     nft_bitmap_location(set, elem->key.val.data, &idx, &off);
0203 
0204     be = nft_bitmap_elem_find(set, this, genmask);
0205     if (!be)
0206         return NULL;
0207 
0208     /* Enter 10 state. */
0209     priv->bitmap[idx] &= ~(genmask << off);
0210     nft_set_elem_change_active(net, set, &be->ext);
0211 
0212     return be;
0213 }
0214 
0215 static void nft_bitmap_walk(const struct nft_ctx *ctx,
0216                 struct nft_set *set,
0217                 struct nft_set_iter *iter)
0218 {
0219     const struct nft_bitmap *priv = nft_set_priv(set);
0220     struct nft_bitmap_elem *be;
0221     struct nft_set_elem elem;
0222 
0223     list_for_each_entry_rcu(be, &priv->list, head) {
0224         if (iter->count < iter->skip)
0225             goto cont;
0226         if (!nft_set_elem_active(&be->ext, iter->genmask))
0227             goto cont;
0228 
0229         elem.priv = be;
0230 
0231         iter->err = iter->fn(ctx, set, iter, &elem);
0232 
0233         if (iter->err < 0)
0234             return;
0235 cont:
0236         iter->count++;
0237     }
0238 }
0239 
0240 /* The bitmap size is pow(2, key length in bits) / bits per byte. This is
0241  * multiplied by two since each element takes two bits. For 8 bit keys, the
0242  * bitmap consumes 66 bytes. For 16 bit keys, 16388 bytes.
0243  */
0244 static inline u32 nft_bitmap_size(u32 klen)
0245 {
0246     return ((2 << ((klen * BITS_PER_BYTE) - 1)) / BITS_PER_BYTE) << 1;
0247 }
0248 
0249 static inline u64 nft_bitmap_total_size(u32 klen)
0250 {
0251     return sizeof(struct nft_bitmap) + nft_bitmap_size(klen);
0252 }
0253 
0254 static u64 nft_bitmap_privsize(const struct nlattr * const nla[],
0255                    const struct nft_set_desc *desc)
0256 {
0257     u32 klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
0258 
0259     return nft_bitmap_total_size(klen);
0260 }
0261 
0262 static int nft_bitmap_init(const struct nft_set *set,
0263                const struct nft_set_desc *desc,
0264                const struct nlattr * const nla[])
0265 {
0266     struct nft_bitmap *priv = nft_set_priv(set);
0267 
0268     INIT_LIST_HEAD(&priv->list);
0269     priv->bitmap_size = nft_bitmap_size(set->klen);
0270 
0271     return 0;
0272 }
0273 
0274 static void nft_bitmap_destroy(const struct nft_set *set)
0275 {
0276     struct nft_bitmap *priv = nft_set_priv(set);
0277     struct nft_bitmap_elem *be, *n;
0278 
0279     list_for_each_entry_safe(be, n, &priv->list, head)
0280         nft_set_elem_destroy(set, be, true);
0281 }
0282 
0283 static bool nft_bitmap_estimate(const struct nft_set_desc *desc, u32 features,
0284                 struct nft_set_estimate *est)
0285 {
0286     /* Make sure bitmaps we don't get bitmaps larger than 16 Kbytes. */
0287     if (desc->klen > 2)
0288         return false;
0289     else if (desc->expr)
0290         return false;
0291 
0292     est->size   = nft_bitmap_total_size(desc->klen);
0293     est->lookup = NFT_SET_CLASS_O_1;
0294     est->space  = NFT_SET_CLASS_O_1;
0295 
0296     return true;
0297 }
0298 
0299 const struct nft_set_type nft_set_bitmap_type = {
0300     .ops        = {
0301         .privsize   = nft_bitmap_privsize,
0302         .elemsize   = offsetof(struct nft_bitmap_elem, ext),
0303         .estimate   = nft_bitmap_estimate,
0304         .init       = nft_bitmap_init,
0305         .destroy    = nft_bitmap_destroy,
0306         .insert     = nft_bitmap_insert,
0307         .remove     = nft_bitmap_remove,
0308         .deactivate = nft_bitmap_deactivate,
0309         .flush      = nft_bitmap_flush,
0310         .activate   = nft_bitmap_activate,
0311         .lookup     = nft_bitmap_lookup,
0312         .walk       = nft_bitmap_walk,
0313         .get        = nft_bitmap_get,
0314     },
0315 };