diff options
author | Harald Welte <laforge@netfilter.org> | 2005-10-26 03:34:24 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@mandriva.com> | 2005-10-31 13:36:08 -0500 |
commit | 6b7d31fcdda5938e5d3f1f8b0922cc25aa200dfc (patch) | |
tree | c44f46d7eab0ea138247018ba1e04da6df8ae9d4 /net | |
parent | 6ede2463c8d7ea949f8e7ef35243490c415ddc2f (diff) |
[NETFILTER]: Add "revision" support to arp_tables and ip6_tables
Like ip_tables already has it for some time, this adds support for
having multiple revisions for each match/target. We steal one byte from
the name in order to accomodate a 8 bit version number.
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 201 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 298 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_MARK.c | 8 |
3 files changed, 323 insertions, 184 deletions
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index a7969286e6e7..3c2e9639bba6 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c | |||
@@ -347,58 +347,106 @@ unsigned int arpt_do_table(struct sk_buff **pskb, | |||
347 | return verdict; | 347 | return verdict; |
348 | } | 348 | } |
349 | 349 | ||
350 | static inline void *find_inlist_lock_noload(struct list_head *head, | 350 | /* |
351 | const char *name, | 351 | * These are weird, but module loading must not be done with mutex |
352 | int *error, | 352 | * held (since they will register), and we have to have a single |
353 | struct semaphore *mutex) | 353 | * function to use try_then_request_module(). |
354 | */ | ||
355 | |||
356 | /* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ | ||
357 | static inline struct arpt_table *find_table_lock(const char *name) | ||
354 | { | 358 | { |
355 | void *ret; | 359 | struct arpt_table *t; |
356 | 360 | ||
357 | *error = down_interruptible(mutex); | 361 | if (down_interruptible(&arpt_mutex) != 0) |
358 | if (*error != 0) | 362 | return ERR_PTR(-EINTR); |
359 | return NULL; | ||
360 | 363 | ||
361 | ret = list_named_find(head, name); | 364 | list_for_each_entry(t, &arpt_tables, list) |
362 | if (!ret) { | 365 | if (strcmp(t->name, name) == 0 && try_module_get(t->me)) |
363 | *error = -ENOENT; | 366 | return t; |
364 | up(mutex); | 367 | up(&arpt_mutex); |
365 | } | 368 | return NULL; |
366 | return ret; | ||
367 | } | 369 | } |
368 | 370 | ||
369 | #ifndef CONFIG_KMOD | 371 | |
370 | #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m)) | 372 | /* Find target, grabs ref. Returns ERR_PTR() on error. */ |
371 | #else | 373 | static inline struct arpt_target *find_target(const char *name, u8 revision) |
372 | static void * | ||
373 | find_inlist_lock(struct list_head *head, | ||
374 | const char *name, | ||
375 | const char *prefix, | ||
376 | int *error, | ||
377 | struct semaphore *mutex) | ||
378 | { | 374 | { |
379 | void *ret; | 375 | struct arpt_target *t; |
376 | int err = 0; | ||
380 | 377 | ||
381 | ret = find_inlist_lock_noload(head, name, error, mutex); | 378 | if (down_interruptible(&arpt_mutex) != 0) |
382 | if (!ret) { | 379 | return ERR_PTR(-EINTR); |
383 | duprintf("find_inlist: loading `%s%s'.\n", prefix, name); | 380 | |
384 | request_module("%s%s", prefix, name); | 381 | list_for_each_entry(t, &arpt_target, list) { |
385 | ret = find_inlist_lock_noload(head, name, error, mutex); | 382 | if (strcmp(t->name, name) == 0) { |
383 | if (t->revision == revision) { | ||
384 | if (try_module_get(t->me)) { | ||
385 | up(&arpt_mutex); | ||
386 | return t; | ||
387 | } | ||
388 | } else | ||
389 | err = -EPROTOTYPE; /* Found something. */ | ||
390 | } | ||
386 | } | 391 | } |
392 | up(&arpt_mutex); | ||
393 | return ERR_PTR(err); | ||
394 | } | ||
387 | 395 | ||
388 | return ret; | 396 | struct arpt_target *arpt_find_target(const char *name, u8 revision) |
397 | { | ||
398 | struct arpt_target *target; | ||
399 | |||
400 | target = try_then_request_module(find_target(name, revision), | ||
401 | "arpt_%s", name); | ||
402 | if (IS_ERR(target) || !target) | ||
403 | return NULL; | ||
404 | return target; | ||
389 | } | 405 | } |
390 | #endif | ||
391 | 406 | ||
392 | static inline struct arpt_table *arpt_find_table_lock(const char *name, int *error, struct semaphore *mutex) | 407 | static int target_revfn(const char *name, u8 revision, int *bestp) |
393 | { | 408 | { |
394 | return find_inlist_lock(&arpt_tables, name, "arptable_", error, mutex); | 409 | struct arpt_target *t; |
410 | int have_rev = 0; | ||
411 | |||
412 | list_for_each_entry(t, &arpt_target, list) { | ||
413 | if (strcmp(t->name, name) == 0) { | ||
414 | if (t->revision > *bestp) | ||
415 | *bestp = t->revision; | ||
416 | if (t->revision == revision) | ||
417 | have_rev =1; | ||
418 | } | ||
419 | } | ||
420 | return have_rev; | ||
395 | } | 421 | } |
396 | 422 | ||
397 | static struct arpt_target *arpt_find_target_lock(const char *name, int *error, struct semaphore *mutex) | 423 | /* Returns true or false (if no such extension at all) */ |
424 | static inline int find_revision(const char *name, u8 revision, | ||
425 | int (*revfn)(const char *, u8, int *), | ||
426 | int *err) | ||
398 | { | 427 | { |
399 | return find_inlist_lock(&arpt_target, name, "arpt_", error, mutex); | 428 | int have_rev, best = -1; |
429 | |||
430 | if (down_interruptible(&arpt_mutex) != 0) { | ||
431 | *err = -EINTR; | ||
432 | return 1; | ||
433 | } | ||
434 | have_rev = revfn(name, revision, &best); | ||
435 | up(&arpt_mutex); | ||
436 | |||
437 | /* Nothing at all? Return 0 to try loading module. */ | ||
438 | if (best == -1) { | ||
439 | *err = -ENOENT; | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | *err = best; | ||
444 | if (!have_rev) | ||
445 | *err = -EPROTONOSUPPORT; | ||
446 | return 1; | ||
400 | } | 447 | } |
401 | 448 | ||
449 | |||
402 | /* All zeroes == unconditional rule. */ | 450 | /* All zeroes == unconditional rule. */ |
403 | static inline int unconditional(const struct arpt_arp *arp) | 451 | static inline int unconditional(const struct arpt_arp *arp) |
404 | { | 452 | { |
@@ -544,17 +592,15 @@ static inline int check_entry(struct arpt_entry *e, const char *name, unsigned i | |||
544 | } | 592 | } |
545 | 593 | ||
546 | t = arpt_get_target(e); | 594 | t = arpt_get_target(e); |
547 | target = arpt_find_target_lock(t->u.user.name, &ret, &arpt_mutex); | 595 | target = try_then_request_module(find_target(t->u.user.name, |
548 | if (!target) { | 596 | t->u.user.revision), |
597 | "arpt_%s", t->u.user.name); | ||
598 | if (IS_ERR(target) || !target) { | ||
549 | duprintf("check_entry: `%s' not found\n", t->u.user.name); | 599 | duprintf("check_entry: `%s' not found\n", t->u.user.name); |
600 | ret = target ? PTR_ERR(target) : -ENOENT; | ||
550 | goto out; | 601 | goto out; |
551 | } | 602 | } |
552 | if (!try_module_get((target->me))) { | ||
553 | ret = -ENOENT; | ||
554 | goto out_unlock; | ||
555 | } | ||
556 | t->u.kernel.target = target; | 603 | t->u.kernel.target = target; |
557 | up(&arpt_mutex); | ||
558 | 604 | ||
559 | if (t->u.kernel.target == &arpt_standard_target) { | 605 | if (t->u.kernel.target == &arpt_standard_target) { |
560 | if (!standard_check(t, size)) { | 606 | if (!standard_check(t, size)) { |
@@ -576,8 +622,6 @@ static inline int check_entry(struct arpt_entry *e, const char *name, unsigned i | |||
576 | (*i)++; | 622 | (*i)++; |
577 | return 0; | 623 | return 0; |
578 | 624 | ||
579 | out_unlock: | ||
580 | up(&arpt_mutex); | ||
581 | out: | 625 | out: |
582 | return ret; | 626 | return ret; |
583 | } | 627 | } |
@@ -846,8 +890,8 @@ static int get_entries(const struct arpt_get_entries *entries, | |||
846 | int ret; | 890 | int ret; |
847 | struct arpt_table *t; | 891 | struct arpt_table *t; |
848 | 892 | ||
849 | t = arpt_find_table_lock(entries->name, &ret, &arpt_mutex); | 893 | t = find_table_lock(entries->name); |
850 | if (t) { | 894 | if (t || !IS_ERR(t)) { |
851 | duprintf("t->private->number = %u\n", | 895 | duprintf("t->private->number = %u\n", |
852 | t->private->number); | 896 | t->private->number); |
853 | if (entries->size == t->private->size) | 897 | if (entries->size == t->private->size) |
@@ -859,10 +903,10 @@ static int get_entries(const struct arpt_get_entries *entries, | |||
859 | entries->size); | 903 | entries->size); |
860 | ret = -EINVAL; | 904 | ret = -EINVAL; |
861 | } | 905 | } |
906 | module_put(t->me); | ||
862 | up(&arpt_mutex); | 907 | up(&arpt_mutex); |
863 | } else | 908 | } else |
864 | duprintf("get_entries: Can't find %s!\n", | 909 | ret = t ? PTR_ERR(t) : -ENOENT; |
865 | entries->name); | ||
866 | 910 | ||
867 | return ret; | 911 | return ret; |
868 | } | 912 | } |
@@ -913,22 +957,19 @@ static int do_replace(void __user *user, unsigned int len) | |||
913 | 957 | ||
914 | duprintf("arp_tables: Translated table\n"); | 958 | duprintf("arp_tables: Translated table\n"); |
915 | 959 | ||
916 | t = arpt_find_table_lock(tmp.name, &ret, &arpt_mutex); | 960 | t = try_then_request_module(find_table_lock(tmp.name), |
917 | if (!t) | 961 | "arptable_%s", tmp.name); |
962 | if (!t || IS_ERR(t)) { | ||
963 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
918 | goto free_newinfo_counters_untrans; | 964 | goto free_newinfo_counters_untrans; |
965 | } | ||
919 | 966 | ||
920 | /* You lied! */ | 967 | /* You lied! */ |
921 | if (tmp.valid_hooks != t->valid_hooks) { | 968 | if (tmp.valid_hooks != t->valid_hooks) { |
922 | duprintf("Valid hook crap: %08X vs %08X\n", | 969 | duprintf("Valid hook crap: %08X vs %08X\n", |
923 | tmp.valid_hooks, t->valid_hooks); | 970 | tmp.valid_hooks, t->valid_hooks); |
924 | ret = -EINVAL; | 971 | ret = -EINVAL; |
925 | goto free_newinfo_counters_untrans_unlock; | 972 | goto put_module; |
926 | } | ||
927 | |||
928 | /* Get a reference in advance, we're not allowed fail later */ | ||
929 | if (!try_module_get(t->me)) { | ||
930 | ret = -EBUSY; | ||
931 | goto free_newinfo_counters_untrans_unlock; | ||
932 | } | 973 | } |
933 | 974 | ||
934 | oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); | 975 | oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); |
@@ -959,7 +1000,6 @@ static int do_replace(void __user *user, unsigned int len) | |||
959 | 1000 | ||
960 | put_module: | 1001 | put_module: |
961 | module_put(t->me); | 1002 | module_put(t->me); |
962 | free_newinfo_counters_untrans_unlock: | ||
963 | up(&arpt_mutex); | 1003 | up(&arpt_mutex); |
964 | free_newinfo_counters_untrans: | 1004 | free_newinfo_counters_untrans: |
965 | ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry, NULL); | 1005 | ARPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry, NULL); |
@@ -989,7 +1029,7 @@ static int do_add_counters(void __user *user, unsigned int len) | |||
989 | unsigned int i; | 1029 | unsigned int i; |
990 | struct arpt_counters_info tmp, *paddc; | 1030 | struct arpt_counters_info tmp, *paddc; |
991 | struct arpt_table *t; | 1031 | struct arpt_table *t; |
992 | int ret; | 1032 | int ret = 0; |
993 | 1033 | ||
994 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | 1034 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) |
995 | return -EFAULT; | 1035 | return -EFAULT; |
@@ -1006,9 +1046,11 @@ static int do_add_counters(void __user *user, unsigned int len) | |||
1006 | goto free; | 1046 | goto free; |
1007 | } | 1047 | } |
1008 | 1048 | ||
1009 | t = arpt_find_table_lock(tmp.name, &ret, &arpt_mutex); | 1049 | t = find_table_lock(tmp.name); |
1010 | if (!t) | 1050 | if (!t || IS_ERR(t)) { |
1051 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1011 | goto free; | 1052 | goto free; |
1053 | } | ||
1012 | 1054 | ||
1013 | write_lock_bh(&t->lock); | 1055 | write_lock_bh(&t->lock); |
1014 | if (t->private->number != paddc->num_counters) { | 1056 | if (t->private->number != paddc->num_counters) { |
@@ -1025,6 +1067,7 @@ static int do_add_counters(void __user *user, unsigned int len) | |||
1025 | unlock_up_free: | 1067 | unlock_up_free: |
1026 | write_unlock_bh(&t->lock); | 1068 | write_unlock_bh(&t->lock); |
1027 | up(&arpt_mutex); | 1069 | up(&arpt_mutex); |
1070 | module_put(t->me); | ||
1028 | free: | 1071 | free: |
1029 | vfree(paddc); | 1072 | vfree(paddc); |
1030 | 1073 | ||
@@ -1079,8 +1122,10 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len | |||
1079 | break; | 1122 | break; |
1080 | } | 1123 | } |
1081 | name[ARPT_TABLE_MAXNAMELEN-1] = '\0'; | 1124 | name[ARPT_TABLE_MAXNAMELEN-1] = '\0'; |
1082 | t = arpt_find_table_lock(name, &ret, &arpt_mutex); | 1125 | |
1083 | if (t) { | 1126 | t = try_then_request_module(find_table_lock(name), |
1127 | "arptable_%s", name); | ||
1128 | if (t && !IS_ERR(t)) { | ||
1084 | struct arpt_getinfo info; | 1129 | struct arpt_getinfo info; |
1085 | 1130 | ||
1086 | info.valid_hooks = t->valid_hooks; | 1131 | info.valid_hooks = t->valid_hooks; |
@@ -1096,9 +1141,10 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len | |||
1096 | ret = -EFAULT; | 1141 | ret = -EFAULT; |
1097 | else | 1142 | else |
1098 | ret = 0; | 1143 | ret = 0; |
1099 | |||
1100 | up(&arpt_mutex); | 1144 | up(&arpt_mutex); |
1101 | } | 1145 | module_put(t->me); |
1146 | } else | ||
1147 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1102 | } | 1148 | } |
1103 | break; | 1149 | break; |
1104 | 1150 | ||
@@ -1119,6 +1165,24 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len | |||
1119 | break; | 1165 | break; |
1120 | } | 1166 | } |
1121 | 1167 | ||
1168 | case ARPT_SO_GET_REVISION_TARGET: { | ||
1169 | struct arpt_get_revision rev; | ||
1170 | |||
1171 | if (*len != sizeof(rev)) { | ||
1172 | ret = -EINVAL; | ||
1173 | break; | ||
1174 | } | ||
1175 | if (copy_from_user(&rev, user, sizeof(rev)) != 0) { | ||
1176 | ret = -EFAULT; | ||
1177 | break; | ||
1178 | } | ||
1179 | |||
1180 | try_then_request_module(find_revision(rev.name, rev.revision, | ||
1181 | target_revfn, &ret), | ||
1182 | "arpt_%s", rev.name); | ||
1183 | break; | ||
1184 | } | ||
1185 | |||
1122 | default: | 1186 | default: |
1123 | duprintf("do_arpt_get_ctl: unknown request %i\n", cmd); | 1187 | duprintf("do_arpt_get_ctl: unknown request %i\n", cmd); |
1124 | ret = -EINVAL; | 1188 | ret = -EINVAL; |
@@ -1136,12 +1200,9 @@ int arpt_register_target(struct arpt_target *target) | |||
1136 | if (ret != 0) | 1200 | if (ret != 0) |
1137 | return ret; | 1201 | return ret; |
1138 | 1202 | ||
1139 | if (!list_named_insert(&arpt_target, target)) { | 1203 | list_add(&target->list, &arpt_target); |
1140 | duprintf("arpt_register_target: `%s' already in list!\n", | ||
1141 | target->name); | ||
1142 | ret = -EINVAL; | ||
1143 | } | ||
1144 | up(&arpt_mutex); | 1204 | up(&arpt_mutex); |
1205 | |||
1145 | return ret; | 1206 | return ret; |
1146 | } | 1207 | } |
1147 | 1208 | ||
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 21deec25a12b..7d492226c16e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Packet matching code. | 2 | * Packet matching code. |
3 | * | 3 | * |
4 | * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling | 4 | * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling |
5 | * Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org> | 5 | * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -23,7 +23,6 @@ | |||
23 | #include <linux/tcp.h> | 23 | #include <linux/tcp.h> |
24 | #include <linux/udp.h> | 24 | #include <linux/udp.h> |
25 | #include <linux/icmpv6.h> | 25 | #include <linux/icmpv6.h> |
26 | #include <net/ip.h> | ||
27 | #include <net/ipv6.h> | 26 | #include <net/ipv6.h> |
28 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
29 | #include <asm/semaphore.h> | 28 | #include <asm/semaphore.h> |
@@ -80,13 +79,12 @@ static DECLARE_MUTEX(ip6t_mutex); | |||
80 | #define inline | 79 | #define inline |
81 | #endif | 80 | #endif |
82 | 81 | ||
83 | /* Locking is simple: we assume at worst case there will be one packet | 82 | /* |
84 | in user context and one from bottom halves (or soft irq if Alexey's | ||
85 | softnet patch was applied). | ||
86 | |||
87 | We keep a set of rules for each CPU, so we can avoid write-locking | 83 | We keep a set of rules for each CPU, so we can avoid write-locking |
88 | them; doing a readlock_bh() stops packets coming through if we're | 84 | them in the softirq when updating the counters and therefore |
89 | in user context. | 85 | only need to read-lock in the softirq; doing a write_lock_bh() in user |
86 | context stops packets coming through and allows user context to read | ||
87 | the counters or update the rules. | ||
90 | 88 | ||
91 | To be cache friendly on SMP, we arrange them like so: | 89 | To be cache friendly on SMP, we arrange them like so: |
92 | [ n-entries ] | 90 | [ n-entries ] |
@@ -356,7 +354,7 @@ ip6t_do_table(struct sk_buff **pskb, | |||
356 | struct ip6t_table *table, | 354 | struct ip6t_table *table, |
357 | void *userdata) | 355 | void *userdata) |
358 | { | 356 | { |
359 | static const char nulldevname[IFNAMSIZ]; | 357 | static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); |
360 | int offset = 0; | 358 | int offset = 0; |
361 | unsigned int protoff = 0; | 359 | unsigned int protoff = 0; |
362 | int hotdrop = 0; | 360 | int hotdrop = 0; |
@@ -369,7 +367,6 @@ ip6t_do_table(struct sk_buff **pskb, | |||
369 | /* Initialization */ | 367 | /* Initialization */ |
370 | indev = in ? in->name : nulldevname; | 368 | indev = in ? in->name : nulldevname; |
371 | outdev = out ? out->name : nulldevname; | 369 | outdev = out ? out->name : nulldevname; |
372 | |||
373 | /* We handle fragments by dealing with the first fragment as | 370 | /* We handle fragments by dealing with the first fragment as |
374 | * if it was a normal packet. All other fragments are treated | 371 | * if it was a normal packet. All other fragments are treated |
375 | * normally, except that they will NEVER match rules that ask | 372 | * normally, except that they will NEVER match rules that ask |
@@ -497,75 +494,145 @@ ip6t_do_table(struct sk_buff **pskb, | |||
497 | #endif | 494 | #endif |
498 | } | 495 | } |
499 | 496 | ||
500 | /* If it succeeds, returns element and locks mutex */ | 497 | /* |
501 | static inline void * | 498 | * These are weird, but module loading must not be done with mutex |
502 | find_inlist_lock_noload(struct list_head *head, | 499 | * held (since they will register), and we have to have a single |
503 | const char *name, | 500 | * function to use try_then_request_module(). |
504 | int *error, | 501 | */ |
505 | struct semaphore *mutex) | 502 | |
503 | /* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ | ||
504 | static inline struct ip6t_table *find_table_lock(const char *name) | ||
506 | { | 505 | { |
507 | void *ret; | 506 | struct ip6t_table *t; |
508 | 507 | ||
509 | #if 1 | 508 | if (down_interruptible(&ip6t_mutex) != 0) |
510 | duprintf("find_inlist: searching for `%s' in %s.\n", | 509 | return ERR_PTR(-EINTR); |
511 | name, head == &ip6t_target ? "ip6t_target" | ||
512 | : head == &ip6t_match ? "ip6t_match" | ||
513 | : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN"); | ||
514 | #endif | ||
515 | 510 | ||
516 | *error = down_interruptible(mutex); | 511 | list_for_each_entry(t, &ip6t_tables, list) |
517 | if (*error != 0) | 512 | if (strcmp(t->name, name) == 0 && try_module_get(t->me)) |
518 | return NULL; | 513 | return t; |
514 | up(&ip6t_mutex); | ||
515 | return NULL; | ||
516 | } | ||
517 | |||
518 | /* Find match, grabs ref. Returns ERR_PTR() on error. */ | ||
519 | static inline struct ip6t_match *find_match(const char *name, u8 revision) | ||
520 | { | ||
521 | struct ip6t_match *m; | ||
522 | int err = 0; | ||
519 | 523 | ||
520 | ret = list_named_find(head, name); | 524 | if (down_interruptible(&ip6t_mutex) != 0) |
521 | if (!ret) { | 525 | return ERR_PTR(-EINTR); |
522 | *error = -ENOENT; | 526 | |
523 | up(mutex); | 527 | list_for_each_entry(m, &ip6t_match, list) { |
528 | if (strcmp(m->name, name) == 0) { | ||
529 | if (m->revision == revision) { | ||
530 | if (try_module_get(m->me)) { | ||
531 | up(&ip6t_mutex); | ||
532 | return m; | ||
533 | } | ||
534 | } else | ||
535 | err = -EPROTOTYPE; /* Found something. */ | ||
536 | } | ||
524 | } | 537 | } |
525 | return ret; | 538 | up(&ip6t_mutex); |
539 | return ERR_PTR(err); | ||
526 | } | 540 | } |
527 | 541 | ||
528 | #ifndef CONFIG_KMOD | 542 | /* Find target, grabs ref. Returns ERR_PTR() on error. */ |
529 | #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m)) | 543 | static inline struct ip6t_target *find_target(const char *name, u8 revision) |
530 | #else | ||
531 | static void * | ||
532 | find_inlist_lock(struct list_head *head, | ||
533 | const char *name, | ||
534 | const char *prefix, | ||
535 | int *error, | ||
536 | struct semaphore *mutex) | ||
537 | { | 544 | { |
538 | void *ret; | 545 | struct ip6t_target *t; |
546 | int err = 0; | ||
539 | 547 | ||
540 | ret = find_inlist_lock_noload(head, name, error, mutex); | 548 | if (down_interruptible(&ip6t_mutex) != 0) |
541 | if (!ret) { | 549 | return ERR_PTR(-EINTR); |
542 | duprintf("find_inlist: loading `%s%s'.\n", prefix, name); | 550 | |
543 | request_module("%s%s", prefix, name); | 551 | list_for_each_entry(t, &ip6t_target, list) { |
544 | ret = find_inlist_lock_noload(head, name, error, mutex); | 552 | if (strcmp(t->name, name) == 0) { |
553 | if (t->revision == revision) { | ||
554 | if (try_module_get(t->me)) { | ||
555 | up(&ip6t_mutex); | ||
556 | return t; | ||
557 | } | ||
558 | } else | ||
559 | err = -EPROTOTYPE; /* Found something. */ | ||
560 | } | ||
545 | } | 561 | } |
562 | up(&ip6t_mutex); | ||
563 | return ERR_PTR(err); | ||
564 | } | ||
546 | 565 | ||
547 | return ret; | 566 | struct ip6t_target *ip6t_find_target(const char *name, u8 revision) |
567 | { | ||
568 | struct ip6t_target *target; | ||
569 | |||
570 | target = try_then_request_module(find_target(name, revision), | ||
571 | "ip6t_%s", name); | ||
572 | if (IS_ERR(target) || !target) | ||
573 | return NULL; | ||
574 | return target; | ||
548 | } | 575 | } |
549 | #endif | ||
550 | 576 | ||
551 | static inline struct ip6t_table * | 577 | static int match_revfn(const char *name, u8 revision, int *bestp) |
552 | ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex) | ||
553 | { | 578 | { |
554 | return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex); | 579 | struct ip6t_match *m; |
580 | int have_rev = 0; | ||
581 | |||
582 | list_for_each_entry(m, &ip6t_match, list) { | ||
583 | if (strcmp(m->name, name) == 0) { | ||
584 | if (m->revision > *bestp) | ||
585 | *bestp = m->revision; | ||
586 | if (m->revision == revision) | ||
587 | have_rev = 1; | ||
588 | } | ||
589 | } | ||
590 | return have_rev; | ||
555 | } | 591 | } |
556 | 592 | ||
557 | static inline struct ip6t_match * | 593 | static int target_revfn(const char *name, u8 revision, int *bestp) |
558 | find_match_lock(const char *name, int *error, struct semaphore *mutex) | ||
559 | { | 594 | { |
560 | return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex); | 595 | struct ip6t_target *t; |
596 | int have_rev = 0; | ||
597 | |||
598 | list_for_each_entry(t, &ip6t_target, list) { | ||
599 | if (strcmp(t->name, name) == 0) { | ||
600 | if (t->revision > *bestp) | ||
601 | *bestp = t->revision; | ||
602 | if (t->revision == revision) | ||
603 | have_rev = 1; | ||
604 | } | ||
605 | } | ||
606 | return have_rev; | ||
561 | } | 607 | } |
562 | 608 | ||
563 | static struct ip6t_target * | 609 | /* Returns true or fals (if no such extension at all) */ |
564 | ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex) | 610 | static inline int find_revision(const char *name, u8 revision, |
611 | int (*revfn)(const char *, u8, int *), | ||
612 | int *err) | ||
565 | { | 613 | { |
566 | return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex); | 614 | int have_rev, best = -1; |
615 | |||
616 | if (down_interruptible(&ip6t_mutex) != 0) { | ||
617 | *err = -EINTR; | ||
618 | return 1; | ||
619 | } | ||
620 | have_rev = revfn(name, revision, &best); | ||
621 | up(&ip6t_mutex); | ||
622 | |||
623 | /* Nothing at all? Return 0 to try loading module. */ | ||
624 | if (best == -1) { | ||
625 | *err = -ENOENT; | ||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | *err = best; | ||
630 | if (!have_rev) | ||
631 | *err = -EPROTONOSUPPORT; | ||
632 | return 1; | ||
567 | } | 633 | } |
568 | 634 | ||
635 | |||
569 | /* All zeroes == unconditional rule. */ | 636 | /* All zeroes == unconditional rule. */ |
570 | static inline int | 637 | static inline int |
571 | unconditional(const struct ip6t_ip6 *ipv6) | 638 | unconditional(const struct ip6t_ip6 *ipv6) |
@@ -725,20 +792,16 @@ check_match(struct ip6t_entry_match *m, | |||
725 | unsigned int hookmask, | 792 | unsigned int hookmask, |
726 | unsigned int *i) | 793 | unsigned int *i) |
727 | { | 794 | { |
728 | int ret; | ||
729 | struct ip6t_match *match; | 795 | struct ip6t_match *match; |
730 | 796 | ||
731 | match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex); | 797 | match = try_then_request_module(find_match(m->u.user.name, |
732 | if (!match) { | 798 | m->u.user.revision), |
733 | // duprintf("check_match: `%s' not found\n", m->u.name); | 799 | "ip6t_%s", m->u.user.name); |
734 | return ret; | 800 | if (IS_ERR(match) || !match) { |
735 | } | 801 | duprintf("check_match: `%s' not found\n", m->u.user.name); |
736 | if (!try_module_get(match->me)) { | 802 | return match ? PTR_ERR(match) : -ENOENT; |
737 | up(&ip6t_mutex); | ||
738 | return -ENOENT; | ||
739 | } | 803 | } |
740 | m->u.kernel.match = match; | 804 | m->u.kernel.match = match; |
741 | up(&ip6t_mutex); | ||
742 | 805 | ||
743 | if (m->u.kernel.match->checkentry | 806 | if (m->u.kernel.match->checkentry |
744 | && !m->u.kernel.match->checkentry(name, ipv6, m->data, | 807 | && !m->u.kernel.match->checkentry(name, ipv6, m->data, |
@@ -776,22 +839,16 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size, | |||
776 | goto cleanup_matches; | 839 | goto cleanup_matches; |
777 | 840 | ||
778 | t = ip6t_get_target(e); | 841 | t = ip6t_get_target(e); |
779 | target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex); | 842 | target = try_then_request_module(find_target(t->u.user.name, |
780 | if (!target) { | 843 | t->u.user.revision), |
844 | "ip6t_%s", t->u.user.name); | ||
845 | if (IS_ERR(target) || !target) { | ||
781 | duprintf("check_entry: `%s' not found\n", t->u.user.name); | 846 | duprintf("check_entry: `%s' not found\n", t->u.user.name); |
782 | goto cleanup_matches; | 847 | ret = target ? PTR_ERR(target) : -ENOENT; |
783 | } | ||
784 | if (!try_module_get(target->me)) { | ||
785 | up(&ip6t_mutex); | ||
786 | ret = -ENOENT; | ||
787 | goto cleanup_matches; | 848 | goto cleanup_matches; |
788 | } | 849 | } |
789 | t->u.kernel.target = target; | 850 | t->u.kernel.target = target; |
790 | up(&ip6t_mutex); | 851 | |
791 | if (!t->u.kernel.target) { | ||
792 | ret = -EBUSY; | ||
793 | goto cleanup_matches; | ||
794 | } | ||
795 | if (t->u.kernel.target == &ip6t_standard_target) { | 852 | if (t->u.kernel.target == &ip6t_standard_target) { |
796 | if (!standard_check(t, size)) { | 853 | if (!standard_check(t, size)) { |
797 | ret = -EINVAL; | 854 | ret = -EINVAL; |
@@ -1118,8 +1175,8 @@ get_entries(const struct ip6t_get_entries *entries, | |||
1118 | int ret; | 1175 | int ret; |
1119 | struct ip6t_table *t; | 1176 | struct ip6t_table *t; |
1120 | 1177 | ||
1121 | t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex); | 1178 | t = find_table_lock(entries->name); |
1122 | if (t) { | 1179 | if (t && !IS_ERR(t)) { |
1123 | duprintf("t->private->number = %u\n", | 1180 | duprintf("t->private->number = %u\n", |
1124 | t->private->number); | 1181 | t->private->number); |
1125 | if (entries->size == t->private->size) | 1182 | if (entries->size == t->private->size) |
@@ -1131,10 +1188,10 @@ get_entries(const struct ip6t_get_entries *entries, | |||
1131 | entries->size); | 1188 | entries->size); |
1132 | ret = -EINVAL; | 1189 | ret = -EINVAL; |
1133 | } | 1190 | } |
1191 | module_put(t->me); | ||
1134 | up(&ip6t_mutex); | 1192 | up(&ip6t_mutex); |
1135 | } else | 1193 | } else |
1136 | duprintf("get_entries: Can't find %s!\n", | 1194 | ret = t ? PTR_ERR(t) : -ENOENT; |
1137 | entries->name); | ||
1138 | 1195 | ||
1139 | return ret; | 1196 | return ret; |
1140 | } | 1197 | } |
@@ -1182,22 +1239,19 @@ do_replace(void __user *user, unsigned int len) | |||
1182 | 1239 | ||
1183 | duprintf("ip_tables: Translated table\n"); | 1240 | duprintf("ip_tables: Translated table\n"); |
1184 | 1241 | ||
1185 | t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex); | 1242 | t = try_then_request_module(find_table_lock(tmp.name), |
1186 | if (!t) | 1243 | "ip6table_%s", tmp.name); |
1244 | if (!t || IS_ERR(t)) { | ||
1245 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1187 | goto free_newinfo_counters_untrans; | 1246 | goto free_newinfo_counters_untrans; |
1247 | } | ||
1188 | 1248 | ||
1189 | /* You lied! */ | 1249 | /* You lied! */ |
1190 | if (tmp.valid_hooks != t->valid_hooks) { | 1250 | if (tmp.valid_hooks != t->valid_hooks) { |
1191 | duprintf("Valid hook crap: %08X vs %08X\n", | 1251 | duprintf("Valid hook crap: %08X vs %08X\n", |
1192 | tmp.valid_hooks, t->valid_hooks); | 1252 | tmp.valid_hooks, t->valid_hooks); |
1193 | ret = -EINVAL; | 1253 | ret = -EINVAL; |
1194 | goto free_newinfo_counters_untrans_unlock; | 1254 | goto put_module; |
1195 | } | ||
1196 | |||
1197 | /* Get a reference in advance, we're not allowed fail later */ | ||
1198 | if (!try_module_get(t->me)) { | ||
1199 | ret = -EBUSY; | ||
1200 | goto free_newinfo_counters_untrans_unlock; | ||
1201 | } | 1255 | } |
1202 | 1256 | ||
1203 | oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); | 1257 | oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret); |
@@ -1219,7 +1273,6 @@ do_replace(void __user *user, unsigned int len) | |||
1219 | /* Decrease module usage counts and free resource */ | 1273 | /* Decrease module usage counts and free resource */ |
1220 | IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL); | 1274 | IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL); |
1221 | vfree(oldinfo); | 1275 | vfree(oldinfo); |
1222 | /* Silent error: too late now. */ | ||
1223 | if (copy_to_user(tmp.counters, counters, | 1276 | if (copy_to_user(tmp.counters, counters, |
1224 | sizeof(struct ip6t_counters) * tmp.num_counters) != 0) | 1277 | sizeof(struct ip6t_counters) * tmp.num_counters) != 0) |
1225 | ret = -EFAULT; | 1278 | ret = -EFAULT; |
@@ -1229,7 +1282,6 @@ do_replace(void __user *user, unsigned int len) | |||
1229 | 1282 | ||
1230 | put_module: | 1283 | put_module: |
1231 | module_put(t->me); | 1284 | module_put(t->me); |
1232 | free_newinfo_counters_untrans_unlock: | ||
1233 | up(&ip6t_mutex); | 1285 | up(&ip6t_mutex); |
1234 | free_newinfo_counters_untrans: | 1286 | free_newinfo_counters_untrans: |
1235 | IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL); | 1287 | IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL); |
@@ -1268,7 +1320,7 @@ do_add_counters(void __user *user, unsigned int len) | |||
1268 | unsigned int i; | 1320 | unsigned int i; |
1269 | struct ip6t_counters_info tmp, *paddc; | 1321 | struct ip6t_counters_info tmp, *paddc; |
1270 | struct ip6t_table *t; | 1322 | struct ip6t_table *t; |
1271 | int ret; | 1323 | int ret = 0; |
1272 | 1324 | ||
1273 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) | 1325 | if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) |
1274 | return -EFAULT; | 1326 | return -EFAULT; |
@@ -1285,9 +1337,11 @@ do_add_counters(void __user *user, unsigned int len) | |||
1285 | goto free; | 1337 | goto free; |
1286 | } | 1338 | } |
1287 | 1339 | ||
1288 | t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex); | 1340 | t = find_table_lock(tmp.name); |
1289 | if (!t) | 1341 | if (!t || IS_ERR(t)) { |
1342 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1290 | goto free; | 1343 | goto free; |
1344 | } | ||
1291 | 1345 | ||
1292 | write_lock_bh(&t->lock); | 1346 | write_lock_bh(&t->lock); |
1293 | if (t->private->number != paddc->num_counters) { | 1347 | if (t->private->number != paddc->num_counters) { |
@@ -1304,6 +1358,7 @@ do_add_counters(void __user *user, unsigned int len) | |||
1304 | unlock_up_free: | 1358 | unlock_up_free: |
1305 | write_unlock_bh(&t->lock); | 1359 | write_unlock_bh(&t->lock); |
1306 | up(&ip6t_mutex); | 1360 | up(&ip6t_mutex); |
1361 | module_put(t->me); | ||
1307 | free: | 1362 | free: |
1308 | vfree(paddc); | 1363 | vfree(paddc); |
1309 | 1364 | ||
@@ -1360,8 +1415,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
1360 | break; | 1415 | break; |
1361 | } | 1416 | } |
1362 | name[IP6T_TABLE_MAXNAMELEN-1] = '\0'; | 1417 | name[IP6T_TABLE_MAXNAMELEN-1] = '\0'; |
1363 | t = ip6t_find_table_lock(name, &ret, &ip6t_mutex); | 1418 | |
1364 | if (t) { | 1419 | t = try_then_request_module(find_table_lock(name), |
1420 | "ip6table_%s", name); | ||
1421 | if (t && !IS_ERR(t)) { | ||
1365 | struct ip6t_getinfo info; | 1422 | struct ip6t_getinfo info; |
1366 | 1423 | ||
1367 | info.valid_hooks = t->valid_hooks; | 1424 | info.valid_hooks = t->valid_hooks; |
@@ -1377,9 +1434,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
1377 | ret = -EFAULT; | 1434 | ret = -EFAULT; |
1378 | else | 1435 | else |
1379 | ret = 0; | 1436 | ret = 0; |
1380 | |||
1381 | up(&ip6t_mutex); | 1437 | up(&ip6t_mutex); |
1382 | } | 1438 | module_put(t->me); |
1439 | } else | ||
1440 | ret = t ? PTR_ERR(t) : -ENOENT; | ||
1383 | } | 1441 | } |
1384 | break; | 1442 | break; |
1385 | 1443 | ||
@@ -1400,6 +1458,31 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
1400 | break; | 1458 | break; |
1401 | } | 1459 | } |
1402 | 1460 | ||
1461 | case IP6T_SO_GET_REVISION_MATCH: | ||
1462 | case IP6T_SO_GET_REVISION_TARGET: { | ||
1463 | struct ip6t_get_revision rev; | ||
1464 | int (*revfn)(const char *, u8, int *); | ||
1465 | |||
1466 | if (*len != sizeof(rev)) { | ||
1467 | ret = -EINVAL; | ||
1468 | break; | ||
1469 | } | ||
1470 | if (copy_from_user(&rev, user, sizeof(rev)) != 0) { | ||
1471 | ret = -EFAULT; | ||
1472 | break; | ||
1473 | } | ||
1474 | |||
1475 | if (cmd == IP6T_SO_GET_REVISION_TARGET) | ||
1476 | revfn = target_revfn; | ||
1477 | else | ||
1478 | revfn = match_revfn; | ||
1479 | |||
1480 | try_then_request_module(find_revision(rev.name, rev.revision, | ||
1481 | revfn, &ret), | ||
1482 | "ip6t_%s", rev.name); | ||
1483 | break; | ||
1484 | } | ||
1485 | |||
1403 | default: | 1486 | default: |
1404 | duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd); | 1487 | duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd); |
1405 | ret = -EINVAL; | 1488 | ret = -EINVAL; |
@@ -1417,12 +1500,7 @@ ip6t_register_target(struct ip6t_target *target) | |||
1417 | ret = down_interruptible(&ip6t_mutex); | 1500 | ret = down_interruptible(&ip6t_mutex); |
1418 | if (ret != 0) | 1501 | if (ret != 0) |
1419 | return ret; | 1502 | return ret; |
1420 | 1503 | list_add(&target->list, &ip6t_target); | |
1421 | if (!list_named_insert(&ip6t_target, target)) { | ||
1422 | duprintf("ip6t_register_target: `%s' already in list!\n", | ||
1423 | target->name); | ||
1424 | ret = -EINVAL; | ||
1425 | } | ||
1426 | up(&ip6t_mutex); | 1504 | up(&ip6t_mutex); |
1427 | return ret; | 1505 | return ret; |
1428 | } | 1506 | } |
@@ -1444,11 +1522,7 @@ ip6t_register_match(struct ip6t_match *match) | |||
1444 | if (ret != 0) | 1522 | if (ret != 0) |
1445 | return ret; | 1523 | return ret; |
1446 | 1524 | ||
1447 | if (!list_named_insert(&ip6t_match, match)) { | 1525 | list_add(&match->list, &ip6t_match); |
1448 | duprintf("ip6t_register_match: `%s' already in list!\n", | ||
1449 | match->name); | ||
1450 | ret = -EINVAL; | ||
1451 | } | ||
1452 | up(&ip6t_mutex); | 1526 | up(&ip6t_mutex); |
1453 | 1527 | ||
1454 | return ret; | 1528 | return ret; |
diff --git a/net/ipv6/netfilter/ip6t_MARK.c b/net/ipv6/netfilter/ip6t_MARK.c index 81924fcc5857..0c7584f92172 100644 --- a/net/ipv6/netfilter/ip6t_MARK.c +++ b/net/ipv6/netfilter/ip6t_MARK.c | |||
@@ -56,8 +56,12 @@ checkentry(const char *tablename, | |||
56 | return 1; | 56 | return 1; |
57 | } | 57 | } |
58 | 58 | ||
59 | static struct ip6t_target ip6t_mark_reg | 59 | static struct ip6t_target ip6t_mark_reg = { |
60 | = { { NULL, NULL }, "MARK", target, checkentry, NULL, THIS_MODULE }; | 60 | .name = "MARK", |
61 | .target = target, | ||
62 | .checkentry = checkentry, | ||
63 | .me = THIS_MODULE | ||
64 | }; | ||
61 | 65 | ||
62 | static int __init init(void) | 66 | static int __init init(void) |
63 | { | 67 | { |