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 19 static u32 jhash_initval __read_mostly; 20 21 struct nft_queue { 22 u8 sreg_qnum; 23 u16 queuenum; 24 u16 queues_total; 25 u16 flags; 26 }; 27 28 static 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 55 static 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 71 static int nft_queue_validate(const struct nft_ctx *ctx, 72 const struct nft_expr *expr, 73 const struct nft_data **data) 74 { 75 static const unsigned int supported_hooks = ((1 << NF_INET_PRE_ROUTING) | 76 (1 << NF_INET_LOCAL_IN) | 77 (1 << NF_INET_FORWARD) | 78 (1 << NF_INET_LOCAL_OUT) | 79 (1 << NF_INET_POST_ROUTING)); 80 81 switch (ctx->family) { 82 case NFPROTO_IPV4: 83 case NFPROTO_IPV6: 84 case NFPROTO_INET: 85 case NFPROTO_BRIDGE: 86 break; 87 case NFPROTO_NETDEV: /* lacks okfn */ 88 fallthrough; 89 default: 90 return -EOPNOTSUPP; 91 } 92 93 return nft_chain_validate_hooks(ctx->chain, supported_hooks); 94 } 95 96 static const struct nla_policy nft_queue_policy[NFTA_QUEUE_MAX + 1] = { 97 [NFTA_QUEUE_NUM] = { .type = NLA_U16 }, 98 [NFTA_QUEUE_TOTAL] = { .type = NLA_U16 }, 99 [NFTA_QUEUE_FLAGS] = { .type = NLA_U16 }, 100 [NFTA_QUEUE_SREG_QNUM] = { .type = NLA_U32 }, 101 }; 102 103 static int nft_queue_init(const struct nft_ctx *ctx, 104 const struct nft_expr *expr, 105 const struct nlattr * const tb[]) 106 { 107 struct nft_queue *priv = nft_expr_priv(expr); 108 u32 maxid; 109 110 priv->queuenum = ntohs(nla_get_be16(tb[NFTA_QUEUE_NUM])); 111 112 if (tb[NFTA_QUEUE_TOTAL]) 113 priv->queues_total = ntohs(nla_get_be16(tb[NFTA_QUEUE_TOTAL])); 114 else 115 priv->queues_total = 1; 116 117 if (priv->queues_total == 0) 118 return -EINVAL; 119 120 maxid = priv->queues_total - 1 + priv->queuenum; 121 if (maxid > U16_MAX) 122 return -ERANGE; 123 124 if (tb[NFTA_QUEUE_FLAGS]) { 125 priv->flags = ntohs(nla_get_be16(tb[NFTA_QUEUE_FLAGS])); 126 if (priv->flags & ~NFT_QUEUE_FLAG_MASK) 127 return -EINVAL; 128 } 129 return 0; 130 } 131 132 static int nft_queue_sreg_init(const struct nft_ctx *ctx, 133 const struct nft_expr *expr, 134 const struct nlattr * const tb[]) 135 { 136 struct nft_queue *priv = nft_expr_priv(expr); 137 int err; 138 139 err = nft_parse_register_load(tb[NFTA_QUEUE_SREG_QNUM], 140 &priv->sreg_qnum, sizeof(u32)); 141 if (err < 0) 142 return err; 143 144 if (tb[NFTA_QUEUE_FLAGS]) { 145 priv->flags = ntohs(nla_get_be16(tb[NFTA_QUEUE_FLAGS])); 146 if (priv->flags & ~NFT_QUEUE_FLAG_MASK) 147 return -EINVAL; 148 if (priv->flags & NFT_QUEUE_FLAG_CPU_FANOUT) 149 return -EOPNOTSUPP; 150 } 151 152 return 0; 153 } 154 155 static int nft_queue_dump(struct sk_buff *skb, 156 const struct nft_expr *expr, bool reset) 157 { 158 const struct nft_queue *priv = nft_expr_priv(expr); 159 160 if (nla_put_be16(skb, NFTA_QUEUE_NUM, htons(priv->queuenum)) || 161 nla_put_be16(skb, NFTA_QUEUE_TOTAL, htons(priv->queues_total)) || 162 nla_put_be16(skb, NFTA_QUEUE_FLAGS, htons(priv->flags))) 163 goto nla_put_failure; 164 165 return 0; 166 167 nla_put_failure: 168 return -1; 169 } 170 171 static int 172 nft_queue_sreg_dump(struct sk_buff *skb, 173 const struct nft_expr *expr, bool reset) 174 { 175 const struct nft_queue *priv = nft_expr_priv(expr); 176 177 if (nft_dump_register(skb, NFTA_QUEUE_SREG_QNUM, priv->sreg_qnum) || 178 nla_put_be16(skb, NFTA_QUEUE_FLAGS, htons(priv->flags))) 179 goto nla_put_failure; 180 181 return 0; 182 183 nla_put_failure: 184 return -1; 185 } 186 187 static struct nft_expr_type nft_queue_type; 188 static const struct nft_expr_ops nft_queue_ops = { 189 .type = &nft_queue_type, 190 .size = NFT_EXPR_SIZE(sizeof(struct nft_queue)), 191 .eval = nft_queue_eval, 192 .init = nft_queue_init, 193 .dump = nft_queue_dump, 194 .validate = nft_queue_validate, 195 .reduce = NFT_REDUCE_READONLY, 196 }; 197 198 static const struct nft_expr_ops nft_queue_sreg_ops = { 199 .type = &nft_queue_type, 200 .size = NFT_EXPR_SIZE(sizeof(struct nft_queue)), 201 .eval = nft_queue_sreg_eval, 202 .init = nft_queue_sreg_init, 203 .dump = nft_queue_sreg_dump, 204 .validate = nft_queue_validate, 205 .reduce = NFT_REDUCE_READONLY, 206 }; 207 208 static const struct nft_expr_ops * 209 nft_queue_select_ops(const struct nft_ctx *ctx, 210 const struct nlattr * const tb[]) 211 { 212 if (tb[NFTA_QUEUE_NUM] && tb[NFTA_QUEUE_SREG_QNUM]) 213 return ERR_PTR(-EINVAL); 214 215 init_hashrandom(&jhash_initval); 216 217 if (tb[NFTA_QUEUE_NUM]) 218 return &nft_queue_ops; 219 220 if (tb[NFTA_QUEUE_SREG_QNUM]) 221 return &nft_queue_sreg_ops; 222 223 return ERR_PTR(-EINVAL); 224 } 225 226 static struct nft_expr_type nft_queue_type __read_mostly = { 227 .name = "queue", 228 .select_ops = nft_queue_select_ops, 229 .policy = nft_queue_policy, 230 .maxattr = NFTA_QUEUE_MAX, 231 .owner = THIS_MODULE, 232 }; 233 234 static int __init nft_queue_module_init(void) 235 { 236 return nft_register_expr(&nft_queue_type); 237 } 238 239 static void __exit nft_queue_module_exit(void) 240 { 241 nft_unregister_expr(&nft_queue_type); 242 } 243 244 module_init(nft_queue_module_init); 245 module_exit(nft_queue_module_exit); 246 247 MODULE_LICENSE("GPL"); 248 MODULE_AUTHOR("Eric Leblond <eric@regit.org>"); 249 MODULE_ALIAS_NFT_EXPR("queue"); 250 MODULE_DESCRIPTION("Netfilter nftables queue module"); 251