1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2012-2016 Pablo Neira Ayuso <pablo@netfilter.org> |
4 | */ |
5 | |
6 | #include <linux/init.h> |
7 | #include <linux/module.h> |
8 | #include <linux/skbuff.h> |
9 | #include <linux/netlink.h> |
10 | #include <linux/netfilter.h> |
11 | #include <linux/netfilter/nf_tables.h> |
12 | #include <net/netfilter/nf_tables_core.h> |
13 | |
14 | #define nft_objref_priv(expr) *((struct nft_object **)nft_expr_priv(expr)) |
15 | |
16 | void nft_objref_eval(const struct nft_expr *expr, |
17 | struct nft_regs *regs, |
18 | const struct nft_pktinfo *pkt) |
19 | { |
20 | struct nft_object *obj = nft_objref_priv(expr); |
21 | |
22 | obj->ops->eval(obj, regs, pkt); |
23 | } |
24 | |
25 | static int nft_objref_init(const struct nft_ctx *ctx, |
26 | const struct nft_expr *expr, |
27 | const struct nlattr * const tb[]) |
28 | { |
29 | struct nft_object *obj = nft_objref_priv(expr); |
30 | u8 genmask = nft_genmask_next(net: ctx->net); |
31 | u32 objtype; |
32 | |
33 | if (!tb[NFTA_OBJREF_IMM_NAME] || |
34 | !tb[NFTA_OBJREF_IMM_TYPE]) |
35 | return -EINVAL; |
36 | |
37 | objtype = ntohl(nla_get_be32(tb[NFTA_OBJREF_IMM_TYPE])); |
38 | obj = nft_obj_lookup(net: ctx->net, table: ctx->table, |
39 | nla: tb[NFTA_OBJREF_IMM_NAME], objtype, |
40 | genmask); |
41 | if (IS_ERR(ptr: obj)) |
42 | return -ENOENT; |
43 | |
44 | if (!nft_use_inc(use: &obj->use)) |
45 | return -EMFILE; |
46 | |
47 | nft_objref_priv(expr) = obj; |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int nft_objref_dump(struct sk_buff *skb, |
53 | const struct nft_expr *expr, bool reset) |
54 | { |
55 | const struct nft_object *obj = nft_objref_priv(expr); |
56 | |
57 | if (nla_put_string(skb, attrtype: NFTA_OBJREF_IMM_NAME, str: obj->key.name) || |
58 | nla_put_be32(skb, attrtype: NFTA_OBJREF_IMM_TYPE, |
59 | htonl(obj->ops->type->type))) |
60 | goto nla_put_failure; |
61 | |
62 | return 0; |
63 | |
64 | nla_put_failure: |
65 | return -1; |
66 | } |
67 | |
68 | static void nft_objref_deactivate(const struct nft_ctx *ctx, |
69 | const struct nft_expr *expr, |
70 | enum nft_trans_phase phase) |
71 | { |
72 | struct nft_object *obj = nft_objref_priv(expr); |
73 | |
74 | if (phase == NFT_TRANS_COMMIT) |
75 | return; |
76 | |
77 | nft_use_dec(use: &obj->use); |
78 | } |
79 | |
80 | static void nft_objref_activate(const struct nft_ctx *ctx, |
81 | const struct nft_expr *expr) |
82 | { |
83 | struct nft_object *obj = nft_objref_priv(expr); |
84 | |
85 | nft_use_inc_restore(use: &obj->use); |
86 | } |
87 | |
88 | static const struct nft_expr_ops nft_objref_ops = { |
89 | .type = &nft_objref_type, |
90 | .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)), |
91 | .eval = nft_objref_eval, |
92 | .init = nft_objref_init, |
93 | .activate = nft_objref_activate, |
94 | .deactivate = nft_objref_deactivate, |
95 | .dump = nft_objref_dump, |
96 | .reduce = NFT_REDUCE_READONLY, |
97 | }; |
98 | |
99 | struct nft_objref_map { |
100 | struct nft_set *set; |
101 | u8 sreg; |
102 | struct nft_set_binding binding; |
103 | }; |
104 | |
105 | void nft_objref_map_eval(const struct nft_expr *expr, |
106 | struct nft_regs *regs, |
107 | const struct nft_pktinfo *pkt) |
108 | { |
109 | struct nft_objref_map *priv = nft_expr_priv(expr); |
110 | const struct nft_set *set = priv->set; |
111 | struct net *net = nft_net(pkt); |
112 | const struct nft_set_ext *ext; |
113 | struct nft_object *obj; |
114 | bool found; |
115 | |
116 | found = nft_set_do_lookup(net, set, key: ®s->data[priv->sreg], ext: &ext); |
117 | if (!found) { |
118 | ext = nft_set_catchall_lookup(net, set); |
119 | if (!ext) { |
120 | regs->verdict.code = NFT_BREAK; |
121 | return; |
122 | } |
123 | } |
124 | obj = *nft_set_ext_obj(ext); |
125 | obj->ops->eval(obj, regs, pkt); |
126 | } |
127 | |
128 | static int nft_objref_map_init(const struct nft_ctx *ctx, |
129 | const struct nft_expr *expr, |
130 | const struct nlattr * const tb[]) |
131 | { |
132 | struct nft_objref_map *priv = nft_expr_priv(expr); |
133 | u8 genmask = nft_genmask_next(net: ctx->net); |
134 | struct nft_set *set; |
135 | int err; |
136 | |
137 | set = nft_set_lookup_global(net: ctx->net, table: ctx->table, |
138 | nla_set_name: tb[NFTA_OBJREF_SET_NAME], |
139 | nla_set_id: tb[NFTA_OBJREF_SET_ID], genmask); |
140 | if (IS_ERR(ptr: set)) |
141 | return PTR_ERR(ptr: set); |
142 | |
143 | if (!(set->flags & NFT_SET_OBJECT)) |
144 | return -EINVAL; |
145 | |
146 | err = nft_parse_register_load(attr: tb[NFTA_OBJREF_SET_SREG], sreg: &priv->sreg, |
147 | len: set->klen); |
148 | if (err < 0) |
149 | return err; |
150 | |
151 | priv->binding.flags = set->flags & NFT_SET_OBJECT; |
152 | |
153 | err = nf_tables_bind_set(ctx, set, binding: &priv->binding); |
154 | if (err < 0) |
155 | return err; |
156 | |
157 | priv->set = set; |
158 | return 0; |
159 | } |
160 | |
161 | static int nft_objref_map_dump(struct sk_buff *skb, |
162 | const struct nft_expr *expr, bool reset) |
163 | { |
164 | const struct nft_objref_map *priv = nft_expr_priv(expr); |
165 | |
166 | if (nft_dump_register(skb, attr: NFTA_OBJREF_SET_SREG, reg: priv->sreg) || |
167 | nla_put_string(skb, attrtype: NFTA_OBJREF_SET_NAME, str: priv->set->name)) |
168 | goto nla_put_failure; |
169 | |
170 | return 0; |
171 | |
172 | nla_put_failure: |
173 | return -1; |
174 | } |
175 | |
176 | static void nft_objref_map_deactivate(const struct nft_ctx *ctx, |
177 | const struct nft_expr *expr, |
178 | enum nft_trans_phase phase) |
179 | { |
180 | struct nft_objref_map *priv = nft_expr_priv(expr); |
181 | |
182 | nf_tables_deactivate_set(ctx, set: priv->set, binding: &priv->binding, phase); |
183 | } |
184 | |
185 | static void nft_objref_map_activate(const struct nft_ctx *ctx, |
186 | const struct nft_expr *expr) |
187 | { |
188 | struct nft_objref_map *priv = nft_expr_priv(expr); |
189 | |
190 | nf_tables_activate_set(ctx, set: priv->set); |
191 | } |
192 | |
193 | static void nft_objref_map_destroy(const struct nft_ctx *ctx, |
194 | const struct nft_expr *expr) |
195 | { |
196 | struct nft_objref_map *priv = nft_expr_priv(expr); |
197 | |
198 | nf_tables_destroy_set(ctx, set: priv->set); |
199 | } |
200 | |
201 | static const struct nft_expr_ops nft_objref_map_ops = { |
202 | .type = &nft_objref_type, |
203 | .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), |
204 | .eval = nft_objref_map_eval, |
205 | .init = nft_objref_map_init, |
206 | .activate = nft_objref_map_activate, |
207 | .deactivate = nft_objref_map_deactivate, |
208 | .destroy = nft_objref_map_destroy, |
209 | .dump = nft_objref_map_dump, |
210 | .reduce = NFT_REDUCE_READONLY, |
211 | }; |
212 | |
213 | static const struct nft_expr_ops * |
214 | nft_objref_select_ops(const struct nft_ctx *ctx, |
215 | const struct nlattr * const tb[]) |
216 | { |
217 | if (tb[NFTA_OBJREF_SET_SREG] && |
218 | (tb[NFTA_OBJREF_SET_NAME] || |
219 | tb[NFTA_OBJREF_SET_ID])) |
220 | return &nft_objref_map_ops; |
221 | else if (tb[NFTA_OBJREF_IMM_NAME] && |
222 | tb[NFTA_OBJREF_IMM_TYPE]) |
223 | return &nft_objref_ops; |
224 | |
225 | return ERR_PTR(error: -EOPNOTSUPP); |
226 | } |
227 | |
228 | static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { |
229 | [NFTA_OBJREF_IMM_NAME] = { .type = NLA_STRING, |
230 | .len = NFT_OBJ_MAXNAMELEN - 1 }, |
231 | [NFTA_OBJREF_IMM_TYPE] = { .type = NLA_U32 }, |
232 | [NFTA_OBJREF_SET_SREG] = { .type = NLA_U32 }, |
233 | [NFTA_OBJREF_SET_NAME] = { .type = NLA_STRING, |
234 | .len = NFT_SET_MAXNAMELEN - 1 }, |
235 | [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, |
236 | }; |
237 | |
238 | struct nft_expr_type nft_objref_type __read_mostly = { |
239 | .name = "objref" , |
240 | .select_ops = nft_objref_select_ops, |
241 | .policy = nft_objref_policy, |
242 | .maxattr = NFTA_OBJREF_MAX, |
243 | .owner = THIS_MODULE, |
244 | }; |
245 | |