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

Commit d91e9015 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ipvlan-corruptions'



Jiri Benc says:

====================
ipvlan: list corruption and rcu fixes

This patch set fixes different issues leading to corrupted lists and
incorrect rcu usage.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 63c60732 e9997c29
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -114,7 +114,9 @@ unsigned int ipvlan_mac_hash(const unsigned char *addr);
rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
				   const void *iaddr, bool is_v6);
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6);
struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
					const void *iaddr, bool is_v6);
void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync);
+20 −8
Original line number Diff line number Diff line
@@ -81,19 +81,20 @@ void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr)
	hash = (addr->atype == IPVL_IPV6) ?
	       ipvlan_get_v6_hash(&addr->ip6addr) :
	       ipvlan_get_v4_hash(&addr->ip4addr);
	if (hlist_unhashed(&addr->hlnode))
		hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
}

void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync)
{
	hlist_del_rcu(&addr->hlnode);
	hlist_del_init_rcu(&addr->hlnode);
	if (sync)
		synchronize_rcu();
}

bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
				   const void *iaddr, bool is_v6)
{
	struct ipvl_port *port = ipvlan->port;
	struct ipvl_addr *addr;

	list_for_each_entry(addr, &ipvlan->addrs, anode) {
@@ -101,12 +102,21 @@ bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
		    ipv6_addr_equal(&addr->ip6addr, iaddr)) ||
		    (!is_v6 && addr->atype == IPVL_IPV4 &&
		    addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr))
			return true;
			return addr;
	}
	return NULL;
}

	if (ipvlan_ht_addr_lookup(port, iaddr, is_v6))
		return true;
bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
{
	struct ipvl_dev *ipvlan;

	ASSERT_RTNL();

	list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
		if (ipvlan_find_addr(ipvlan, iaddr, is_v6))
			return true;
	}
	return false;
}

@@ -192,7 +202,8 @@ static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb,
	if (skb->protocol == htons(ETH_P_PAUSE))
		return;

	list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
	rcu_read_lock();
	list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {
		if (local && (ipvlan == in_dev))
			continue;

@@ -219,6 +230,7 @@ static void ipvlan_multicast_frame(struct ipvl_port *port, struct sk_buff *skb,
mcast_acct:
		ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);
	}
	rcu_read_unlock();

	/* Locally generated? ...Forward a copy to the main-device as
	 * well. On the RX side we'll ignore it (wont give it to any
+19 −11
Original line number Diff line number Diff line
@@ -505,7 +505,7 @@ static void ipvlan_link_delete(struct net_device *dev, struct list_head *head)
	if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
		list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
			ipvlan_ht_addr_del(addr, !dev->dismantle);
			list_del_rcu(&addr->anode);
			list_del(&addr->anode);
		}
	}
	list_del_rcu(&ipvlan->pnode);
@@ -607,7 +607,7 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
{
	struct ipvl_addr *addr;

	if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) {
	if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) {
		netif_err(ipvlan, ifup, ipvlan->dev,
			  "Failed to add IPv6=%pI6c addr for %s intf\n",
			  ip6_addr, ipvlan->dev->name);
@@ -620,8 +620,12 @@ static int ipvlan_add_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
	addr->master = ipvlan;
	memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr));
	addr->atype = IPVL_IPV6;
	list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
	list_add_tail(&addr->anode, &ipvlan->addrs);
	ipvlan->ipv6cnt++;
	/* If the interface is not up, the address will be added to the hash
	 * list by ipvlan_open.
	 */
	if (netif_running(ipvlan->dev))
		ipvlan_ht_addr_add(ipvlan, addr);

	return 0;
@@ -631,12 +635,12 @@ static void ipvlan_del_addr6(struct ipvl_dev *ipvlan, struct in6_addr *ip6_addr)
{
	struct ipvl_addr *addr;

	addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true);
	addr = ipvlan_find_addr(ipvlan, ip6_addr, true);
	if (!addr)
		return;

	ipvlan_ht_addr_del(addr, true);
	list_del_rcu(&addr->anode);
	list_del(&addr->anode);
	ipvlan->ipv6cnt--;
	WARN_ON(ipvlan->ipv6cnt < 0);
	kfree_rcu(addr, rcu);
@@ -675,7 +679,7 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
{
	struct ipvl_addr *addr;

	if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) {
	if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) {
		netif_err(ipvlan, ifup, ipvlan->dev,
			  "Failed to add IPv4=%pI4 on %s intf.\n",
			  ip4_addr, ipvlan->dev->name);
@@ -688,8 +692,12 @@ static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
	addr->master = ipvlan;
	memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr));
	addr->atype = IPVL_IPV4;
	list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
	list_add_tail(&addr->anode, &ipvlan->addrs);
	ipvlan->ipv4cnt++;
	/* If the interface is not up, the address will be added to the hash
	 * list by ipvlan_open.
	 */
	if (netif_running(ipvlan->dev))
		ipvlan_ht_addr_add(ipvlan, addr);
	ipvlan_set_broadcast_mac_filter(ipvlan, true);

@@ -700,12 +708,12 @@ static void ipvlan_del_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
{
	struct ipvl_addr *addr;

	addr = ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false);
	addr = ipvlan_find_addr(ipvlan, ip4_addr, false);
	if (!addr)
		return;

	ipvlan_ht_addr_del(addr, true);
	list_del_rcu(&addr->anode);
	list_del(&addr->anode);
	ipvlan->ipv4cnt--;
	WARN_ON(ipvlan->ipv4cnt < 0);
	if (!ipvlan->ipv4cnt)