1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 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/list.h> 11 #include <linux/rbtree.h> 12 #include <linux/netlink.h> 13 #include <linux/netfilter.h> 14 #include <linux/netfilter/nf_tables.h> 15 #include <net/netfilter/nf_tables.h> 16 #include <net/netfilter/nf_tables_core.h> 17 18 struct nft_lookup { 19 struct nft_set *set; 20 enum nft_registers sreg:8; 21 enum nft_registers dreg:8; 22 bool invert; 23 struct nft_set_binding binding; 24 }; 25 26 void nft_lookup_eval(const struct nft_expr *expr, 27 struct nft_regs *regs, 28 const struct nft_pktinfo *pkt) 29 { 30 const struct nft_lookup *priv = nft_expr_priv(expr); 31 const struct nft_set *set = priv->set; 32 const struct nft_set_ext *ext = NULL; 33 bool found; 34 35 found = set->ops->lookup(nft_net(pkt), set, ®s->data[priv->sreg], 36 &ext) ^ priv->invert; 37 if (!found) { 38 regs->verdict.code = NFT_BREAK; 39 return; 40 } 41 42 if (ext) { 43 if (set->flags & NFT_SET_MAP) 44 nft_data_copy(®s->data[priv->dreg], 45 nft_set_ext_data(ext), set->dlen); 46 47 nft_set_elem_update_expr(ext, regs, pkt); 48 } 49 } 50 51 static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = { 52 [NFTA_LOOKUP_SET] = { .type = NLA_STRING, 53 .len = NFT_SET_MAXNAMELEN - 1 }, 54 [NFTA_LOOKUP_SET_ID] = { .type = NLA_U32 }, 55 [NFTA_LOOKUP_SREG] = { .type = NLA_U32 }, 56 [NFTA_LOOKUP_DREG] = { .type = NLA_U32 }, 57 [NFTA_LOOKUP_FLAGS] = { .type = NLA_U32 }, 58 }; 59 60 static int nft_lookup_init(const struct nft_ctx *ctx, 61 const struct nft_expr *expr, 62 const struct nlattr * const tb[]) 63 { 64 struct nft_lookup *priv = nft_expr_priv(expr); 65 u8 genmask = nft_genmask_next(ctx->net); 66 struct nft_set *set; 67 u32 flags; 68 int err; 69 70 if (tb[NFTA_LOOKUP_SET] == NULL || 71 tb[NFTA_LOOKUP_SREG] == NULL) 72 return -EINVAL; 73 74 set = nft_set_lookup_global(ctx->net, ctx->table, tb[NFTA_LOOKUP_SET], 75 tb[NFTA_LOOKUP_SET_ID], genmask); 76 if (IS_ERR(set)) 77 return PTR_ERR(set); 78 79 priv->sreg = nft_parse_register(tb[NFTA_LOOKUP_SREG]); 80 err = nft_validate_register_load(priv->sreg, set->klen); 81 if (err < 0) 82 return err; 83 84 if (tb[NFTA_LOOKUP_FLAGS]) { 85 flags = ntohl(nla_get_be32(tb[NFTA_LOOKUP_FLAGS])); 86 87 if (flags & ~NFT_LOOKUP_F_INV) 88 return -EINVAL; 89 90 if (flags & NFT_LOOKUP_F_INV) { 91 if (set->flags & NFT_SET_MAP) 92 return -EINVAL; 93 priv->invert = true; 94 } 95 } 96 97 if (tb[NFTA_LOOKUP_DREG] != NULL) { 98 if (priv->invert) 99 return -EINVAL; 100 if (!(set->flags & NFT_SET_MAP)) 101 return -EINVAL; 102 103 priv->dreg = nft_parse_register(tb[NFTA_LOOKUP_DREG]); 104 err = nft_validate_register_store(ctx, priv->dreg, NULL, 105 set->dtype, set->dlen); 106 if (err < 0) 107 return err; 108 } else if (set->flags & NFT_SET_MAP) 109 return -EINVAL; 110 111 priv->binding.flags = set->flags & NFT_SET_MAP; 112 113 err = nf_tables_bind_set(ctx, set, &priv->binding); 114 if (err < 0) 115 return err; 116 117 priv->set = set; 118 return 0; 119 } 120 121 static void nft_lookup_deactivate(const struct nft_ctx *ctx, 122 const struct nft_expr *expr, 123 enum nft_trans_phase phase) 124 { 125 struct nft_lookup *priv = nft_expr_priv(expr); 126 127 nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase); 128 } 129 130 static void nft_lookup_activate(const struct nft_ctx *ctx, 131 const struct nft_expr *expr) 132 { 133 struct nft_lookup *priv = nft_expr_priv(expr); 134 135 priv->set->use++; 136 } 137 138 static void nft_lookup_destroy(const struct nft_ctx *ctx, 139 const struct nft_expr *expr) 140 { 141 struct nft_lookup *priv = nft_expr_priv(expr); 142 143 nf_tables_destroy_set(ctx, priv->set); 144 } 145 146 static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr) 147 { 148 const struct nft_lookup *priv = nft_expr_priv(expr); 149 u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0; 150 151 if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name)) 152 goto nla_put_failure; 153 if (nft_dump_register(skb, NFTA_LOOKUP_SREG, priv->sreg)) 154 goto nla_put_failure; 155 if (priv->set->flags & NFT_SET_MAP) 156 if (nft_dump_register(skb, NFTA_LOOKUP_DREG, priv->dreg)) 157 goto nla_put_failure; 158 if (nla_put_be32(skb, NFTA_LOOKUP_FLAGS, htonl(flags))) 159 goto nla_put_failure; 160 return 0; 161 162 nla_put_failure: 163 return -1; 164 } 165 166 static int nft_lookup_validate_setelem(const struct nft_ctx *ctx, 167 struct nft_set *set, 168 const struct nft_set_iter *iter, 169 struct nft_set_elem *elem) 170 { 171 const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); 172 struct nft_ctx *pctx = (struct nft_ctx *)ctx; 173 const struct nft_data *data; 174 int err; 175 176 if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && 177 *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END) 178 return 0; 179 180 data = nft_set_ext_data(ext); 181 switch (data->verdict.code) { 182 case NFT_JUMP: 183 case NFT_GOTO: 184 pctx->level++; 185 err = nft_chain_validate(ctx, data->verdict.chain); 186 if (err < 0) 187 return err; 188 pctx->level--; 189 break; 190 default: 191 break; 192 } 193 194 return 0; 195 } 196 197 static int nft_lookup_validate(const struct nft_ctx *ctx, 198 const struct nft_expr *expr, 199 const struct nft_data **d) 200 { 201 const struct nft_lookup *priv = nft_expr_priv(expr); 202 struct nft_set_iter iter; 203 204 if (!(priv->set->flags & NFT_SET_MAP) || 205 priv->set->dtype != NFT_DATA_VERDICT) 206 return 0; 207 208 iter.genmask = nft_genmask_next(ctx->net); 209 iter.skip = 0; 210 iter.count = 0; 211 iter.err = 0; 212 iter.fn = nft_lookup_validate_setelem; 213 214 priv->set->ops->walk(ctx, priv->set, &iter); 215 if (iter.err < 0) 216 return iter.err; 217 218 return 0; 219 } 220 221 static const struct nft_expr_ops nft_lookup_ops = { 222 .type = &nft_lookup_type, 223 .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), 224 .eval = nft_lookup_eval, 225 .init = nft_lookup_init, 226 .activate = nft_lookup_activate, 227 .deactivate = nft_lookup_deactivate, 228 .destroy = nft_lookup_destroy, 229 .dump = nft_lookup_dump, 230 .validate = nft_lookup_validate, 231 }; 232 233 struct nft_expr_type nft_lookup_type __read_mostly = { 234 .name = "lookup", 235 .ops = &nft_lookup_ops, 236 .policy = nft_lookup_policy, 237 .maxattr = NFTA_LOOKUP_MAX, 238 .owner = THIS_MODULE, 239 }; 240