1 /* 2 * Copyright (c) 2009 Patrick McHardy <kaber@trash.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * Development of this code funded by Astaro AG (http://www.astaro.com/) 9 */ 10 11 #include <linux/kernel.h> 12 #include <linux/init.h> 13 #include <linux/list.h> 14 #include <linux/rbtree.h> 15 #include <linux/netlink.h> 16 #include <linux/netfilter.h> 17 #include <linux/netfilter/nf_tables.h> 18 #include <net/netfilter/nf_tables.h> 19 #include <net/netfilter/nf_tables_core.h> 20 21 struct nft_lookup { 22 struct nft_set *set; 23 enum nft_registers sreg:8; 24 enum nft_registers dreg:8; 25 bool invert; 26 struct nft_set_binding binding; 27 }; 28 29 void nft_lookup_eval(const struct nft_expr *expr, 30 struct nft_regs *regs, 31 const struct nft_pktinfo *pkt) 32 { 33 const struct nft_lookup *priv = nft_expr_priv(expr); 34 const struct nft_set *set = priv->set; 35 const struct nft_set_ext *ext; 36 bool found; 37 38 found = set->ops->lookup(nft_net(pkt), set, ®s->data[priv->sreg], 39 &ext) ^ priv->invert; 40 if (!found) { 41 regs->verdict.code = NFT_BREAK; 42 return; 43 } 44 45 if (set->flags & NFT_SET_MAP) 46 nft_data_copy(®s->data[priv->dreg], 47 nft_set_ext_data(ext), set->dlen); 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 if (set->flags & NFT_SET_EVAL) 80 return -EOPNOTSUPP; 81 82 priv->sreg = nft_parse_register(tb[NFTA_LOOKUP_SREG]); 83 err = nft_validate_register_load(priv->sreg, set->klen); 84 if (err < 0) 85 return err; 86 87 if (tb[NFTA_LOOKUP_FLAGS]) { 88 flags = ntohl(nla_get_be32(tb[NFTA_LOOKUP_FLAGS])); 89 90 if (flags & ~NFT_LOOKUP_F_INV) 91 return -EINVAL; 92 93 if (flags & NFT_LOOKUP_F_INV) { 94 if (set->flags & NFT_SET_MAP) 95 return -EINVAL; 96 priv->invert = true; 97 } 98 } 99 100 if (tb[NFTA_LOOKUP_DREG] != NULL) { 101 if (priv->invert) 102 return -EINVAL; 103 if (!(set->flags & NFT_SET_MAP)) 104 return -EINVAL; 105 106 priv->dreg = nft_parse_register(tb[NFTA_LOOKUP_DREG]); 107 err = nft_validate_register_store(ctx, priv->dreg, NULL, 108 set->dtype, set->dlen); 109 if (err < 0) 110 return err; 111 } else if (set->flags & NFT_SET_MAP) 112 return -EINVAL; 113 114 priv->binding.flags = set->flags & NFT_SET_MAP; 115 116 err = nf_tables_bind_set(ctx, set, &priv->binding); 117 if (err < 0) 118 return err; 119 120 priv->set = set; 121 return 0; 122 } 123 124 static void nft_lookup_deactivate(const struct nft_ctx *ctx, 125 const struct nft_expr *expr, 126 enum nft_trans_phase phase) 127 { 128 struct nft_lookup *priv = nft_expr_priv(expr); 129 130 nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase); 131 } 132 133 static void nft_lookup_activate(const struct nft_ctx *ctx, 134 const struct nft_expr *expr) 135 { 136 struct nft_lookup *priv = nft_expr_priv(expr); 137 138 priv->set->use++; 139 } 140 141 static void nft_lookup_destroy(const struct nft_ctx *ctx, 142 const struct nft_expr *expr) 143 { 144 struct nft_lookup *priv = nft_expr_priv(expr); 145 146 nf_tables_destroy_set(ctx, priv->set); 147 } 148 149 static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr) 150 { 151 const struct nft_lookup *priv = nft_expr_priv(expr); 152 u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0; 153 154 if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name)) 155 goto nla_put_failure; 156 if (nft_dump_register(skb, NFTA_LOOKUP_SREG, priv->sreg)) 157 goto nla_put_failure; 158 if (priv->set->flags & NFT_SET_MAP) 159 if (nft_dump_register(skb, NFTA_LOOKUP_DREG, priv->dreg)) 160 goto nla_put_failure; 161 if (nla_put_be32(skb, NFTA_LOOKUP_FLAGS, htonl(flags))) 162 goto nla_put_failure; 163 return 0; 164 165 nla_put_failure: 166 return -1; 167 } 168 169 static int nft_lookup_validate_setelem(const struct nft_ctx *ctx, 170 struct nft_set *set, 171 const struct nft_set_iter *iter, 172 struct nft_set_elem *elem) 173 { 174 const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); 175 struct nft_ctx *pctx = (struct nft_ctx *)ctx; 176 const struct nft_data *data; 177 int err; 178 179 if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) && 180 *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END) 181 return 0; 182 183 data = nft_set_ext_data(ext); 184 switch (data->verdict.code) { 185 case NFT_JUMP: 186 case NFT_GOTO: 187 pctx->level++; 188 err = nft_chain_validate(ctx, data->verdict.chain); 189 if (err < 0) 190 return err; 191 pctx->level--; 192 break; 193 default: 194 break; 195 } 196 197 return 0; 198 } 199 200 static int nft_lookup_validate(const struct nft_ctx *ctx, 201 const struct nft_expr *expr, 202 const struct nft_data **d) 203 { 204 const struct nft_lookup *priv = nft_expr_priv(expr); 205 struct nft_set_iter iter; 206 207 if (!(priv->set->flags & NFT_SET_MAP) || 208 priv->set->dtype != NFT_DATA_VERDICT) 209 return 0; 210 211 iter.genmask = nft_genmask_next(ctx->net); 212 iter.skip = 0; 213 iter.count = 0; 214 iter.err = 0; 215 iter.fn = nft_lookup_validate_setelem; 216 217 priv->set->ops->walk(ctx, priv->set, &iter); 218 if (iter.err < 0) 219 return iter.err; 220 221 return 0; 222 } 223 224 static const struct nft_expr_ops nft_lookup_ops = { 225 .type = &nft_lookup_type, 226 .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), 227 .eval = nft_lookup_eval, 228 .init = nft_lookup_init, 229 .activate = nft_lookup_activate, 230 .deactivate = nft_lookup_deactivate, 231 .destroy = nft_lookup_destroy, 232 .dump = nft_lookup_dump, 233 .validate = nft_lookup_validate, 234 }; 235 236 struct nft_expr_type nft_lookup_type __read_mostly = { 237 .name = "lookup", 238 .ops = &nft_lookup_ops, 239 .policy = nft_lookup_policy, 240 .maxattr = NFTA_LOOKUP_MAX, 241 .owner = THIS_MODULE, 242 }; 243