cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nft_queue.c (5400B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2013 Eric Leblond <eric@regit.org>
      4 *
      5 * Development of this code partly funded by OISF
      6 * (http://www.openinfosecfoundation.org/)
      7 */
      8
      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>
     18
     19static u32 jhash_initval __read_mostly;
     20
     21struct nft_queue {
     22	u8	sreg_qnum;
     23	u16	queuenum;
     24	u16	queues_total;
     25	u16	flags;
     26};
     27
     28static void nft_queue_eval(const struct nft_expr *expr,
     29			   struct nft_regs *regs,
     30			   const struct nft_pktinfo *pkt)
     31{
     32	struct nft_queue *priv = nft_expr_priv(expr);
     33	u32 queue = priv->queuenum;
     34	u32 ret;
     35
     36	if (priv->queues_total > 1) {
     37		if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT) {
     38			int cpu = raw_smp_processor_id();
     39
     40			queue = priv->queuenum + cpu % priv->queues_total;
     41		} else {
     42			queue = nfqueue_hash(pkt->skb, queue,
     43					     priv->queues_total, nft_pf(pkt),
     44					     jhash_initval);
     45		}
     46	}
     47
     48	ret = NF_QUEUE_NR(queue);
     49	if (priv->flags & NFT_QUEUE_FLAG_BYPASS)
     50		ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
     51
     52	regs->verdict.code = ret;
     53}
     54
     55static void nft_queue_sreg_eval(const struct nft_expr *expr,
     56				struct nft_regs *regs,
     57				const struct nft_pktinfo *pkt)
     58{
     59	struct nft_queue *priv = nft_expr_priv(expr);
     60	u32 queue, ret;
     61
     62	queue = regs->data[priv->sreg_qnum];
     63
     64	ret = NF_QUEUE_NR(queue);
     65	if (priv->flags & NFT_QUEUE_FLAG_BYPASS)
     66		ret |= NF_VERDICT_FLAG_QUEUE_BYPASS;
     67
     68	regs->verdict.code = ret;
     69}
     70
     71static 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 },
     76};
     77
     78static int nft_queue_init(const struct nft_ctx *ctx,
     79			  const struct nft_expr *expr,
     80			  const struct nlattr * const tb[])
     81{
     82	struct nft_queue *priv = nft_expr_priv(expr);
     83	u32 maxid;
     84
     85	priv->queuenum = ntohs(nla_get_be16(tb[NFTA_QUEUE_NUM]));
     86
     87	if (tb[NFTA_QUEUE_TOTAL])
     88		priv->queues_total = ntohs(nla_get_be16(tb[NFTA_QUEUE_TOTAL]));
     89	else
     90		priv->queues_total = 1;
     91
     92	if (priv->queues_total == 0)
     93		return -EINVAL;
     94
     95	maxid = priv->queues_total - 1 + priv->queuenum;
     96	if (maxid > U16_MAX)
     97		return -ERANGE;
     98
     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)
    102			return -EINVAL;
    103	}
    104	return 0;
    105}
    106
    107static int nft_queue_sreg_init(const struct nft_ctx *ctx,
    108			       const struct nft_expr *expr,
    109			       const struct nlattr * const tb[])
    110{
    111	struct nft_queue *priv = nft_expr_priv(expr);
    112	int err;
    113
    114	err = nft_parse_register_load(tb[NFTA_QUEUE_SREG_QNUM],
    115				      &priv->sreg_qnum, sizeof(u32));
    116	if (err < 0)
    117		return err;
    118
    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)
    122			return -EINVAL;
    123		if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT)
    124			return -EOPNOTSUPP;
    125	}
    126
    127	return 0;
    128}
    129
    130static int nft_queue_dump(struct sk_buff *skb, const struct nft_expr *expr)
    131{
    132	const struct nft_queue *priv = nft_expr_priv(expr);
    133
    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;
    138
    139	return 0;
    140
    141nla_put_failure:
    142	return -1;
    143}
    144
    145static int
    146nft_queue_sreg_dump(struct sk_buff *skb, const struct nft_expr *expr)
    147{
    148	const struct nft_queue *priv = nft_expr_priv(expr);
    149
    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;
    153
    154	return 0;
    155
    156nla_put_failure:
    157	return -1;
    158}
    159
    160static struct nft_expr_type nft_queue_type;
    161static 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,
    167	.reduce		= NFT_REDUCE_READONLY,
    168};
    169
    170static const struct nft_expr_ops nft_queue_sreg_ops = {
    171	.type		= &nft_queue_type,
    172	.size		= NFT_EXPR_SIZE(sizeof(struct nft_queue)),
    173	.eval		= nft_queue_sreg_eval,
    174	.init		= nft_queue_sreg_init,
    175	.dump		= nft_queue_sreg_dump,
    176	.reduce		= NFT_REDUCE_READONLY,
    177};
    178
    179static const struct nft_expr_ops *
    180nft_queue_select_ops(const struct nft_ctx *ctx,
    181		     const struct nlattr * const tb[])
    182{
    183	if (tb[NFTA_QUEUE_NUM] && tb[NFTA_QUEUE_SREG_QNUM])
    184		return ERR_PTR(-EINVAL);
    185
    186	init_hashrandom(&jhash_initval);
    187
    188	if (tb[NFTA_QUEUE_NUM])
    189		return &nft_queue_ops;
    190
    191	if (tb[NFTA_QUEUE_SREG_QNUM])
    192		return &nft_queue_sreg_ops;
    193
    194	return ERR_PTR(-EINVAL);
    195}
    196
    197static struct nft_expr_type nft_queue_type __read_mostly = {
    198	.name		= "queue",
    199	.select_ops	= nft_queue_select_ops,
    200	.policy		= nft_queue_policy,
    201	.maxattr	= NFTA_QUEUE_MAX,
    202	.owner		= THIS_MODULE,
    203};
    204
    205static int __init nft_queue_module_init(void)
    206{
    207	return nft_register_expr(&nft_queue_type);
    208}
    209
    210static void __exit nft_queue_module_exit(void)
    211{
    212	nft_unregister_expr(&nft_queue_type);
    213}
    214
    215module_init(nft_queue_module_init);
    216module_exit(nft_queue_module_exit);
    217
    218MODULE_LICENSE("GPL");
    219MODULE_AUTHOR("Eric Leblond <eric@regit.org>");
    220MODULE_ALIAS_NFT_EXPR("queue");
    221MODULE_DESCRIPTION("Netfilter nftables queue module");