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

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

Merge tag 'batman-adv-fix-for-davem' of git://git.open-mesh.org/linux-merge



Antonio Quartulli says:

====================
During the Wireless Battle Mesh v9 in Porto (PT) at the beginning of
May, we managed to uncover and fix some important bugs in our
new B.A.T.M.A.N. V algorithm. These are the fixes we came up with
together with others that I collected in the past weeks:
- avoid potential crash due to NULL pointer dereference in
  B.A.T.M.A.N. V routine when a neigh_ifinfo object is not found, by
  Sven Eckelmann
- avoid use-after-free of skb when counting outgoing bytes, by Florian
  Westphal
- fix neigh_ifinfo object reference counting imbalance when using
  B.A.T.M.A.N. V, by Sven Eckelmann. Such imbalance may lead to the
  impossibility of releasing the related netdev object on shutdown
- avoid invalid memory access in case of error while allocating
  bcast_own_sum when a new hard-interface is added, by Sven Eckelmann
- ensure originator address is updated in OMG/ELP packet content upon
  primary interface address change, by Antonio Quartulli
- fix integer overflow when computing TQ metric (B.A.T.M.A.N. IV), by
  Sven Eckelmann
- avoid race condition while adding new neigh_node which would result
  in having two objects mapping to the same physical neighbour, by
  Linus Lüssing
- ensure originator address is initialized in ELP packet content on
  secondary interfaces, by Marek Lindner
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6104503c ebe24cea
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -157,10 +157,8 @@ static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node,
	orig_node->bat_iv.bcast_own = data_ptr;

	data_ptr = kmalloc_array(max_if_num, sizeof(u8), GFP_ATOMIC);
	if (!data_ptr) {
		kfree(orig_node->bat_iv.bcast_own);
	if (!data_ptr)
		goto unlock;
	}

	memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum,
	       (max_if_num - 1) * sizeof(u8));
@@ -1182,9 +1180,10 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
	u8 total_count;
	u8 orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own;
	unsigned int neigh_rq_inv_cube, neigh_rq_max_cube;
	int tq_asym_penalty, inv_asym_penalty, if_num;
	int if_num;
	unsigned int tq_asym_penalty, inv_asym_penalty;
	unsigned int combined_tq;
	int tq_iface_penalty;
	unsigned int tq_iface_penalty;
	bool ret = false;

	/* find corresponding one hop neighbor */
+58 −8
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/seq_file.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/workqueue.h>

@@ -39,6 +40,16 @@

static void batadv_v_iface_activate(struct batadv_hard_iface *hard_iface)
{
	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
	struct batadv_hard_iface *primary_if;

	primary_if = batadv_primary_if_get_selected(bat_priv);

	if (primary_if) {
		batadv_v_elp_iface_activate(primary_if, hard_iface);
		batadv_hardif_put(primary_if);
	}

	/* B.A.T.M.A.N. V does not use any queuing mechanism, therefore it can
	 * set the interface as ACTIVE right away, without any risk of race
	 * condition
@@ -72,16 +83,34 @@ static void batadv_v_iface_disable(struct batadv_hard_iface *hard_iface)
	batadv_v_elp_iface_disable(hard_iface);
}

static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface)
{
}

static void batadv_v_primary_iface_set(struct batadv_hard_iface *hard_iface)
{
	batadv_v_elp_primary_iface_set(hard_iface);
	batadv_v_ogm_primary_iface_set(hard_iface);
}

/**
 * batadv_v_iface_update_mac - react to hard-interface MAC address change
 * @hard_iface: the modified interface
 *
 * If the modified interface is the primary one, update the originator
 * address in the ELP and OGM messages to reflect the new MAC address.
 */
static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface)
{
	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
	struct batadv_hard_iface *primary_if;

	primary_if = batadv_primary_if_get_selected(bat_priv);
	if (primary_if != hard_iface)
		goto out;

	batadv_v_primary_iface_set(hard_iface);
out:
	if (primary_if)
		batadv_hardif_put(primary_if);
}

static void
batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh)
{
@@ -255,14 +284,23 @@ static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1,
			      struct batadv_hard_iface *if_outgoing2)
{
	struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
	int ret = 0;

	ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
	if (WARN_ON(!ifinfo1))
		goto err_ifinfo1;

	ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
	if (WARN_ON(!ifinfo2))
		goto err_ifinfo2;

