1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> 4 * 5 * Development of this code funded by Astaro AG (http://www.astaro.com/) 6 */ 7 8 #include <linux/kernel.h> 9 #include <linux/init.h> 10 #include <linux/module.h> 11 #include <linux/netlink.h> 12 #include <linux/netfilter.h> 13 #include <linux/netfilter/nf_tables.h> 14 #include <net/netfilter/nf_tables_core.h> 15 #include <net/netfilter/nf_tables_offload.h> 16 #include <net/netfilter/nf_tables.h> 17 18 struct nft_cmp_expr { 19 struct nft_data data; 20 enum nft_registers sreg:8; 21 u8 len; 22 enum nft_cmp_ops op:8; 23 }; 24 25 void nft_cmp_eval(const struct nft_expr *expr, 26 struct nft_regs *regs, 27 const struct nft_pktinfo *pkt) 28 { 29 const struct nft_cmp_expr *priv = nft_expr_priv(expr); 30 int d; 31 32 d = memcmp(®s->data[priv->sreg], &priv->data, priv->len); 33 switch (priv->op) { 34 case NFT_CMP_EQ: 35 if (d != 0) 36 goto mismatch; 37 break; 38 case NFT_CMP_NEQ: 39 if (d == 0) 40 goto mismatch; 41 break; 42 case NFT_CMP_LT: 43 if (d == 0) 44 goto mismatch; 45 /* fall through */ 46 case NFT_CMP_LTE: 47 if (d > 0) 48 goto mismatch; 49 break; 50 case NFT_CMP_GT: 51 if (d == 0) 52 goto mismatch; 53 /* fall through */ 54 case NFT_CMP_GTE: 55 if (d < 0) 56 goto mismatch; 57 break; 58 } 59 return; 60 61 mismatch: 62 regs->verdict.code = NFT_BREAK; 63 } 64 65 static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { 66 [NFTA_CMP_SREG] = { .type = NLA_U32 }, 67 [NFTA_CMP_OP] = { .type = NLA_U32 }, 68 [NFTA_CMP_DATA] = { .type = NLA_NESTED }, 69 }; 70 71 static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, 72 const struct nlattr * const tb[]) 73 { 74 struct nft_cmp_expr *priv = nft_expr_priv(expr); 75 struct nft_data_desc desc; 76 int err; 77 78 err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, 79 tb[NFTA_CMP_DATA]); 80 if (err < 0) 81 return err; 82 83 priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]); 84 err = nft_validate_register_load(priv->sreg, desc.len); 85 if (err < 0) 86 return err; 87 88 priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 89 priv->len = desc.len; 90 return 0; 91 } 92 93 static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) 94 { 95 const struct nft_cmp_expr *priv = nft_expr_priv(expr); 96 97 if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) 98 goto nla_put_failure; 99 if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op))) 100 goto nla_put_failure; 101 102 if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, 103 NFT_DATA_VALUE, priv->len) < 0) 104 goto nla_put_failure; 105 return 0; 106 107 nla_put_failure: 108 return -1; 109 } 110 111 static int __nft_cmp_offload(struct nft_offload_ctx *ctx, 112 struct nft_flow_rule *flow, 113 const struct nft_cmp_expr *priv) 114 { 115 struct nft_offload_reg *reg = &ctx->regs[priv->sreg]; 116 u8 *mask = (u8 *)&flow->match.mask; 117 u8 *key = (u8 *)&flow->match.key; 118 119 if (priv->op != NFT_CMP_EQ) 120 return -EOPNOTSUPP; 121 122 memcpy(key + reg->offset, &priv->data, priv->len); 123 memcpy(mask + reg->offset, ®->mask, priv->len); 124 125 flow->match.dissector.used_keys |= BIT(reg->key); 126 flow->match.dissector.offset[reg->key] = reg->base_offset; 127 128 nft_offload_update_dependency(ctx, &priv->data, priv->len); 129 130 return 0; 131 } 132 133 static int nft_cmp_offload(struct nft_offload_ctx *ctx, 134 struct nft_flow_rule *flow, 135 const struct nft_expr *expr) 136 { 137 const struct nft_cmp_expr *priv = nft_expr_priv(expr); 138 139 return __nft_cmp_offload(ctx, flow, priv); 140 } 141 142 static const struct nft_expr_ops nft_cmp_ops = { 143 .type = &nft_cmp_type, 144 .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), 145 .eval = nft_cmp_eval, 146 .init = nft_cmp_init, 147 .dump = nft_cmp_dump, 148 .offload = nft_cmp_offload, 149 }; 150 151 static int nft_cmp_fast_init(const struct nft_ctx *ctx, 152 const struct nft_expr *expr, 153 const struct nlattr * const tb[]) 154 { 155 struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 156 struct nft_data_desc desc; 157 struct nft_data data; 158 u32 mask; 159 int err; 160 161 err = nft_data_init(NULL, &data, sizeof(data), &desc, 162 tb[NFTA_CMP_DATA]); 163 if (err < 0) 164 return err; 165 166 priv->sreg = nft_parse_register(tb[NFTA_CMP_SREG]); 167 err = nft_validate_register_load(priv->sreg, desc.len); 168 if (err < 0) 169 return err; 170 171 desc.len *= BITS_PER_BYTE; 172 mask = nft_cmp_fast_mask(desc.len); 173 174 priv->data = data.data[0] & mask; 175 priv->len = desc.len; 176 return 0; 177 } 178 179 static int nft_cmp_fast_offload(struct nft_offload_ctx *ctx, 180 struct nft_flow_rule *flow, 181 const struct nft_expr *expr) 182 { 183 const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 184 struct nft_cmp_expr cmp = { 185 .data = { 186 .data = { 187 [0] = priv->data, 188 }, 189 }, 190 .sreg = priv->sreg, 191 .len = priv->len / BITS_PER_BYTE, 192 .op = NFT_CMP_EQ, 193 }; 194 195 return __nft_cmp_offload(ctx, flow, &cmp); 196 } 197 198 static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) 199 { 200 const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); 201 struct nft_data data; 202 203 if (nft_dump_register(skb, NFTA_CMP_SREG, priv->sreg)) 204 goto nla_put_failure; 205 if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ))) 206 goto nla_put_failure; 207 208 data.data[0] = priv->data; 209 if (nft_data_dump(skb, NFTA_CMP_DATA, &data, 210 NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0) 211 goto nla_put_failure; 212 return 0; 213 214 nla_put_failure: 215 return -1; 216 } 217 218 const struct nft_expr_ops nft_cmp_fast_ops = { 219 .type = &nft_cmp_type, 220 .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)), 221 .eval = NULL, /* inlined */ 222 .init = nft_cmp_fast_init, 223 .dump = nft_cmp_fast_dump, 224 .offload = nft_cmp_fast_offload, 225 }; 226 227 static const struct nft_expr_ops * 228 nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) 229 { 230 struct nft_data_desc desc; 231 struct nft_data data; 232 enum nft_cmp_ops op; 233 int err; 234 235 if (tb[NFTA_CMP_SREG] == NULL || 236 tb[NFTA_CMP_OP] == NULL || 237 tb[NFTA_CMP_DATA] == NULL) 238 return ERR_PTR(-EINVAL); 239 240 op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); 241 switch (op) { 242 case NFT_CMP_EQ: 243 case NFT_CMP_NEQ: 244 case NFT_CMP_LT: 245 case NFT_CMP_LTE: 246 case NFT_CMP_GT: 247 case NFT_CMP_GTE: 248 break; 249 default: 250 return ERR_PTR(-EINVAL); 251 } 252 253 err = nft_data_init(NULL, &data, sizeof(data), &desc, 254 tb[NFTA_CMP_DATA]); 255 if (err < 0) 256 return ERR_PTR(err); 257 258 if (desc.type != NFT_DATA_VALUE) { 259 err = -EINVAL; 260 goto err1; 261 } 262 263 if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ) 264 return &nft_cmp_fast_ops; 265 266 return &nft_cmp_ops; 267 err1: 268 nft_data_release(&data, desc.type); 269 return ERR_PTR(-EINVAL); 270 } 271 272 struct nft_expr_type nft_cmp_type __read_mostly = { 273 .name = "cmp", 274 .select_ops = nft_cmp_select_ops, 275 .policy = nft_cmp_policy, 276 .maxattr = NFTA_CMP_MAX, 277 .owner = THIS_MODULE, 278 }; 279