2 * Copyright (c) 2013 Eric Leblond <eric@regit.org>
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.
8 * Development of this code partly funded by OISF
9 * (http://www.openinfosecfoundation.org/)
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>
22 static u32 jhash_initval __read_mostly
;
25 enum nft_registers sreg_qnum
:8;
31 static void nft_queue_eval(const struct nft_expr
*expr
,
32 struct nft_regs
*regs
,
33 const struct nft_pktinfo
*pkt
)
35 struct nft_queue
*priv
= nft_expr_priv(expr
);
36 u32 queue
= priv
->queuenum
;
39 if (priv
->queues_total
> 1) {
40 if (priv
->flags
& NFT_QUEUE_FLAG_CPU_FANOUT
) {
41 int cpu
= raw_smp_processor_id();
43 queue
= priv
->queuenum
+ cpu
% priv
->queues_total
;
45 queue
= nfqueue_hash(pkt
->skb
, queue
,
46 priv
->queues_total
, nft_pf(pkt
),
51 ret
= NF_QUEUE_NR(queue
);
52 if (priv
->flags
& NFT_QUEUE_FLAG_BYPASS
)
53 ret
|= NF_VERDICT_FLAG_QUEUE_BYPASS
;
55 regs
->verdict
.code
= ret
;
58 static void nft_queue_sreg_eval(const struct nft_expr
*expr
,
59 struct nft_regs
*regs
,
60 const struct nft_pktinfo
*pkt
)
62 struct nft_queue
*priv
= nft_expr_priv(expr
);
65 queue
= regs
->data
[priv
->sreg_qnum
];
67 ret
= NF_QUEUE_NR(queue
);
68 if (priv
->flags
& NFT_QUEUE_FLAG_BYPASS
)
69 ret
|= NF_VERDICT_FLAG_QUEUE_BYPASS
;
71 regs
->verdict
.code
= ret
;
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
},
78 [NFTA_QUEUE_SREG_QNUM
] = { .type
= NLA_U32
},
81 static int nft_queue_init(const struct nft_ctx
*ctx
,
82 const struct nft_expr
*expr
,
83 const struct nlattr
* const tb
[])
85 struct nft_queue
*priv
= nft_expr_priv(expr
);
88 priv
->queuenum
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_NUM
]));
90 if (tb
[NFTA_QUEUE_TOTAL
])
91 priv
->queues_total
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_TOTAL
]));
93 priv
->queues_total
= 1;
95 if (priv
->queues_total
== 0)
98 maxid
= priv
->queues_total
- 1 + priv
->queuenum
;
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
)
110 static int nft_queue_sreg_init(const struct nft_ctx
*ctx
,
111 const struct nft_expr
*expr
,
112 const struct nlattr
* const tb
[])
114 struct nft_queue
*priv
= nft_expr_priv(expr
);
117 priv
->sreg_qnum
= nft_parse_register(tb
[NFTA_QUEUE_SREG_QNUM
]);
118 err
= nft_validate_register_load(priv
->sreg_qnum
, sizeof(u32
));
122 if (tb
[NFTA_QUEUE_FLAGS
]) {
123 priv
->flags
= ntohs(nla_get_be16(tb
[NFTA_QUEUE_FLAGS
]));
124 if (priv
->flags
& ~NFT_QUEUE_FLAG_MASK
)
126 if (priv
->flags
& NFT_QUEUE_FLAG_CPU_FANOUT
)
133 static int nft_queue_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
135 const struct nft_queue
*priv
= nft_expr_priv(expr
);
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
;
149 nft_queue_sreg_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
151 const struct nft_queue
*priv
= nft_expr_priv(expr
);
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
;
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
,
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
,
180 static const struct nft_expr_ops
*
181 nft_queue_select_ops(const struct nft_ctx
*ctx
,
182 const struct nlattr
* const tb
[])
184 if (tb
[NFTA_QUEUE_NUM
] && tb
[NFTA_QUEUE_SREG_QNUM
])
185 return ERR_PTR(-EINVAL
);
187 init_hashrandom(&jhash_initval
);
189 if (tb
[NFTA_QUEUE_NUM
])
190 return &nft_queue_ops
;
192 if (tb
[NFTA_QUEUE_SREG_QNUM
])
193 return &nft_queue_sreg_ops
;
195 return ERR_PTR(-EINVAL
);
198 static struct nft_expr_type nft_queue_type __read_mostly
= {
200 .select_ops
= &nft_queue_select_ops
,
201 .policy
= nft_queue_policy
,
202 .maxattr
= NFTA_QUEUE_MAX
,
203 .owner
= THIS_MODULE
,
206 static int __init
nft_queue_module_init(void)
208 return nft_register_expr(&nft_queue_type
);
211 static void __exit
nft_queue_module_exit(void)
213 nft_unregister_expr(&nft_queue_type
);
216 module_init(nft_queue_module_init
);
217 module_exit(nft_queue_module_exit
);
219 MODULE_LICENSE("GPL");
220 MODULE_AUTHOR("Eric Leblond <eric@regit.org>");
221 MODULE_ALIAS_NFT_EXPR("queue");