diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2012-11-27 04:58:09 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-12-03 08:36:08 -0500 |
commit | 9076aea76538556224e7d73ab718f8841330818a (patch) | |
tree | 50d4a6413f9c5dd7d65e9da485179af2e9b824de /net/netfilter | |
parent | 60e476d02129acb1f863a9b4932358678ee6a355 (diff) |
netfilter: ipset: Increase the number of maximal sets automatically
The max number of sets was hardcoded at kernel cofiguration time and
could only be modified via a module parameter. The patch adds the support
of increasing the max number of sets automatically, as needed.
The array of sets is incremented by 64 new slots if we run out of
empty slots. The absolute limit for the maximal number of sets
is limited by 65534.
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/ipset/ip_set_core.c | 243 |
1 files changed, 160 insertions, 83 deletions
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index fed899f600b2..6d6d8f2b033e 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c | |||
@@ -28,9 +28,10 @@ static LIST_HEAD(ip_set_type_list); /* all registered set types */ | |||
28 | static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ | 28 | static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ |
29 | static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ | 29 | static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ |
30 | 30 | ||
31 | static struct ip_set **ip_set_list; /* all individual sets */ | 31 | static struct ip_set * __rcu *ip_set_list; /* all individual sets */ |
32 | static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ | 32 | static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ |
33 | 33 | ||
34 | #define IP_SET_INC 64 | ||
34 | #define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) | 35 | #define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) |
35 | 36 | ||
36 | static unsigned int max_sets; | 37 | static unsigned int max_sets; |
@@ -42,6 +43,12 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |||
42 | MODULE_DESCRIPTION("core IP set support"); | 43 | MODULE_DESCRIPTION("core IP set support"); |
43 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); | 44 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); |
44 | 45 | ||
46 | /* When the nfnl mutex is held: */ | ||
47 | #define nfnl_dereference(p) \ | ||
48 | rcu_dereference_protected(p, 1) | ||
49 | #define nfnl_set(id) \ | ||
50 | nfnl_dereference(ip_set_list)[id] | ||
51 | |||
45 | /* | 52 | /* |
46 | * The set types are implemented in modules and registered set types | 53 | * The set types are implemented in modules and registered set types |
47 | * can be found in ip_set_type_list. Adding/deleting types is | 54 | * can be found in ip_set_type_list. Adding/deleting types is |
@@ -321,19 +328,19 @@ EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); | |||
321 | */ | 328 | */ |
322 | 329 | ||
323 | static inline void | 330 | static inline void |
324 | __ip_set_get(ip_set_id_t index) | 331 | __ip_set_get(struct ip_set *set) |
325 | { | 332 | { |
326 | write_lock_bh(&ip_set_ref_lock); | 333 | write_lock_bh(&ip_set_ref_lock); |
327 | ip_set_list[index]->ref++; | 334 | set->ref++; |
328 | write_unlock_bh(&ip_set_ref_lock); | 335 | write_unlock_bh(&ip_set_ref_lock); |
329 | } | 336 | } |
330 | 337 | ||
331 | static inline void | 338 | static inline void |
332 | __ip_set_put(ip_set_id_t index) | 339 | __ip_set_put(struct ip_set *set) |
333 | { | 340 | { |
334 | write_lock_bh(&ip_set_ref_lock); | 341 | write_lock_bh(&ip_set_ref_lock); |
335 | BUG_ON(ip_set_list[index]->ref == 0); | 342 | BUG_ON(set->ref == 0); |
336 | ip_set_list[index]->ref--; | 343 | set->ref--; |
337 | write_unlock_bh(&ip_set_ref_lock); | 344 | write_unlock_bh(&ip_set_ref_lock); |
338 | } | 345 | } |
339 | 346 | ||
@@ -344,12 +351,25 @@ __ip_set_put(ip_set_id_t index) | |||
344 | * so it can't be destroyed (or changed) under our foot. | 351 | * so it can't be destroyed (or changed) under our foot. |
345 | */ | 352 | */ |
346 | 353 | ||
354 | static inline struct ip_set * | ||
355 | ip_set_rcu_get(ip_set_id_t index) | ||
356 | { | ||
357 | struct ip_set *set; | ||
358 | |||
359 | rcu_read_lock(); | ||
360 | /* ip_set_list itself needs to be protected */ | ||
361 | set = rcu_dereference(ip_set_list)[index]; | ||
362 | rcu_read_unlock(); | ||
363 | |||
364 | return set; | ||
365 | } | ||
366 | |||
347 | int | 367 | int |
348 | ip_set_test(ip_set_id_t index, const struct sk_buff *skb, | 368 | ip_set_test(ip_set_id_t index, const struct sk_buff *skb, |
349 | const struct xt_action_param *par, | 369 | const struct xt_action_param *par, |
350 | const struct ip_set_adt_opt *opt) | 370 | const struct ip_set_adt_opt *opt) |
351 | { | 371 | { |
352 | struct ip_set *set = ip_set_list[index]; | 372 | struct ip_set *set = ip_set_rcu_get(index); |
353 | int ret = 0; | 373 | int ret = 0; |
354 | 374 | ||
355 | BUG_ON(set == NULL); | 375 | BUG_ON(set == NULL); |
@@ -388,7 +408,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb, | |||
388 | const struct xt_action_param *par, | 408 | const struct xt_action_param *par, |
389 | const struct ip_set_adt_opt *opt) | 409 | const struct ip_set_adt_opt *opt) |
390 | { | 410 | { |
391 | struct ip_set *set = ip_set_list[index]; | 411 | struct ip_set *set = ip_set_rcu_get(index); |
392 | int ret; | 412 | int ret; |
393 | 413 | ||
394 | BUG_ON(set == NULL); | 414 | BUG_ON(set == NULL); |
@@ -411,7 +431,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb, | |||
411 | const struct xt_action_param *par, | 431 | const struct xt_action_param *par, |
412 | const struct ip_set_adt_opt *opt) | 432 | const struct ip_set_adt_opt *opt) |
413 | { | 433 | { |
414 | struct ip_set *set = ip_set_list[index]; | 434 | struct ip_set *set = ip_set_rcu_get(index); |
415 | int ret = 0; | 435 | int ret = 0; |
416 | 436 | ||
417 | BUG_ON(set == NULL); | 437 | BUG_ON(set == NULL); |
@@ -440,14 +460,17 @@ ip_set_get_byname(const char *name, struct ip_set **set) | |||
440 | ip_set_id_t i, index = IPSET_INVALID_ID; | 460 | ip_set_id_t i, index = IPSET_INVALID_ID; |
441 | struct ip_set *s; | 461 | struct ip_set *s; |
442 | 462 | ||
463 | rcu_read_lock(); | ||
443 | for (i = 0; i < ip_set_max; i++) { | 464 | for (i = 0; i < ip_set_max; i++) { |
444 | s = ip_set_list[i]; | 465 | s = rcu_dereference(ip_set_list)[i]; |
445 | if (s != NULL && STREQ(s->name, name)) { | 466 | if (s != NULL && STREQ(s->name, name)) { |
446 | __ip_set_get(i); | 467 | __ip_set_get(s); |
447 | index = i; | 468 | index = i; |
448 | *set = s; | 469 | *set = s; |
470 | break; | ||
449 | } | 471 | } |
450 | } | 472 | } |
473 | rcu_read_unlock(); | ||
451 | 474 | ||
452 | return index; | 475 | return index; |
453 | } | 476 | } |
@@ -462,8 +485,13 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname); | |||
462 | void | 485 | void |
463 | ip_set_put_byindex(ip_set_id_t index) | 486 | ip_set_put_byindex(ip_set_id_t index) |
464 | { | 487 | { |
465 | if (ip_set_list[index] != NULL) | 488 | struct ip_set *set; |
466 | __ip_set_put(index); | 489 | |
490 | rcu_read_lock(); | ||
491 | set = rcu_dereference(ip_set_list)[index]; | ||
492 | if (set != NULL) | ||
493 | __ip_set_put(set); | ||
494 | rcu_read_unlock(); | ||
467 | } | 495 | } |
468 | EXPORT_SYMBOL_GPL(ip_set_put_byindex); | 496 | EXPORT_SYMBOL_GPL(ip_set_put_byindex); |
469 | 497 | ||
@@ -477,7 +505,7 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex); | |||
477 | const char * | 505 | const char * |
478 | ip_set_name_byindex(ip_set_id_t index) | 506 | ip_set_name_byindex(ip_set_id_t index) |
479 | { | 507 | { |
480 | const struct ip_set *set = ip_set_list[index]; | 508 | const struct ip_set *set = ip_set_rcu_get(index); |
481 | 509 | ||
482 | BUG_ON(set == NULL); | 510 | BUG_ON(set == NULL); |
483 | BUG_ON(set->ref == 0); | 511 | BUG_ON(set->ref == 0); |
@@ -501,11 +529,18 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex); | |||
501 | ip_set_id_t | 529 | ip_set_id_t |
502 | ip_set_nfnl_get(const char *name) | 530 | ip_set_nfnl_get(const char *name) |
503 | { | 531 | { |
532 | ip_set_id_t i, index = IPSET_INVALID_ID; | ||
504 | struct ip_set *s; | 533 | struct ip_set *s; |
505 | ip_set_id_t index; | ||
506 | 534 | ||
507 | nfnl_lock(); | 535 | nfnl_lock(); |
508 | index = ip_set_get_byname(name, &s); | 536 | for (i = 0; i < ip_set_max; i++) { |
537 | s = nfnl_set(i); | ||
538 | if (s != NULL && STREQ(s->name, name)) { | ||
539 | __ip_set_get(s); | ||
540 | index = i; | ||
541 | break; | ||
542 | } | ||
543 | } | ||
509 | nfnl_unlock(); | 544 | nfnl_unlock(); |
510 | 545 | ||
511 | return index; | 546 | return index; |
@@ -521,12 +556,15 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get); | |||
521 | ip_set_id_t | 556 | ip_set_id_t |
522 | ip_set_nfnl_get_byindex(ip_set_id_t index) | 557 | ip_set_nfnl_get_byindex(ip_set_id_t index) |
523 | { | 558 | { |
559 | struct ip_set *set; | ||
560 | |||
524 | if (index > ip_set_max) | 561 | if (index > ip_set_max) |
525 | return IPSET_INVALID_ID; | 562 | return IPSET_INVALID_ID; |
526 | 563 | ||
527 | nfnl_lock(); | 564 | nfnl_lock(); |
528 | if (ip_set_list[index]) | 565 | set = nfnl_set(index); |
529 | __ip_set_get(index); | 566 | if (set) |
567 | __ip_set_get(set); | ||
530 | else | 568 | else |
531 | index = IPSET_INVALID_ID; | 569 | index = IPSET_INVALID_ID; |
532 | nfnl_unlock(); | 570 | nfnl_unlock(); |
@@ -545,8 +583,11 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex); | |||
545 | void | 583 | void |
546 | ip_set_nfnl_put(ip_set_id_t index) | 584 | ip_set_nfnl_put(ip_set_id_t index) |
547 | { | 585 | { |
586 | struct ip_set *set; | ||
548 | nfnl_lock(); | 587 | nfnl_lock(); |
549 | ip_set_put_byindex(index); | 588 | set = nfnl_set(index); |
589 | if (set != NULL) | ||
590 | __ip_set_put(set); | ||
550 | nfnl_unlock(); | 591 | nfnl_unlock(); |
551 | } | 592 | } |
552 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); | 593 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); |
@@ -603,41 +644,46 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = { | |||
603 | [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, | 644 | [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, |
604 | }; | 645 | }; |
605 | 646 | ||
606 | static ip_set_id_t | 647 | static struct ip_set * |
607 | find_set_id(const char *name) | 648 | find_set_and_id(const char *name, ip_set_id_t *id) |
608 | { | 649 | { |
609 | ip_set_id_t i, index = IPSET_INVALID_ID; | 650 | struct ip_set *set = NULL; |
610 | const struct ip_set *set; | 651 | ip_set_id_t i; |
611 | 652 | ||
612 | for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) { | 653 | *id = IPSET_INVALID_ID; |
613 | set = ip_set_list[i]; | 654 | for (i = 0; i < ip_set_max; i++) { |
614 | if (set != NULL && STREQ(set->name, name)) | 655 | set = nfnl_set(i); |
615 | index = i; | 656 | if (set != NULL && STREQ(set->name, name)) { |
657 | *id = i; | ||
658 | break; | ||
659 | } | ||
616 | } | 660 | } |
617 | return index; | 661 | return (*id == IPSET_INVALID_ID ? NULL : set); |
618 | } | 662 | } |
619 | 663 | ||
620 | static inline struct ip_set * | 664 | static inline struct ip_set * |
621 | find_set(const char *name) | 665 | find_set(const char *name) |
622 | { | 666 | { |
623 | ip_set_id_t index = find_set_id(name); | 667 | ip_set_id_t id; |
624 | 668 | ||
625 | return index == IPSET_INVALID_ID ? NULL : ip_set_list[index]; | 669 | return find_set_and_id(name, &id); |
626 | } | 670 | } |
627 | 671 | ||
628 | static int | 672 | static int |
629 | find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) | 673 | find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) |
630 | { | 674 | { |
675 | struct ip_set *s; | ||
631 | ip_set_id_t i; | 676 | ip_set_id_t i; |
632 | 677 | ||
633 | *index = IPSET_INVALID_ID; | 678 | *index = IPSET_INVALID_ID; |
634 | for (i = 0; i < ip_set_max; i++) { | 679 | for (i = 0; i < ip_set_max; i++) { |
635 | if (ip_set_list[i] == NULL) { | 680 | s = nfnl_set(i); |
681 | if (s == NULL) { | ||
636 | if (*index == IPSET_INVALID_ID) | 682 | if (*index == IPSET_INVALID_ID) |
637 | *index = i; | 683 | *index = i; |
638 | } else if (STREQ(name, ip_set_list[i]->name)) { | 684 | } else if (STREQ(name, s->name)) { |
639 | /* Name clash */ | 685 | /* Name clash */ |
640 | *set = ip_set_list[i]; | 686 | *set = s; |
641 | return -EEXIST; | 687 | return -EEXIST; |
642 | } | 688 | } |
643 | } | 689 | } |
@@ -730,10 +776,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, | |||
730 | * and check clashing. | 776 | * and check clashing. |
731 | */ | 777 | */ |
732 | ret = find_free_id(set->name, &index, &clash); | 778 | ret = find_free_id(set->name, &index, &clash); |
733 | if (ret != 0) { | 779 | if (ret == -EEXIST) { |
734 | /* If this is the same set and requested, ignore error */ | 780 | /* If this is the same set and requested, ignore error */ |
735 | if (ret == -EEXIST && | 781 | if ((flags & IPSET_FLAG_EXIST) && |
736 | (flags & IPSET_FLAG_EXIST) && | ||
737 | STREQ(set->type->name, clash->type->name) && | 782 | STREQ(set->type->name, clash->type->name) && |
738 | set->type->family == clash->type->family && | 783 | set->type->family == clash->type->family && |
739 | set->type->revision_min == clash->type->revision_min && | 784 | set->type->revision_min == clash->type->revision_min && |
@@ -741,13 +786,36 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, | |||
741 | set->variant->same_set(set, clash)) | 786 | set->variant->same_set(set, clash)) |
742 | ret = 0; | 787 | ret = 0; |
743 | goto cleanup; | 788 | goto cleanup; |
744 | } | 789 | } else if (ret == -IPSET_ERR_MAX_SETS) { |
790 | struct ip_set **list, **tmp; | ||
791 | ip_set_id_t i = ip_set_max + IP_SET_INC; | ||
792 | |||
793 | if (i < ip_set_max || i == IPSET_INVALID_ID) | ||
794 | /* Wraparound */ | ||
795 | goto cleanup; | ||
796 | |||
797 | list = kzalloc(sizeof(struct ip_set *) * i, GFP_KERNEL); | ||
798 | if (!list) | ||
799 | goto cleanup; | ||
800 | /* nfnl mutex is held, both lists are valid */ | ||
801 | tmp = nfnl_dereference(ip_set_list); | ||
802 | memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max); | ||
803 | rcu_assign_pointer(ip_set_list, list); | ||
804 | /* Make sure all current packets have passed through */ | ||
805 | synchronize_net(); | ||
806 | /* Use new list */ | ||
807 | index = ip_set_max; | ||
808 | ip_set_max = i; | ||
809 | kfree(tmp); | ||
810 | ret = 0; | ||
811 | } else if (ret) | ||
812 | goto cleanup; | ||
745 | 813 | ||
746 | /* | 814 | /* |
747 | * Finally! Add our shiny new set to the list, and be done. | 815 | * Finally! Add our shiny new set to the list, and be done. |
748 | */ | 816 | */ |
749 | pr_debug("create: '%s' created with index %u!\n", set->name, index); | 817 | pr_debug("create: '%s' created with index %u!\n", set->name, index); |
750 | ip_set_list[index] = set; | 818 | nfnl_set(index) = set; |
751 | 819 | ||
752 | return ret; | 820 | return ret; |
753 | 821 | ||
@@ -772,10 +840,10 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { | |||
772 | static void | 840 | static void |
773 | ip_set_destroy_set(ip_set_id_t index) | 841 | ip_set_destroy_set(ip_set_id_t index) |
774 | { | 842 | { |
775 | struct ip_set *set = ip_set_list[index]; | 843 | struct ip_set *set = nfnl_set(index); |
776 | 844 | ||
777 | pr_debug("set: %s\n", set->name); | 845 | pr_debug("set: %s\n", set->name); |
778 | ip_set_list[index] = NULL; | 846 | nfnl_set(index) = NULL; |
779 | 847 | ||
780 | /* Must call it without holding any lock */ | 848 | /* Must call it without holding any lock */ |
781 | set->variant->destroy(set); | 849 | set->variant->destroy(set); |
@@ -788,6 +856,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, | |||
788 | const struct nlmsghdr *nlh, | 856 | const struct nlmsghdr *nlh, |
789 | const struct nlattr * const attr[]) | 857 | const struct nlattr * const attr[]) |
790 | { | 858 | { |
859 | struct ip_set *s; | ||
791 | ip_set_id_t i; | 860 | ip_set_id_t i; |
792 | int ret = 0; | 861 | int ret = 0; |
793 | 862 | ||
@@ -807,22 +876,24 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, | |||
807 | read_lock_bh(&ip_set_ref_lock); | 876 | read_lock_bh(&ip_set_ref_lock); |
808 | if (!attr[IPSET_ATTR_SETNAME]) { | 877 | if (!attr[IPSET_ATTR_SETNAME]) { |
809 | for (i = 0; i < ip_set_max; i++) { | 878 | for (i = 0; i < ip_set_max; i++) { |
810 | if (ip_set_list[i] != NULL && ip_set_list[i]->ref) { | 879 | s = nfnl_set(i); |
880 | if (s != NULL && s->ref) { | ||
811 | ret = -IPSET_ERR_BUSY; | 881 | ret = -IPSET_ERR_BUSY; |
812 | goto out; | 882 | goto out; |
813 | } | 883 | } |
814 | } | 884 | } |
815 | read_unlock_bh(&ip_set_ref_lock); | 885 | read_unlock_bh(&ip_set_ref_lock); |
816 | for (i = 0; i < ip_set_max; i++) { | 886 | for (i = 0; i < ip_set_max; i++) { |
817 | if (ip_set_list[i] != NULL) | 887 | s = nfnl_set(i); |
888 | if (s != NULL) | ||
818 | ip_set_destroy_set(i); | 889 | ip_set_destroy_set(i); |
819 | } | 890 | } |
820 | } else { | 891 | } else { |
821 | i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | 892 | s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i); |
822 | if (i == IPSET_INVALID_ID) { | 893 | if (s == NULL) { |
823 | ret = -ENOENT; | 894 | ret = -ENOENT; |
824 | goto out; | 895 | goto out; |
825 | } else if (ip_set_list[i]->ref) { | 896 | } else if (s->ref) { |
826 | ret = -IPSET_ERR_BUSY; | 897 | ret = -IPSET_ERR_BUSY; |
827 | goto out; | 898 | goto out; |
828 | } | 899 | } |
@@ -853,21 +924,24 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, | |||
853 | const struct nlmsghdr *nlh, | 924 | const struct nlmsghdr *nlh, |
854 | const struct nlattr * const attr[]) | 925 | const struct nlattr * const attr[]) |
855 | { | 926 | { |
927 | struct ip_set *s; | ||
856 | ip_set_id_t i; | 928 | ip_set_id_t i; |
857 | 929 | ||
858 | if (unlikely(protocol_failed(attr))) | 930 | if (unlikely(protocol_failed(attr))) |
859 | return -IPSET_ERR_PROTOCOL; | 931 | return -IPSET_ERR_PROTOCOL; |
860 | 932 | ||
861 | if (!attr[IPSET_ATTR_SETNAME]) { | 933 | if (!attr[IPSET_ATTR_SETNAME]) { |
862 | for (i = 0; i < ip_set_max; i++) | 934 | for (i = 0; i < ip_set_max; i++) { |
863 | if (ip_set_list[i] != NULL) | 935 | s = nfnl_set(i); |
864 | ip_set_flush_set(ip_set_list[i]); | 936 | if (s != NULL) |
937 | ip_set_flush_set(s); | ||
938 | } | ||
865 | } else { | 939 | } else { |
866 | i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | 940 | s = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); |
867 | if (i == IPSET_INVALID_ID) | 941 | if (s == NULL) |
868 | return -ENOENT; | 942 | return -ENOENT; |
869 | 943 | ||
870 | ip_set_flush_set(ip_set_list[i]); | 944 | ip_set_flush_set(s); |
871 | } | 945 | } |
872 | 946 | ||
873 | return 0; | 947 | return 0; |
@@ -889,7 +963,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, | |||
889 | const struct nlmsghdr *nlh, | 963 | const struct nlmsghdr *nlh, |
890 | const struct nlattr * const attr[]) | 964 | const struct nlattr * const attr[]) |
891 | { | 965 | { |
892 | struct ip_set *set; | 966 | struct ip_set *set, *s; |
893 | const char *name2; | 967 | const char *name2; |
894 | ip_set_id_t i; | 968 | ip_set_id_t i; |
895 | int ret = 0; | 969 | int ret = 0; |
@@ -911,8 +985,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, | |||
911 | 985 | ||
912 | name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); | 986 | name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); |
913 | for (i = 0; i < ip_set_max; i++) { | 987 | for (i = 0; i < ip_set_max; i++) { |
914 | if (ip_set_list[i] != NULL && | 988 | s = nfnl_set(i); |
915 | STREQ(ip_set_list[i]->name, name2)) { | 989 | if (s != NULL && STREQ(s->name, name2)) { |
916 | ret = -IPSET_ERR_EXIST_SETNAME2; | 990 | ret = -IPSET_ERR_EXIST_SETNAME2; |
917 | goto out; | 991 | goto out; |
918 | } | 992 | } |
@@ -947,17 +1021,14 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, | |||
947 | attr[IPSET_ATTR_SETNAME2] == NULL)) | 1021 | attr[IPSET_ATTR_SETNAME2] == NULL)) |
948 | return -IPSET_ERR_PROTOCOL; | 1022 | return -IPSET_ERR_PROTOCOL; |
949 | 1023 | ||
950 | from_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | 1024 | from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id); |
951 | if (from_id == IPSET_INVALID_ID) | 1025 | if (from == NULL) |
952 | return -ENOENT; | 1026 | return -ENOENT; |
953 | 1027 | ||
954 | to_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME2])); | 1028 | to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id); |
955 | if (to_id == IPSET_INVALID_ID) | 1029 | if (to == NULL) |
956 | return -IPSET_ERR_EXIST_SETNAME2; | 1030 | return -IPSET_ERR_EXIST_SETNAME2; |
957 | 1031 | ||
958 | from = ip_set_list[from_id]; | ||
959 | to = ip_set_list[to_id]; | ||
960 | |||
961 | /* Features must not change. | 1032 | /* Features must not change. |
962 | * Not an artificial restriction anymore, as we must prevent | 1033 | * Not an artificial restriction anymore, as we must prevent |
963 | * possible loops created by swapping in setlist type of sets. */ | 1034 | * possible loops created by swapping in setlist type of sets. */ |
@@ -971,8 +1042,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, | |||
971 | 1042 | ||
972 | write_lock_bh(&ip_set_ref_lock); | 1043 | write_lock_bh(&ip_set_ref_lock); |
973 | swap(from->ref, to->ref); | 1044 | swap(from->ref, to->ref); |
974 | ip_set_list[from_id] = to; | 1045 | nfnl_set(from_id) = to; |
975 | ip_set_list[to_id] = from; | 1046 | nfnl_set(to_id) = from; |
976 | write_unlock_bh(&ip_set_ref_lock); | 1047 | write_unlock_bh(&ip_set_ref_lock); |
977 | 1048 | ||
978 | return 0; | 1049 | return 0; |
@@ -992,7 +1063,7 @@ static int | |||
992 | ip_set_dump_done(struct netlink_callback *cb) | 1063 | ip_set_dump_done(struct netlink_callback *cb) |
993 | { | 1064 | { |
994 | if (cb->args[2]) { | 1065 | if (cb->args[2]) { |
995 | pr_debug("release set %s\n", ip_set_list[cb->args[1]]->name); | 1066 | pr_debug("release set %s\n", nfnl_set(cb->args[1])->name); |
996 | ip_set_put_byindex((ip_set_id_t) cb->args[1]); | 1067 | ip_set_put_byindex((ip_set_id_t) cb->args[1]); |
997 | } | 1068 | } |
998 | return 0; | 1069 | return 0; |
@@ -1030,8 +1101,11 @@ dump_init(struct netlink_callback *cb) | |||
1030 | */ | 1101 | */ |
1031 | 1102 | ||
1032 | if (cda[IPSET_ATTR_SETNAME]) { | 1103 | if (cda[IPSET_ATTR_SETNAME]) { |
1033 | index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME])); | 1104 | struct ip_set *set; |
1034 | if (index == IPSET_INVALID_ID) | 1105 | |
1106 | set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]), | ||
1107 | &index); | ||
1108 | if (set == NULL) | ||
1035 | return -ENOENT; | 1109 | return -ENOENT; |
1036 | 1110 | ||
1037 | dump_type = DUMP_ONE; | 1111 | dump_type = DUMP_ONE; |
@@ -1081,7 +1155,7 @@ dump_last: | |||
1081 | dump_type, dump_flags, cb->args[1]); | 1155 | dump_type, dump_flags, cb->args[1]); |
1082 | for (; cb->args[1] < max; cb->args[1]++) { | 1156 | for (; cb->args[1] < max; cb->args[1]++) { |
1083 | index = (ip_set_id_t) cb->args[1]; | 1157 | index = (ip_set_id_t) cb->args[1]; |
1084 | set = ip_set_list[index]; | 1158 | set = nfnl_set(index); |
1085 | if (set == NULL) { | 1159 | if (set == NULL) { |
1086 | if (dump_type == DUMP_ONE) { | 1160 | if (dump_type == DUMP_ONE) { |
1087 | ret = -ENOENT; | 1161 | ret = -ENOENT; |
@@ -1100,7 +1174,7 @@ dump_last: | |||
1100 | if (!cb->args[2]) { | 1174 | if (!cb->args[2]) { |
1101 | /* Start listing: make sure set won't be destroyed */ | 1175 | /* Start listing: make sure set won't be destroyed */ |
1102 | pr_debug("reference set\n"); | 1176 | pr_debug("reference set\n"); |
1103 | __ip_set_get(index); | 1177 | __ip_set_get(set); |
1104 | } | 1178 | } |
1105 | nlh = start_msg(skb, NETLINK_CB(cb->skb).portid, | 1179 | nlh = start_msg(skb, NETLINK_CB(cb->skb).portid, |
1106 | cb->nlh->nlmsg_seq, flags, | 1180 | cb->nlh->nlmsg_seq, flags, |
@@ -1159,7 +1233,7 @@ next_set: | |||
1159 | release_refcount: | 1233 | release_refcount: |
1160 | /* If there was an error or set is done, release set */ | 1234 | /* If there was an error or set is done, release set */ |
1161 | if (ret || !cb->args[2]) { | 1235 | if (ret || !cb->args[2]) { |
1162 | pr_debug("release set %s\n", ip_set_list[index]->name); | 1236 | pr_debug("release set %s\n", nfnl_set(index)->name); |
1163 | ip_set_put_byindex(index); | 1237 | ip_set_put_byindex(index); |
1164 | cb->args[2] = 0; | 1238 | cb->args[2] = 0; |
1165 | } | 1239 | } |
@@ -1409,17 +1483,15 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, | |||
1409 | const struct ip_set *set; | 1483 | const struct ip_set *set; |
1410 | struct sk_buff *skb2; | 1484 | struct sk_buff *skb2; |
1411 | struct nlmsghdr *nlh2; | 1485 | struct nlmsghdr *nlh2; |
1412 | ip_set_id_t index; | ||
1413 | int ret = 0; | 1486 | int ret = 0; |
1414 | 1487 | ||
1415 | if (unlikely(protocol_failed(attr) || | 1488 | if (unlikely(protocol_failed(attr) || |
1416 | attr[IPSET_ATTR_SETNAME] == NULL)) | 1489 | attr[IPSET_ATTR_SETNAME] == NULL)) |
1417 | return -IPSET_ERR_PROTOCOL; | 1490 | return -IPSET_ERR_PROTOCOL; |
1418 | 1491 | ||
1419 | index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME])); | 1492 | set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); |
1420 | if (index == IPSET_INVALID_ID) | 1493 | if (set == NULL) |
1421 | return -ENOENT; | 1494 | return -ENOENT; |
1422 | set = ip_set_list[index]; | ||
1423 | 1495 | ||
1424 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 1496 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1425 | if (skb2 == NULL) | 1497 | if (skb2 == NULL) |
@@ -1684,6 +1756,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) | |||
1684 | } | 1756 | } |
1685 | case IP_SET_OP_GET_BYNAME: { | 1757 | case IP_SET_OP_GET_BYNAME: { |
1686 | struct ip_set_req_get_set *req_get = data; | 1758 | struct ip_set_req_get_set *req_get = data; |
1759 | ip_set_id_t id; | ||
1687 | 1760 | ||
1688 | if (*len != sizeof(struct ip_set_req_get_set)) { | 1761 | if (*len != sizeof(struct ip_set_req_get_set)) { |
1689 | ret = -EINVAL; | 1762 | ret = -EINVAL; |
@@ -1691,12 +1764,14 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) | |||
1691 | } | 1764 | } |
1692 | req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; | 1765 | req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; |
1693 | nfnl_lock(); | 1766 | nfnl_lock(); |
1694 | req_get->set.index = find_set_id(req_get->set.name); | 1767 | find_set_and_id(req_get->set.name, &id); |
1768 | req_get->set.index = id; | ||
1695 | nfnl_unlock(); | 1769 | nfnl_unlock(); |
1696 | goto copy; | 1770 | goto copy; |
1697 | } | 1771 | } |
1698 | case IP_SET_OP_GET_BYINDEX: { | 1772 | case IP_SET_OP_GET_BYINDEX: { |
1699 | struct ip_set_req_get_set *req_get = data; | 1773 | struct ip_set_req_get_set *req_get = data; |
1774 | struct ip_set *set; | ||
1700 | 1775 | ||
1701 | if (*len != sizeof(struct ip_set_req_get_set) || | 1776 | if (*len != sizeof(struct ip_set_req_get_set) || |
1702 | req_get->set.index >= ip_set_max) { | 1777 | req_get->set.index >= ip_set_max) { |
@@ -1704,9 +1779,8 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) | |||
1704 | goto done; | 1779 | goto done; |
1705 | } | 1780 | } |
1706 | nfnl_lock(); | 1781 | nfnl_lock(); |
1707 | strncpy(req_get->set.name, | 1782 | set = nfnl_set(req_get->set.index); |
1708 | ip_set_list[req_get->set.index] | 1783 | strncpy(req_get->set.name, set ? set->name : "", |
1709 | ? ip_set_list[req_get->set.index]->name : "", | ||
1710 | IPSET_MAXNAMELEN); | 1784 | IPSET_MAXNAMELEN); |
1711 | nfnl_unlock(); | 1785 | nfnl_unlock(); |
1712 | goto copy; | 1786 | goto copy; |
@@ -1737,6 +1811,7 @@ static struct nf_sockopt_ops so_set __read_mostly = { | |||
1737 | static int __init | 1811 | static int __init |
1738 | ip_set_init(void) | 1812 | ip_set_init(void) |
1739 | { | 1813 | { |
1814 | struct ip_set **list; | ||
1740 | int ret; | 1815 | int ret; |
1741 | 1816 | ||
1742 | if (max_sets) | 1817 | if (max_sets) |
@@ -1744,22 +1819,22 @@ ip_set_init(void) | |||
1744 | if (ip_set_max >= IPSET_INVALID_ID) | 1819 | if (ip_set_max >= IPSET_INVALID_ID) |
1745 | ip_set_max = IPSET_INVALID_ID - 1; | 1820 | ip_set_max = IPSET_INVALID_ID - 1; |
1746 | 1821 | ||
1747 | ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max, | 1822 | list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL); |
1748 | GFP_KERNEL); | 1823 | if (!list) |
1749 | if (!ip_set_list) | ||
1750 | return -ENOMEM; | 1824 | return -ENOMEM; |
1751 | 1825 | ||
1826 | rcu_assign_pointer(ip_set_list, list); | ||
1752 | ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); | 1827 | ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); |
1753 | if (ret != 0) { | 1828 | if (ret != 0) { |
1754 | pr_err("ip_set: cannot register with nfnetlink.\n"); | 1829 | pr_err("ip_set: cannot register with nfnetlink.\n"); |
1755 | kfree(ip_set_list); | 1830 | kfree(list); |
1756 | return ret; | 1831 | return ret; |
1757 | } | 1832 | } |
1758 | ret = nf_register_sockopt(&so_set); | 1833 | ret = nf_register_sockopt(&so_set); |
1759 | if (ret != 0) { | 1834 | if (ret != 0) { |
1760 | pr_err("SO_SET registry failed: %d\n", ret); | 1835 | pr_err("SO_SET registry failed: %d\n", ret); |
1761 | nfnetlink_subsys_unregister(&ip_set_netlink_subsys); | 1836 | nfnetlink_subsys_unregister(&ip_set_netlink_subsys); |
1762 | kfree(ip_set_list); | 1837 | kfree(list); |
1763 | return ret; | 1838 | return ret; |
1764 | } | 1839 | } |
1765 | 1840 | ||
@@ -1770,10 +1845,12 @@ ip_set_init(void) | |||
1770 | static void __exit | 1845 | static void __exit |
1771 | ip_set_fini(void) | 1846 | ip_set_fini(void) |
1772 | { | 1847 | { |
1848 | struct ip_set **list = rcu_dereference_protected(ip_set_list, 1); | ||
1849 | |||
1773 | /* There can't be any existing set */ | 1850 | /* There can't be any existing set */ |
1774 | nf_unregister_sockopt(&so_set); | 1851 | nf_unregister_sockopt(&so_set); |
1775 | nfnetlink_subsys_unregister(&ip_set_netlink_subsys); | 1852 | nfnetlink_subsys_unregister(&ip_set_netlink_subsys); |
1776 | kfree(ip_set_list); | 1853 | kfree(list); |
1777 | pr_debug("these are the famous last words\n"); | 1854 | pr_debug("these are the famous last words\n"); |
1778 | } | 1855 | } |
1779 | 1856 | ||