[NETFILTER]: ip_conntrack: properly use RCU API for ip_ct_protos array
Replace preempt_{enable,disable} based RCU by proper use of the RCU API and add missing rcu_read_lock/rcu_read_unlock calls in all paths not obviously only used within packet process context (nfnetlink_conntrack). Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e22a054869
commit
642d628b2c
|
@ -318,9 +318,11 @@ destroy_conntrack(struct nf_conntrack *nfct)
|
||||||
/* To make sure we don't get any weird locking issues here:
|
/* To make sure we don't get any weird locking issues here:
|
||||||
* destroy_conntrack() MUST NOT be called with a write lock
|
* destroy_conntrack() MUST NOT be called with a write lock
|
||||||
* to ip_conntrack_lock!!! -HW */
|
* to ip_conntrack_lock!!! -HW */
|
||||||
|
rcu_read_lock();
|
||||||
proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
|
proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);
|
||||||
if (proto && proto->destroy)
|
if (proto && proto->destroy)
|
||||||
proto->destroy(ct);
|
proto->destroy(ct);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (ip_conntrack_destroyed)
|
if (ip_conntrack_destroyed)
|
||||||
ip_conntrack_destroyed(ct);
|
ip_conntrack_destroyed(ct);
|
||||||
|
@ -595,13 +597,13 @@ ip_conntrack_proto_find_get(u_int8_t protocol)
|
||||||
{
|
{
|
||||||
struct ip_conntrack_protocol *p;
|
struct ip_conntrack_protocol *p;
|
||||||
|
|
||||||
preempt_disable();
|
rcu_read_lock();
|
||||||
p = __ip_conntrack_proto_find(protocol);
|
p = __ip_conntrack_proto_find(protocol);
|
||||||
if (p) {
|
if (p) {
|
||||||
if (!try_module_get(p->me))
|
if (!try_module_get(p->me))
|
||||||
p = &ip_conntrack_generic_protocol;
|
p = &ip_conntrack_generic_protocol;
|
||||||
}
|
}
|
||||||
preempt_enable();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
@ -830,6 +832,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* rcu_read_lock()ed by nf_hook_slow */
|
||||||
proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol);
|
proto = __ip_conntrack_proto_find((*pskb)->nh.iph->protocol);
|
||||||
|
|
||||||
/* It may be an special packet, error, unclean...
|
/* It may be an special packet, error, unclean...
|
||||||
|
@ -875,8 +878,15 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
|
||||||
int invert_tuplepr(struct ip_conntrack_tuple *inverse,
|
int invert_tuplepr(struct ip_conntrack_tuple *inverse,
|
||||||
const struct ip_conntrack_tuple *orig)
|
const struct ip_conntrack_tuple *orig)
|
||||||
{
|
{
|
||||||
return ip_ct_invert_tuple(inverse, orig,
|
struct ip_conntrack_protocol *proto;
|
||||||
__ip_conntrack_proto_find(orig->dst.protonum));
|
int ret;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
proto = __ip_conntrack_proto_find(orig->dst.protonum);
|
||||||
|
ret = ip_ct_invert_tuple(inverse, orig, proto);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Would two expected things clash? */
|
/* Would two expected things clash? */
|
||||||
|
@ -1507,11 +1517,11 @@ int __init ip_conntrack_init(void)
|
||||||
/* Don't NEED lock here, but good form anyway. */
|
/* Don't NEED lock here, but good form anyway. */
|
||||||
write_lock_bh(&ip_conntrack_lock);
|
write_lock_bh(&ip_conntrack_lock);
|
||||||
for (i = 0; i < MAX_IP_CT_PROTO; i++)
|
for (i = 0; i < MAX_IP_CT_PROTO; i++)
|
||||||
ip_ct_protos[i] = &ip_conntrack_generic_protocol;
|
rcu_assign_pointer(ip_ct_protos[i], &ip_conntrack_generic_protocol);
|
||||||
/* Sew in builtin protocols. */
|
/* Sew in builtin protocols. */
|
||||||
ip_ct_protos[IPPROTO_TCP] = &ip_conntrack_protocol_tcp;
|
rcu_assign_pointer(ip_ct_protos[IPPROTO_TCP], &ip_conntrack_protocol_tcp);
|
||||||
ip_ct_protos[IPPROTO_UDP] = &ip_conntrack_protocol_udp;
|
rcu_assign_pointer(ip_ct_protos[IPPROTO_UDP], &ip_conntrack_protocol_udp);
|
||||||
ip_ct_protos[IPPROTO_ICMP] = &ip_conntrack_protocol_icmp;
|
rcu_assign_pointer(ip_ct_protos[IPPROTO_ICMP], &ip_conntrack_protocol_icmp);
|
||||||
write_unlock_bh(&ip_conntrack_lock);
|
write_unlock_bh(&ip_conntrack_lock);
|
||||||
|
|
||||||
/* For use by ipt_REJECT */
|
/* For use by ipt_REJECT */
|
||||||
|
|
|
@ -796,7 +796,7 @@ int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto)
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ip_ct_protos[proto->proto] = proto;
|
rcu_assign_pointer(ip_ct_protos[proto->proto], proto);
|
||||||
out:
|
out:
|
||||||
write_unlock_bh(&ip_conntrack_lock);
|
write_unlock_bh(&ip_conntrack_lock);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -805,11 +805,10 @@ int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto)
|
||||||
void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
|
void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
|
||||||
{
|
{
|
||||||
write_lock_bh(&ip_conntrack_lock);
|
write_lock_bh(&ip_conntrack_lock);
|
||||||
ip_ct_protos[proto->proto] = &ip_conntrack_generic_protocol;
|
rcu_assign_pointer(ip_ct_protos[proto->proto],
|
||||||
|
&ip_conntrack_generic_protocol);
|
||||||
write_unlock_bh(&ip_conntrack_lock);
|
write_unlock_bh(&ip_conntrack_lock);
|
||||||
|
synchronize_rcu();
|
||||||
/* Somebody could be still looking at the proto in bh. */
|
|
||||||
synchronize_net();
|
|
||||||
|
|
||||||
/* Remove all contrack entries for this protocol */
|
/* Remove all contrack entries for this protocol */
|
||||||
ip_ct_iterate_cleanup(kill_proto, &proto->proto);
|
ip_ct_iterate_cleanup(kill_proto, &proto->proto);
|
||||||
|
|
|
@ -420,6 +420,7 @@ int ip_nat_icmp_reply_translation(struct ip_conntrack *ct,
|
||||||
struct icmphdr icmp;
|
struct icmphdr icmp;
|
||||||
struct iphdr ip;
|
struct iphdr ip;
|
||||||
} *inside;
|
} *inside;
|
||||||
|
struct ip_conntrack_protocol *proto;
|
||||||
struct ip_conntrack_tuple inner, target;
|
struct ip_conntrack_tuple inner, target;
|
||||||
int hdrlen = (*pskb)->nh.iph->ihl * 4;
|
int hdrlen = (*pskb)->nh.iph->ihl * 4;
|
||||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||||
|
@ -455,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",
|
DEBUGP("icmp_reply_translation: translating error %p manp %u dir %s\n",
|
||||||
*pskb, manip, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
|
*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 +
|
if (!ip_ct_get_tuple(&inside->ip, *pskb, (*pskb)->nh.iph->ihl*4 +
|
||||||
sizeof(struct icmphdr) + inside->ip.ihl*4,
|
sizeof(struct icmphdr) + inside->ip.ihl*4,
|
||||||
&inner,
|
&inner, proto))
|
||||||
__ip_conntrack_proto_find(inside->ip.protocol)))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Change inner back to look like incoming packet. We do the
|
/* Change inner back to look like incoming packet. We do the
|
||||||
|
|
Loading…
Reference in a new issue