nft_queue.c (5400B)
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 .reduce = NFT_REDUCE_READONLY, 168}; 169 170static const struct nft_expr_ops nft_queue_sreg_ops = { 171 .type = &nft_queue_type, 172 .size = NFT_EXPR_SIZE(sizeof(struct nft_queue)), 173 .eval = nft_queue_sreg_eval, 174 .init = nft_queue_sreg_init, 175 .dump = nft_queue_sreg_dump, 176 .reduce = NFT_REDUCE_READONLY, 177}; 178 179static const struct nft_expr_ops * 180nft_queue_select_ops(const struct nft_ctx *ctx, 181 const struct nlattr * const tb[]) 182{ 183 if (tb[NFTA_QUEUE_NUM] && tb[NFTA_QUEUE_SREG_QNUM]) 184 return ERR_PTR(-EINVAL); 185 186 init_hashrandom(&jhash_initval); 187 188 if (tb[NFTA_QUEUE_NUM]) 189 return &nft_queue_ops; 190 191 if (tb[NFTA_QUEUE_SREG_QNUM]) 192 return &nft_queue_sreg_ops; 193 194 return ERR_PTR(-EINVAL); 195} 196 197static struct nft_expr_type nft_queue_type __read_mostly = { 198 .name = "queue", 199 .select_ops = nft_queue_select_ops, 200 .policy = nft_queue_policy, 201 .maxattr = NFTA_QUEUE_MAX, 202 .owner = THIS_MODULE, 203}; 204 205static int __init nft_queue_module_init(void) 206{ 207 return nft_register_expr(&nft_queue_type); 208} 209 210static void __exit nft_queue_module_exit(void) 211{ 212 nft_unregister_expr(&nft_queue_type); 213} 214 215module_init(nft_queue_module_init); 216module_exit(nft_queue_module_exit); 217 218MODULE_LICENSE("GPL"); 219MODULE_AUTHOR("Eric Leblond <eric@regit.org>"); 220MODULE_ALIAS_NFT_EXPR("queue"); 221MODULE_DESCRIPTION("Netfilter nftables queue module");