1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2013 Eric Leblond <eric@regit.org>
5 * Development of this code partly funded by OISF
6 * (http://www.openinfosecfoundation.org/)
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>
19 static u32 jhash_initval __read_mostly
;
28 static void nft_queue_eval(const struct nft_expr
*expr
,
29 struct nft_regs
*regs
,
30 const struct nft_pktinfo
*pkt
)
32 struct nft_queue
*priv
= nft_expr_priv(expr
);
33 u32 queue
= priv
->queuenum
;
36 if (priv
->queues_total
> 1) {
37 if (priv
->flags
& NFT_QUEUE_FLAG_CPU_FANOUT
) {
38 int cpu
= raw_smp_processor_id();
40 queue
= priv
->queuenum
+ cpu
% priv
->queues_total
;
42 queue
= nfqueue_hash(pkt
->skb
, queue
,
43 priv
->queues_total
, nft_pf(pkt
),
48 ret
= NF_QUEUE_NR(queue
);
49 if (priv
->flags
& NFT_QUEUE_FLAG_BYPASS
)
50 ret
|= NF_VERDICT_FLAG_QUEUE_BYPASS
;
52 regs
->verdict
.code
= ret
;
55 static void nft_queue_sreg_eval(const struct nft_expr
*expr
,
56 struct nft_regs
*regs
,
57 const struct nft_pktinfo
*pkt
)
59 struct nft_queue
*priv
= nft_expr_priv(expr
);
62 queue
= regs
->data
[priv
->sreg_qnum
];
64 ret
= NF_QUEUE_NR(queue
);
65 if (priv
->flags
& NFT_QUEUE_FLAG_BYPASS
)
66 ret
|= NF_VERDICT_FLAG_QUEUE_BYPASS
;
68 regs
->verdict
.code
= ret
;
71 static int nft_queue_validate(const struct nft_ctx
*ctx
,
72 const struct nft_expr
*expr
)
74 static const unsigned int supported_hooks
= ((1 << NF_INET_PRE_ROUTING
) |
75 (1 << NF_INET_LOCAL_IN
) |
76 (1 << NF_INET_FORWARD
) |
77 (1 << NF_INET_LOCAL_OUT
) |
78 (1 << NF_INET_POST_ROUTING
));
80 switch (ctx
->family
) {
86 case NFPROTO_NETDEV
: /* lacks okfn */
92 return nft_chain_validate_hooks(ctx
->chain
, supported_hooks
);
95 static const struct nla_policy nft_queue_policy
[NFTA_QUEUE_MAX
+ 1] = {
96 [NFTA_QUEUE_NUM
] = { .type
= NLA_U16
},
97 [NFTA_QUEUE_TOTAL
] = { .type
= NLA_U16
},
98 [NFTA_QUEUE_FLAGS
] = { .type
= NLA_U16
},
99 [NFTA_QUEUE_SREG_QNUM
] = { .type
= NLA_U32
},
102 static int nft_queue_init(const struct nft_ctx
*ctx
,
103 const struct nft_expr
*expr
,
104 const struct nlattr
* const tb
[])
106 struct nft_queue
*priv
= nft_expr_priv(expr
);
109 priv
->queuenum
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_NUM
]));
111 if (tb
[NFTA_QUEUE_TOTAL
])
112 priv
->queues_total
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_TOTAL
]));
114 priv
->queues_total
= 1;
116 if (priv
->queues_total
== 0)
119 maxid
= priv
->queues_total
- 1 + priv
->queuenum
;
123 if (tb
[NFTA_QUEUE_FLAGS
]) {
124 priv
->flags
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_FLAGS
]));
125 if (priv
->flags
& ~NFT_QUEUE_FLAG_MASK
)
131 static int nft_queue_sreg_init(const struct nft_ctx
*ctx
,
132 const struct nft_expr
*expr
,
133 const struct nlattr
* const tb
[])
135 struct nft_queue
*priv
= nft_expr_priv(expr
);
138 err
= nft_parse_register_load(ctx
, tb
[NFTA_QUEUE_SREG_QNUM
],
139 &priv
->sreg_qnum
, sizeof(u32
));
143 if (tb
[NFTA_QUEUE_FLAGS
]) {
144 priv
->flags
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_FLAGS
]));
145 if (priv
->flags
& ~NFT_QUEUE_FLAG_MASK
)
147 if (priv
->flags
& NFT_QUEUE_FLAG_CPU_FANOUT
)
154 static int nft_queue_dump(struct sk_buff
*skb
,
155 const struct nft_expr
*expr
, bool reset
)
157 const struct nft_queue
*priv
= nft_expr_priv(expr
);
159 if (nla_put_be16(skb
, NFTA_QUEUE_NUM
, htons(priv
->queuenum
)) ||
160 nla_put_be16(skb
, NFTA_QUEUE_TOTAL
, htons(priv
->queues_total
)) ||
161 nla_put_be16(skb
, NFTA_QUEUE_FLAGS
, htons(priv
->flags
)))
162 goto nla_put_failure
;
171 nft_queue_sreg_dump(struct sk_buff
*skb
,
172 const struct nft_expr
*expr
, bool reset
)
174 const struct nft_queue
*priv
= nft_expr_priv(expr
);
176 if (nft_dump_register(skb
, NFTA_QUEUE_SREG_QNUM
, priv
->sreg_qnum
) ||
177 nla_put_be16(skb
, NFTA_QUEUE_FLAGS
, htons(priv
->flags
)))
178 goto nla_put_failure
;
186 static struct nft_expr_type nft_queue_type
;
187 static const struct nft_expr_ops nft_queue_ops
= {
188 .type
= &nft_queue_type
,
189 .size
= NFT_EXPR_SIZE(sizeof(struct nft_queue
)),
190 .eval
= nft_queue_eval
,
191 .init
= nft_queue_init
,
192 .dump
= nft_queue_dump
,
193 .validate
= nft_queue_validate
,
194 .reduce
= NFT_REDUCE_READONLY
,
197 static const struct nft_expr_ops nft_queue_sreg_ops
= {
198 .type
= &nft_queue_type
,
199 .size
= NFT_EXPR_SIZE(sizeof(struct nft_queue
)),
200 .eval
= nft_queue_sreg_eval
,
201 .init
= nft_queue_sreg_init
,
202 .dump
= nft_queue_sreg_dump
,
203 .validate
= nft_queue_validate
,
204 .reduce
= NFT_REDUCE_READONLY
,
207 static const struct nft_expr_ops
*
208 nft_queue_select_ops(const struct nft_ctx
*ctx
,
209 const struct nlattr
* const tb
[])
211 if (tb
[NFTA_QUEUE_NUM
] && tb
[NFTA_QUEUE_SREG_QNUM
])
212 return ERR_PTR(-EINVAL
);
214 init_hashrandom(&jhash_initval
);
216 if (tb
[NFTA_QUEUE_NUM
])
217 return &nft_queue_ops
;
219 if (tb
[NFTA_QUEUE_SREG_QNUM
])
220 return &nft_queue_sreg_ops
;
222 return ERR_PTR(-EINVAL
);
225 static struct nft_expr_type nft_queue_type __read_mostly
= {
227 .select_ops
= nft_queue_select_ops
,
228 .policy
= nft_queue_policy
,
229 .maxattr
= NFTA_QUEUE_MAX
,
230 .owner
= THIS_MODULE
,
233 static int __init
nft_queue_module_init(void)
235 return nft_register_expr(&nft_queue_type
);
238 static void __exit
nft_queue_module_exit(void)
240 nft_unregister_expr(&nft_queue_type
);
243 module_init(nft_queue_module_init
);
244 module_exit(nft_queue_module_exit
);
246 MODULE_LICENSE("GPL");
247 MODULE_AUTHOR("Eric Leblond <eric@regit.org>");
248 MODULE_ALIAS_NFT_EXPR("queue");
249 MODULE_DESCRIPTION("Netfilter nftables queue module");