diff options
Diffstat (limited to 'net/ipv4/netfilter/ip_nat_core.c')
-rw-r--r-- | net/ipv4/netfilter/ip_nat_core.c | 100 |
1 files changed, 54 insertions, 46 deletions
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 9d1a517..40737fd 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]; static inline struct ip_nat_protocol * __ip_nat_proto_find(u_int8_t protonum) { - return ip_nat_protos[protonum]; + return rcu_dereference(ip_nat_protos[protonum]); } struct ip_nat_protocol * @@ -58,13 +58,11 @@ ip_nat_proto_find_get(u_int8_t protonum) { struct ip_nat_protocol *p; - /* we need to disable preemption to make sure 'p' doesn't get - * removed until we've grabbed the reference */ - preempt_disable(); + rcu_read_lock(); p = __ip_nat_proto_find(protonum); if (!try_module_get(p->me)) p = &ip_nat_unknown_protocol; - preempt_enable(); + rcu_read_unlock(); return p; } @@ -120,8 +118,8 @@ static int in_range(const struct ip_conntrack_tuple *tuple, const struct ip_nat_range *range) { - struct ip_nat_protocol *proto = - __ip_nat_proto_find(tuple->dst.protonum); + struct ip_nat_protocol *proto; + int ret = 0; /* If we are supposed to map IPs, then we must be in the range specified, otherwise let this drag us onto a new src IP. */ @@ -131,12 +129,15 @@ in_range(const struct ip_conntrack_tuple *tuple, return 0; } + rcu_read_lock(); + proto = __ip_nat_proto_find(tuple->dst.protonum); if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || proto->in_range(tuple, IP_NAT_MANIP_SRC, &range->min, &range->max)) - return 1; + ret = 1; + rcu_read_unlock(); - return 0; + return ret; } static inline int @@ -246,8 +247,9 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple, if (maniptype == IP_NAT_MANIP_SRC) { if (find_appropriate_src(orig_tuple, tuple, range)) { DEBUGP("get_unique_tuple: Found current src map\n"); - if (!ip_nat_used_tuple(tuple, conntrack)) - return; + if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) + if (!ip_nat_used_tuple(tuple, conntrack)) + return; } } @@ -259,20 +261,25 @@ get_unique_tuple(struct ip_conntrack_tuple *tuple, /* 3) The per-protocol part of the manip is made to map into the range to make a unique tuple. */ - proto = ip_nat_proto_find_get(orig_tuple->dst.protonum); + rcu_read_lock(); + proto = __ip_nat_proto_find(orig_tuple->dst.protonum); + + /* Change protocol info to have some randomization */ + if (range->flags & IP_NAT_RANGE_PROTO_RANDOM) { + proto->unique_tuple(tuple, range, maniptype, conntrack); + goto out; + } /* Only bother mapping if it's not already in range and unique */ if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) || proto->in_range(tuple, maniptype, &range->min, &range->max)) - && !ip_nat_used_tuple(tuple, conntrack)) { - ip_nat_proto_put(proto); - return; - } + && !ip_nat_used_tuple(tuple, conntrack)) + goto out; /* Last change: get protocol to try to obtain unique tuple. */ proto->unique_tuple(tuple, range, maniptype, conntrack); - - ip_nat_proto_put(proto); +out: + rcu_read_unlock(); } unsigned int @@ -352,12 +359,11 @@ manip_pkt(u_int16_t proto, iph = (void *)(*pskb)->data + iphdroff; /* Manipulate protcol part. */ - p = ip_nat_proto_find_get(proto); - if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) { - ip_nat_proto_put(p); + + /* rcu_read_lock()ed by nf_hook_slow */ + p = __ip_nat_proto_find(proto); + if (!p->manip_pkt(pskb, iphdroff, target, maniptype)) return 0; - } - ip_nat_proto_put(p); iph = (void *)(*pskb)->data + iphdroff; @@ -414,6 +420,7 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, struct icmphdr icmp; struct iphdr ip; } *inside; + struct ip_conntrack_protocol *proto; struct ip_conntrack_tuple inner, target; int hdrlen = (*pskb)->nh.iph->ihl * 4; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); @@ -435,8 +442,8 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, (*pskb)->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY); /* Redirects on non-null nats must be dropped, else they'll - start talking to each other without our translation, and be - confused... --RR */ + start talking to each other without our translation, and be + confused... --RR */ if (inside->icmp.type == ICMP_REDIRECT) { /* If NAT isn't finished, assume it and drop. */ if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) @@ -449,10 +456,11 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n", *pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); + /* rcu_read_lock()ed by nf_hook_slow */ + proto = __ip_conntrack_proto_find(inside->ip.protocol); if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 + - sizeof(struct icmphdr) + inside->ip.ihl*4, - &inner, - __ip_conntrack_proto_find(inside->ip.protocol))) + sizeof(struct icmphdr) + inside->ip.ihl*4, + &inner, proto)) return 0; /* Change inner back to look like incoming packet. We do the @@ -507,7 +515,7 @@ int ip_nat_protocol_register(struct ip_nat_protocol *proto) ret = -EBUSY; goto out; } - ip_nat_protos[proto->protonum] = proto; + rcu_assign_pointer(ip_nat_protos[proto->protonum], proto); out: write_unlock_bh(&ip_nat_lock); return ret; @@ -518,18 +526,17 @@ EXPORT_SYMBOL(ip_nat_protocol_register); void ip_nat_protocol_unregister(struct ip_nat_protocol *proto) { write_lock_bh(&ip_nat_lock); - ip_nat_protos[proto->protonum] = &ip_nat_unknown_protocol; + rcu_assign_pointer(ip_nat_protos[proto->protonum], + &ip_nat_unknown_protocol); write_unlock_bh(&ip_nat_lock); - - /* Someone could be still looking at the proto in a bh. */ - synchronize_net(); + synchronize_rcu(); } EXPORT_SYMBOL(ip_nat_protocol_unregister); #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) int -ip_nat_port_range_to_nfattr(struct sk_buff *skb, +ip_nat_port_range_to_nfattr(struct sk_buff *skb, const struct ip_nat_range *range) { NFA_PUT(skb, CTA_PROTONAT_PORT_MIN, sizeof(__be16), @@ -547,21 +554,21 @@ int ip_nat_port_nfattr_to_range(struct nfattr *tb[], struct ip_nat_range *range) { int ret = 0; - + /* we have to return whether we actually parsed something or not */ if (tb[CTA_PROTONAT_PORT_MIN-1]) { ret = 1; - range->min.tcp.port = + range->min.tcp.port = *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MIN-1]); } - + if (!tb[CTA_PROTONAT_PORT_MAX-1]) { - if (ret) + if (ret) range->max.tcp.port = range->min.tcp.port; } else { ret = 1; - range->max.tcp.port = + range->max.tcp.port = *(__be16 *)NFA_DATA(tb[CTA_PROTONAT_PORT_MAX-1]); } @@ -586,10 +593,10 @@ static int __init ip_nat_init(void) /* Sew in builtin protocols. */ write_lock_bh(&ip_nat_lock); for (i = 0; i < MAX_IP_NAT_PROTO; i++) - ip_nat_protos[i] = &ip_nat_unknown_protocol; - ip_nat_protos[IPPROTO_TCP] = &ip_nat_protocol_tcp; - ip_nat_protos[IPPROTO_UDP] = &ip_nat_protocol_udp; - ip_nat_protos[IPPROTO_ICMP] = &ip_nat_protocol_icmp; + rcu_assign_pointer(ip_nat_protos[i], &ip_nat_unknown_protocol); + rcu_assign_pointer(ip_nat_protos[IPPROTO_TCP], &ip_nat_protocol_tcp); + rcu_assign_pointer(ip_nat_protos[IPPROTO_UDP], &ip_nat_protocol_udp); + rcu_assign_pointer(ip_nat_protos[IPPROTO_ICMP], &ip_nat_protocol_icmp); write_unlock_bh(&ip_nat_lock); for (i = 0; i < ip_nat_htable_size; i++) { @@ -597,8 +604,8 @@ static int __init ip_nat_init(void) } /* FIXME: Man, this is a hack. <SIGH> */ - IP_NF_ASSERT(ip_conntrack_destroyed == NULL); - ip_conntrack_destroyed = &ip_nat_cleanup_conntrack; + IP_NF_ASSERT(rcu_dereference(ip_conntrack_destroyed) == NULL); + rcu_assign_pointer(ip_conntrack_destroyed, ip_nat_cleanup_conntrack); /* Initialize fake conntrack so that NAT will skip it */ ip_conntrack_untracked.status |= IPS_NAT_DONE_MASK; @@ -616,7 +623,8 @@ static int clean_nat(struct ip_conntrack *i, void *data) static void __exit ip_nat_cleanup(void) { ip_ct_iterate_cleanup(&clean_nat, NULL); - ip_conntrack_destroyed = NULL; + rcu_assign_pointer(ip_conntrack_destroyed, NULL); + synchronize_rcu(); vfree(bysource); } |