Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 642d628b authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e22a0548
Loading
Loading
Loading
Loading
+18 −8
Original line number Original line Diff line number Diff line
@@ -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 */
+4 −5
Original line number Original line Diff line number Diff line
@@ -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);
+4 −2
Original line number Original line Diff line number Diff line
@@ -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