Blame view

net/netfilter/nft_lookup.c 5.42 KB
20a69341f   Patrick McHardy   netfilter: nf_tab...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * Copyright (c) 2009 Patrick McHardy <kaber@trash.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * Development of this code funded by Astaro AG (http://www.astaro.com/)
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/rbtree.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables.h>
bd76ed36b   Rashika Kheria   net: Include appr...
19
  #include <net/netfilter/nf_tables_core.h>
20a69341f   Patrick McHardy   netfilter: nf_tab...
20
21
22
23
24
  
  struct nft_lookup {
  	struct nft_set			*set;
  	enum nft_registers		sreg:8;
  	enum nft_registers		dreg:8;
0071e184a   Arturo Borrero   netfilter: nf_tab...
25
  	bool				invert;
20a69341f   Patrick McHardy   netfilter: nf_tab...
26
27
28
29
  	struct nft_set_binding		binding;
  };
  
  static void nft_lookup_eval(const struct nft_expr *expr,
a55e22e92   Patrick McHardy   netfilter: nf_tab...
30
  			    struct nft_regs *regs,
20a69341f   Patrick McHardy   netfilter: nf_tab...
31
32
33
34
  			    const struct nft_pktinfo *pkt)
  {
  	const struct nft_lookup *priv = nft_expr_priv(expr);
  	const struct nft_set *set = priv->set;
b2832dd66   Patrick McHardy   netfilter: nf_tab...
35
  	const struct nft_set_ext *ext;
0071e184a   Arturo Borrero   netfilter: nf_tab...
36
  	bool found;
20a69341f   Patrick McHardy   netfilter: nf_tab...
37

0e5a1c7eb   Pablo Neira Ayuso   netfilter: nf_tab...
38
39
  	found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
  				 &ext) ^ priv->invert;
0071e184a   Arturo Borrero   netfilter: nf_tab...
40
41
  	if (!found) {
  		regs->verdict.code = NFT_BREAK;
20a69341f   Patrick McHardy   netfilter: nf_tab...
42
  		return;
b2832dd66   Patrick McHardy   netfilter: nf_tab...
43
  	}
0071e184a   Arturo Borrero   netfilter: nf_tab...
44

4004d5c37   Pablo Neira Ayuso   netfilter: nft_lo...
45
  	if (set->flags & NFT_SET_MAP)
0071e184a   Arturo Borrero   netfilter: nf_tab...
46
47
  		nft_data_copy(&regs->data[priv->dreg],
  			      nft_set_ext_data(ext), set->dlen);
20a69341f   Patrick McHardy   netfilter: nf_tab...
48
49
50
  }
  
  static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = {
b2fbd0449   Liping Zhang   netfilter: nf_tab...
51
52
  	[NFTA_LOOKUP_SET]	= { .type = NLA_STRING,
  				    .len = NFT_SET_MAXNAMELEN - 1 },
4c1017aa8   Patrick McHardy   netfilter: nft_lo...
53
  	[NFTA_LOOKUP_SET_ID]	= { .type = NLA_U32 },
20a69341f   Patrick McHardy   netfilter: nf_tab...
54
55
  	[NFTA_LOOKUP_SREG]	= { .type = NLA_U32 },
  	[NFTA_LOOKUP_DREG]	= { .type = NLA_U32 },
0071e184a   Arturo Borrero   netfilter: nf_tab...
56
  	[NFTA_LOOKUP_FLAGS]	= { .type = NLA_U32 },
20a69341f   Patrick McHardy   netfilter: nf_tab...
57
58
59
60
61
62
63
  };
  
  static int nft_lookup_init(const struct nft_ctx *ctx,
  			   const struct nft_expr *expr,
  			   const struct nlattr * const tb[])
  {
  	struct nft_lookup *priv = nft_expr_priv(expr);
37a9cc525   Pablo Neira Ayuso   netfilter: nf_tab...
64
  	u8 genmask = nft_genmask_next(ctx->net);
20a69341f   Patrick McHardy   netfilter: nf_tab...
65
  	struct nft_set *set;
0071e184a   Arturo Borrero   netfilter: nf_tab...
66
  	u32 flags;
20a69341f   Patrick McHardy   netfilter: nf_tab...
67
68
69
70
71
  	int err;
  
  	if (tb[NFTA_LOOKUP_SET] == NULL ||
  	    tb[NFTA_LOOKUP_SREG] == NULL)
  		return -EINVAL;
10659cbab   Pablo Neira Ayuso   netfilter: nf_tab...
72
73
  	set = nft_set_lookup_global(ctx->net, ctx->table, tb[NFTA_LOOKUP_SET],
  				    tb[NFTA_LOOKUP_SET_ID], genmask);
c7a72e3fd   Pablo Neira Ayuso   netfilter: nf_tab...
74
75
  	if (IS_ERR(set))
  		return PTR_ERR(set);
20a69341f   Patrick McHardy   netfilter: nf_tab...
76

7c6c6e95a   Patrick McHardy   netfilter: nf_tab...
77
78
  	if (set->flags & NFT_SET_EVAL)
  		return -EOPNOTSUPP;
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
79
  	priv->sreg = nft_parse_register(tb[NFTA_LOOKUP_SREG]);
d07db9884   Patrick McHardy   netfilter: nf_tab...
80
  	err = nft_validate_register_load(priv->sreg, set->klen);
20a69341f   Patrick McHardy   netfilter: nf_tab...
81
82
  	if (err < 0)
  		return err;
0071e184a   Arturo Borrero   netfilter: nf_tab...
83
84
85
86
87
88
89
90
91
92
93
94
  	if (tb[NFTA_LOOKUP_FLAGS]) {
  		flags = ntohl(nla_get_be32(tb[NFTA_LOOKUP_FLAGS]));
  
  		if (flags & ~NFT_LOOKUP_F_INV)
  			return -EINVAL;
  
  		if (flags & NFT_LOOKUP_F_INV) {
  			if (set->flags & NFT_SET_MAP)
  				return -EINVAL;
  			priv->invert = true;
  		}
  	}
20a69341f   Patrick McHardy   netfilter: nf_tab...
95
  	if (tb[NFTA_LOOKUP_DREG] != NULL) {
0071e184a   Arturo Borrero   netfilter: nf_tab...
96
97
  		if (priv->invert)
  			return -EINVAL;
20a69341f   Patrick McHardy   netfilter: nf_tab...
98
99
  		if (!(set->flags & NFT_SET_MAP))
  			return -EINVAL;
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
100
  		priv->dreg = nft_parse_register(tb[NFTA_LOOKUP_DREG]);
58f40ab6e   Patrick McHardy   netfilter: nft_lo...
101
102
103
104
  		err = nft_validate_register_store(ctx, priv->dreg, NULL,
  						  set->dtype, set->dlen);
  		if (err < 0)
  			return err;
20a69341f   Patrick McHardy   netfilter: nf_tab...
105
106
  	} else if (set->flags & NFT_SET_MAP)
  		return -EINVAL;
11113e190   Patrick McHardy   netfilter: nf_tab...
107
  	priv->binding.flags = set->flags & NFT_SET_MAP;
20a69341f   Patrick McHardy   netfilter: nf_tab...
108
109
110
111
112
113
114
  	err = nf_tables_bind_set(ctx, set, &priv->binding);
  	if (err < 0)
  		return err;
  
  	priv->set = set;
  	return 0;
  }
