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