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
;
22 enum nft_registers sreg_qnum
:8;
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 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
},
78 static int nft_queue_init(const struct nft_ctx
*ctx
,
79 const struct nft_expr
*expr
,
80 const struct nlattr
* const tb
[])
82 struct nft_queue
*priv
= nft_expr_priv(expr
);
85 priv
->queuenum
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_NUM
]));
87 if (tb
[NFTA_QUEUE_TOTAL
])
88 priv
->queues_total
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_TOTAL
]));
90 priv
->queues_total
= 1;
92 if (priv
->queues_total
== 0)
95 maxid
= priv
->queues_total
- 1 + priv
->queuenum
;
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
)
107 static int nft_queue_sreg_init(const struct nft_ctx
*ctx
,
108 const struct nft_expr
*expr
,
109 const struct nlattr
* const tb
[])
111 struct nft_queue
*priv
= nft_expr_priv(expr
);
114 priv
->sreg_qnum
= nft_parse_register(tb
[NFTA_QUEUE_SREG_QNUM
]);
115 err
= nft_validate_register_load(priv
->sreg_qnum
, sizeof(u32
));
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
)
123 if (priv
->flags
& NFT_QUEUE_FLAG_CPU_FANOUT
)
130 static int nft_queue_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
132 const struct nft_queue
*priv
= nft_expr_priv(expr
);
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
;
146 nft_queue_sreg_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
148 const struct nft_queue
*priv
= nft_expr_priv(expr
);
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
;
160 static struct nft_expr_type nft_queue_type
;
161 static 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
,
169 static 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
,
177 static const struct nft_expr_ops
*
178 nft_queue_select_ops(const struct nft_ctx
*ctx
,
179 const struct nlattr
* const tb
[])
181 if (tb
[NFTA_QUEUE_NUM
] && tb
[NFTA_QUEUE_SREG_QNUM
])
182 return ERR_PTR(-EINVAL
);
184 init_hashrandom(&jhash_initval
);
186 if (tb
[NFTA_QUEUE_NUM
])
187 return &nft_queue_ops
;
189 if (tb
[NFTA_QUEUE_SREG_QNUM
])
190 return &nft_queue_sreg_ops
;
192 return ERR_PTR(-EINVAL
);
195 static struct nft_expr_type nft_queue_type __read_mostly
= {
197 .select_ops
= nft_queue_select_ops
,
198 .policy
= nft_queue_policy
,
199 .maxattr
= NFTA_QUEUE_MAX
,
200 .owner
= THIS_MODULE
,
203 static int __init
nft_queue_module_init(void)
205 return nft_register_expr(&nft_queue_type
);
208 static void __exit
nft_queue_module_exit(void)
210 nft_unregister_expr(&nft_queue_type
);
213 module_init(nft_queue_module_init
);
214 module_exit(nft_queue_module_exit
);
216 MODULE_LICENSE("GPL");
217 MODULE_AUTHOR("Eric Leblond <eric@regit.org>");
218 MODULE_ALIAS_NFT_EXPR("queue");