62472bcef   Patrick McHardy   netfilter: nf_tab...
115
116
  static void nft_lookup_destroy(const struct nft_ctx *ctx,
  			       const struct nft_expr *expr)
20a69341f   Patrick McHardy   netfilter: nf_tab...
117
118
  {
  	struct nft_lookup *priv = nft_expr_priv(expr);
ab9da5c19   Patrick McHardy   netfilter: nf_tab...
119
  	nf_tables_unbind_set(ctx, priv->set, &priv->binding);
20a69341f   Patrick McHardy   netfilter: nf_tab...
120
121
122
123
124
  }
  
  static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_lookup *priv = nft_expr_priv(expr);
0071e184a   Arturo Borrero   netfilter: nf_tab...
125
  	u32 flags = priv->invert ? NFT_LOOKUP_F_INV : 0;
20a69341f   Patrick McHardy   netfilter: nf_tab...
126
127
128
  
  	if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name))
  		goto nla_put_failure;
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
129
  	if (nft_dump_register(skb, NFTA_LOOKUP_SREG, priv->sreg))
20a69341f   Patrick McHardy   netfilter: nf_tab...
130
131
  		goto nla_put_failure;
  	if (priv->set->flags & NFT_SET_MAP)
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
132
  		if (nft_dump_register(skb, NFTA_LOOKUP_DREG, priv->dreg))