	if (WARN_ON(!ifinfo1 || !ifinfo2))
		return 0;
	ret = ifinfo1->bat_v.throughput - ifinfo2->bat_v.throughput;

	return ifinfo1->bat_v.throughput - ifinfo2->bat_v.throughput;
	batadv_neigh_ifinfo_put(ifinfo2);
err_ifinfo2:
	batadv_neigh_ifinfo_put(ifinfo1);
err_ifinfo1:
	return ret;
}

static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1,
@@ -272,14 +310,26 @@ static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1,
{
	struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
	u32 threshold;
	bool ret = false;

	ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
	if (WARN_ON(!ifinfo1))
		goto err_ifinfo1;

	ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
	if (WARN_ON(!ifinfo2))
		goto err_ifinfo2;

	threshold = ifinfo1->bat_v.throughput / 4;
	threshold = ifinfo1->bat_v.throughput - threshold;

	return ifinfo2->bat_v.throughput > threshold;
	ret = ifinfo2->bat_v.throughput > threshold;

	batadv_neigh_ifinfo_put(ifinfo2);
err_ifinfo2:
	batadv_neigh_ifinfo_put(ifinfo1);
err_ifinfo1:
	return ret;
}

static struct batadv_algo_ops batadv_batman_v __read_mostly = {
+22 −9
Original line number Diff line number Diff line
@@ -376,6 +376,27 @@ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface)
	hard_iface->bat_v.elp_skb = NULL;
}

/**
 * batadv_v_elp_iface_activate - update the ELP buffer belonging to the given
 *  hard-interface
 * @primary_iface: the new primary interface
 * @hard_iface: interface holding the to-be-updated buffer
 */
void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface,
				 struct batadv_hard_iface *hard_iface)
{
	struct batadv_elp_packet *elp_packet;
	struct sk_buff *skb;

	if (!hard_iface->bat_v.elp_skb)
		return;

	skb = hard_iface->bat_v.elp_skb;
	elp_packet = (struct batadv_elp_packet *)skb->data;
	ether_addr_copy(elp_packet->orig,
			primary_iface->net_dev->dev_addr);
}

/**
 * batadv_v_elp_primary_iface_set - change internal data to reflect the new
 *  primary interface
@@ -384,8 +405,6 @@ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface)
void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
{
	struct batadv_hard_iface *hard_iface;
	struct batadv_elp_packet *elp_packet;
	struct sk_buff *skb;

	/* update orig field of every elp iface belonging to this mesh */
	rcu_read_lock();
@@ -393,13 +412,7 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)
		if (primary_iface->soft_iface != hard_iface->soft_iface)
			continue;

		if (!hard_iface->bat_v.elp_skb)
			continue;

		skb = hard_iface->bat_v.elp_skb;
		elp_packet = (struct batadv_elp_packet *)skb->data;
		ether_addr_copy(elp_packet->orig,
				primary_iface->net_dev->dev_addr);
		batadv_v_elp_iface_activate(primary_iface, hard_iface);
	}
	rcu_read_unlock();
}
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ struct work_struct;

int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface);
void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface);
void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface,
				 struct batadv_hard_iface *hard_iface);
void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface);
int batadv_v_elp_packet_recv(struct sk_buff *skb,
			     struct batadv_hard_iface *if_incoming);
+4 −2
Original line number Diff line number Diff line
@@ -619,6 +619,8 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
	struct batadv_neigh_node *neigh_node;
	struct batadv_hardif_neigh_node *hardif_neigh = NULL;

	spin_lock_bh(&orig_node->neigh_list_lock);

	neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);
	if (neigh_node)
		goto out;
@@ -650,15 +652,15 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
	kref_init(&neigh_node->refcount);
	kref_get(&neigh_node->refcount);

	spin_lock_bh(&orig_node->neigh_list_lock);
	hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
	spin_unlock_bh(&orig_node->neigh_list_lock);

	batadv_dbg(BATADV_DBG_BATMAN, orig_node->bat_priv,
		   "Creating new neighbor %pM for orig_node %pM on interface %s\n",
		   neigh_addr, orig_node->orig, hard_iface->net_dev->name);

out:
	spin_unlock_bh(&orig_node->neigh_list_lock);

	if (hardif_neigh)
		batadv_hardif_neigh_put(hardif_neigh);
	return neigh_node;
Loading