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