1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 296518518SPatrick McHardy /* 3ef1f7df9SPatrick McHardy * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> 496518518SPatrick McHardy * 596518518SPatrick McHardy * Development of this code funded by Astaro AG (http://www.astaro.com/) 696518518SPatrick McHardy */ 796518518SPatrick McHardy 896518518SPatrick McHardy #include <linux/kernel.h> 996518518SPatrick McHardy #include <linux/init.h> 1096518518SPatrick McHardy #include <linux/module.h> 1196518518SPatrick McHardy #include <linux/netlink.h> 1296518518SPatrick McHardy #include <linux/netfilter.h> 1396518518SPatrick McHardy #include <linux/netfilter/nf_tables.h> 1496518518SPatrick McHardy #include <net/netfilter/nf_tables_core.h> 1596518518SPatrick McHardy #include <net/netfilter/nf_tables.h> 1696518518SPatrick McHardy 1796518518SPatrick McHardy struct nft_cmp_expr { 1896518518SPatrick McHardy struct nft_data data; 1996518518SPatrick McHardy enum nft_registers sreg:8; 2096518518SPatrick McHardy u8 len; 2196518518SPatrick McHardy enum nft_cmp_ops op:8; 2296518518SPatrick McHardy }; 2396518518SPatrick McHardy 2410870dd8SFlorian Westphal void nft_cmp_eval(const struct nft_expr *expr, 25a55e22e9SPatrick McHardy struct nft_regs *regs, 2696518518SPatrick McHardy const struct nft_pktinfo *pkt) 2796518518SPatrick McHardy { 2896518518SPatrick McHardy const struct nft_cmp_expr *priv = nft_expr_priv(expr); 2996518518SPatrick McHardy int d; 3096518518SPatrick McHardy 31e562d860SPatrick McHardy d = memcmp(®s->data[priv->sreg], &priv->data, priv->len); 3296518518SPatrick McHardy switch (priv->op) { 3396518518SPatrick McHardy case NFT_CMP_EQ: 3496518518SPatrick McHardy if (d != 0) 3596518518SPatrick McHardy goto mismatch; 3696518518SPatrick McHardy break; 3796518518SPatrick McHardy case NFT_CMP_NEQ: 3896518518SPatrick McHardy if (d == 0) 3996518518SPatrick McHardy goto mismatch; 4096518518SPatrick McHardy break; 4196518518SPatrick McHardy case NFT_CMP_LT: 4296518518SPatrick McHardy if (d == 0) 4396518518SPatrick McHardy goto mismatch; 44e8542dceSGustavo A. R. Silva /* fall through */ 4596518518SPatrick McHardy case NFT_CMP_LTE: 4696518518SPatrick McHardy if (d > 0) 4796518518SPatrick McHardy goto mismatch; 4896518518SPatrick McHardy break; 4996518518SPatrick McHardy case NFT_CMP_GT: 5096518518SPatrick McHardy if (d == 0) 5196518518SPatrick McHardy goto mismatch; 52e8542dceSGustavo A. R. Silva /* fall through */ 5396518518SPatrick McHardy case NFT_CMP_GTE: 5496518518SPatrick McHardy if (d < 0) 5596518518SPatrick McHardy goto mismatch; 5696518518SPatrick McHardy break; 5796518518SPatrick McHardy } 5896518518SPatrick McHardy return; 5996518518SPatrick McHardy 6096518518SPatrick McHardy mismatch: 61a55e22e9SPatrick McHardy regs->verdict.code = NFT_BREAK; 6296518518SPatrick McHardy } 6396518518SPatrick McHardy 6496518518SPatrick McHardy static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { 6596518518SPatrick McHardy [NFTA_CMP_SREG] = { .type = NLA_U32 }, 6696518518SPatrick McHardy [NFTA_CMP_OP] = { .type = NLA_U32 }, 6796518518SPatrick McHardy [NFTA_CMP_DATA] = { .type = NLA_NESTED }, 6896518518SPatrick McHardy }; 6996518518SPatrick McHardy 7096518518SPatrick McHardy static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 7196518518SPatrick McHardy const struct nlattr * const tb[]) 7296518518SPatrick McHardy { 7396518518SPatrick McHardy struct nft_cmp_expr *priv = nft_expr_priv(expr); 7496518518SPatrick McHardy struct nft_data_desc desc; 7596518518SPatrick McHardy int err; 7696518518SPatrick McHardy 77d0a11fc3SPatrick McHardy err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, 78d0a11fc3SPatrick McHardy tb[NFTA_CMP_DATA]); 79fa5950e4SFlorian Westphal if (err < 0) 80fa5950e4SFlorian Westphal return err; 8196518518SPatrick McHardy 82b1c96ed3SPatrick McHardy priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]); 83d07db988SPatrick McHardy err = nft_validate_register_load(priv->sreg, desc.len); 84d07db988SPatrick McHardy if (err < 0) 85d07db988SPatrick McHardy return err; 86d07db988SPatrick McHardy 87d07db988SPatrick McHardy priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 8896518518SPatrick McHardy priv->len = desc.len; 8996518518SPatrick McHardy return 0; 9096518518SPatrick McHardy } 9196518518SPatrick McHardy 9296518518SPatrick McHardy static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) 9396518518SPatrick McHardy { 9496518518SPatrick McHardy const struct nft_cmp_expr *priv = nft_expr_priv(expr); 9596518518SPatrick McHardy 96b1c96ed3SPatrick McHardy if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) 9796518518SPatrick McHardy goto nla_put_failure; 9896518518SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op))) 9996518518SPatrick McHardy goto nla_put_failure; 10096518518SPatrick McHardy 10196518518SPatrick McHardy if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, 10296518518SPatrick McHardy NFT_DATA_VALUE, priv->len) < 0) 10396518518SPatrick McHardy goto nla_put_failure; 10496518518SPatrick McHardy return 0; 10596518518SPatrick McHardy 10696518518SPatrick McHardy nla_put_failure: 10796518518SPatrick McHardy return -1; 10896518518SPatrick McHardy } 10996518518SPatrick McHardy 110ef1f7df9SPatrick McHardy static const struct nft_expr_ops nft_cmp_ops = { 111ef1f7df9SPatrick McHardy .type = &nft_cmp_type, 11296518518SPatrick McHardy .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), 11396518518SPatrick McHardy .eval = nft_cmp_eval, 11496518518SPatrick McHardy .init = nft_cmp_init, 11596518518SPatrick McHardy .dump = nft_cmp_dump, 116ef1f7df9SPatrick McHardy }; 117ef1f7df9SPatrick McHardy 118cb7dbfd0SPatrick McHardy static int nft_cmp_fast_init(const struct nft_ctx *ctx, 119cb7dbfd0SPatrick McHardy const struct nft_expr *expr, 120cb7dbfd0SPatrick McHardy const struct nlattr * const tb[]) 121cb7dbfd0SPatrick McHardy { 122cb7dbfd0SPatrick McHardy struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 123cb7dbfd0SPatrick McHardy struct nft_data_desc desc; 124cb7dbfd0SPatrick McHardy struct nft_data data; 125cb7dbfd0SPatrick McHardy u32 mask; 126cb7dbfd0SPatrick McHardy int err; 127cb7dbfd0SPatrick McHardy 128d0a11fc3SPatrick McHardy err = nft_data_init(NULL, &data, sizeof(data), &desc, 129d0a11fc3SPatrick McHardy tb[NFTA_CMP_DATA]); 130fa5950e4SFlorian Westphal if (err < 0) 131fa5950e4SFlorian Westphal return err; 132cb7dbfd0SPatrick McHardy 133b1c96ed3SPatrick McHardy priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]); 134d07db988SPatrick McHardy err = nft_validate_register_load(priv->sreg, desc.len); 135d07db988SPatrick McHardy if (err < 0) 136d07db988SPatrick McHardy return err; 137d07db988SPatrick McHardy 138d07db988SPatrick McHardy desc.len *= BITS_PER_BYTE; 139b855d416SPatrick McHardy mask = nft_cmp_fast_mask(desc.len); 140d07db988SPatrick McHardy 141cb7dbfd0SPatrick McHardy priv->data = data.data[0] & mask; 142cb7dbfd0SPatrick McHardy priv->len = desc.len; 143cb7dbfd0SPatrick McHardy return 0; 144cb7dbfd0SPatrick McHardy } 145cb7dbfd0SPatrick McHardy 146cb7dbfd0SPatrick McHardy static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) 147cb7dbfd0SPatrick McHardy { 148cb7dbfd0SPatrick McHardy const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 149cb7dbfd0SPatrick McHardy struct nft_data data; 150cb7dbfd0SPatrick McHardy 151b1c96ed3SPatrick McHardy if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) 152cb7dbfd0SPatrick McHardy goto nla_put_failure; 153cb7dbfd0SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ))) 154cb7dbfd0SPatrick McHardy goto nla_put_failure; 155cb7dbfd0SPatrick McHardy 156cb7dbfd0SPatrick McHardy data.data[0] = priv->data; 157cb7dbfd0SPatrick McHardy if (nft_data_dump(skb, NFTA_CMP_DATA, &data, 158cb7dbfd0SPatrick McHardy NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0) 159cb7dbfd0SPatrick McHardy goto nla_put_failure; 160cb7dbfd0SPatrick McHardy return 0; 161cb7dbfd0SPatrick McHardy 162cb7dbfd0SPatrick McHardy nla_put_failure: 163cb7dbfd0SPatrick McHardy return -1; 164cb7dbfd0SPatrick McHardy } 165cb7dbfd0SPatrick McHardy 166cb7dbfd0SPatrick McHardy const struct nft_expr_ops nft_cmp_fast_ops = { 167cb7dbfd0SPatrick McHardy .type = &nft_cmp_type, 168cb7dbfd0SPatrick McHardy .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)), 169cb7dbfd0SPatrick McHardy .eval = NULL, /* inlined */ 170cb7dbfd0SPatrick McHardy .init = nft_cmp_fast_init, 171cb7dbfd0SPatrick McHardy .dump = nft_cmp_fast_dump, 172cb7dbfd0SPatrick McHardy }; 173cb7dbfd0SPatrick McHardy 1740ca743a5SPablo Neira Ayuso static const struct nft_expr_ops * 1750ca743a5SPablo Neira Ayuso nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) 176cb7dbfd0SPatrick McHardy { 177cb7dbfd0SPatrick McHardy struct nft_data_desc desc; 178cb7dbfd0SPatrick McHardy struct nft_data data; 179cb7dbfd0SPatrick McHardy enum nft_cmp_ops op; 180cb7dbfd0SPatrick McHardy int err; 181cb7dbfd0SPatrick McHardy 182cb7dbfd0SPatrick McHardy if (tb[NFTA_CMP_SREG] == NULL || 183cb7dbfd0SPatrick McHardy tb[NFTA_CMP_OP] == NULL || 184cb7dbfd0SPatrick McHardy tb[NFTA_CMP_DATA] == NULL) 185cb7dbfd0SPatrick McHardy return ERR_PTR(-EINVAL); 186cb7dbfd0SPatrick McHardy 187cb7dbfd0SPatrick McHardy op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 188cb7dbfd0SPatrick McHardy switch (op) { 189cb7dbfd0SPatrick McHardy case NFT_CMP_EQ: 190cb7dbfd0SPatrick McHardy case NFT_CMP_NEQ: 191cb7dbfd0SPatrick McHardy case NFT_CMP_LT: 192cb7dbfd0SPatrick McHardy case NFT_CMP_LTE: 193cb7dbfd0SPatrick McHardy case NFT_CMP_GT: 194cb7dbfd0SPatrick McHardy case NFT_CMP_GTE: 195cb7dbfd0SPatrick McHardy break; 196cb7dbfd0SPatrick McHardy default: 197cb7dbfd0SPatrick McHardy return ERR_PTR(-EINVAL); 198cb7dbfd0SPatrick McHardy } 199cb7dbfd0SPatrick McHardy 200d0a11fc3SPatrick McHardy err = nft_data_init(NULL, &data, sizeof(data), &desc, 201d0a11fc3SPatrick McHardy tb[NFTA_CMP_DATA]); 202cb7dbfd0SPatrick McHardy if (err < 0) 203cb7dbfd0SPatrick McHardy return ERR_PTR(err); 204cb7dbfd0SPatrick McHardy 20571df14b0SPablo Neira Ayuso if (desc.type != NFT_DATA_VALUE) { 20671df14b0SPablo Neira Ayuso err = -EINVAL; 20771df14b0SPablo Neira Ayuso goto err1; 20871df14b0SPablo Neira Ayuso } 20971df14b0SPablo Neira Ayuso 210cb7dbfd0SPatrick McHardy if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ) 211cb7dbfd0SPatrick McHardy return &nft_cmp_fast_ops; 21271df14b0SPablo Neira Ayuso 213cb7dbfd0SPatrick McHardy return &nft_cmp_ops; 21471df14b0SPablo Neira Ayuso err1: 21559105446SPablo Neira Ayuso nft_data_release(&data, desc.type); 21671df14b0SPablo Neira Ayuso return ERR_PTR(-EINVAL); 217cb7dbfd0SPatrick McHardy } 218cb7dbfd0SPatrick McHardy 2194e24877eSLiping Zhang struct nft_expr_type nft_cmp_type __read_mostly = { 220ef1f7df9SPatrick McHardy .name = "cmp", 221cb7dbfd0SPatrick McHardy .select_ops = nft_cmp_select_ops, 22296518518SPatrick McHardy .policy = nft_cmp_policy, 22396518518SPatrick McHardy .maxattr = NFTA_CMP_MAX, 224ef1f7df9SPatrick McHardy .owner = THIS_MODULE, 22596518518SPatrick McHardy }; 226