diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2011-07-21 06:06:18 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-07-21 06:06:18 -0400 |
commit | 89dc79b787d20e4b6c4077dcee1c5b1be4ab55b8 (patch) | |
tree | 24ebd4da0fe7e239e45cbc5a4ec599ee1abba94d /include | |
parent | a6a7b759ba62e62542308e091f7fc9cfac4f978e (diff) |
netfilter: ipset: hash:net,iface fixed to handle overlapping nets behind different interfaces
If overlapping networks with different interfaces was added to
the set, the type did not handle it properly. Example
ipset create test hash:net,iface
ipset add test 192.168.0.0/16,eth0
ipset add test 192.168.0.0/24,eth1
Now, if a packet was sent from 192.168.0.0/24,eth0, the type returned
a match.
In the patch the algorithm is fixed in order to correctly handle
overlapping networks.
Limitation: the same network cannot be stored with more than 64 different
interfaces in a single set.
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/netfilter/ipset/ip_set_ahash.h | 92 |
1 files changed, 61 insertions, 31 deletions
diff --git a/include/linux/netfilter/ipset/ip_set_ahash.h b/include/linux/netfilter/ipset/ip_set_ahash.h index 42b7d25a1b2e..1e7f7594cd02 100644 --- a/include/linux/netfilter/ipset/ip_set_ahash.h +++ b/include/linux/netfilter/ipset/ip_set_ahash.h | |||
@@ -28,7 +28,32 @@ | |||
28 | /* Number of elements to store in an initial array block */ | 28 | /* Number of elements to store in an initial array block */ |
29 | #define AHASH_INIT_SIZE 4 | 29 | #define AHASH_INIT_SIZE 4 |
30 | /* Max number of elements to store in an array block */ | 30 | /* Max number of elements to store in an array block */ |
31 | #define AHASH_MAX_SIZE (3*4) | 31 | #define AHASH_MAX_SIZE (3*AHASH_INIT_SIZE) |
32 | |||
33 | /* Max number of elements can be tuned */ | ||
34 | #ifdef IP_SET_HASH_WITH_MULTI | ||
35 | #define AHASH_MAX(h) ((h)->ahash_max) | ||
36 | |||
37 | static inline u8 | ||
38 | tune_ahash_max(u8 curr, u32 multi) | ||
39 | { | ||
40 | u32 n; | ||
41 | |||
42 | if (multi < curr) | ||
43 | return curr; | ||
44 | |||
45 | n = curr + AHASH_INIT_SIZE; | ||
46 | /* Currently, at listing one hash bucket must fit into a message. | ||
47 | * Therefore we have a hard limit here. | ||
48 | */ | ||
49 | return n > curr && n <= 64 ? n : curr; | ||
50 | } | ||
51 | #define TUNE_AHASH_MAX(h, multi) \ | ||
52 | ((h)->ahash_max = tune_ahash_max((h)->ahash_max, multi)) | ||
53 | #else | ||
54 | #define AHASH_MAX(h) AHASH_MAX_SIZE | ||
55 | #define TUNE_AHASH_MAX(h, multi) | ||
56 | #endif | ||
32 | 57 | ||
33 | /* A hash bucket */ | 58 | /* A hash bucket */ |
34 | struct hbucket { | 59 | struct hbucket { |
@@ -60,6 +85,9 @@ struct ip_set_hash { | |||
60 | u32 timeout; /* timeout value, if enabled */ | 85 | u32 timeout; /* timeout value, if enabled */ |
61 | struct timer_list gc; /* garbage collection when timeout enabled */ | 86 | struct timer_list gc; /* garbage collection when timeout enabled */ |
62 | struct type_pf_next next; /* temporary storage for uadd */ | 87 | struct type_pf_next next; /* temporary storage for uadd */ |
88 | #ifdef IP_SET_HASH_WITH_MULTI | ||
89 | u8 ahash_max; /* max elements in an array block */ | ||
90 | #endif | ||
63 | #ifdef IP_SET_HASH_WITH_NETMASK | 91 | #ifdef IP_SET_HASH_WITH_NETMASK |
64 | u8 netmask; /* netmask value for subnets to store */ | 92 | u8 netmask; /* netmask value for subnets to store */ |
65 | #endif | 93 | #endif |
@@ -279,12 +307,13 @@ ip_set_hash_destroy(struct ip_set *set) | |||
279 | /* Add an element to the hash table when resizing the set: | 307 | /* Add an element to the hash table when resizing the set: |
280 | * we spare the maintenance of the internal counters. */ | 308 | * we spare the maintenance of the internal counters. */ |
281 | static int | 309 | static int |
282 | type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value) | 310 | type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value, |
311 | u8 ahash_max) | ||
283 | { | 312 | { |
284 | if (n->pos >= n->size) { | 313 | if (n->pos >= n->size) { |
285 | void *tmp; | 314 | void *tmp; |
286 | 315 | ||
287 | if (n->size >= AHASH_MAX_SIZE) | 316 | if (n->size >= ahash_max) |
288 | /* Trigger rehashing */ | 317 | /* Trigger rehashing */ |
289 | return -EAGAIN; | 318 | return -EAGAIN; |
290 | 319 | ||
@@ -339,7 +368,7 @@ retry: | |||
339 | for (j = 0; j < n->pos; j++) { | 368 | for (j = 0; j < n->pos; j++) { |
340 | data = ahash_data(n, j); | 369 | data = ahash_data(n, j); |
341 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); | 370 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); |
342 | ret = type_pf_elem_add(m, data); | 371 | ret = type_pf_elem_add(m, data, AHASH_MAX(h)); |
343 | if (ret < 0) { | 372 | if (ret < 0) { |
344 | read_unlock_bh(&set->lock); | 373 | read_unlock_bh(&set->lock); |
345 | ahash_destroy(t); | 374 | ahash_destroy(t); |
@@ -376,7 +405,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
376 | const struct type_pf_elem *d = value; | 405 | const struct type_pf_elem *d = value; |
377 | struct hbucket *n; | 406 | struct hbucket *n; |
378 | int i, ret = 0; | 407 | int i, ret = 0; |
379 | u32 key; | 408 | u32 key, multi = 0; |
380 | 409 | ||
381 | if (h->elements >= h->maxelem) | 410 | if (h->elements >= h->maxelem) |
382 | return -IPSET_ERR_HASH_FULL; | 411 | return -IPSET_ERR_HASH_FULL; |
@@ -386,12 +415,12 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
386 | key = HKEY(value, h->initval, t->htable_bits); | 415 | key = HKEY(value, h->initval, t->htable_bits); |
387 | n = hbucket(t, key); | 416 | n = hbucket(t, key); |
388 | for (i = 0; i < n->pos; i++) | 417 | for (i = 0; i < n->pos; i++) |
389 | if (type_pf_data_equal(ahash_data(n, i), d)) { | 418 | if (type_pf_data_equal(ahash_data(n, i), d, &multi)) { |
390 | ret = -IPSET_ERR_EXIST; | 419 | ret = -IPSET_ERR_EXIST; |
391 | goto out; | 420 | goto out; |
392 | } | 421 | } |
393 | 422 | TUNE_AHASH_MAX(h, multi); | |
394 | ret = type_pf_elem_add(n, value); | 423 | ret = type_pf_elem_add(n, value, AHASH_MAX(h)); |
395 | if (ret != 0) { | 424 | if (ret != 0) { |
396 | if (ret == -EAGAIN) | 425 | if (ret == -EAGAIN) |
397 | type_pf_data_next(h, d); | 426 | type_pf_data_next(h, d); |
@@ -419,13 +448,13 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
419 | struct hbucket *n; | 448 | struct hbucket *n; |
420 | int i; | 449 | int i; |
421 | struct type_pf_elem *data; | 450 | struct type_pf_elem *data; |
422 | u32 key; | 451 | u32 key, multi = 0; |
423 | 452 | ||
424 | key = HKEY(value, h->initval, t->htable_bits); | 453 | key = HKEY(value, h->initval, t->htable_bits); |
425 | n = hbucket(t, key); | 454 | n = hbucket(t, key); |
426 | for (i = 0; i < n->pos; i++) { | 455 | for (i = 0; i < n->pos; i++) { |
427 | data = ahash_data(n, i); | 456 | data = ahash_data(n, i); |
428 | if (!type_pf_data_equal(data, d)) | 457 | if (!type_pf_data_equal(data, d, &multi)) |
429 | continue; | 458 | continue; |
430 | if (i != n->pos - 1) | 459 | if (i != n->pos - 1) |
431 | /* Not last one */ | 460 | /* Not last one */ |
@@ -466,17 +495,17 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) | |||
466 | struct hbucket *n; | 495 | struct hbucket *n; |
467 | const struct type_pf_elem *data; | 496 | const struct type_pf_elem *data; |
468 | int i, j = 0; | 497 | int i, j = 0; |
469 | u32 key; | 498 | u32 key, multi = 0; |
470 | u8 host_mask = SET_HOST_MASK(set->family); | 499 | u8 host_mask = SET_HOST_MASK(set->family); |
471 | 500 | ||
472 | pr_debug("test by nets\n"); | 501 | pr_debug("test by nets\n"); |
473 | for (; j < host_mask && h->nets[j].cidr; j++) { | 502 | for (; j < host_mask && h->nets[j].cidr && !multi; j++) { |
474 | type_pf_data_netmask(d, h->nets[j].cidr); | 503 | type_pf_data_netmask(d, h->nets[j].cidr); |
475 | key = HKEY(d, h->initval, t->htable_bits); | 504 | key = HKEY(d, h->initval, t->htable_bits); |
476 | n = hbucket(t, key); | 505 | n = hbucket(t, key); |
477 | for (i = 0; i < n->pos; i++) { | 506 | for (i = 0; i < n->pos; i++) { |
478 | data = ahash_data(n, i); | 507 | data = ahash_data(n, i); |
479 | if (type_pf_data_equal(data, d)) | 508 | if (type_pf_data_equal(data, d, &multi)) |
480 | return 1; | 509 | return 1; |
481 | } | 510 | } |
482 | } | 511 | } |
@@ -494,7 +523,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
494 | struct hbucket *n; | 523 | struct hbucket *n; |
495 | const struct type_pf_elem *data; | 524 | const struct type_pf_elem *data; |
496 | int i; | 525 | int i; |
497 | u32 key; | 526 | u32 key, multi = 0; |
498 | 527 | ||
499 | #ifdef IP_SET_HASH_WITH_NETS | 528 | #ifdef IP_SET_HASH_WITH_NETS |
500 | /* If we test an IP address and not a network address, | 529 | /* If we test an IP address and not a network address, |
@@ -507,7 +536,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
507 | n = hbucket(t, key); | 536 | n = hbucket(t, key); |
508 | for (i = 0; i < n->pos; i++) { | 537 | for (i = 0; i < n->pos; i++) { |
509 | data = ahash_data(n, i); | 538 | data = ahash_data(n, i); |
510 | if (type_pf_data_equal(data, d)) | 539 | if (type_pf_data_equal(data, d, &multi)) |
511 | return 1; | 540 | return 1; |
512 | } | 541 | } |
513 | return 0; | 542 | return 0; |
@@ -664,14 +693,14 @@ type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout) | |||
664 | 693 | ||
665 | static int | 694 | static int |
666 | type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, | 695 | type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, |
667 | u32 timeout) | 696 | u8 ahash_max, u32 timeout) |
668 | { | 697 | { |
669 | struct type_pf_elem *data; | 698 | struct type_pf_elem *data; |
670 | 699 | ||
671 | if (n->pos >= n->size) { | 700 | if (n->pos >= n->size) { |
672 | void *tmp; | 701 | void *tmp; |
673 | 702 | ||
674 | if (n->size >= AHASH_MAX_SIZE) | 703 | if (n->size >= ahash_max) |
675 | /* Trigger rehashing */ | 704 | /* Trigger rehashing */ |
676 | return -EAGAIN; | 705 | return -EAGAIN; |
677 | 706 | ||
@@ -776,7 +805,7 @@ retry: | |||
776 | for (j = 0; j < n->pos; j++) { | 805 | for (j = 0; j < n->pos; j++) { |
777 | data = ahash_tdata(n, j); | 806 | data = ahash_tdata(n, j); |
778 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); | 807 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); |
779 | ret = type_pf_elem_tadd(m, data, | 808 | ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), |
780 | type_pf_data_timeout(data)); | 809 | type_pf_data_timeout(data)); |
781 | if (ret < 0) { | 810 | if (ret < 0) { |
782 | read_unlock_bh(&set->lock); | 811 | read_unlock_bh(&set->lock); |
@@ -807,9 +836,9 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
807 | const struct type_pf_elem *d = value; | 836 | const struct type_pf_elem *d = value; |
808 | struct hbucket *n; | 837 | struct hbucket *n; |
809 | struct type_pf_elem *data; | 838 | struct type_pf_elem *data; |
810 | int ret = 0, i, j = AHASH_MAX_SIZE + 1; | 839 | int ret = 0, i, j = AHASH_MAX(h) + 1; |
811 | bool flag_exist = flags & IPSET_FLAG_EXIST; | 840 | bool flag_exist = flags & IPSET_FLAG_EXIST; |
812 | u32 key; | 841 | u32 key, multi = 0; |
813 | 842 | ||
814 | if (h->elements >= h->maxelem) | 843 | if (h->elements >= h->maxelem) |
815 | /* FIXME: when set is full, we slow down here */ | 844 | /* FIXME: when set is full, we slow down here */ |
@@ -823,18 +852,18 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
823 | n = hbucket(t, key); | 852 | n = hbucket(t, key); |
824 | for (i = 0; i < n->pos; i++) { | 853 | for (i = 0; i < n->pos; i++) { |
825 | data = ahash_tdata(n, i); | 854 | data = ahash_tdata(n, i); |
826 | if (type_pf_data_equal(data, d)) { | 855 | if (type_pf_data_equal(data, d, &multi)) { |
827 | if (type_pf_data_expired(data) || flag_exist) | 856 | if (type_pf_data_expired(data) || flag_exist) |
828 | j = i; | 857 | j = i; |
829 | else { | 858 | else { |
830 | ret = -IPSET_ERR_EXIST; | 859 | ret = -IPSET_ERR_EXIST; |
831 | goto out; | 860 | goto out; |
832 | } | 861 | } |
833 | } else if (j == AHASH_MAX_SIZE + 1 && | 862 | } else if (j == AHASH_MAX(h) + 1 && |
834 | type_pf_data_expired(data)) | 863 | type_pf_data_expired(data)) |
835 | j = i; | 864 | j = i; |
836 | } | 865 | } |
837 | if (j != AHASH_MAX_SIZE + 1) { | 866 | if (j != AHASH_MAX(h) + 1) { |
838 | data = ahash_tdata(n, j); | 867 | data = ahash_tdata(n, j); |
839 | #ifdef IP_SET_HASH_WITH_NETS | 868 | #ifdef IP_SET_HASH_WITH_NETS |
840 | del_cidr(h, data->cidr, HOST_MASK); | 869 | del_cidr(h, data->cidr, HOST_MASK); |
@@ -844,7 +873,8 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
844 | type_pf_data_timeout_set(data, timeout); | 873 | type_pf_data_timeout_set(data, timeout); |
845 | goto out; | 874 | goto out; |
846 | } | 875 | } |
847 | ret = type_pf_elem_tadd(n, d, timeout); | 876 | TUNE_AHASH_MAX(h, multi); |
877 | ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), timeout); | ||
848 | if (ret != 0) { | 878 | if (ret != 0) { |
849 | if (ret == -EAGAIN) | 879 | if (ret == -EAGAIN) |
850 | type_pf_data_next(h, d); | 880 | type_pf_data_next(h, d); |
@@ -869,13 +899,13 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
869 | struct hbucket *n; | 899 | struct hbucket *n; |
870 | int i; | 900 | int i; |
871 | struct type_pf_elem *data; | 901 | struct type_pf_elem *data; |
872 | u32 key; | 902 | u32 key, multi = 0; |
873 | 903 | ||
874 | key = HKEY(value, h->initval, t->htable_bits); | 904 | key = HKEY(value, h->initval, t->htable_bits); |
875 | n = hbucket(t, key); | 905 | n = hbucket(t, key); |
876 | for (i = 0; i < n->pos; i++) { | 906 | for (i = 0; i < n->pos; i++) { |
877 | data = ahash_tdata(n, i); | 907 | data = ahash_tdata(n, i); |
878 | if (!type_pf_data_equal(data, d)) | 908 | if (!type_pf_data_equal(data, d, &multi)) |
879 | continue; | 909 | continue; |
880 | if (type_pf_data_expired(data)) | 910 | if (type_pf_data_expired(data)) |
881 | return -IPSET_ERR_EXIST; | 911 | return -IPSET_ERR_EXIST; |
@@ -915,16 +945,16 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) | |||
915 | struct type_pf_elem *data; | 945 | struct type_pf_elem *data; |
916 | struct hbucket *n; | 946 | struct hbucket *n; |
917 | int i, j = 0; | 947 | int i, j = 0; |
918 | u32 key; | 948 | u32 key, multi = 0; |
919 | u8 host_mask = SET_HOST_MASK(set->family); | 949 | u8 host_mask = SET_HOST_MASK(set->family); |
920 | 950 | ||
921 | for (; j < host_mask && h->nets[j].cidr; j++) { | 951 | for (; j < host_mask && h->nets[j].cidr && !multi; j++) { |
922 | type_pf_data_netmask(d, h->nets[j].cidr); | 952 | type_pf_data_netmask(d, h->nets[j].cidr); |
923 | key = HKEY(d, h->initval, t->htable_bits); | 953 | key = HKEY(d, h->initval, t->htable_bits); |
924 | n = hbucket(t, key); | 954 | n = hbucket(t, key); |
925 | for (i = 0; i < n->pos; i++) { | 955 | for (i = 0; i < n->pos; i++) { |
926 | data = ahash_tdata(n, i); | 956 | data = ahash_tdata(n, i); |
927 | if (type_pf_data_equal(data, d)) | 957 | if (type_pf_data_equal(data, d, &multi)) |
928 | return !type_pf_data_expired(data); | 958 | return !type_pf_data_expired(data); |
929 | } | 959 | } |
930 | } | 960 | } |
@@ -940,7 +970,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
940 | struct type_pf_elem *data, *d = value; | 970 | struct type_pf_elem *data, *d = value; |
941 | struct hbucket *n; | 971 | struct hbucket *n; |
942 | int i; | 972 | int i; |
943 | u32 key; | 973 | u32 key, multi = 0; |
944 | 974 | ||
945 | #ifdef IP_SET_HASH_WITH_NETS | 975 | #ifdef IP_SET_HASH_WITH_NETS |
946 | if (d->cidr == SET_HOST_MASK(set->family)) | 976 | if (d->cidr == SET_HOST_MASK(set->family)) |
@@ -950,7 +980,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) | |||
950 | n = hbucket(t, key); | 980 | n = hbucket(t, key); |
951 | for (i = 0; i < n->pos; i++) { | 981 | for (i = 0; i < n->pos; i++) { |
952 | data = ahash_tdata(n, i); | 982 | data = ahash_tdata(n, i); |
953 | if (type_pf_data_equal(data, d)) | 983 | if (type_pf_data_equal(data, d, &multi)) |
954 | return !type_pf_data_expired(data); | 984 | return !type_pf_data_expired(data); |
955 | } | 985 | } |
956 | return 0; | 986 | return 0; |