~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netfilter/nft_compat.c

Version: ~ [ linux-5.16 ] ~ [ linux-5.15.13 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.90 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.170 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.224 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.261 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.296 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.298 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp