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