aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/netfilter/ip_nat_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/netfilter/ip_nat_core.c')
-rw-r--r--net/ipv4/netfilter/ip_nat_core.c100
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);
}