diff options
-rw-r--r-- | net/ipv4/netfilter/ipt_CLUSTERIP.c | 80 |
1 files changed, 62 insertions, 18 deletions
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 7d38913754b1..adbf4d752d0f 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c | |||
@@ -49,6 +49,8 @@ MODULE_DESCRIPTION("iptables target for CLUSTERIP"); | |||
49 | struct clusterip_config { | 49 | struct clusterip_config { |
50 | struct list_head list; /* list of all configs */ | 50 | struct list_head list; /* list of all configs */ |
51 | atomic_t refcount; /* reference count */ | 51 | atomic_t refcount; /* reference count */ |
52 | atomic_t entries; /* number of entries/rules | ||
53 | * referencing us */ | ||
52 | 54 | ||
53 | u_int32_t clusterip; /* the IP address */ | 55 | u_int32_t clusterip; /* the IP address */ |
54 | u_int8_t clustermac[ETH_ALEN]; /* the MAC address */ | 56 | u_int8_t clustermac[ETH_ALEN]; /* the MAC address */ |
@@ -76,23 +78,48 @@ static struct proc_dir_entry *clusterip_procdir; | |||
76 | #endif | 78 | #endif |
77 | 79 | ||
78 | static inline void | 80 | static inline void |
79 | clusterip_config_get(struct clusterip_config *c) { | 81 | clusterip_config_get(struct clusterip_config *c) |
82 | { | ||
80 | atomic_inc(&c->refcount); | 83 | atomic_inc(&c->refcount); |
81 | } | 84 | } |
82 | 85 | ||
83 | static inline void | 86 | static inline void |
84 | clusterip_config_put(struct clusterip_config *c) { | 87 | clusterip_config_put(struct clusterip_config *c) |
85 | if (atomic_dec_and_test(&c->refcount)) { | 88 | { |
89 | if (atomic_dec_and_test(&c->refcount)) | ||
90 | kfree(c); | ||
91 | } | ||
92 | |||
93 | /* increase the count of entries(rules) using/referencing this config */ | ||
94 | static inline void | ||
95 | clusterip_config_entry_get(struct clusterip_config *c) | ||
96 | { | ||
97 | atomic_inc(&c->entries); | ||
98 | } | ||
99 | |||
100 | /* decrease the count of entries using/referencing this config. If last | ||
101 | * entry(rule) is removed, remove the config from lists, but don't free it | ||
102 | * yet, since proc-files could still be holding references */ | ||
103 | static inline void | ||
104 | clusterip_config_entry_put(struct clusterip_config *c) | ||
105 | { | ||
106 | if (atomic_dec_and_test(&c->entries)) { | ||
86 | write_lock_bh(&clusterip_lock); | 107 | write_lock_bh(&clusterip_lock); |
87 | list_del(&c->list); | 108 | list_del(&c->list); |
88 | write_unlock_bh(&clusterip_lock); | 109 | write_unlock_bh(&clusterip_lock); |
110 | |||
89 | dev_mc_delete(c->dev, c->clustermac, ETH_ALEN, 0); | 111 | dev_mc_delete(c->dev, c->clustermac, ETH_ALEN, 0); |
90 | dev_put(c->dev); | 112 | dev_put(c->dev); |
91 | kfree(c); | 113 | |
114 | /* In case anyone still accesses the file, the open/close | ||
115 | * functions are also incrementing the refcount on their own, | ||
116 | * so it's safe to remove the entry even if it's in use. */ | ||
117 | #ifdef CONFIG_PROC_FS | ||
118 | remove_proc_entry(c->pde->name, c->pde->parent); | ||
119 | #endif | ||
92 | } | 120 | } |
93 | } | 121 | } |
94 | 122 | ||
95 | |||
96 | static struct clusterip_config * | 123 | static struct clusterip_config * |
97 | __clusterip_config_find(u_int32_t clusterip) | 124 | __clusterip_config_find(u_int32_t clusterip) |
98 | { | 125 | { |
@@ -111,7 +138,7 @@ __clusterip_config_find(u_int32_t clusterip) | |||
111 | } | 138 | } |
112 | 139 | ||
113 | static inline struct clusterip_config * | 140 | static inline struct clusterip_config * |
114 | clusterip_config_find_get(u_int32_t clusterip) | 141 | clusterip_config_find_get(u_int32_t clusterip, int entry) |
115 | { | 142 | { |
116 | struct clusterip_config *c; | 143 | struct clusterip_config *c; |
117 | 144 | ||
@@ -122,6 +149,8 @@ clusterip_config_find_get(u_int32_t clusterip) | |||
122 | return NULL; | 149 | return NULL; |
123 | } | 150 | } |
124 | atomic_inc(&c->refcount); | 151 | atomic_inc(&c->refcount); |
152 | if (entry) | ||
153 | atomic_inc(&c->entries); | ||
125 | read_unlock_bh(&clusterip_lock); | 154 | read_unlock_bh(&clusterip_lock); |
126 | 155 | ||
127 | return c; | 156 | return c; |
@@ -148,6 +177,7 @@ clusterip_config_init(struct ipt_clusterip_tgt_info *i, u_int32_t ip, | |||
148 | c->hash_mode = i->hash_mode; | 177 | c->hash_mode = i->hash_mode; |
149 | c->hash_initval = i->hash_initval; | 178 | c->hash_initval = i->hash_initval; |
150 | atomic_set(&c->refcount, 1); | 179 | atomic_set(&c->refcount, 1); |
180 | atomic_set(&c->entries, 1); | ||
151 | 181 | ||
152 | #ifdef CONFIG_PROC_FS | 182 | #ifdef CONFIG_PROC_FS |
153 | /* create proc dir entry */ | 183 | /* create proc dir entry */ |
@@ -415,8 +445,26 @@ checkentry(const char *tablename, | |||
415 | 445 | ||
416 | /* FIXME: further sanity checks */ | 446 | /* FIXME: further sanity checks */ |
417 | 447 | ||
418 | config = clusterip_config_find_get(e->ip.dst.s_addr); | 448 | config = clusterip_config_find_get(e->ip.dst.s_addr, 1); |
419 | if (!config) { | 449 | if (config) { |
450 | if (cipinfo->config != NULL) { | ||
451 | /* Case A: This is an entry that gets reloaded, since | ||
452 | * it still has a cipinfo->config pointer. Simply | ||
453 | * increase the entry refcount and return */ | ||
454 | if (cipinfo->config != config) { | ||
455 | printk(KERN_ERR "CLUSTERIP: Reloaded entry " | ||
456 | "has invalid config pointer!\n"); | ||
457 | return 0; | ||
458 | } | ||
459 | clusterip_config_entry_get(cipinfo->config); | ||
460 | } else { | ||
461 | /* Case B: This is a new rule referring to an existing | ||
462 | * clusterip config. */ | ||
463 | cipinfo->config = config; | ||
464 | clusterip_config_entry_get(cipinfo->config); | ||
465 | } | ||
466 | } else { | ||
467 | /* Case C: This is a completely new clusterip config */ | ||
420 | if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) { | 468 | if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) { |
421 | printk(KERN_WARNING "CLUSTERIP: no config found for %u.%u.%u.%u, need 'new'\n", NIPQUAD(e->ip.dst.s_addr)); | 469 | printk(KERN_WARNING "CLUSTERIP: no config found for %u.%u.%u.%u, need 'new'\n", NIPQUAD(e->ip.dst.s_addr)); |
422 | return 0; | 470 | return 0; |
@@ -443,10 +491,9 @@ checkentry(const char *tablename, | |||
443 | } | 491 | } |
444 | dev_mc_add(config->dev,config->clustermac, ETH_ALEN, 0); | 492 | dev_mc_add(config->dev,config->clustermac, ETH_ALEN, 0); |
445 | } | 493 | } |
494 | cipinfo->config = config; | ||
446 | } | 495 | } |
447 | 496 | ||
448 | cipinfo->config = config; | ||
449 | |||
450 | return 1; | 497 | return 1; |
451 | } | 498 | } |
452 | 499 | ||
@@ -455,13 +502,10 @@ static void destroy(void *matchinfo, unsigned int matchinfosize) | |||
455 | { | 502 | { |
456 | struct ipt_clusterip_tgt_info *cipinfo = matchinfo; | 503 | struct ipt_clusterip_tgt_info *cipinfo = matchinfo; |
457 | 504 | ||
458 | /* we first remove the proc entry and then drop the reference | 505 | /* if no more entries are referencing the config, remove it |
459 | * count. In case anyone still accesses the file, the open/close | 506 | * from the list and destroy the proc entry */ |
460 | * functions are also incrementing the refcount on their own */ | 507 | clusterip_config_entry_put(cipinfo->config); |
461 | #ifdef CONFIG_PROC_FS | 508 | |
462 | remove_proc_entry(cipinfo->config->pde->name, | ||
463 | cipinfo->config->pde->parent); | ||
464 | #endif | ||
465 | clusterip_config_put(cipinfo->config); | 509 | clusterip_config_put(cipinfo->config); |
466 | } | 510 | } |
467 | 511 | ||
@@ -533,7 +577,7 @@ arp_mangle(unsigned int hook, | |||
533 | 577 | ||
534 | /* if there is no clusterip configuration for the arp reply's | 578 | /* if there is no clusterip configuration for the arp reply's |
535 | * source ip, we don't want to mangle it */ | 579 | * source ip, we don't want to mangle it */ |
536 | c = clusterip_config_find_get(payload->src_ip); | 580 | c = clusterip_config_find_get(payload->src_ip, 0); |
537 | if (!c) | 581 | if (!c) |
538 | return NF_ACCEPT; | 582 | return NF_ACCEPT; |
539 | 583 | ||