diff options
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/ip_nat_core.c | 61 |
1 files changed, 29 insertions, 32 deletions
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 275a4d3faf0a..85ae0cabc8b5 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c | |||
@@ -50,7 +50,7 @@ static struct ip_nat_protocol *ip_nat_protos[MAX_IP_NAT_PROTO]; | |||
50 | static inline struct ip_nat_protocol * | 50 | static inline struct ip_nat_protocol * |
51 | __ip_nat_proto_find(u_int8_t protonum) | 51 | __ip_nat_proto_find(u_int8_t protonum) |
52 | { | 52 | { |
53 | return ip_nat_protos[protonum]; | 53 | return rcu_dereference(ip_nat_protos[protonum]); |
54 | } | 54 | } |
55 | 55 | ||
56 | struct ip_nat_protocol * | 56 | struct ip_nat_protocol * |
@@ -58,13 +58,11 @@ ip_nat_proto_find_get(u_int8_t protonum) | |||
58 | { | 58 | { |
59 | struct ip_nat_protocol *p; | 59 | struct ip_nat_protocol *p; |
60 | 60 | ||
61 | /* we need to disable preemption to make sure 'p' doesn't get | 61 | rcu_read_lock(); |
62 | * removed until we've grabbed the reference */ | ||
63 | preempt_disable(); | ||
64 | p = __ip_nat_proto_find(protonum); | 62 | p = __ip_nat_proto_find(protonum); |
65 | if (!try_module_get(p->me)) | 63 | if (!try_module_get(p->me)) |
66 | p = &ip_nat_unknown_protocol; | 64 | p = &ip_nat_unknown_protocol; |
67 | preempt_enable(); | 65 | rcu_read_unlock(); |
68 | 66 | ||
69 | return p; | 67 | return p; |
70 | } | 68 | } |
@@ -120,8 +118,8 @@ static int | |||
120 | in_range(const struct ip_conntrack_tuple *tuple, | 118 | in_range(const struct ip_conntrack_tuple *tuple, |
121 | const struct ip_nat_range *range) | 119 | const struct ip_nat_range *range) |
122 | { | 120 | { |
123 | struct ip_nat_protocol *proto = | 121 | struct ip_nat_protocol *proto; |
124 | __ip_nat_proto_find(tuple->dst.protonum); | 122 | int ret = 0; |
125 | 123 | ||
126 | /* If we are supposed to map IPs, then we must be in the | 124 | /* If we are supposed to map IPs, then we must be in the |
127 | range specified, otherwise let this drag us onto a new src IP. */ | 125 | range specified, otherwise let this drag us onto a new src IP. */ |
@@ -131,12 +129,15 @@ in_range(const struct ip_conntrack_tuple *tuple, | |||
131 | return 0; | 129 | return 0; |
132 | } | 130 | } |
133 | 131 | ||
132 | rcu_read_lock(); | ||
133 | proto = __ip_nat_proto_find(tuple->dst.protonum); | ||
134 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) | 134 | if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) |
135 | || proto->in_range(tuple, IP_NAT_MANIP_SRC, | 135 | || proto->in_range(tuple, IP_NAT_MANIP_SRC, |
136 | &range->min, &range->max)) | 136 | &range->min, &range->max)) |
137 | return 1; | 137 | ret = 1; |
138 | rcu_read_unlock(); | ||
138 | 139 | ||
139 | return 0; | 140 | return ret; |
140 | } | 141 | } |
141 | 142 | ||
142 | static inline int | 143 | static inline int |
@@ -260,27 +261,25 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple, | |||
260 | /* 3) The per-protocol part of the manip is made to map into | 261 | /* 3) The per-protocol part of the manip is made to map into |
261 | the range to make a unique tuple. */ | 262 | the range to make a unique tuple. */ |
262 | 263 | ||
263 | proto = ip_nat_proto_find_get(orig_tuple->dst.protonum); | 264 | rcu_read_lock(); |
265 | proto = __ip_nat_proto_find(orig_tuple->dst.protonum); | ||
264 | 266 | ||
265 | /* Change protocol info to have some randomization */ | 267 | /* Change protocol info to have some randomization */ |
266 | if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { | 268 | if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { |
267 | proto->unique_tuple(tuple, range, maniptype, conntrack); | 269 | proto->unique_tuple(tuple, range, maniptype, conntrack); |
268 | ip_nat_proto_put(proto); | 270 | goto out; |
269 | return; | ||
270 | } | 271 | } |
271 | 272 | ||
272 | /* Only bother mapping if it's not already in range and unique */ | 273 | /* Only bother mapping if it's not already in range and unique */ |
273 | if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) | 274 | if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) |
274 | || proto->in_range(tuple, maniptype, &range->min, &range->max)) | 275 | || proto->in_range(tuple, maniptype, &range->min, &range->max)) |
275 | && !ip_nat_used_tuple(tuple, conntrack)) { | 276 | && !ip_nat_used_tuple(tuple, conntrack)) |
276 | ip_nat_proto_put(proto); | 277 | goto out; |
277 | return; | ||
278 | } | ||
279 | 278 | ||
280 | /* Last change: get protocol to try to obtain unique tuple. */ | 279 | /* Last change: get protocol to try to obtain unique tuple. */ |
281 | proto->unique_tuple(tuple, range, maniptype, conntrack); | 280 | proto->unique_tuple(tuple, range, maniptype, conntrack); |
282 | 281 | out: | |
283 | ip_nat_proto_put(proto); | 282 | rcu_read_unlock(); |
284 | } | 283 | } |
285 | 284 | ||
286 | unsigned int | 285 | unsigned int |
@@ -360,12 +359,11 @@ manip_pkt(u_int16_t proto, | |||
360 | iph = (void *)(*pskb)->data + iphdroff; | 359 | iph = (void *)(*pskb)->data + iphdroff; |
361 | 360 | ||
362 | /* Manipulate protcol part. */ | 361 | /* Manipulate protcol part. */ |
363 | p = ip_nat_proto_find_get(proto); | 362 | |
364 | if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) { | 363 | /* rcu_read_lock()ed by nf_hook_slow */ |
365 | ip_nat_proto_put(p); | 364 | p = __ip_nat_proto_find(proto); |
365 | if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) | ||
366 | return 0; | 366 | return 0; |
367 | } | ||
368 | ip_nat_proto_put(p); | ||
369 | 367 | ||
370 | iph = (void *)(*pskb)->data + iphdroff; | 368 | iph = (void *)(*pskb)->data + iphdroff; |
371 | 369 | ||
@@ -515,7 +513,7 @@ int ip_nat_protocol_register(struct ip_nat_protocol *proto) | |||
515 | ret = -EBUSY; | 513 | ret = -EBUSY; |
516 | goto out; | 514 | goto out; |
517 | } | 515 | } |
518 | ip_nat_protos[proto->protonum] = proto; | 516 | rcu_assign_pointer(ip_nat_protos[proto->protonum], proto); |
519 | out: | 517 | out: |
520 | write_unlock_bh(&ip_nat_lock); | 518 | write_unlock_bh(&ip_nat_lock); |
521 | return ret; | 519 | return ret; |
@@ -526,11 +524,10 @@ EXPORT_SYMBOL(ip_nat_protocol_register); | |||
526 | void ip_nat_protocol_unregister(struct ip_nat_protocol *proto) | 524 | void ip_nat_protocol_unregister(struct ip_nat_protocol *proto) |
527 | { | 525 | { |
528 | write_lock_bh(&ip_nat_lock); | 526 | write_lock_bh(&ip_nat_lock); |
529 | ip_nat_protos[proto->protonum] = &ip_nat_unknown_protocol; | 527 | rcu_assign_pointer(ip_nat_protos[proto->protonum], |
528 | &ip_nat_unknown_protocol); | ||
530 | write_unlock_bh(&ip_nat_lock); | 529 | write_unlock_bh(&ip_nat_lock); |
531 | 530 | synchronize_rcu(); | |
532 | /* Someone could be still looking at the proto in a bh. */ | ||
533 | synchronize_net(); | ||
534 | } | 531 | } |
535 | EXPORT_SYMBOL(ip_nat_protocol_unregister); | 532 | EXPORT_SYMBOL(ip_nat_protocol_unregister); |
536 | 533 | ||
@@ -594,10 +591,10 @@ static int __init ip_nat_init(void) | |||
594 | /* Sew in builtin protocols. */ | 591 | /* Sew in builtin protocols. */ |
595 | write_lock_bh(&ip_nat_lock); | 592 | write_lock_bh(&ip_nat_lock); |
596 | for (i = 0; i < MAX_IP_NAT_PROTO; i++) | 593 | for (i = 0; i < MAX_IP_NAT_PROTO; i++) |
597 | ip_nat_protos[i] = &ip_nat_unknown_protocol; | 594 | rcu_assign_pointer(ip_nat_protos[i], &ip_nat_unknown_protocol); |
598 | ip_nat_protos[IPPROTO_TCP] = &ip_nat_protocol_tcp; | 595 | rcu_assign_pointer(ip_nat_protos[IPPROTO_TCP], &ip_nat_protocol_tcp); |
599 | ip_nat_protos[IPPROTO_UDP] = &ip_nat_protocol_udp; | 596 | rcu_assign_pointer(ip_nat_protos[IPPROTO_UDP], &ip_nat_protocol_udp); |
600 | ip_nat_protos[IPPROTO_ICMP] = &ip_nat_protocol_icmp; | 597 | rcu_assign_pointer(ip_nat_protos[IPPROTO_ICMP], &ip_nat_protocol_icmp); |
601 | write_unlock_bh(&ip_nat_lock); | 598 | write_unlock_bh(&ip_nat_lock); |
602 | 599 | ||
603 | for (i = 0; i < ip_nat_htable_size; i++) { | 600 | for (i = 0; i < ip_nat_htable_size; i++) { |