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> 138819efc9SPablo Neira Ayuso #include <linux/if_arp.h> 1496518518SPatrick McHardy #include <linux/netfilter/nf_tables.h> 1596518518SPatrick McHardy #include <net/netfilter/nf_tables_core.h> 16c9626a2cSPablo Neira Ayuso #include <net/netfilter/nf_tables_offload.h> 1796518518SPatrick McHardy #include <net/netfilter/nf_tables.h> 1896518518SPatrick McHardy 1996518518SPatrick McHardy struct nft_cmp_expr { 2096518518SPatrick McHardy struct nft_data data; 21*4f16d25cSPablo Neira Ayuso u8 sreg; 2296518518SPatrick McHardy u8 len; 2396518518SPatrick McHardy enum nft_cmp_ops op:8; 2496518518SPatrick McHardy }; 2596518518SPatrick McHardy 2610870dd8SFlorian Westphal void nft_cmp_eval(const struct nft_expr *expr, 27a55e22e9SPatrick McHardy struct nft_regs *regs, 2896518518SPatrick McHardy const struct nft_pktinfo *pkt) 2996518518SPatrick McHardy { 3096518518SPatrick McHardy const struct nft_cmp_expr *priv = nft_expr_priv(expr); 3196518518SPatrick McHardy int d; 3296518518SPatrick McHardy 33e562d860SPatrick McHardy d = memcmp(®s->data[priv->sreg], &priv->data, priv->len); 3496518518SPatrick McHardy switch (priv->op) { 3596518518SPatrick McHardy case NFT_CMP_EQ: 3696518518SPatrick McHardy if (d != 0) 3796518518SPatrick McHardy goto mismatch; 3896518518SPatrick McHardy break; 3996518518SPatrick McHardy case NFT_CMP_NEQ: 4096518518SPatrick McHardy if (d == 0) 4196518518SPatrick McHardy goto mismatch; 4296518518SPatrick McHardy break; 4396518518SPatrick McHardy case NFT_CMP_LT: 4496518518SPatrick McHardy if (d == 0) 4596518518SPatrick McHardy goto mismatch; 46954d8297SGustavo A. R. Silva fallthrough; 4796518518SPatrick McHardy case NFT_CMP_LTE: 4896518518SPatrick McHardy if (d > 0) 4996518518SPatrick McHardy goto mismatch; 5096518518SPatrick McHardy break; 5196518518SPatrick McHardy case NFT_CMP_GT: 5296518518SPatrick McHardy if (d == 0) 5396518518SPatrick McHardy goto mismatch; 54954d8297SGustavo A. R. Silva fallthrough; 5596518518SPatrick McHardy case NFT_CMP_GTE: 5696518518SPatrick McHardy if (d < 0) 5796518518SPatrick McHardy goto mismatch; 5896518518SPatrick McHardy break; 5996518518SPatrick McHardy } 6096518518SPatrick McHardy return; 6196518518SPatrick McHardy 6296518518SPatrick McHardy mismatch: 63a55e22e9SPatrick McHardy regs->verdict.code = NFT_BREAK; 6496518518SPatrick McHardy } 6596518518SPatrick McHardy 6696518518SPatrick McHardy static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { 6796518518SPatrick McHardy [NFTA_CMP_SREG] = { .type = NLA_U32 }, 6896518518SPatrick McHardy [NFTA_CMP_OP] = { .type = NLA_U32 }, 6996518518SPatrick McHardy [NFTA_CMP_DATA] = { .type = NLA_NESTED }, 7096518518SPatrick McHardy }; 7196518518SPatrick McHardy 7296518518SPatrick McHardy static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 7396518518SPatrick McHardy const struct nlattr * const tb[]) 7496518518SPatrick McHardy { 7596518518SPatrick McHardy struct nft_cmp_expr *priv = nft_expr_priv(expr); 7696518518SPatrick McHardy struct nft_data_desc desc; 7796518518SPatrick McHardy int err; 7896518518SPatrick McHardy 79d0a11fc3SPatrick McHardy err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, 80d0a11fc3SPatrick McHardy tb[NFTA_CMP_DATA]); 81fa5950e4SFlorian Westphal if (err < 0) 82fa5950e4SFlorian Westphal return err; 8396518518SPatrick McHardy 840d2c96afSPablo Neira Ayuso if (desc.type != NFT_DATA_VALUE) { 850d2c96afSPablo Neira Ayuso err = -EINVAL; 860d2c96afSPablo Neira Ayuso nft_data_release(&priv->data, desc.type); 870d2c96afSPablo Neira Ayuso return err; 880d2c96afSPablo Neira Ayuso } 890d2c96afSPablo Neira Ayuso 90*4f16d25cSPablo Neira Ayuso err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len); 91d07db988SPatrick McHardy if (err < 0) 92d07db988SPatrick McHardy return err; 93d07db988SPatrick McHardy 94d07db988SPatrick McHardy priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 9596518518SPatrick McHardy priv->len = desc.len; 9696518518SPatrick McHardy return 0; 9796518518SPatrick McHardy } 9896518518SPatrick McHardy 9996518518SPatrick McHardy static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) 10096518518SPatrick McHardy { 10196518518SPatrick McHardy const struct nft_cmp_expr *priv = nft_expr_priv(expr); 10296518518SPatrick McHardy 103b1c96ed3SPatrick McHardy if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) 10496518518SPatrick McHardy goto nla_put_failure; 10596518518SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op))) 10696518518SPatrick McHardy goto nla_put_failure; 10796518518SPatrick McHardy 10896518518SPatrick McHardy if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, 10996518518SPatrick McHardy NFT_DATA_VALUE, priv->len) < 0) 11096518518SPatrick McHardy goto nla_put_failure; 11196518518SPatrick McHardy return 0; 11296518518SPatrick McHardy 11396518518SPatrick McHardy nla_put_failure: 11496518518SPatrick McHardy return -1; 11596518518SPatrick McHardy } 11696518518SPatrick McHardy 117c9626a2cSPablo Neira Ayuso static int __nft_cmp_offload(struct nft_offload_ctx *ctx, 118c9626a2cSPablo Neira Ayuso struct nft_flow_rule *flow, 119c9626a2cSPablo Neira Ayuso const struct nft_cmp_expr *priv) 120c9626a2cSPablo Neira Ayuso { 121c9626a2cSPablo Neira Ayuso struct nft_offload_reg *reg = &ctx->regs[priv->sreg]; 122c9626a2cSPablo Neira Ayuso u8 *mask = (u8 *)&flow->match.mask; 123c9626a2cSPablo Neira Ayuso u8 *key = (u8 *)&flow->match.key; 124c9626a2cSPablo Neira Ayuso 125a5d45bc0SPablo Neira Ayuso if (priv->op != NFT_CMP_EQ || priv->len > reg->len) 126c9626a2cSPablo Neira Ayuso return -EOPNOTSUPP; 127c9626a2cSPablo Neira Ayuso 128a5d45bc0SPablo Neira Ayuso memcpy(key + reg->offset, &priv->data, reg->len); 129a5d45bc0SPablo Neira Ayuso memcpy(mask + reg->offset, ®->mask, reg->len); 130c9626a2cSPablo Neira Ayuso 131c9626a2cSPablo Neira Ayuso flow->match.dissector.used_keys |= BIT(reg->key); 132c9626a2cSPablo Neira Ayuso flow->match.dissector.offset[reg->key] = reg->base_offset; 133c9626a2cSPablo Neira Ayuso 1348819efc9SPablo Neira Ayuso if (reg->key == FLOW_DISSECTOR_KEY_META && 1358819efc9SPablo Neira Ayuso reg->offset == offsetof(struct nft_flow_key, meta.ingress_iftype) && 1368819efc9SPablo Neira Ayuso nft_reg_load16(priv->data.data) != ARPHRD_ETHER) 1378819efc9SPablo Neira Ayuso return -EOPNOTSUPP; 1388819efc9SPablo Neira Ayuso 139a5d45bc0SPablo Neira Ayuso nft_offload_update_dependency(ctx, &priv->data, reg->len); 140c9626a2cSPablo Neira Ayuso 141c9626a2cSPablo Neira Ayuso return 0; 142c9626a2cSPablo Neira Ayuso } 143c9626a2cSPablo Neira Ayuso 144c9626a2cSPablo Neira Ayuso static int nft_cmp_offload(struct nft_offload_ctx *ctx, 145c9626a2cSPablo Neira Ayuso struct nft_flow_rule *flow, 146c9626a2cSPablo Neira Ayuso const struct nft_expr *expr) 147c9626a2cSPablo Neira Ayuso { 148c9626a2cSPablo Neira Ayuso const struct nft_cmp_expr *priv = nft_expr_priv(expr); 149c9626a2cSPablo Neira Ayuso 150c9626a2cSPablo Neira Ayuso return __nft_cmp_offload(ctx, flow, priv); 151c9626a2cSPablo Neira Ayuso } 152c9626a2cSPablo Neira Ayuso 153ef1f7df9SPatrick McHardy static const struct nft_expr_ops nft_cmp_ops = { 154ef1f7df9SPatrick McHardy .type = &nft_cmp_type, 15596518518SPatrick McHardy .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), 15696518518SPatrick McHardy .eval = nft_cmp_eval, 15796518518SPatrick McHardy .init = nft_cmp_init, 15896518518SPatrick McHardy .dump = nft_cmp_dump, 159c9626a2cSPablo Neira Ayuso .offload = nft_cmp_offload, 160ef1f7df9SPatrick McHardy }; 161ef1f7df9SPatrick McHardy 162cb7dbfd0SPatrick McHardy static int nft_cmp_fast_init(const struct nft_ctx *ctx, 163cb7dbfd0SPatrick McHardy const struct nft_expr *expr, 164cb7dbfd0SPatrick McHardy const struct nlattr * const tb[]) 165cb7dbfd0SPatrick McHardy { 166cb7dbfd0SPatrick McHardy struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 167cb7dbfd0SPatrick McHardy struct nft_data_desc desc; 168cb7dbfd0SPatrick McHardy struct nft_data data; 169cb7dbfd0SPatrick McHardy int err; 170cb7dbfd0SPatrick McHardy 171d0a11fc3SPatrick McHardy err = nft_data_init(NULL, &data, sizeof(data), &desc, 172d0a11fc3SPatrick McHardy tb[NFTA_CMP_DATA]); 173fa5950e4SFlorian Westphal if (err < 0) 174fa5950e4SFlorian Westphal return err; 175cb7dbfd0SPatrick McHardy 176*4f16d25cSPablo Neira Ayuso err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len); 177d07db988SPatrick McHardy if (err < 0) 178d07db988SPatrick McHardy return err; 179d07db988SPatrick McHardy 180d07db988SPatrick McHardy desc.len *= BITS_PER_BYTE; 181d07db988SPatrick McHardy 1825f48846dSPhil Sutter priv->mask = nft_cmp_fast_mask(desc.len); 1835f48846dSPhil Sutter priv->data = data.data[0] & priv->mask; 184cb7dbfd0SPatrick McHardy priv->len = desc.len; 1855f48846dSPhil Sutter priv->inv = ntohl(nla_get_be32(tb[NFTA_CMP_OP])) != NFT_CMP_EQ; 186cb7dbfd0SPatrick McHardy return 0; 187cb7dbfd0SPatrick McHardy } 188cb7dbfd0SPatrick McHardy 189c9626a2cSPablo Neira Ayuso static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx, 190c9626a2cSPablo Neira Ayuso struct nft_flow_rule *flow, 191c9626a2cSPablo Neira Ayuso const struct nft_expr *expr) 192c9626a2cSPablo Neira Ayuso { 193c9626a2cSPablo Neira Ayuso const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 194c9626a2cSPablo Neira Ayuso struct nft_cmp_expr cmp = { 195c9626a2cSPablo Neira Ayuso .data = { 196c9626a2cSPablo Neira Ayuso .data = { 197c9626a2cSPablo Neira Ayuso [0] = priv->data, 198c9626a2cSPablo Neira Ayuso }, 199c9626a2cSPablo Neira Ayuso }, 200c9626a2cSPablo Neira Ayuso .sreg = priv->sreg, 201c9626a2cSPablo Neira Ayuso .len = priv->len / BITS_PER_BYTE, 2025f48846dSPhil Sutter .op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ, 203c9626a2cSPablo Neira Ayuso }; 204c9626a2cSPablo Neira Ayuso 205c9626a2cSPablo Neira Ayuso return __nft_cmp_offload(ctx, flow, &cmp); 206c9626a2cSPablo Neira Ayuso } 207c9626a2cSPablo Neira Ayuso 208cb7dbfd0SPatrick McHardy static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) 209cb7dbfd0SPatrick McHardy { 210cb7dbfd0SPatrick McHardy const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 2115f48846dSPhil Sutter enum nft_cmp_ops op = priv->inv ? NFT_CMP_NEQ : NFT_CMP_EQ; 212cb7dbfd0SPatrick McHardy struct nft_data data; 213cb7dbfd0SPatrick McHardy 214b1c96ed3SPatrick McHardy if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) 215cb7dbfd0SPatrick McHardy goto nla_put_failure; 2165f48846dSPhil Sutter if (nla_put_be32(skb, NFTA_CMP_OP, htonl(op))) 217cb7dbfd0SPatrick McHardy goto nla_put_failure; 218cb7dbfd0SPatrick McHardy 219cb7dbfd0SPatrick McHardy data.data[0] = priv->data; 220cb7dbfd0SPatrick McHardy if (nft_data_dump(skb, NFTA_CMP_DATA, &data, 221cb7dbfd0SPatrick McHardy NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0) 222cb7dbfd0SPatrick McHardy goto nla_put_failure; 223cb7dbfd0SPatrick McHardy return 0; 224cb7dbfd0SPatrick McHardy 225cb7dbfd0SPatrick McHardy nla_put_failure: 226cb7dbfd0SPatrick McHardy return -1; 227cb7dbfd0SPatrick McHardy } 228cb7dbfd0SPatrick McHardy 229cb7dbfd0SPatrick McHardy const struct nft_expr_ops nft_cmp_fast_ops = { 230cb7dbfd0SPatrick McHardy .type = &nft_cmp_type, 231cb7dbfd0SPatrick McHardy .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)), 232cb7dbfd0SPatrick McHardy .eval = NULL, /* inlined */ 233cb7dbfd0SPatrick McHardy .init = nft_cmp_fast_init, 234cb7dbfd0SPatrick McHardy .dump = nft_cmp_fast_dump, 235c9626a2cSPablo Neira Ayuso .offload = nft_cmp_fast_offload, 236cb7dbfd0SPatrick McHardy }; 237cb7dbfd0SPatrick McHardy 2380ca743a5SPablo Neira Ayuso static const struct nft_expr_ops * 2390ca743a5SPablo Neira Ayuso nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) 240cb7dbfd0SPatrick McHardy { 241cb7dbfd0SPatrick McHardy struct nft_data_desc desc; 242cb7dbfd0SPatrick McHardy struct nft_data data; 243cb7dbfd0SPatrick McHardy enum nft_cmp_ops op; 244cb7dbfd0SPatrick McHardy int err; 245cb7dbfd0SPatrick McHardy 246cb7dbfd0SPatrick McHardy if (tb[NFTA_CMP_SREG] == NULL || 247cb7dbfd0SPatrick McHardy tb[NFTA_CMP_OP] == NULL || 248cb7dbfd0SPatrick McHardy tb[NFTA_CMP_DATA] == NULL) 249cb7dbfd0SPatrick McHardy return ERR_PTR(-EINVAL); 250cb7dbfd0SPatrick McHardy 251cb7dbfd0SPatrick McHardy op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 252cb7dbfd0SPatrick McHardy switch (op) { 253cb7dbfd0SPatrick McHardy case NFT_CMP_EQ: 254cb7dbfd0SPatrick McHardy case NFT_CMP_NEQ: 255cb7dbfd0SPatrick McHardy case NFT_CMP_LT: 256cb7dbfd0SPatrick McHardy case NFT_CMP_LTE: 257cb7dbfd0SPatrick McHardy case NFT_CMP_GT: 258cb7dbfd0SPatrick McHardy case NFT_CMP_GTE: 259cb7dbfd0SPatrick McHardy break; 260cb7dbfd0SPatrick McHardy default: 261cb7dbfd0SPatrick McHardy return ERR_PTR(-EINVAL); 262cb7dbfd0SPatrick McHardy } 263cb7dbfd0SPatrick McHardy 264d0a11fc3SPatrick McHardy err = nft_data_init(NULL, &data, sizeof(data), &desc, 265d0a11fc3SPatrick McHardy tb[NFTA_CMP_DATA]); 266cb7dbfd0SPatrick McHardy if (err < 0) 267cb7dbfd0SPatrick McHardy return ERR_PTR(err); 268cb7dbfd0SPatrick McHardy 26971df14b0SPablo Neira Ayuso if (desc.type != NFT_DATA_VALUE) { 27071df14b0SPablo Neira Ayuso err = -EINVAL; 27171df14b0SPablo Neira Ayuso goto err1; 27271df14b0SPablo Neira Ayuso } 27371df14b0SPablo Neira Ayuso 2745f48846dSPhil Sutter if (desc.len <= sizeof(u32) && (op == NFT_CMP_EQ || op == NFT_CMP_NEQ)) 275cb7dbfd0SPatrick McHardy return &nft_cmp_fast_ops; 27671df14b0SPablo Neira Ayuso 277cb7dbfd0SPatrick McHardy return &nft_cmp_ops; 27871df14b0SPablo Neira Ayuso err1: 27959105446SPablo Neira Ayuso nft_data_release(&data, desc.type); 28071df14b0SPablo Neira Ayuso return ERR_PTR(-EINVAL); 281cb7dbfd0SPatrick McHardy } 282cb7dbfd0SPatrick McHardy 2834e24877eSLiping Zhang struct nft_expr_type nft_cmp_type __read_mostly = { 284ef1f7df9SPatrick McHardy .name = "cmp", 285cb7dbfd0SPatrick McHardy .select_ops = nft_cmp_select_ops, 28696518518SPatrick McHardy .policy = nft_cmp_policy, 28796518518SPatrick McHardy .maxattr = NFTA_CMP_MAX, 288ef1f7df9SPatrick McHardy .owner = THIS_MODULE, 28996518518SPatrick McHardy }; 290