diff options
Diffstat (limited to 'net/netfilter/x_tables.c')
-rw-r--r-- | net/netfilter/x_tables.c | 124 |
1 files changed, 81 insertions, 43 deletions
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index c9423763107..a9adf4c6b29 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/mutex.h> | 23 | #include <linux/mutex.h> |
24 | #include <linux/mm.h> | 24 | #include <linux/mm.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/audit.h> | ||
26 | #include <net/net_namespace.h> | 27 | #include <net/net_namespace.h> |
27 | 28 | ||
28 | #include <linux/netfilter/x_tables.h> | 29 | #include <linux/netfilter/x_tables.h> |
@@ -38,9 +39,8 @@ MODULE_DESCRIPTION("{ip,ip6,arp,eb}_tables backend module"); | |||
38 | #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) | 39 | #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1)) |
39 | 40 | ||
40 | struct compat_delta { | 41 | struct compat_delta { |
41 | struct compat_delta *next; | 42 | unsigned int offset; /* offset in kernel */ |
42 | unsigned int offset; | 43 | int delta; /* delta in 32bit user land */ |
43 | int delta; | ||
44 | }; | 44 | }; |
45 | 45 | ||
46 | struct xt_af { | 46 | struct xt_af { |
@@ -49,7 +49,9 @@ struct xt_af { | |||
49 | struct list_head target; | 49 | struct list_head target; |
50 | #ifdef CONFIG_COMPAT | 50 | #ifdef CONFIG_COMPAT |
51 | struct mutex compat_mutex; | 51 | struct mutex compat_mutex; |
52 | struct compat_delta *compat_offsets; | 52 | struct compat_delta *compat_tab; |
53 | unsigned int number; /* number of slots in compat_tab[] */ | ||
54 | unsigned int cur; /* number of used slots in compat_tab[] */ | ||
53 | #endif | 55 | #endif |
54 | }; | 56 | }; |
55 | 57 | ||
@@ -181,14 +183,14 @@ EXPORT_SYMBOL(xt_unregister_matches); | |||
181 | /* | 183 | /* |
182 | * These are weird, but module loading must not be done with mutex | 184 | * These are weird, but module loading must not be done with mutex |
183 | * held (since they will register), and we have to have a single | 185 | * held (since they will register), and we have to have a single |
184 | * function to use try_then_request_module(). | 186 | * function to use. |
185 | */ | 187 | */ |
186 | 188 | ||
187 | /* Find match, grabs ref. Returns ERR_PTR() on error. */ | 189 | /* Find match, grabs ref. Returns ERR_PTR() on error. */ |
188 | struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) | 190 | struct xt_match *xt_find_match(u8 af, const char *name, u8 revision) |
189 | { | 191 | { |
190 | struct xt_match *m; | 192 | struct xt_match *m; |
191 | int err = 0; | 193 | int err = -ENOENT; |
192 | 194 | ||
193 | if (mutex_lock_interruptible(&xt[af].mutex) != 0) | 195 | if (mutex_lock_interruptible(&xt[af].mutex) != 0) |
194 | return ERR_PTR(-EINTR); | 196 | return ERR_PTR(-EINTR); |
@@ -219,9 +221,13 @@ xt_request_find_match(uint8_t nfproto, const char *name, uint8_t revision) | |||
219 | { | 221 | { |
220 | struct xt_match *match; | 222 | struct xt_match *match; |
221 | 223 | ||
222 | match = try_then_request_module(xt_find_match(nfproto, name, revision), | 224 | match = xt_find_match(nfproto, name, revision); |
223 | "%st_%s", xt_prefix[nfproto], name); | 225 | if (IS_ERR(match)) { |
224 | return (match != NULL) ? match : ERR_PTR(-ENOENT); | 226 | request_module("%st_%s", xt_prefix[nfproto], name); |
227 | match = xt_find_match(nfproto, name, revision); | ||
228 | } | ||
229 | |||
230 | return match; | ||
225 | } | 231 | } |
226 | EXPORT_SYMBOL_GPL(xt_request_find_match); | 232 | EXPORT_SYMBOL_GPL(xt_request_find_match); |
227 | 233 | ||
@@ -229,7 +235,7 @@ EXPORT_SYMBOL_GPL(xt_request_find_match); | |||
229 | struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) | 235 | struct xt_target *xt_find_target(u8 af, const char *name, u8 revision) |
230 | { | 236 | { |
231 | struct xt_target *t; | 237 | struct xt_target *t; |
232 | int err = 0; | 238 | int err = -ENOENT; |
233 | 239 | ||
234 | if (mutex_lock_interruptible(&xt[af].mutex) != 0) | 240 | if (mutex_lock_interruptible(&xt[af].mutex) != 0) |
235 | return ERR_PTR(-EINTR); | 241 | return ERR_PTR(-EINTR); |
@@ -259,9 +265,13 @@ struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision) | |||
259 | { | 265 | { |
260 | struct xt_target *target; | 266 | struct xt_target *target; |
261 | 267 | ||
262 | target = try_then_request_module(xt_find_target(af, name, revision), | 268 | target = xt_find_target(af, name, revision); |
263 | "%st_%s", xt_prefix[af], name); | 269 | if (IS_ERR(target)) { |
264 | return (target != NULL) ? target : ERR_PTR(-ENOENT); | 270 | request_module("%st_%s", xt_prefix[af], name); |
271 | target = xt_find_target(af, name, revision); | ||
272 | } | ||
273 | |||
274 | return target; | ||
265 | } | 275 | } |
266 | EXPORT_SYMBOL_GPL(xt_request_find_target); | 276 | EXPORT_SYMBOL_GPL(xt_request_find_target); |
267 | 277 | ||
@@ -414,54 +424,67 @@ int xt_check_match(struct xt_mtchk_param *par, | |||
414 | EXPORT_SYMBOL_GPL(xt_check_match); | 424 | EXPORT_SYMBOL_GPL(xt_check_match); |
415 | 425 | ||
416 | #ifdef CONFIG_COMPAT | 426 | #ifdef CONFIG_COMPAT |
417 | int xt_compat_add_offset(u_int8_t af, unsigned int offset, short delta) | 427 | int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta) |
418 | { | 428 | { |
419 | struct compat_delta *tmp; | 429 | struct xt_af *xp = &xt[af]; |
420 | 430 | ||
421 | tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL); | 431 | if (!xp->compat_tab) { |
422 | if (!tmp) | 432 | if (!xp->number) |
423 | return -ENOMEM; | 433 | return -EINVAL; |
434 | xp->compat_tab = vmalloc(sizeof(struct compat_delta) * xp->number); | ||
435 | if (!xp->compat_tab) | ||
436 | return -ENOMEM; | ||
437 | xp->cur = 0; | ||
438 | } | ||
424 | 439 | ||
425 | tmp->offset = offset; | 440 | if (xp->cur >= xp->number) |
426 | tmp->delta = delta; | 441 | return -EINVAL; |
427 | 442 | ||
428 | if (xt[af].compat_offsets) { | 443 | if (xp->cur) |
429 | tmp->next = xt[af].compat_offsets->next; | 444 | delta += xp->compat_tab[xp->cur - 1].delta; |
430 | xt[af].compat_offsets->next = tmp; | 445 | xp->compat_tab[xp->cur].offset = offset; |
431 | } else { | 446 | xp->compat_tab[xp->cur].delta = delta; |
432 | xt[af].compat_offsets = tmp; | 447 | xp->cur++; |
433 | tmp->next = NULL; | ||
434 | } | ||
435 | return 0; | 448 | return 0; |
436 | } | 449 | } |
437 | EXPORT_SYMBOL_GPL(xt_compat_add_offset); | 450 | EXPORT_SYMBOL_GPL(xt_compat_add_offset); |
438 | 451 | ||
439 | void xt_compat_flush_offsets(u_int8_t af) | 452 | void xt_compat_flush_offsets(u_int8_t af) |
440 | { | 453 | { |
441 | struct compat_delta *tmp, *next; | 454 | if (xt[af].compat_tab) { |
442 | 455 | vfree(xt[af].compat_tab); | |
443 | if (xt[af].compat_offsets) { | 456 | xt[af].compat_tab = NULL; |
444 | for (tmp = xt[af].compat_offsets; tmp; tmp = next) { | 457 | xt[af].number = 0; |
445 | next = tmp->next; | ||
446 | kfree(tmp); | ||
447 | } | ||
448 | xt[af].compat_offsets = NULL; | ||
449 | } | 458 | } |
450 | } | 459 | } |
451 | EXPORT_SYMBOL_GPL(xt_compat_flush_offsets); | 460 | EXPORT_SYMBOL_GPL(xt_compat_flush_offsets); |
452 | 461 | ||
453 | int xt_compat_calc_jump(u_int8_t af, unsigned int offset) | 462 | int xt_compat_calc_jump(u_int8_t af, unsigned int offset) |
454 | { | 463 | { |
455 | struct compat_delta *tmp; | 464 | struct compat_delta *tmp = xt[af].compat_tab; |
456 | int delta; | 465 | int mid, left = 0, right = xt[af].cur - 1; |
457 | 466 | ||
458 | for (tmp = xt[af].compat_offsets, delta = 0; tmp; tmp = tmp->next) | 467 | while (left <= right) { |
459 | if (tmp->offset < offset) | 468 | mid = (left + right) >> 1; |
460 | delta += tmp->delta; | 469 | if (offset > tmp[mid].offset) |
461 | return delta; | 470 | left = mid + 1; |
471 | else if (offset < tmp[mid].offset) | ||
472 | right = mid - 1; | ||
473 | else | ||
474 | return mid ? tmp[mid - 1].delta : 0; | ||
475 | } | ||
476 | WARN_ON_ONCE(1); | ||
477 | return 0; | ||
462 | } | 478 | } |
463 | EXPORT_SYMBOL_GPL(xt_compat_calc_jump); | 479 | EXPORT_SYMBOL_GPL(xt_compat_calc_jump); |
464 | 480 | ||
481 | void xt_compat_init_offsets(u_int8_t af, unsigned int number) | ||
482 | { | ||
483 | xt[af].number = number; | ||
484 | xt[af].cur = 0; | ||
485 | } | ||
486 | EXPORT_SYMBOL(xt_compat_init_offsets); | ||
487 | |||
465 | int xt_compat_match_offset(const struct xt_match *match) | 488 | int xt_compat_match_offset(const struct xt_match *match) |
466 | { | 489 | { |
467 | u_int16_t csize = match->compatsize ? : match->matchsize; | 490 | u_int16_t csize = match->compatsize ? : match->matchsize; |
@@ -820,6 +843,21 @@ xt_replace_table(struct xt_table *table, | |||
820 | */ | 843 | */ |
821 | local_bh_enable(); | 844 | local_bh_enable(); |
822 | 845 | ||
846 | #ifdef CONFIG_AUDIT | ||
847 | if (audit_enabled) { | ||
848 | struct audit_buffer *ab; | ||
849 | |||
850 | ab = audit_log_start(current->audit_context, GFP_KERNEL, | ||
851 | AUDIT_NETFILTER_CFG); | ||
852 | if (ab) { | ||
853 | audit_log_format(ab, "table=%s family=%u entries=%u", | ||
854 | table->name, table->af, | ||
855 | private->number); | ||
856 | audit_log_end(ab); | ||
857 | } | ||
858 | } | ||
859 | #endif | ||
860 | |||
823 | return private; | 861 | return private; |
824 | } | 862 | } |
825 | EXPORT_SYMBOL_GPL(xt_replace_table); | 863 | EXPORT_SYMBOL_GPL(xt_replace_table); |
@@ -1338,7 +1376,7 @@ static int __init xt_init(void) | |||
1338 | mutex_init(&xt[i].mutex); | 1376 | mutex_init(&xt[i].mutex); |
1339 | #ifdef CONFIG_COMPAT | 1377 | #ifdef CONFIG_COMPAT |
1340 | mutex_init(&xt[i].compat_mutex); | 1378 | mutex_init(&xt[i].compat_mutex); |
1341 | xt[i].compat_offsets = NULL; | 1379 | xt[i].compat_tab = NULL; |
1342 | #endif | 1380 | #endif |
1343 | INIT_LIST_HEAD(&xt[i].target); | 1381 | INIT_LIST_HEAD(&xt[i].target); |
1344 | INIT_LIST_HEAD(&xt[i].match); | 1382 | INIT_LIST_HEAD(&xt[i].match); |