]>
Commit | Line | Data |
---|---|---|
0ca743a5 PNA |
1 | /* |
2 | * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org> | |
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 | * This software has been sponsored by Sophos Astaro <http://www.sophos.com> | |
9 | */ | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/netlink.h> | |
15 | #include <linux/netfilter.h> | |
16 | #include <linux/netfilter/nfnetlink.h> | |
17 | #include <linux/netfilter/nf_tables.h> | |
18 | #include <linux/netfilter/nf_tables_compat.h> | |
19 | #include <linux/netfilter/x_tables.h> | |
20 | #include <linux/netfilter_ipv4/ip_tables.h> | |
21 | #include <linux/netfilter_ipv6/ip6_tables.h> | |
5191f4d8 | 22 | #include <linux/netfilter_bridge/ebtables.h> |
5f158939 | 23 | #include <linux/netfilter_arp/arp_tables.h> |
0ca743a5 | 24 | #include <net/netfilter/nf_tables.h> |
cf52572e | 25 | #include <net/netns/generic.h> |
0ca743a5 | 26 | |
4b512e1c LZ |
27 | struct nft_xt { |
28 | struct list_head head; | |
29 | struct nft_expr_ops ops; | |
12c44aba | 30 | refcount_t refcnt; |
b8e9dc1c FW |
31 | |
32 | /* Unlike other expressions, ops doesn't have static storage duration. | |
33 | * nft core assumes they do. We use kfree_rcu so that nft core can | |
34 | * can check expr->ops->size even after nft_compat->destroy() frees | |
35 | * the nft_xt struct that holds the ops structure. | |
36 | */ | |
37 | struct rcu_head rcu_head; | |
4b512e1c LZ |
38 | }; |
39 | ||
732a8049 FW |
40 | /* Used for matches where *info is larger than X byte */ |
41 | #define NFT_MATCH_LARGE_THRESH 192 | |
42 | ||
43 | struct nft_xt_match_priv { | |
44 | void *info; | |
45 | }; | |
46 | ||
cf52572e FW |
47 | struct nft_compat_net { |
48 | struct list_head nft_target_list; | |
49 | struct list_head nft_match_list; | |
50 | }; | |
51 | ||
52 | static unsigned int nft_compat_net_id __read_mostly; | |
53 | static struct nft_expr_type nft_match_type; | |
54 | static struct nft_expr_type nft_target_type; | |
55 | ||
56 | static struct nft_compat_net *nft_compat_pernet(struct net *net) | |
57 | { | |
58 | return net_generic(net, nft_compat_net_id); | |
59 | } | |
60 | ||
b8e9dc1c | 61 | static bool nft_xt_put(struct nft_xt *xt) |
4b512e1c | 62 | { |
12c44aba | 63 | if (refcount_dec_and_test(&xt->refcnt)) { |
4b512e1c | 64 | list_del(&xt->head); |
b8e9dc1c FW |
65 | kfree_rcu(xt, rcu_head); |
66 | return true; | |
4b512e1c | 67 | } |
b8e9dc1c FW |
68 | |
69 | return false; | |
4b512e1c LZ |
70 | } |
71 | ||
e4844c9c FW |
72 | static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx, |
73 | const char *tablename) | |
f3f5dded | 74 | { |
e4844c9c FW |
75 | enum nft_chain_types type = NFT_CHAIN_T_DEFAULT; |
76 | const struct nft_chain *chain = ctx->chain; | |
f3f5dded PNA |
77 | const struct nft_base_chain *basechain; |
78 | ||
f323d954 PNA |
79 | if (!tablename || |
80 | !nft_is_base_chain(chain)) | |
f3f5dded PNA |
81 | return 0; |
82 | ||
f3f5dded | 83 | basechain = nft_base_chain(chain); |
e4844c9c FW |
84 | if (strcmp(tablename, "nat") == 0) { |
85 | if (ctx->family != NFPROTO_BRIDGE) | |
86 | type = NFT_CHAIN_T_NAT; | |
87 | if (basechain->type->type != type) | |
88 | return -EINVAL; | |
89 | } | |
f3f5dded PNA |
90 | |
91 | return 0; | |
92 | } | |
93 | ||
0ca743a5 PNA |
94 | union nft_entry { |
95 | struct ipt_entry e4; | |
96 | struct ip6t_entry e6; | |
5191f4d8 | 97 | struct ebt_entry ebt; |
5f158939 | 98 | struct arpt_entry arp; |
0ca743a5 PNA |
99 | }; |
100 | ||
101 | static inline void | |
102 | nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info) | |
103 | { | |
104 | par->target = xt; | |
105 | par->targinfo = xt_info; | |
106 | par->hotdrop = false; | |
107 | } | |
108 | ||
5191f4d8 | 109 | static void nft_target_eval_xt(const struct nft_expr *expr, |
a55e22e9 | 110 | struct nft_regs *regs, |
5191f4d8 | 111 | const struct nft_pktinfo *pkt) |
0ca743a5 PNA |
112 | { |
113 | void *info = nft_expr_priv(expr); | |
114 | struct xt_target *target = expr->ops->data; | |
115 | struct sk_buff *skb = pkt->skb; | |
116 | int ret; | |
117 | ||
118 | nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info); | |
119 | ||
120 | ret = target->target(skb, &pkt->xt); | |
121 | ||
122 | if (pkt->xt.hotdrop) | |
123 | ret = NF_DROP; | |
124 | ||
5191f4d8 | 125 | switch (ret) { |
0ca743a5 | 126 | case XT_CONTINUE: |
a55e22e9 | 127 | regs->verdict.code = NFT_CONTINUE; |
0ca743a5 PNA |
128 | break; |
129 | default: | |
a55e22e9 | 130 | regs->verdict.code = ret; |
0ca743a5 PNA |
131 | break; |
132 | } | |
5191f4d8 AB |
133 | } |
134 | ||
135 | static void nft_target_eval_bridge(const struct nft_expr *expr, | |
a55e22e9 | 136 | struct nft_regs *regs, |
5191f4d8 AB |
137 | const struct nft_pktinfo *pkt) |
138 | { | |
139 | void *info = nft_expr_priv(expr); | |
140 | struct xt_target *target = expr->ops->data; | |
141 | struct sk_buff *skb = pkt->skb; | |
142 | int ret; | |
143 | ||
144 | nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info); | |
145 | ||
146 | ret = target->target(skb, &pkt->xt); | |
147 | ||
148 | if (pkt->xt.hotdrop) | |
149 | ret = NF_DROP; | |
150 | ||
151 | switch (ret) { | |
152 | case EBT_ACCEPT: | |
a55e22e9 | 153 | regs->verdict.code = NF_ACCEPT; |
5191f4d8 AB |
154 | break; |
155 | case EBT_DROP: | |
a55e22e9 | 156 | regs->verdict.code = NF_DROP; |
5191f4d8 AB |
157 | break; |
158 | case EBT_CONTINUE: | |
a55e22e9 | 159 | regs->verdict.code = NFT_CONTINUE; |
5191f4d8 AB |
160 | break; |
161 | case EBT_RETURN: | |
a55e22e9 | 162 | regs->verdict.code = NFT_RETURN; |
5191f4d8 AB |
163 | break; |
164 | default: | |
a55e22e9 | 165 | regs->verdict.code = ret; |
5191f4d8 AB |
166 | break; |
167 | } | |
0ca743a5 PNA |
168 | } |
169 | ||
170 | static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = { | |
171 | [NFTA_TARGET_NAME] = { .type = NLA_NUL_STRING }, | |
172 | [NFTA_TARGET_REV] = { .type = NLA_U32 }, | |
173 | [NFTA_TARGET_INFO] = { .type = NLA_BINARY }, | |
174 | }; | |
175 | ||
176 | static void | |
177 | nft_target_set_tgchk_param(struct xt_tgchk_param *par, | |
178 | const struct nft_ctx *ctx, | |
179 | struct xt_target *target, void *info, | |
2156d321 | 180 | union nft_entry *entry, u16 proto, bool inv) |
0ca743a5 | 181 | { |
2daf1b4d | 182 | par->net = ctx->net; |
0ca743a5 | 183 | par->table = ctx->table->name; |
36596dad | 184 | switch (ctx->family) { |
0ca743a5 PNA |
185 | case AF_INET: |
186 | entry->e4.ip.proto = proto; | |
187 | entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; | |
188 | break; | |
189 | case AF_INET6: | |
749177cc PNA |
190 | if (proto) |
191 | entry->e6.ipv6.flags |= IP6T_F_PROTO; | |
192 | ||
0ca743a5 PNA |
193 | entry->e6.ipv6.proto = proto; |
194 | entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; | |
195 | break; | |
5191f4d8 | 196 | case NFPROTO_BRIDGE: |
2156d321 | 197 | entry->ebt.ethproto = (__force __be16)proto; |
5191f4d8 AB |
198 | entry->ebt.invflags = inv ? EBT_IPROTO : 0; |
199 | break; | |
5f158939 AB |
200 | case NFPROTO_ARP: |
201 | break; | |
0ca743a5 PNA |
202 | } |
203 | par->entryinfo = entry; | |
204 | par->target = target; | |
205 | par->targinfo = info; | |
f323d954 | 206 | if (nft_is_base_chain(ctx->chain)) { |
0ca743a5 PNA |
207 | const struct nft_base_chain *basechain = |
208 | nft_base_chain(ctx->chain); | |
c974a3a3 | 209 | const struct nf_hook_ops *ops = &basechain->ops; |
0ca743a5 PNA |
210 | |
211 | par->hook_mask = 1 << ops->hooknum; | |
493618a9 PNA |
212 | } else { |
213 | par->hook_mask = 0; | |
0ca743a5 | 214 | } |
36596dad | 215 | par->family = ctx->family; |
55917a21 | 216 | par->nft_compat = true; |
0ca743a5 PNA |
217 | } |
218 | ||
219 | static void target_compat_from_user(struct xt_target *t, void *in, void *out) | |
220 | { | |
756c1b1a | 221 | int pad; |
0ca743a5 | 222 | |
756c1b1a PNA |
223 | memcpy(out, in, t->targetsize); |
224 | pad = XT_ALIGN(t->targetsize) - t->targetsize; | |
225 | if (pad > 0) | |
226 | memset(out + t->targetsize, 0, pad); | |
0ca743a5 PNA |
227 | } |
228 | ||
229 | static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = { | |
230 | [NFTA_RULE_COMPAT_PROTO] = { .type = NLA_U32 }, | |
231 | [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 }, | |
232 | }; | |
233 | ||
2156d321 | 234 | static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv) |
0ca743a5 PNA |
235 | { |
236 | struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1]; | |
237 | u32 flags; | |
238 | int err; | |
239 | ||
240 | err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr, | |
fceb6435 | 241 | nft_rule_compat_policy, NULL); |
0ca743a5 PNA |
242 | if (err < 0) |
243 | return err; | |
244 | ||
245 | if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS]) | |
246 | return -EINVAL; | |
247 | ||
248 | flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS])); | |
249 | if (flags & ~NFT_RULE_COMPAT_F_MASK) | |
250 | return -EINVAL; | |
251 | if (flags & NFT_RULE_COMPAT_F_INV) | |
252 | *inv = true; | |
253 | ||
8691a9a3 PNA |
254 | *proto = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO])); |
255 | return 0; | |
0ca743a5 PNA |
256 | } |
257 | ||
258 | static int | |
259 | nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | |
260 | const struct nlattr * const tb[]) | |
261 | { | |
262 | void *info = nft_expr_priv(expr); | |
263 | struct xt_target *target = expr->ops->data; | |
264 | struct xt_tgchk_param par; | |
265 | size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); | |
b8e9dc1c | 266 | struct nft_xt *nft_xt; |
2156d321 | 267 | u16 proto = 0; |
0ca743a5 PNA |
268 | bool inv = false; |
269 | union nft_entry e = {}; | |
270 | int ret; | |
271 | ||
272 | target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); | |
273 | ||
8691a9a3 PNA |
274 | if (ctx->nla[NFTA_RULE_COMPAT]) { |
275 | ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); | |
276 | if (ret < 0) | |
b8e9dc1c | 277 | return ret; |
8691a9a3 | 278 | } |
0ca743a5 PNA |
279 | |
280 | nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); | |
281 | ||
282 | ret = xt_check_target(&par, size, proto, inv); | |
283 | if (ret < 0) | |
b8e9dc1c | 284 | return ret; |
0ca743a5 PNA |
285 | |
286 | /* The standard target cannot be used */ | |
b8e9dc1c FW |
287 | if (!target->target) |
288 | return -EINVAL; | |
0ca743a5 | 289 | |
b8e9dc1c | 290 | nft_xt = container_of(expr->ops, struct nft_xt, ops); |
12c44aba | 291 | refcount_inc(&nft_xt->refcnt); |
0ca743a5 | 292 | return 0; |
0ca743a5 PNA |
293 | } |
294 | ||
295 | static void | |
62472bce | 296 | nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) |
0ca743a5 PNA |
297 | { |
298 | struct xt_target *target = expr->ops->data; | |
3d9b1421 PNA |
299 | void *info = nft_expr_priv(expr); |
300 | struct xt_tgdtor_param par; | |
301 | ||
302 | par.net = ctx->net; | |
303 | par.target = target; | |
304 | par.targinfo = info; | |
36596dad | 305 | par.family = ctx->family; |
3d9b1421 PNA |
306 | if (par.target->destroy != NULL) |
307 | par.target->destroy(&par); | |
0ca743a5 | 308 | |
b8e9dc1c FW |
309 | if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops))) |
310 | module_put(target->me); | |
0ca743a5 PNA |
311 | } |
312 | ||
d701d811 PNA |
313 | static int nft_extension_dump_info(struct sk_buff *skb, int attr, |
314 | const void *info, | |
315 | unsigned int size, unsigned int user_size) | |
316 | { | |
317 | unsigned int info_size, aligned_size = XT_ALIGN(size); | |
318 | struct nlattr *nla; | |
319 | ||
320 | nla = nla_reserve(skb, attr, aligned_size); | |
321 | if (!nla) | |
322 | return -1; | |
323 | ||
324 | info_size = user_size ? : size; | |
325 | memcpy(nla_data(nla), info, info_size); | |
326 | memset(nla_data(nla) + info_size, 0, aligned_size - info_size); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
0ca743a5 PNA |
331 | static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr) |
332 | { | |
333 | const struct xt_target *target = expr->ops->data; | |
334 | void *info = nft_expr_priv(expr); | |
335 | ||
336 | if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) || | |
337 | nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) || | |
d701d811 PNA |
338 | nft_extension_dump_info(skb, NFTA_TARGET_INFO, info, |
339 | target->targetsize, target->usersize)) | |
0ca743a5 PNA |
340 | goto nla_put_failure; |
341 | ||
342 | return 0; | |
343 | ||
344 | nla_put_failure: | |
345 | return -1; | |
346 | } | |
347 | ||
348 | static int nft_target_validate(const struct nft_ctx *ctx, | |
349 | const struct nft_expr *expr, | |
350 | const struct nft_data **data) | |
351 | { | |
352 | struct xt_target *target = expr->ops->data; | |
353 | unsigned int hook_mask = 0; | |
f3f5dded | 354 | int ret; |
0ca743a5 | 355 | |
f323d954 | 356 | if (nft_is_base_chain(ctx->chain)) { |
0ca743a5 PNA |
357 | const struct nft_base_chain *basechain = |
358 | nft_base_chain(ctx->chain); | |
c974a3a3 | 359 | const struct nf_hook_ops *ops = &basechain->ops; |
0ca743a5 PNA |
360 | |
361 | hook_mask = 1 << ops->hooknum; | |
f7fb77fc | 362 | if (target->hooks && !(hook_mask & target->hooks)) |
f3f5dded | 363 | return -EINVAL; |
0ca743a5 | 364 | |
e4844c9c | 365 | ret = nft_compat_chain_validate_dependency(ctx, target->table); |
f3f5dded PNA |
366 | if (ret < 0) |
367 | return ret; | |
0ca743a5 PNA |
368 | } |
369 | return 0; | |
370 | } | |
371 | ||
8bdf1647 FW |
372 | static void __nft_match_eval(const struct nft_expr *expr, |
373 | struct nft_regs *regs, | |
374 | const struct nft_pktinfo *pkt, | |
375 | void *info) | |
0ca743a5 | 376 | { |
0ca743a5 PNA |
377 | struct xt_match *match = expr->ops->data; |
378 | struct sk_buff *skb = pkt->skb; | |
379 | bool ret; | |
380 | ||
381 | nft_compat_set_par((struct xt_action_param *)&pkt->xt, match, info); | |
382 | ||
383 | ret = match->match(skb, (struct xt_action_param *)&pkt->xt); | |
384 | ||
385 | if (pkt->xt.hotdrop) { | |
a55e22e9 | 386 | regs->verdict.code = NF_DROP; |
0ca743a5 PNA |
387 | return; |
388 | } | |
389 | ||
c1f86676 DM |
390 | switch (ret ? 1 : 0) { |
391 | case 1: | |
a55e22e9 | 392 | regs->verdict.code = NFT_CONTINUE; |
0ca743a5 | 393 | break; |
c1f86676 | 394 | case 0: |
a55e22e9 | 395 | regs->verdict.code = NFT_BREAK; |
0ca743a5 PNA |
396 | break; |
397 | } | |
398 | } | |
399 | ||
732a8049 FW |
400 | static void nft_match_large_eval(const struct nft_expr *expr, |
401 | struct nft_regs *regs, | |
402 | const struct nft_pktinfo *pkt) | |
403 | { | |
404 | struct nft_xt_match_priv *priv = nft_expr_priv(expr); | |
405 | ||
406 | __nft_match_eval(expr, regs, pkt, priv->info); | |
407 | } | |
408 | ||
8bdf1647 FW |
409 | static void nft_match_eval(const struct nft_expr *expr, |
410 | struct nft_regs *regs, | |
411 | const struct nft_pktinfo *pkt) | |
412 | { | |
413 | __nft_match_eval(expr, regs, pkt, nft_expr_priv(expr)); | |
414 | } | |
415 | ||
0ca743a5 PNA |
416 | static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { |
417 | [NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING }, | |
418 | [NFTA_MATCH_REV] = { .type = NLA_U32 }, | |
419 | [NFTA_MATCH_INFO] = { .type = NLA_BINARY }, | |
420 | }; | |
421 | ||
422 | /* struct xt_mtchk_param and xt_tgchk_param look very similar */ | |
423 | static void | |
424 | nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, | |
425 | struct xt_match *match, void *info, | |
2156d321 | 426 | union nft_entry *entry, u16 proto, bool inv) |
0ca743a5 | 427 | { |
2daf1b4d | 428 | par->net = ctx->net; |
0ca743a5 | 429 | par->table = ctx->table->name; |
36596dad | 430 | switch (ctx->family) { |
0ca743a5 PNA |
431 | case AF_INET: |
432 | entry->e4.ip.proto = proto; | |
433 | entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; | |
434 | break; | |
435 | case AF_INET6: | |
749177cc PNA |
436 | if (proto) |
437 | entry->e6.ipv6.flags |= IP6T_F_PROTO; | |
438 | ||
0ca743a5 PNA |
439 | entry->e6.ipv6.proto = proto; |
440 | entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; | |
441 | break; | |
5191f4d8 | 442 | case NFPROTO_BRIDGE: |
2156d321 | 443 | entry->ebt.ethproto = (__force __be16)proto; |
5191f4d8 AB |
444 | entry->ebt.invflags = inv ? EBT_IPROTO : 0; |
445 | break; | |
5f158939 AB |
446 | case NFPROTO_ARP: |
447 | break; | |
0ca743a5 PNA |
448 | } |
449 | par->entryinfo = entry; | |
450 | par->match = match; | |
451 | par->matchinfo = info; | |
f323d954 | 452 | if (nft_is_base_chain(ctx->chain)) { |
0ca743a5 PNA |
453 | const struct nft_base_chain *basechain = |
454 | nft_base_chain(ctx->chain); | |
c974a3a3 | 455 | const struct nf_hook_ops *ops = &basechain->ops; |
0ca743a5 PNA |
456 | |
457 | par->hook_mask = 1 << ops->hooknum; | |
493618a9 PNA |
458 | } else { |
459 | par->hook_mask = 0; | |
0ca743a5 | 460 | } |
36596dad | 461 | par->family = ctx->family; |
55917a21 | 462 | par->nft_compat = true; |
0ca743a5 PNA |
463 | } |
464 | ||
465 | static void match_compat_from_user(struct xt_match *m, void *in, void *out) | |
466 | { | |
756c1b1a PNA |
467 | int pad; |
468 | ||
469 | memcpy(out, in, m->matchsize); | |
470 | pad = XT_ALIGN(m->matchsize) - m->matchsize; | |
471 | if (pad > 0) | |
472 | memset(out + m->matchsize, 0, pad); | |
0ca743a5 PNA |
473 | } |
474 | ||
475 | static int | |
8bdf1647 FW |
476 | __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, |
477 | const struct nlattr * const tb[], | |
478 | void *info) | |
0ca743a5 | 479 | { |
0ca743a5 PNA |
480 | struct xt_match *match = expr->ops->data; |
481 | struct xt_mtchk_param par; | |
482 | size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); | |
b8e9dc1c | 483 | struct nft_xt *nft_xt; |
2156d321 | 484 | u16 proto = 0; |
0ca743a5 PNA |
485 | bool inv = false; |
486 | union nft_entry e = {}; | |
487 | int ret; | |
488 | ||
489 | match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); | |
490 | ||
8691a9a3 PNA |
491 | if (ctx->nla[NFTA_RULE_COMPAT]) { |
492 | ret = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &proto, &inv); | |
493 | if (ret < 0) | |
b8e9dc1c | 494 | return ret; |
8691a9a3 | 495 | } |
0ca743a5 PNA |
496 | |
497 | nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); | |
498 | ||
499 | ret = xt_check_match(&par, size, proto, inv); | |
500 | if (ret < 0) | |
b8e9dc1c | 501 | return ret; |
0ca743a5 | 502 | |
b8e9dc1c | 503 | nft_xt = container_of(expr->ops, struct nft_xt, ops); |
12c44aba | 504 | refcount_inc(&nft_xt->refcnt); |
0ca743a5 | 505 | return 0; |
0ca743a5 PNA |
506 | } |
507 | ||
8bdf1647 FW |
508 | static int |
509 | nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | |
510 | const struct nlattr * const tb[]) | |
511 | { | |
512 | return __nft_match_init(ctx, expr, tb, nft_expr_priv(expr)); | |
513 | } | |
514 | ||
732a8049 FW |
515 | static int |
516 | nft_match_large_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | |
517 | const struct nlattr * const tb[]) | |
518 | { | |
519 | struct nft_xt_match_priv *priv = nft_expr_priv(expr); | |
520 | struct xt_match *m = expr->ops->data; | |
521 | int ret; | |
522 | ||
523 | priv->info = kmalloc(XT_ALIGN(m->matchsize), GFP_KERNEL); | |
524 | if (!priv->info) | |
525 | return -ENOMEM; | |
526 | ||
527 | ret = __nft_match_init(ctx, expr, tb, priv->info); | |
528 | if (ret) | |
529 | kfree(priv->info); | |
530 | return ret; | |
531 | } | |
532 | ||
0ca743a5 | 533 | static void |
8bdf1647 FW |
534 | __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr, |
535 | void *info) | |
0ca743a5 PNA |
536 | { |
537 | struct xt_match *match = expr->ops->data; | |
29e38801 | 538 | struct module *me = match->me; |
3d9b1421 PNA |
539 | struct xt_mtdtor_param par; |
540 | ||
541 | par.net = ctx->net; | |
542 | par.match = match; | |
543 | par.matchinfo = info; | |
36596dad | 544 | par.family = ctx->family; |
3d9b1421 PNA |
545 | if (par.match->destroy != NULL) |
546 | par.match->destroy(&par); | |
0ca743a5 | 547 | |
b8e9dc1c | 548 | if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops))) |
29e38801 | 549 | module_put(me); |
0ca743a5 PNA |
550 | } |
551 | ||
8bdf1647 FW |
552 | static void |
553 | nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) | |
554 | { | |
555 | __nft_match_destroy(ctx, expr, nft_expr_priv(expr)); | |
556 | } | |
557 | ||
732a8049 FW |
558 | static void |
559 | nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) | |
560 | { | |
561 | struct nft_xt_match_priv *priv = nft_expr_priv(expr); | |
562 | ||
563 | __nft_match_destroy(ctx, expr, priv->info); | |
564 | kfree(priv->info); | |
565 | } | |
566 | ||
8bdf1647 FW |
567 | static int __nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr, |
568 | void *info) | |
0ca743a5 | 569 | { |
0ca743a5 PNA |
570 | struct xt_match *match = expr->ops->data; |
571 | ||
572 | if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) || | |
573 | nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) || | |
d701d811 PNA |
574 | nft_extension_dump_info(skb, NFTA_MATCH_INFO, info, |
575 | match->matchsize, match->usersize)) | |
0ca743a5 PNA |
576 | goto nla_put_failure; |
577 | ||
578 | return 0; | |
579 | ||
580 | nla_put_failure: | |
581 | return -1; | |
582 | } | |
583 | ||
8bdf1647 FW |
584 | static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) |
585 | { | |
586 | return __nft_match_dump(skb, expr, nft_expr_priv(expr)); | |
587 | } | |
588 | ||
732a8049 FW |
589 | static int nft_match_large_dump(struct sk_buff *skb, const struct nft_expr *e) |
590 | { | |
591 | struct nft_xt_match_priv *priv = nft_expr_priv(e); | |
592 | ||
593 | return __nft_match_dump(skb, e, priv->info); | |
594 | } | |
595 | ||
0ca743a5 PNA |
596 | static int nft_match_validate(const struct nft_ctx *ctx, |
597 | const struct nft_expr *expr, | |
598 | const struct nft_data **data) | |
599 | { | |
600 | struct xt_match *match = expr->ops->data; | |
601 | unsigned int hook_mask = 0; | |
f3f5dded | 602 | int ret; |
0ca743a5 | 603 | |
f323d954 | 604 | if (nft_is_base_chain(ctx->chain)) { |
0ca743a5 PNA |
605 | const struct nft_base_chain *basechain = |
606 | nft_base_chain(ctx->chain); | |
c974a3a3 | 607 | const struct nf_hook_ops *ops = &basechain->ops; |
0ca743a5 PNA |
608 | |
609 | hook_mask = 1 << ops->hooknum; | |
f7fb77fc | 610 | if (match->hooks && !(hook_mask & match->hooks)) |
f3f5dded | 611 | return -EINVAL; |
0ca743a5 | 612 | |
e4844c9c | 613 | ret = nft_compat_chain_validate_dependency(ctx, match->table); |
f3f5dded PNA |
614 | if (ret < 0) |
615 | return ret; | |
0ca743a5 PNA |
616 | } |
617 | return 0; | |
618 | } | |
619 | ||
620 | static int | |
621 | nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, | |
622 | int event, u16 family, const char *name, | |
623 | int rev, int target) | |
624 | { | |
625 | struct nlmsghdr *nlh; | |
626 | struct nfgenmsg *nfmsg; | |
627 | unsigned int flags = portid ? NLM_F_MULTI : 0; | |
628 | ||
dedb67c4 | 629 | event = nfnl_msg_type(NFNL_SUBSYS_NFT_COMPAT, event); |
0ca743a5 PNA |
630 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); |
631 | if (nlh == NULL) | |
632 | goto nlmsg_failure; | |
633 | ||
634 | nfmsg = nlmsg_data(nlh); | |
635 | nfmsg->nfgen_family = family; | |
636 | nfmsg->version = NFNETLINK_V0; | |
637 | nfmsg->res_id = 0; | |
638 | ||
639 | if (nla_put_string(skb, NFTA_COMPAT_NAME, name) || | |
640 | nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) || | |
641 | nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target))) | |
642 | goto nla_put_failure; | |
643 | ||
644 | nlmsg_end(skb, nlh); | |
645 | return skb->len; | |
646 | ||
647 | nlmsg_failure: | |
648 | nla_put_failure: | |
649 | nlmsg_cancel(skb, nlh); | |
650 | return -1; | |
651 | } | |
652 | ||
eb1fb147 FW |
653 | static int nfnl_compat_get_rcu(struct net *net, struct sock *nfnl, |
654 | struct sk_buff *skb, const struct nlmsghdr *nlh, | |
655 | const struct nlattr * const tb[], | |
656 | struct netlink_ext_ack *extack) | |
0ca743a5 PNA |
657 | { |
658 | int ret = 0, target; | |
659 | struct nfgenmsg *nfmsg; | |
660 | const char *fmt; | |
661 | const char *name; | |
662 | u32 rev; | |
663 | struct sk_buff *skb2; | |
664 | ||
665 | if (tb[NFTA_COMPAT_NAME] == NULL || | |
666 | tb[NFTA_COMPAT_REV] == NULL || | |
667 | tb[NFTA_COMPAT_TYPE] == NULL) | |
668 | return -EINVAL; | |
669 | ||
670 | name = nla_data(tb[NFTA_COMPAT_NAME]); | |
671 | rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV])); | |
672 | target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE])); | |
673 | ||
674 | nfmsg = nlmsg_data(nlh); | |
675 | ||
676 | switch(nfmsg->nfgen_family) { | |
677 | case AF_INET: | |
678 | fmt = "ipt_%s"; | |
679 | break; | |
680 | case AF_INET6: | |
681 | fmt = "ip6t_%s"; | |
682 | break; | |
5191f4d8 AB |
683 | case NFPROTO_BRIDGE: |
684 | fmt = "ebt_%s"; | |
685 | break; | |
5f158939 AB |
686 | case NFPROTO_ARP: |
687 | fmt = "arpt_%s"; | |
688 | break; | |
0ca743a5 PNA |
689 | default: |
690 | pr_err("nft_compat: unsupported protocol %d\n", | |
691 | nfmsg->nfgen_family); | |
692 | return -EINVAL; | |
693 | } | |
694 | ||
eb1fb147 FW |
695 | if (!try_module_get(THIS_MODULE)) |
696 | return -EINVAL; | |
697 | ||
698 | rcu_read_unlock(); | |
0ca743a5 PNA |
699 | try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name, |
700 | rev, target, &ret), | |
701 | fmt, name); | |
0ca743a5 | 702 | if (ret < 0) |
eb1fb147 | 703 | goto out_put; |
0ca743a5 PNA |
704 | |
705 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
eb1fb147 FW |
706 | if (skb2 == NULL) { |
707 | ret = -ENOMEM; | |
708 | goto out_put; | |
709 | } | |
0ca743a5 PNA |
710 | |
711 | /* include the best revision for this extension in the message */ | |
712 | if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid, | |
713 | nlh->nlmsg_seq, | |
714 | NFNL_MSG_TYPE(nlh->nlmsg_type), | |
715 | NFNL_MSG_COMPAT_GET, | |
716 | nfmsg->nfgen_family, | |
717 | name, ret, target) <= 0) { | |
718 | kfree_skb(skb2); | |
eb1fb147 | 719 | goto out_put; |
0ca743a5 PNA |
720 | } |
721 | ||
722 | ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, | |
723 | MSG_DONTWAIT); | |
724 | if (ret > 0) | |
725 | ret = 0; | |
eb1fb147 FW |
726 | out_put: |
727 | rcu_read_lock(); | |
728 | module_put(THIS_MODULE); | |
0ca743a5 PNA |
729 | return ret == -EAGAIN ? -ENOBUFS : ret; |
730 | } | |
731 | ||
732 | static const struct nla_policy nfnl_compat_policy_get[NFTA_COMPAT_MAX+1] = { | |
733 | [NFTA_COMPAT_NAME] = { .type = NLA_NUL_STRING, | |
734 | .len = NFT_COMPAT_NAME_MAX-1 }, | |
735 | [NFTA_COMPAT_REV] = { .type = NLA_U32 }, | |
736 | [NFTA_COMPAT_TYPE] = { .type = NLA_U32 }, | |
737 | }; | |
738 | ||
739 | static const struct nfnl_callback nfnl_nft_compat_cb[NFNL_MSG_COMPAT_MAX] = { | |
eb1fb147 | 740 | [NFNL_MSG_COMPAT_GET] = { .call_rcu = nfnl_compat_get_rcu, |
0ca743a5 PNA |
741 | .attr_count = NFTA_COMPAT_MAX, |
742 | .policy = nfnl_compat_policy_get }, | |
743 | }; | |
744 | ||
745 | static const struct nfnetlink_subsystem nfnl_compat_subsys = { | |
746 | .name = "nft-compat", | |
747 | .subsys_id = NFNL_SUBSYS_NFT_COMPAT, | |
748 | .cb_count = NFNL_MSG_COMPAT_MAX, | |
749 | .cb = nfnl_nft_compat_cb, | |
750 | }; | |
751 | ||
ba378ca9 PNA |
752 | static bool nft_match_cmp(const struct xt_match *match, |
753 | const char *name, u32 rev, u32 family) | |
754 | { | |
755 | return strcmp(match->name, name) == 0 && match->revision == rev && | |
756 | (match->family == NFPROTO_UNSPEC || match->family == family); | |
757 | } | |
758 | ||
0ca743a5 PNA |
759 | static const struct nft_expr_ops * |
760 | nft_match_select_ops(const struct nft_ctx *ctx, | |
761 | const struct nlattr * const tb[]) | |
762 | { | |
cf52572e | 763 | struct nft_compat_net *cn; |
0ca743a5 PNA |
764 | struct nft_xt *nft_match; |
765 | struct xt_match *match; | |
732a8049 | 766 | unsigned int matchsize; |
0ca743a5 | 767 | char *mt_name; |
ba378ca9 | 768 | u32 rev, family; |
2bf4fade | 769 | int err; |
0ca743a5 PNA |
770 | |
771 | if (tb[NFTA_MATCH_NAME] == NULL || | |
772 | tb[NFTA_MATCH_REV] == NULL || | |
773 | tb[NFTA_MATCH_INFO] == NULL) | |
774 | return ERR_PTR(-EINVAL); | |
775 | ||
776 | mt_name = nla_data(tb[NFTA_MATCH_NAME]); | |
777 | rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV])); | |
36596dad | 778 | family = ctx->family; |
0ca743a5 | 779 | |
cf52572e FW |
780 | cn = nft_compat_pernet(ctx->net); |
781 | ||
0ca743a5 | 782 | /* Re-use the existing match if it's already loaded. */ |
cf52572e | 783 | list_for_each_entry(nft_match, &cn->nft_match_list, head) { |
0ca743a5 PNA |
784 | struct xt_match *match = nft_match->ops.data; |
785 | ||
b8e9dc1c | 786 | if (nft_match_cmp(match, mt_name, rev, family)) |
0ca743a5 PNA |
787 | return &nft_match->ops; |
788 | } | |
789 | ||
790 | match = xt_request_find_match(family, mt_name, rev); | |
791 | if (IS_ERR(match)) | |
792 | return ERR_PTR(-ENOENT); | |
793 | ||
2bf4fade LZ |
794 | if (match->matchsize > nla_len(tb[NFTA_MATCH_INFO])) { |
795 | err = -EINVAL; | |
796 | goto err; | |
797 | } | |
f0716cd6 | 798 | |
0ca743a5 PNA |
799 | /* This is the first time we use this match, allocate operations */ |
800 | nft_match = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); | |
2bf4fade LZ |
801 | if (nft_match == NULL) { |
802 | err = -ENOMEM; | |
803 | goto err; | |
804 | } | |
0ca743a5 | 805 | |
12c44aba | 806 | refcount_set(&nft_match->refcnt, 0); |
0ca743a5 | 807 | nft_match->ops.type = &nft_match_type; |
0ca743a5 PNA |
808 | nft_match->ops.eval = nft_match_eval; |
809 | nft_match->ops.init = nft_match_init; | |
810 | nft_match->ops.destroy = nft_match_destroy; | |
811 | nft_match->ops.dump = nft_match_dump; | |
812 | nft_match->ops.validate = nft_match_validate; | |
813 | nft_match->ops.data = match; | |
814 | ||
732a8049 FW |
815 | matchsize = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize)); |
816 | if (matchsize > NFT_MATCH_LARGE_THRESH) { | |
817 | matchsize = NFT_EXPR_SIZE(sizeof(struct nft_xt_match_priv)); | |
818 | ||
819 | nft_match->ops.eval = nft_match_large_eval; | |
820 | nft_match->ops.init = nft_match_large_init; | |
821 | nft_match->ops.destroy = nft_match_large_destroy; | |
822 | nft_match->ops.dump = nft_match_large_dump; | |
823 | } | |
824 | ||
825 | nft_match->ops.size = matchsize; | |
826 | ||
cf52572e | 827 | list_add(&nft_match->head, &cn->nft_match_list); |
0ca743a5 PNA |
828 | |
829 | return &nft_match->ops; | |
2bf4fade LZ |
830 | err: |
831 | module_put(match->me); | |
832 | return ERR_PTR(err); | |
0ca743a5 PNA |
833 | } |
834 | ||
0ca743a5 PNA |
835 | static struct nft_expr_type nft_match_type __read_mostly = { |
836 | .name = "match", | |
837 | .select_ops = nft_match_select_ops, | |
838 | .policy = nft_match_policy, | |
839 | .maxattr = NFTA_MATCH_MAX, | |
840 | .owner = THIS_MODULE, | |
841 | }; | |
842 | ||
ba378ca9 PNA |
843 | static bool nft_target_cmp(const struct xt_target *tg, |
844 | const char *name, u32 rev, u32 family) | |
845 | { | |
846 | return strcmp(tg->name, name) == 0 && tg->revision == rev && | |
847 | (tg->family == NFPROTO_UNSPEC || tg->family == family); | |
848 | } | |
849 | ||
0ca743a5 PNA |
850 | static const struct nft_expr_ops * |
851 | nft_target_select_ops(const struct nft_ctx *ctx, | |
852 | const struct nlattr * const tb[]) | |
853 | { | |
cf52572e | 854 | struct nft_compat_net *cn; |
0ca743a5 PNA |
855 | struct nft_xt *nft_target; |
856 | struct xt_target *target; | |
857 | char *tg_name; | |
ba378ca9 | 858 | u32 rev, family; |
2bf4fade | 859 | int err; |
0ca743a5 PNA |
860 | |
861 | if (tb[NFTA_TARGET_NAME] == NULL || | |
862 | tb[NFTA_TARGET_REV] == NULL || | |
863 | tb[NFTA_TARGET_INFO] == NULL) | |
864 | return ERR_PTR(-EINVAL); | |
865 | ||
866 | tg_name = nla_data(tb[NFTA_TARGET_NAME]); | |
867 | rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV])); | |
36596dad | 868 | family = ctx->family; |
0ca743a5 | 869 | |
21d5e078 FW |
870 | if (strcmp(tg_name, XT_ERROR_TARGET) == 0 || |
871 | strcmp(tg_name, XT_STANDARD_TARGET) == 0 || | |
872 | strcmp(tg_name, "standard") == 0) | |
873 | return ERR_PTR(-EINVAL); | |
874 | ||
cf52572e | 875 | cn = nft_compat_pernet(ctx->net); |
0ca743a5 | 876 | /* Re-use the existing target if it's already loaded. */ |
cf52572e | 877 | list_for_each_entry(nft_target, &cn->nft_target_list, head) { |
0ca743a5 PNA |
878 | struct xt_target *target = nft_target->ops.data; |
879 | ||
21d5e078 FW |
880 | if (!target->target) |
881 | continue; | |
882 | ||
b8e9dc1c | 883 | if (nft_target_cmp(target, tg_name, rev, family)) |
0ca743a5 PNA |
884 | return &nft_target->ops; |
885 | } | |
886 | ||
887 | target = xt_request_find_target(family, tg_name, rev); | |
888 | if (IS_ERR(target)) | |
889 | return ERR_PTR(-ENOENT); | |
890 | ||
21d5e078 FW |
891 | if (!target->target) { |
892 | err = -EINVAL; | |
893 | goto err; | |
894 | } | |
895 | ||
2bf4fade LZ |
896 | if (target->targetsize > nla_len(tb[NFTA_TARGET_INFO])) { |
897 | err = -EINVAL; | |
898 | goto err; | |
899 | } | |
f0716cd6 | 900 | |
0ca743a5 PNA |
901 | /* This is the first time we use this target, allocate operations */ |
902 | nft_target = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); | |
2bf4fade LZ |
903 | if (nft_target == NULL) { |
904 | err = -ENOMEM; | |
905 | goto err; | |
906 | } | |
0ca743a5 | 907 | |
12c44aba | 908 | refcount_set(&nft_target->refcnt, 0); |
0ca743a5 | 909 | nft_target->ops.type = &nft_target_type; |
756c1b1a | 910 | nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize)); |
0ca743a5 PNA |
911 | nft_target->ops.init = nft_target_init; |
912 | nft_target->ops.destroy = nft_target_destroy; | |
913 | nft_target->ops.dump = nft_target_dump; | |
914 | nft_target->ops.validate = nft_target_validate; | |
915 | nft_target->ops.data = target; | |
916 | ||
5191f4d8 AB |
917 | if (family == NFPROTO_BRIDGE) |
918 | nft_target->ops.eval = nft_target_eval_bridge; | |
919 | else | |
920 | nft_target->ops.eval = nft_target_eval_xt; | |
921 | ||
cf52572e | 922 | list_add(&nft_target->head, &cn->nft_target_list); |
0ca743a5 PNA |
923 | |
924 | return &nft_target->ops; | |
2bf4fade LZ |
925 | err: |
926 | module_put(target->me); | |
927 | return ERR_PTR(err); | |
0ca743a5 PNA |
928 | } |
929 | ||
0ca743a5 PNA |
930 | static struct nft_expr_type nft_target_type __read_mostly = { |
931 | .name = "target", | |
932 | .select_ops = nft_target_select_ops, | |
933 | .policy = nft_target_policy, | |
934 | .maxattr = NFTA_TARGET_MAX, | |
935 | .owner = THIS_MODULE, | |
936 | }; | |
937 | ||
cf52572e FW |
938 | static int __net_init nft_compat_init_net(struct net *net) |
939 | { | |
940 | struct nft_compat_net *cn = nft_compat_pernet(net); | |
941 | ||
942 | INIT_LIST_HEAD(&cn->nft_target_list); | |
943 | INIT_LIST_HEAD(&cn->nft_match_list); | |
944 | ||
945 | return 0; | |
946 | } | |
947 | ||
948 | static void __net_exit nft_compat_exit_net(struct net *net) | |
949 | { | |
950 | struct nft_compat_net *cn = nft_compat_pernet(net); | |
951 | struct nft_xt *xt, *next; | |
952 | ||
953 | if (list_empty(&cn->nft_match_list) && | |
954 | list_empty(&cn->nft_target_list)) | |
955 | return; | |
956 | ||
957 | /* If there was an error that caused nft_xt expr to not be initialized | |
958 | * fully and noone else requested the same expression later, the lists | |
959 | * contain 0-refcount entries that still hold module reference. | |
960 | * | |
961 | * Clean them here. | |
962 | */ | |
963 | mutex_lock(&net->nft.commit_mutex); | |
964 | list_for_each_entry_safe(xt, next, &cn->nft_target_list, head) { | |
965 | struct xt_target *target = xt->ops.data; | |
966 | ||
967 | list_del_init(&xt->head); | |
968 | ||
969 | if (refcount_read(&xt->refcnt)) | |
970 | continue; | |
971 | module_put(target->me); | |
972 | kfree(xt); | |
973 | } | |
974 | ||
975 | list_for_each_entry_safe(xt, next, &cn->nft_match_list, head) { | |
976 | struct xt_match *match = xt->ops.data; | |
977 | ||
978 | list_del_init(&xt->head); | |
979 | ||
980 | if (refcount_read(&xt->refcnt)) | |
981 | continue; | |
982 | module_put(match->me); | |
983 | kfree(xt); | |
984 | } | |
985 | mutex_unlock(&net->nft.commit_mutex); | |
986 | } | |
987 | ||
988 | static struct pernet_operations nft_compat_net_ops = { | |
989 | .init = nft_compat_init_net, | |
990 | .exit = nft_compat_exit_net, | |
991 | .id = &nft_compat_net_id, | |
992 | .size = sizeof(struct nft_compat_net), | |
993 | }; | |
994 | ||
0ca743a5 PNA |
995 | static int __init nft_compat_module_init(void) |
996 | { | |
997 | int ret; | |
998 | ||
cf52572e FW |
999 | ret = register_pernet_subsys(&nft_compat_net_ops); |
1000 | if (ret < 0) | |
1001 | goto err_target; | |
1002 | ||
0ca743a5 PNA |
1003 | ret = nft_register_expr(&nft_match_type); |
1004 | if (ret < 0) | |
cf52572e | 1005 | goto err_pernet; |
0ca743a5 PNA |
1006 | |
1007 | ret = nft_register_expr(&nft_target_type); | |
1008 | if (ret < 0) | |
1009 | goto err_match; | |
1010 | ||
1011 | ret = nfnetlink_subsys_register(&nfnl_compat_subsys); | |
1012 | if (ret < 0) { | |
1013 | pr_err("nft_compat: cannot register with nfnetlink.\n"); | |
1014 | goto err_target; | |
1015 | } | |
1016 | ||
0ca743a5 | 1017 | return ret; |
0ca743a5 PNA |
1018 | err_target: |
1019 | nft_unregister_expr(&nft_target_type); | |
1020 | err_match: | |
1021 | nft_unregister_expr(&nft_match_type); | |
cf52572e FW |
1022 | err_pernet: |
1023 | unregister_pernet_subsys(&nft_compat_net_ops); | |
0ca743a5 PNA |
1024 | return ret; |
1025 | } | |
1026 | ||
1027 | static void __exit nft_compat_module_exit(void) | |
1028 | { | |
1029 | nfnetlink_subsys_unregister(&nfnl_compat_subsys); | |
1030 | nft_unregister_expr(&nft_target_type); | |
1031 | nft_unregister_expr(&nft_match_type); | |
cf52572e | 1032 | unregister_pernet_subsys(&nft_compat_net_ops); |
0ca743a5 PNA |
1033 | } |
1034 | ||
1035 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT); | |
1036 | ||
1037 | module_init(nft_compat_module_init); | |
1038 | module_exit(nft_compat_module_exit); | |
1039 | ||
1040 | MODULE_LICENSE("GPL"); | |
1041 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | |
1042 | MODULE_ALIAS_NFT_EXPR("match"); | |
1043 | MODULE_ALIAS_NFT_EXPR("target"); |