196518518SPatrick McHardy /* 2ef1f7df9SPatrick McHardy * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> 396518518SPatrick McHardy * 496518518SPatrick McHardy * This program is free software; you can redistribute it and/or modify 596518518SPatrick McHardy * it under the terms of the GNU General Public License version 2 as 696518518SPatrick McHardy * published by the Free Software Foundation. 796518518SPatrick McHardy * 896518518SPatrick McHardy * Development of this code funded by Astaro AG (http://www.astaro.com/) 996518518SPatrick McHardy */ 1096518518SPatrick McHardy 1196518518SPatrick McHardy #include <linux/kernel.h> 1296518518SPatrick McHardy #include <linux/init.h> 1396518518SPatrick McHardy #include <linux/module.h> 1496518518SPatrick McHardy #include <linux/netlink.h> 1596518518SPatrick McHardy #include <linux/netfilter.h> 1696518518SPatrick McHardy #include <linux/netfilter/nf_tables.h> 1796518518SPatrick McHardy #include <net/netfilter/nf_tables_core.h> 1896518518SPatrick McHardy #include <net/netfilter/nf_tables.h> 1996518518SPatrick McHardy 2096518518SPatrick McHardy struct nft_cmp_expr { 2196518518SPatrick McHardy struct nft_data data; 2296518518SPatrick McHardy enum nft_registers sreg:8; 2396518518SPatrick McHardy u8 len; 2496518518SPatrick McHardy enum nft_cmp_ops op:8; 2596518518SPatrick McHardy }; 2696518518SPatrick McHardy 2796518518SPatrick McHardy static void nft_cmp_eval(const struct nft_expr *expr, 2896518518SPatrick McHardy struct nft_data data[NFT_REG_MAX + 1], 2996518518SPatrick McHardy const struct nft_pktinfo *pkt) 3096518518SPatrick McHardy { 3196518518SPatrick McHardy const struct nft_cmp_expr *priv = nft_expr_priv(expr); 3296518518SPatrick McHardy int d; 3396518518SPatrick McHardy 3496518518SPatrick McHardy d = nft_data_cmp(&data[priv->sreg], &priv->data, priv->len); 3596518518SPatrick McHardy switch (priv->op) { 3696518518SPatrick McHardy case NFT_CMP_EQ: 3796518518SPatrick McHardy if (d != 0) 3896518518SPatrick McHardy goto mismatch; 3996518518SPatrick McHardy break; 4096518518SPatrick McHardy case NFT_CMP_NEQ: 4196518518SPatrick McHardy if (d == 0) 4296518518SPatrick McHardy goto mismatch; 4396518518SPatrick McHardy break; 4496518518SPatrick McHardy case NFT_CMP_LT: 4596518518SPatrick McHardy if (d == 0) 4696518518SPatrick McHardy goto mismatch; 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; 5496518518SPatrick McHardy case NFT_CMP_GTE: 5596518518SPatrick McHardy if (d < 0) 5696518518SPatrick McHardy goto mismatch; 5796518518SPatrick McHardy break; 5896518518SPatrick McHardy } 5996518518SPatrick McHardy return; 6096518518SPatrick McHardy 6196518518SPatrick McHardy mismatch: 6296518518SPatrick McHardy data[NFT_REG_VERDICT].verdict = NFT_BREAK; 6396518518SPatrick McHardy } 6496518518SPatrick McHardy 6596518518SPatrick McHardy static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { 6696518518SPatrick McHardy [NFTA_CMP_SREG] = { .type = NLA_U32 }, 6796518518SPatrick McHardy [NFTA_CMP_OP] = { .type = NLA_U32 }, 6896518518SPatrick McHardy [NFTA_CMP_DATA] = { .type = NLA_NESTED }, 6996518518SPatrick McHardy }; 7096518518SPatrick McHardy 7196518518SPatrick McHardy static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 7296518518SPatrick McHardy const struct nlattr * const tb[]) 7396518518SPatrick McHardy { 7496518518SPatrick McHardy struct nft_cmp_expr *priv = nft_expr_priv(expr); 7596518518SPatrick McHardy struct nft_data_desc desc; 7696518518SPatrick McHardy int err; 7796518518SPatrick McHardy 7896518518SPatrick McHardy priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); 7996518518SPatrick McHardy priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 8096518518SPatrick McHardy 8196518518SPatrick McHardy err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); 82cb7dbfd0SPatrick McHardy BUG_ON(err < 0); 8396518518SPatrick McHardy 8496518518SPatrick McHardy priv->len = desc.len; 8596518518SPatrick McHardy return 0; 8696518518SPatrick McHardy } 8796518518SPatrick McHardy 8896518518SPatrick McHardy static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) 8996518518SPatrick McHardy { 9096518518SPatrick McHardy const struct nft_cmp_expr *priv = nft_expr_priv(expr); 9196518518SPatrick McHardy 9296518518SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg))) 9396518518SPatrick McHardy goto nla_put_failure; 9496518518SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op))) 9596518518SPatrick McHardy goto nla_put_failure; 9696518518SPatrick McHardy 9796518518SPatrick McHardy if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, 9896518518SPatrick McHardy NFT_DATA_VALUE, priv->len) < 0) 9996518518SPatrick McHardy goto nla_put_failure; 10096518518SPatrick McHardy return 0; 10196518518SPatrick McHardy 10296518518SPatrick McHardy nla_put_failure: 10396518518SPatrick McHardy return -1; 10496518518SPatrick McHardy } 10596518518SPatrick McHardy 106ef1f7df9SPatrick McHardy static struct nft_expr_type nft_cmp_type; 107ef1f7df9SPatrick McHardy static const struct nft_expr_ops nft_cmp_ops = { 108ef1f7df9SPatrick McHardy .type = &nft_cmp_type, 10996518518SPatrick McHardy .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), 11096518518SPatrick McHardy .eval = nft_cmp_eval, 11196518518SPatrick McHardy .init = nft_cmp_init, 11296518518SPatrick McHardy .dump = nft_cmp_dump, 113ef1f7df9SPatrick McHardy }; 114ef1f7df9SPatrick McHardy 115cb7dbfd0SPatrick McHardy static int nft_cmp_fast_init(const struct nft_ctx *ctx, 116cb7dbfd0SPatrick McHardy const struct nft_expr *expr, 117cb7dbfd0SPatrick McHardy const struct nlattr * const tb[]) 118cb7dbfd0SPatrick McHardy { 119cb7dbfd0SPatrick McHardy struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 120cb7dbfd0SPatrick McHardy struct nft_data_desc desc; 121cb7dbfd0SPatrick McHardy struct nft_data data; 122cb7dbfd0SPatrick McHardy u32 mask; 123cb7dbfd0SPatrick McHardy int err; 124cb7dbfd0SPatrick McHardy 125cb7dbfd0SPatrick McHardy priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); 126cb7dbfd0SPatrick McHardy 127cb7dbfd0SPatrick McHardy err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); 128cb7dbfd0SPatrick McHardy BUG_ON(err < 0); 129cb7dbfd0SPatrick McHardy desc.len *= BITS_PER_BYTE; 130cb7dbfd0SPatrick McHardy 131b855d416SPatrick McHardy mask = nft_cmp_fast_mask(desc.len); 132cb7dbfd0SPatrick McHardy priv->data = data.data[0] & mask; 133cb7dbfd0SPatrick McHardy priv->len = desc.len; 134cb7dbfd0SPatrick McHardy return 0; 135cb7dbfd0SPatrick McHardy } 136cb7dbfd0SPatrick McHardy 137cb7dbfd0SPatrick McHardy static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) 138cb7dbfd0SPatrick McHardy { 139cb7dbfd0SPatrick McHardy const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 140cb7dbfd0SPatrick McHardy struct nft_data data; 141cb7dbfd0SPatrick McHardy 142cb7dbfd0SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg))) 143cb7dbfd0SPatrick McHardy goto nla_put_failure; 144cb7dbfd0SPatrick McHardy if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ))) 145cb7dbfd0SPatrick McHardy goto nla_put_failure; 146cb7dbfd0SPatrick McHardy 147cb7dbfd0SPatrick McHardy data.data[0] = priv->data; 148cb7dbfd0SPatrick McHardy if (nft_data_dump(skb, NFTA_CMP_DATA, &data, 149cb7dbfd0SPatrick McHardy NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0) 150cb7dbfd0SPatrick McHardy goto nla_put_failure; 151cb7dbfd0SPatrick McHardy return 0; 152cb7dbfd0SPatrick McHardy 153cb7dbfd0SPatrick McHardy nla_put_failure: 154cb7dbfd0SPatrick McHardy return -1; 155cb7dbfd0SPatrick McHardy } 156cb7dbfd0SPatrick McHardy 157cb7dbfd0SPatrick McHardy const struct nft_expr_ops nft_cmp_fast_ops = { 158cb7dbfd0SPatrick McHardy .type = &nft_cmp_type, 159cb7dbfd0SPatrick McHardy .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)), 160cb7dbfd0SPatrick McHardy .eval = NULL, /* inlined */ 161cb7dbfd0SPatrick McHardy .init = nft_cmp_fast_init, 162cb7dbfd0SPatrick McHardy .dump = nft_cmp_fast_dump, 163cb7dbfd0SPatrick McHardy }; 164cb7dbfd0SPatrick McHardy 1650ca743a5SPablo Neira Ayuso static const struct nft_expr_ops * 1660ca743a5SPablo Neira Ayuso nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) 167cb7dbfd0SPatrick McHardy { 168cb7dbfd0SPatrick McHardy struct nft_data_desc desc; 169cb7dbfd0SPatrick McHardy struct nft_data data; 170cb7dbfd0SPatrick McHardy enum nft_registers sreg; 171cb7dbfd0SPatrick McHardy enum nft_cmp_ops op; 172cb7dbfd0SPatrick McHardy int err; 173cb7dbfd0SPatrick McHardy 174cb7dbfd0SPatrick McHardy if (tb[NFTA_CMP_SREG] == NULL || 175cb7dbfd0SPatrick McHardy tb[NFTA_CMP_OP] == NULL || 176cb7dbfd0SPatrick McHardy tb[NFTA_CMP_DATA] == NULL) 177cb7dbfd0SPatrick McHardy return ERR_PTR(-EINVAL); 178cb7dbfd0SPatrick McHardy 179cb7dbfd0SPatrick McHardy sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); 180cb7dbfd0SPatrick McHardy err = nft_validate_input_register(sreg); 181cb7dbfd0SPatrick McHardy if (err < 0) 182cb7dbfd0SPatrick McHardy return ERR_PTR(err); 183cb7dbfd0SPatrick McHardy 184cb7dbfd0SPatrick McHardy op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 185cb7dbfd0SPatrick McHardy switch (op) { 186cb7dbfd0SPatrick McHardy case NFT_CMP_EQ: 187cb7dbfd0SPatrick McHardy case NFT_CMP_NEQ: 188cb7dbfd0SPatrick McHardy case NFT_CMP_LT: 189cb7dbfd0SPatrick McHardy case NFT_CMP_LTE: 190cb7dbfd0SPatrick McHardy case NFT_CMP_GT: 191cb7dbfd0SPatrick McHardy case NFT_CMP_GTE: 192cb7dbfd0SPatrick McHardy break; 193cb7dbfd0SPatrick McHardy default: 194cb7dbfd0SPatrick McHardy return ERR_PTR(-EINVAL); 195cb7dbfd0SPatrick McHardy } 196cb7dbfd0SPatrick McHardy 197cb7dbfd0SPatrick McHardy err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); 198cb7dbfd0SPatrick McHardy if (err < 0) 199cb7dbfd0SPatrick McHardy return ERR_PTR(err); 200cb7dbfd0SPatrick McHardy 201cb7dbfd0SPatrick McHardy if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ) 202cb7dbfd0SPatrick McHardy return &nft_cmp_fast_ops; 203cb7dbfd0SPatrick McHardy else 204cb7dbfd0SPatrick McHardy return &nft_cmp_ops; 205cb7dbfd0SPatrick McHardy } 206cb7dbfd0SPatrick McHardy 207ef1f7df9SPatrick McHardy static struct nft_expr_type nft_cmp_type __read_mostly = { 208ef1f7df9SPatrick McHardy .name = "cmp", 209cb7dbfd0SPatrick McHardy .select_ops = nft_cmp_select_ops, 21096518518SPatrick McHardy .policy = nft_cmp_policy, 21196518518SPatrick McHardy .maxattr = NFTA_CMP_MAX, 212ef1f7df9SPatrick McHardy .owner = THIS_MODULE, 21396518518SPatrick McHardy }; 21496518518SPatrick McHardy 21596518518SPatrick McHardy int __init nft_cmp_module_init(void) 21696518518SPatrick McHardy { 217ef1f7df9SPatrick McHardy return nft_register_expr(&nft_cmp_type); 21896518518SPatrick McHardy } 21996518518SPatrick McHardy 22096518518SPatrick McHardy void nft_cmp_module_exit(void) 22196518518SPatrick McHardy { 222ef1f7df9SPatrick McHardy nft_unregister_expr(&nft_cmp_type); 22396518518SPatrick McHardy } 224