linux/net/netfilter/nft_queue.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2013 Eric Leblond <eric@regit.org>
   4 *
   5 * Development of this code partly funded by OISF
   6 * (http://www.openinfosecfoundation.org/)
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/init.h>
  11#include <linux/module.h>
  12#include <linux/netlink.h>
  13#include <linux/jhash.h>
  14#include <linux/netfilter.h>
  15#include <linux/netfilter/nf_tables.h>
  16#include <net/netfilter/nf_tables.h>
  17#include <net/netfilter/nf_queue.h>
  18
  19static u32 jhash_initval __read_mostly;
  20
  21struct nft_queue {
  22        u8      sreg_qnum;
  23        u16     queuenum;
  24        u16     queues_total;
  25        u16     flags;
  26};
  27
  28static void nft_queue_eval(const struct nft_expr *expr,
  29                           struct nft_regs *regs,
  30                           const struct nft_pktinfo *pkt)
  31{
  32        struct nft_queue *priv = nft_expr_priv(expr);
  33        u32 queue = priv->queuenum;
  34        u32 ret;
  35
  36        if (priv->queues_total > 1) {
  37                if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT) {
  38                        int cpu = raw_smp_processor_id();
  39
  40                        queue = priv->queuenum + cpu % priv->queues_total;
  41                } else {
  42                        queue = nfqueue_hash(pkt->skb, queue,
  43                                             priv->queues_total, nft_pf(pkt),
  44                                             jhash_initval);
  45                }
  46        }
  47
  48        ret = NF_QUEUE_NR(queue);
  49        if (priv->flags & NFT_QUEUE_FLAG_BYPASS)
  50                ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
  51
  52        regs->verdict.code = ret;
  53}
  54
  55static void nft_queue_sreg_eval(const struct nft_expr *expr,
  56                                struct nft_regs *regs,
  57                                const struct nft_pktinfo *pkt)
  58{
  59        struct nft_queue *priv = nft_expr_priv(expr);
  60        u32 queue, ret;
  61
  62        queue = regs->data[priv->sreg_qnum];
  63
  64        ret = NF_QUEUE_NR(queue);
  65        if (priv->flags & NFT_QUEUE_FLAG_BYPASS)
  66                ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
  67
  68        regs->verdict.code = ret;
  69}
  70
  71static const struct nla_policy nft_queue_policy[NFTA_QUEUE_MAX + 1] = {
  72        [NFTA_QUEUE_NUM]        = { .type = NLA_U16 },
  73        [NFTA_QUEUE_TOTAL]      = { .type = NLA_U16 },
  74        [NFTA_QUEUE_FLAGS]      = { .type = NLA_U16 },
  75        [NFTA_QUEUE_SREG_QNUM]  = { .type = NLA_U32 },
  76};
  77
  78static int nft_queue_init(const struct nft_ctx *ctx,
  79                          const struct nft_expr *expr,
  80                          const struct nlattr * const tb[])
  81{
  82        struct nft_queue *priv = nft_expr_priv(expr);
  83        u32 maxid;
  84
  85        priv->queuenum = ntohs(nla_get_be16(tb[NFTA_QUEUE_NUM]));
  86
  87        if (tb[NFTA_QUEUE_TOTAL])
  88                priv->queues_total = ntohs(nla_get_be16(tb[NFTA_QUEUE_TOTAL]));
  89        else
  90                priv->queues_total = 1;
  91
  92        if (priv->queues_total == 0)
  93                return -EINVAL;
  94
  95        maxid = priv->queues_total - 1 + priv->queuenum;
  96        if (maxid > U16_MAX)
  97                return -ERANGE;
  98
  99        if (tb[NFTA_QUEUE_FLAGS]) {
 100                priv->flags = ntohs(nla_get_be16(tb[NFTA_QUEUE_FLAGS]));
 101                if (priv->flags & ~NFT_QUEUE_FLAG_MASK)
 102                        return -EINVAL;
 103        }
 104        return 0;
 105}
 106
 107static int nft_queue_sreg_init(const struct nft_ctx *ctx,
 108                               const struct nft_expr *expr,
 109                               const struct nlattr * const tb[])
 110{
 111        struct nft_queue *priv = nft_expr_priv(expr);
 112        int err;
 113
 114        err = nft_parse_register_load(tb[NFTA_QUEUE_SREG_QNUM],
 115                                      &priv->sreg_qnum, sizeof(u32));
 116        if (err < 0)
 117                return err;
 118
 119        if (tb[NFTA_QUEUE_FLAGS]) {
 120                priv->flags = ntohs(nla_get_be16(tb[NFTA_QUEUE_FLAGS]));
 121                if (priv->flags & ~NFT_QUEUE_FLAG_MASK)
 122                        return -EINVAL;
 123                if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT)
 124                        return -EOPNOTSUPP;
 125        }
 126
 127        return 0;
 128}
 129
 130static int nft_queue_dump(struct sk_buff *skb, const struct nft_expr *expr)
 131{
 132        const struct nft_queue *priv = nft_expr_priv(expr);
 133
 134        if (nla_put_be16(skb, NFTA_QUEUE_NUM, htons(priv->queuenum)) ||
 135            nla_put_be16(skb, NFTA_QUEUE_TOTAL, htons(priv->queues_total)) ||
 136            nla_put_be16(skb, NFTA_QUEUE_FLAGS, htons(priv->flags)))
 137                goto nla_put_failure;
 138
 139        return 0;
 140
 141nla_put_failure:
 142        return -1;
 143}
 144
 145static int
 146nft_queue_sreg_dump(struct sk_buff *skb, const struct nft_expr *expr)
 147{
 148        const struct nft_queue *priv = nft_expr_priv(expr);
 149
 150        if (nft_dump_register(skb, NFTA_QUEUE_SREG_QNUM, priv->sreg_qnum) ||
 151            nla_put_be16(skb, NFTA_QUEUE_FLAGS, htons(priv->flags)))
 152                goto nla_put_failure;
 153
 154        return 0;
 155
 156nla_put_failure:
 157        return -1;
 158}
 159
 160static struct nft_expr_type nft_queue_type;
 161static const struct nft_expr_ops nft_queue_ops = {
 162        .type           = &nft_queue_type,
 163        .size           = NFT_EXPR_SIZE(sizeof(struct nft_queue)),
 164        .eval           = nft_queue_eval,
 165        .init           = nft_queue_init,
 166        .dump           = nft_queue_dump,
 167};
 168
 169static const struct nft_expr_ops nft_queue_sreg_ops = {
 170        .type           = &nft_queue_type,
 171        .size           = NFT_EXPR_SIZE(sizeof(struct nft_queue)),
 172        .eval           = nft_queue_sreg_eval,
 173        .init           = nft_queue_sreg_init,
 174        .dump           = nft_queue_sreg_dump,
 175};
 176
 177static const struct nft_expr_ops *
 178nft_queue_select_ops(const struct nft_ctx *ctx,
 179                     const struct nlattr * const tb[])
 180{
 181        if (tb[NFTA_QUEUE_NUM] && tb[NFTA_QUEUE_SREG_QNUM])
 182                return ERR_PTR(-EINVAL);
 183
 184        init_hashrandom(&jhash_initval);
 185
 186        if (tb[NFTA_QUEUE_NUM])
 187                return &nft_queue_ops;
 188
 189        if (tb[NFTA_QUEUE_SREG_QNUM])
 190                return &nft_queue_sreg_ops;
 191
 192        return ERR_PTR(-EINVAL);
 193}
 194
 195static struct nft_expr_type nft_queue_type __read_mostly = {
 196        .name           = "queue",
 197        .select_ops     = nft_queue_select_ops,
 198        .policy         = nft_queue_policy,
 199        .maxattr        = NFTA_QUEUE_MAX,
 200        .owner          = THIS_MODULE,
 201};
 202
 203static int __init nft_queue_module_init(void)
 204{
 205        return nft_register_expr(&nft_queue_type);
 206}
 207
 208static void __exit nft_queue_module_exit(void)
 209{
 210        nft_unregister_expr(&nft_queue_type);
 211}
 212
 213module_init(nft_queue_module_init);
 214module_exit(nft_queue_module_exit);
 215
 216MODULE_LICENSE("GPL");
 217MODULE_AUTHOR("Eric Leblond <eric@regit.org>");
 218MODULE_ALIAS_NFT_EXPR("queue");
 219MODULE_DESCRIPTION("Netfilter nftables queue module");
 220