20a69341f   Patrick McHardy   netfilter: nf_tab...
133
  			goto nla_put_failure;
0071e184a   Arturo Borrero   netfilter: nf_tab...
134
135
  	if (nla_put_be32(skb, NFTA_LOOKUP_FLAGS, htonl(flags)))
  		goto nla_put_failure;
20a69341f   Patrick McHardy   netfilter: nf_tab...
136
137
138
139
140
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
a654de8fd   Pablo Neira Ayuso   netfilter: nf_tab...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  static int nft_lookup_validate_setelem(const struct nft_ctx *ctx,
  				       struct nft_set *set,
  				       const struct nft_set_iter *iter,
  				       struct nft_set_elem *elem)
  {
  	const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
  	const struct nft_data *data;
  
  	if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
  	    *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
  		return 0;
  
  	data = nft_set_ext_data(ext);
  	switch (data->verdict.code) {
  	case NFT_JUMP:
  	case NFT_GOTO:
  		return nft_chain_validate(ctx, data->verdict.chain);
  	default:
  		return 0;
  	}
  }
  
  static int nft_lookup_validate(const struct nft_ctx *ctx,
  			       const struct nft_expr *expr,
  			       const struct nft_data **d)
  {
  	const struct nft_lookup *priv = nft_expr_priv(expr);
  	struct nft_set_iter iter;
  
  	if (!(priv->set->flags & NFT_SET_MAP) ||
  	    priv->set->dtype != NFT_DATA_VERDICT)
  		return 0;
  
  	iter.genmask	= nft_genmask_next(ctx->net);
  	iter.skip	= 0;
  	iter.count	= 0;
  	iter.err	= 0;
  	iter.fn		= nft_lookup_validate_setelem;
  
  	priv->set->ops->walk(ctx, priv->set, &iter);
  	if (iter.err < 0)
  		return iter.err;
  
  	return 0;
  }
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
186
187
  static const struct nft_expr_ops nft_lookup_ops = {
  	.type		= &nft_lookup_type,
20a69341f   Patrick McHardy   netfilter: nf_tab...
188
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
20a69341f   Patrick McHardy   netfilter: nf_tab...
189
190
191
192
  	.eval		= nft_lookup_eval,
  	.init		= nft_lookup_init,
  	.destroy	= nft_lookup_destroy,
  	.dump		= nft_lookup_dump,
a654de8fd   Pablo Neira Ayuso   netfilter: nf_tab...
193
  	.validate	= nft_lookup_validate,
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
194
  };
4e24877e6   Liping Zhang   netfilter: nf_tab...
195
  struct nft_expr_type nft_lookup_type __read_mostly = {
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
196
197
  	.name		= "lookup",
  	.ops		= &nft_lookup_ops,
20a69341f   Patrick McHardy   netfilter: nf_tab...
198
199
  	.policy		= nft_lookup_policy,
  	.maxattr	= NFTA_LOOKUP_MAX,
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
200
  	.owner		= THIS_MODULE,
20a69341f   Patrick McHardy   netfilter: nf_tab...
201
  };