10aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond/*
20aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond * Copyright (c) 2013 Eric Leblond <eric@regit.org>
30aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond *
40aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond * This program is free software; you can redistribute it and/or modify
50aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond * it under the terms of the GNU General Public License version 2 as
60aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond * published by the Free Software Foundation.
70aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond *
80aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond * Development of this code partly funded by OISF
90aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond * (http://www.openinfosecfoundation.org/)
100aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond */
110aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
120aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/kernel.h>
130aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/init.h>
140aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/module.h>
150aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/netlink.h>
160aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/jhash.h>
170aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/netfilter.h>
180aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <linux/netfilter/nf_tables.h>
190aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <net/netfilter/nf_tables.h>
200aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond#include <net/netfilter/nf_queue.h>
210aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
220aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic u32 jhash_initval __read_mostly;
230aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
240aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstruct nft_queue {
250aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	u16	queuenum;
260aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	u16	queues_total;
270aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	u16	flags;
280aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond};
290aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
300aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic void nft_queue_eval(const struct nft_expr *expr,
310aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			   struct nft_data data[NFT_REG_MAX + 1],
320aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			   const struct nft_pktinfo *pkt)
330aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond{
340aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	struct nft_queue *priv = nft_expr_priv(expr);
350aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	u32 queue = priv->queuenum;
360aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	u32 ret;
370aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
380aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	if (priv->queues_total > 1) {
390aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT) {
400aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			int cpu = smp_processor_id();
410aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
420aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			queue = priv->queuenum + cpu % priv->queues_total;
430aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		} else {
440aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			queue = nfqueue_hash(pkt->skb, queue,
45b8ecbee67c732ef9fc47fcf50aed6b7bb6231d98Patrick McHardy					     priv->queues_total, pkt->ops->pf,
460aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond					     jhash_initval);
470aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		}
480aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	}
490aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
500aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	ret = NF_QUEUE_NR(queue);
510aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	if (priv->flags & NFT_QUEUE_FLAG_BYPASS)
520aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
530aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
540aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	data[NFT_REG_VERDICT].verdict = ret;
550aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond}
560aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
570aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic const struct nla_policy nft_queue_policy[NFTA_QUEUE_MAX + 1] = {
580aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	[NFTA_QUEUE_NUM]	= { .type = NLA_U16 },
590aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	[NFTA_QUEUE_TOTAL]	= { .type = NLA_U16 },
600aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	[NFTA_QUEUE_FLAGS]	= { .type = NLA_U16 },
610aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond};
620aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
630aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic int nft_queue_init(const struct nft_ctx *ctx,
640aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			   const struct nft_expr *expr,
650aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			   const struct nlattr * const tb[])
660aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond{
670aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	struct nft_queue *priv = nft_expr_priv(expr);
680aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
690aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	if (tb[NFTA_QUEUE_NUM] == NULL)
700aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		return -EINVAL;
710aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
720aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	init_hashrandom(&jhash_initval);
730aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	priv->queuenum = ntohs(nla_get_be16(tb[NFTA_QUEUE_NUM]));
740aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
750aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	if (tb[NFTA_QUEUE_TOTAL] != NULL)
760aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		priv->queues_total = ntohs(nla_get_be16(tb[NFTA_QUEUE_TOTAL]));
770aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	if (tb[NFTA_QUEUE_FLAGS] != NULL) {
780aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		priv->flags = ntohs(nla_get_be16(tb[NFTA_QUEUE_FLAGS]));
790aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		if (priv->flags & ~NFT_QUEUE_FLAG_MASK)
800aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond			return -EINVAL;
810aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	}
820aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	return 0;
830aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond}
840aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
850aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic int nft_queue_dump(struct sk_buff *skb, const struct nft_expr *expr)
860aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond{
870aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	const struct nft_queue *priv = nft_expr_priv(expr);
880aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
890aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	if (nla_put_be16(skb, NFTA_QUEUE_NUM, htons(priv->queuenum)) ||
900aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	    nla_put_be16(skb, NFTA_QUEUE_TOTAL, htons(priv->queues_total)) ||
910aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	    nla_put_be16(skb, NFTA_QUEUE_FLAGS, htons(priv->flags)))
920aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond		goto nla_put_failure;
930aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
940aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	return 0;
950aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
960aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondnla_put_failure:
970aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	return -1;
980aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond}
990aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
1000aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic struct nft_expr_type nft_queue_type;
1010aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic const struct nft_expr_ops nft_queue_ops = {
1020aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.type		= &nft_queue_type,
1030aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.size		= NFT_EXPR_SIZE(sizeof(struct nft_queue)),
1040aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.eval		= nft_queue_eval,
1050aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.init		= nft_queue_init,
1060aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.dump		= nft_queue_dump,
1070aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond};
1080aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
1090aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic struct nft_expr_type nft_queue_type __read_mostly = {
1100aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.name		= "queue",
1110aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.ops		= &nft_queue_ops,
1120aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.policy		= nft_queue_policy,
1130aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.maxattr	= NFTA_QUEUE_MAX,
1140aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	.owner		= THIS_MODULE,
1150aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond};
1160aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
1170aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic int __init nft_queue_module_init(void)
1180aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond{
1190aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	return nft_register_expr(&nft_queue_type);
1200aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond}
1210aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
1220aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondstatic void __exit nft_queue_module_exit(void)
1230aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond{
1240aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond	nft_unregister_expr(&nft_queue_type);
1250aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond}
1260aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
1270aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondmodule_init(nft_queue_module_init);
1280aff078d58e1c69139189e45ba5e929c030e8056Eric Leblondmodule_exit(nft_queue_module_exit);
1290aff078d58e1c69139189e45ba5e929c030e8056Eric Leblond
1300aff078d58e1c69139189e45ba5e929c030e8056Eric LeblondMODULE_LICENSE("GPL");
1310aff078d58e1c69139189e45ba5e929c030e8056Eric LeblondMODULE_AUTHOR("Eric Leblond <eric@regit.org>");
1320aff078d58e1c69139189e45ba5e929c030e8056Eric LeblondMODULE_ALIAS_NFT_EXPR("queue");
133