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 static 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_destroy(const struct nft_ctx *ctx, 125 const struct nft_expr *expr) 126 { 127 struct nft_lookup *priv = nft_expr_priv(expr); 128 129 nf_tables_unbind_set(ctx, priv->set, &priv->binding); 130 } 131 132 static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr) 133 { 134 const struct nft_lookup *priv = nft_expr_priv(expr); 135 u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0; 136 137 if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name)) 138 goto nla_put_failure; 139 if (nft_dump_register(skb, NFTA_LOOKUP_SREG, priv->sreg)) 140 goto nla_put_failure; 141 if (priv->set->flags & NFT_SET_MAP) 142 if (nft_dump_register(skb, NFTA_LOOKUP_DREG, priv->dreg)) 143 goto nla_put_failure; 144 if (nla_put_be32(skb, NFTA_LOOKUP_FLAGS, htonl(flags))) 145 goto nla_put_failure; 146 return 0; 147 148 nla_put_failure: 149 return -1; 150 } 151 152 static const struct nft_expr_ops nft_lookup_ops = { 153 .type = &nft_lookup_type, 154 .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), 155 .eval = nft_lookup_eval, 156 .init = nft_lookup_init, 157 .destroy = nft_lookup_destroy, 158 .dump = nft_lookup_dump, 159 }; 160 161 struct nft_expr_type nft_lookup_type __read_mostly = { 162 .name = "lookup", 163 .ops = &nft_lookup_ops, 164 .policy = nft_lookup_policy, 165 .maxattr = NFTA_LOOKUP_MAX, 166 .owner = THIS_MODULE, 167 }; 168