aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/netfilter/ip_nat_core.c61
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];
50static inline struct ip_nat_protocol * 50static 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
56struct ip_nat_protocol * 56struct 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
120in_range(const struct ip_conntrack_tuple *tuple, 118in_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
142static inline int 143static 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 281out:
283 ip_nat_proto_put(proto); 282 rcu_read_unlock();
284} 283}
285 284
286unsigned int 285unsigned 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);
526void ip_nat_protocol_unregister(struct ip_nat_protocol *proto) 524void 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}
535EXPORT_SYMBOL(ip_nat_protocol_unregister); 532EXPORT_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++) {