diff options
-rw-r--r-- | net/ipv4/netfilter/nf_nat_core.c | 59 |
1 files changed, 28 insertions, 31 deletions
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index cf1010827be1..6d0061f05810 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c | |||
@@ -53,7 +53,7 @@ static struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO]; | |||
53 | static inline struct nf_nat_protocol * | 53 | static inline struct nf_nat_protocol * |
54 | __nf_nat_proto_find(u_int8_t protonum) | 54 | __nf_nat_proto_find(u_int8_t protonum) |
55 | { | 55 | { |
56 | return nf_nat_protos[protonum]; | 56 | return rcu_dereference(nf_nat_protos[protonum]); |
57 | } | 57 | } |
58 | 58 | ||
59 | struct nf_nat_protocol * | 59 | struct nf_nat_protocol * |
@@ -61,13 +61,11 @@ nf_nat_proto_find_get(u_int8_t protonum) | |||
61 | { | 61 | { |
62 | struct nf_nat_protocol *p; | 62 | struct nf_nat_protocol *p; |
63 | 63 | ||
64 | /* we need to disable preemption to make sure 'p' doesn't get | 64 | rcu_read_lock(); |
65 | * removed until we've grabbed the reference */ | ||
66 | preempt_disable(); | ||
67 | p = __nf_nat_proto_find(protonum); | 65 | p = __nf_nat_proto_find(protonum); |
68 | if (!try_module_get(p->me)) | 66 | if (!try_module_get(p->me)) |
69 | p = &nf_nat_unknown_protocol; | 67 | p = &nf_nat_unknown_protocol; |
70 | preempt_enable(); | 68 | rcu_read_unlock(); |
71 | 69 | ||
72 | return p; | 70 | return p; |
73 | } | 71 | } |
@@ -126,8 +124,8 @@ in_range(const struct nf_conntrack_tuple *tuple, | |||
126 | const struct nf_nat_range *range) | 124 | const struct nf_nat_range *range) |
127 | { | 125 | { |
128 | struct nf_nat_protocol *proto; | 126 | struct nf_nat_protocol *proto; |
127 | int ret = 0; | ||
129 | 128 | ||
130 | proto = __nf_nat_proto_find(tuple->dst.protonum); | ||
131 | /* If we are supposed to map IPs, then we must be in the | 129 | /* If we are supposed to map IPs, then we must be in the |
132 | range specified, otherwise let this drag us onto a new src IP. */ | 130 | range specified, otherwise let this drag us onto a new src IP. */ |
133 | if (range->flags & IP_NAT_RANGE_MAP_IPS) { | 131 | if (range->flags & IP_NAT_RANGE_MAP_IPS) { |
@@ -136,12 +134,15 @@ in_range(const struct nf_conntrack_tuple *tuple, | |||
136 | return 0; | 134 | return 0; |
137 | } | 135 | } |
138 | 136 | ||
137 | rcu_read_lock(); | ||
138 | proto = __nf_nat_proto_find(tuple->dst.protonum); | ||
139 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || | 139 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || |
140 | proto->in_range(tuple, IP_NAT_MANIP_SRC, | 140 | proto->in_range(tuple, IP_NAT_MANIP_SRC, |
141 | &range->min, &range->max)) | 141 | &range->min, &range->max)) |
142 | return 1; | 142 | ret = 1; |
143 | rcu_read_unlock(); | ||
143 | 144 | ||
144 | return 0; | 145 | return ret; |
145 | } | 146 | } |
146 | 147 | ||
147 | static inline int | 148 | static inline int |
@@ -268,27 +269,25 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple, | |||
268 | /* 3) The per-protocol part of the manip is made to map into | 269 | /* 3) The per-protocol part of the manip is made to map into |
269 | the range to make a unique tuple. */ | 270 | the range to make a unique tuple. */ |
270 | 271 | ||
271 | proto = nf_nat_proto_find_get(orig_tuple->dst.protonum); | 272 | rcu_read_lock(); |
273 | proto = __nf_nat_proto_find(orig_tuple->dst.protonum); | ||
272 | 274 | ||
273 | /* Change protocol info to have some randomization */ | 275 | /* Change protocol info to have some randomization */ |
274 | if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { | 276 | if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { |
275 | proto->unique_tuple(tuple, range, maniptype, ct); | 277 | proto->unique_tuple(tuple, range, maniptype, ct); |
276 | nf_nat_proto_put(proto); | 278 | goto out; |
277 | return; | ||
278 | } | 279 | } |
279 | 280 | ||
280 | /* Only bother mapping if it's not already in range and unique */ | 281 | /* Only bother mapping if it's not already in range and unique */ |
281 | if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || | 282 | if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || |
282 | proto->in_range(tuple, maniptype, &range->min, &range->max)) && | 283 | proto->in_range(tuple, maniptype, &range->min, &range->max)) && |
283 | !nf_nat_used_tuple(tuple, ct)) { | 284 | !nf_nat_used_tuple(tuple, ct)) |
284 | nf_nat_proto_put(proto); | 285 | goto out; |
285 | return; | ||
286 | } | ||
287 | 286 | ||
288 | /* Last change: get protocol to try to obtain unique tuple. */ | 287 | /* Last change: get protocol to try to obtain unique tuple. */ |
289 | proto->unique_tuple(tuple, range, maniptype, ct); | 288 | proto->unique_tuple(tuple, range, maniptype, ct); |
290 | 289 | out: | |
291 | nf_nat_proto_put(proto); | 290 | rcu_read_unlock(); |
292 | } | 291 | } |
293 | 292 | ||
294 | unsigned int | 293 | unsigned int |
@@ -369,12 +368,11 @@ manip_pkt(u_int16_t proto, | |||
369 | iph = (void *)(*pskb)->data + iphdroff; | 368 | iph = (void *)(*pskb)->data + iphdroff; |
370 | 369 | ||
371 | /* Manipulate protcol part. */ | 370 | /* Manipulate protcol part. */ |
372 | p = nf_nat_proto_find_get(proto); | 371 | |
373 | if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) { | 372 | /* rcu_read_lock()ed by nf_hook_slow */ |
374 | nf_nat_proto_put(p); | 373 | p = __nf_nat_proto_find(proto); |
374 | if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) | ||
375 | return 0; | 375 | return 0; |
376 | } | ||
377 | nf_nat_proto_put(p); | ||
378 | 376 | ||
379 | iph = (void *)(*pskb)->data + iphdroff; | 377 | iph = (void *)(*pskb)->data + iphdroff; |
380 | 378 | ||
@@ -529,7 +527,7 @@ int nf_nat_protocol_register(struct nf_nat_protocol *proto) | |||
529 | ret = -EBUSY; | 527 | ret = -EBUSY; |
530 | goto out; | 528 | goto out; |
531 | } | 529 | } |
532 | nf_nat_protos[proto->protonum] = proto; | 530 | rcu_assign_pointer(nf_nat_protos[proto->protonum], proto); |
533 | out: | 531 | out: |
534 | write_unlock_bh(&nf_nat_lock); | 532 | write_unlock_bh(&nf_nat_lock); |
535 | return ret; | 533 | return ret; |
@@ -540,11 +538,10 @@ EXPORT_SYMBOL(nf_nat_protocol_register); | |||
540 | void nf_nat_protocol_unregister(struct nf_nat_protocol *proto) | 538 | void nf_nat_protocol_unregister(struct nf_nat_protocol *proto) |
541 | { | 539 | { |
542 | write_lock_bh(&nf_nat_lock); | 540 | write_lock_bh(&nf_nat_lock); |
543 | nf_nat_protos[proto->protonum] = &nf_nat_unknown_protocol; | 541 | rcu_assign_pointer(nf_nat_protos[proto->protonum], |
542 | &nf_nat_unknown_protocol); | ||
544 | write_unlock_bh(&nf_nat_lock); | 543 | write_unlock_bh(&nf_nat_lock); |
545 | 544 | synchronize_rcu(); | |
546 | /* Someone could be still looking at the proto in a bh. */ | ||
547 | synchronize_net(); | ||
548 | } | 545 | } |
549 | EXPORT_SYMBOL(nf_nat_protocol_unregister); | 546 | EXPORT_SYMBOL(nf_nat_protocol_unregister); |
550 | 547 | ||
@@ -608,10 +605,10 @@ static int __init nf_nat_init(void) | |||
608 | /* Sew in builtin protocols. */ | 605 | /* Sew in builtin protocols. */ |
609 | write_lock_bh(&nf_nat_lock); | 606 | write_lock_bh(&nf_nat_lock); |
610 | for (i = 0; i < MAX_IP_NAT_PROTO; i++) | 607 | for (i = 0; i < MAX_IP_NAT_PROTO; i++) |
611 | nf_nat_protos[i] = &nf_nat_unknown_protocol; | 608 | rcu_assign_pointer(nf_nat_protos[i], &nf_nat_unknown_protocol); |
612 | nf_nat_protos[IPPROTO_TCP] = &nf_nat_protocol_tcp; | 609 | rcu_assign_pointer(nf_nat_protos[IPPROTO_TCP], &nf_nat_protocol_tcp); |
613 | nf_nat_protos[IPPROTO_UDP] = &nf_nat_protocol_udp; | 610 | rcu_assign_pointer(nf_nat_protos[IPPROTO_UDP], &nf_nat_protocol_udp); |
614 | nf_nat_protos[IPPROTO_ICMP] = &nf_nat_protocol_icmp; | 611 | rcu_assign_pointer(nf_nat_protos[IPPROTO_ICMP], &nf_nat_protocol_icmp); |
615 | write_unlock_bh(&nf_nat_lock); | 612 | write_unlock_bh(&nf_nat_lock); |
616 | 613 | ||
617 | for (i = 0; i < nf_nat_htable_size; i++) { | 614 | for (i = 0; i < nf_nat_htable_size; i++) { |