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

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

Merge tag 'batadv-net-for-davem-20160708' of git://git.open-mesh.org/linux-merge



Simon Wunderlich says:

====================
Here are a couple batman-adv bugfix patches, all by Sven Eckelmann:

 - Fix possible NULL pointer dereference for vlan_insert_tag (two patches)

 - Fix reference handling in some features, which may lead to reference
   leaks or invalid memory access (four patches)

 - Fix speedy join: DHCP packets handled by the gateway feature should
   be sent with 4-address unicast instead of 3-address unicast to make
   speedy join work. This fixes/speeds up DHCP assignment for clients
   which join a mesh for the first time. (one patch)
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 95556a88 d1fe176c
Loading
Loading
Loading
Loading
+92 −24
Original line number Diff line number Diff line
@@ -177,10 +177,21 @@ static void batadv_backbone_gw_put(struct batadv_bla_backbone_gw *backbone_gw)
static void batadv_claim_release(struct kref *ref)
{
	struct batadv_bla_claim *claim;
	struct batadv_bla_backbone_gw *old_backbone_gw;

	claim = container_of(ref, struct batadv_bla_claim, refcount);

	batadv_backbone_gw_put(claim->backbone_gw);
	spin_lock_bh(&claim->backbone_lock);
	old_backbone_gw = claim->backbone_gw;
	claim->backbone_gw = NULL;
	spin_unlock_bh(&claim->backbone_lock);

	spin_lock_bh(&old_backbone_gw->crc_lock);
	old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
	spin_unlock_bh(&old_backbone_gw->crc_lock);

	batadv_backbone_gw_put(old_backbone_gw);

	kfree_rcu(claim, rcu);
}

@@ -418,9 +429,12 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac,
		break;
	}

	if (vid & BATADV_VLAN_HAS_TAG)
	if (vid & BATADV_VLAN_HAS_TAG) {
		skb = vlan_insert_tag(skb, htons(ETH_P_8021Q),
				      vid & VLAN_VID_MASK);
		if (!skb)
			goto out;
	}

	skb_reset_mac_header(skb);
	skb->protocol = eth_type_trans(skb, soft_iface);
@@ -674,8 +688,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
				 const u8 *mac, const unsigned short vid,
				 struct batadv_bla_backbone_gw *backbone_gw)
{
	struct batadv_bla_backbone_gw *old_backbone_gw;
	struct batadv_bla_claim *claim;
	struct batadv_bla_claim search_claim;
	bool remove_crc = false;
	int hash_added;

	ether_addr_copy(search_claim.addr, mac);
@@ -689,8 +705,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
			return;

		ether_addr_copy(claim->addr, mac);
		spin_lock_init(&claim->backbone_lock);
		claim->vid = vid;
		claim->lasttime = jiffies;
		kref_get(&backbone_gw->refcount);
		claim->backbone_gw = backbone_gw;

		kref_init(&claim->refcount);
@@ -718,15 +736,26 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
			   "bla_add_claim(): changing ownership for %pM, vid %d\n",
			   mac, BATADV_PRINT_VID(vid));

		spin_lock_bh(&claim->backbone_gw->crc_lock);
		claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
		spin_unlock_bh(&claim->backbone_gw->crc_lock);
		batadv_backbone_gw_put(claim->backbone_gw);
		remove_crc = true;
	}
	/* set (new) backbone gw */

	/* replace backbone_gw atomically and adjust reference counters */
	spin_lock_bh(&claim->backbone_lock);
	old_backbone_gw = claim->backbone_gw;
	kref_get(&backbone_gw->refcount);
	claim->backbone_gw = backbone_gw;
	spin_unlock_bh(&claim->backbone_lock);

	if (remove_crc) {
		/* remove claim address from old backbone_gw */
		spin_lock_bh(&old_backbone_gw->crc_lock);
		old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
		spin_unlock_bh(&old_backbone_gw->crc_lock);
	}

	batadv_backbone_gw_put(old_backbone_gw);

	/* add claim address to new backbone_gw */
	spin_lock_bh(&backbone_gw->crc_lock);
	backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
	spin_unlock_bh(&backbone_gw->crc_lock);
@@ -736,6 +765,26 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
	batadv_claim_put(claim);
}

/**
 * batadv_bla_claim_get_backbone_gw - Get valid reference for backbone_gw of
 *  claim
 * @claim: claim whose backbone_gw should be returned
 *
 * Return: valid reference to claim::backbone_gw
 */
static struct batadv_bla_backbone_gw *
batadv_bla_claim_get_backbone_gw(struct batadv_bla_claim *claim)
{
	struct batadv_bla_backbone_gw *backbone_gw;

	spin_lock_bh(&claim->backbone_lock);
	backbone_gw = claim->backbone_gw;
	kref_get(&backbone_gw->refcount);
	spin_unlock_bh(&claim->backbone_lock);

	return backbone_gw;
}

/**
 * batadv_bla_del_claim - delete a claim from the claim hash
 * @bat_priv: the bat priv with all the soft interface information
@@ -760,10 +809,6 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
			   batadv_choose_claim, claim);
	batadv_claim_put(claim); /* reference from the hash is gone */

	spin_lock_bh(&claim->backbone_gw->crc_lock);
	claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
	spin_unlock_bh(&claim->backbone_gw->crc_lock);

	/* don't need the reference from hash_find() anymore */
	batadv_claim_put(claim);
}
@@ -1216,6 +1261,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
				    struct batadv_hard_iface *primary_if,
				    int now)
{
	struct batadv_bla_backbone_gw *backbone_gw;
	struct batadv_bla_claim *claim;
	struct hlist_head *head;
	struct batadv_hashtable *hash;
@@ -1230,14 +1276,17 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,

		rcu_read_lock();
		hlist_for_each_entry_rcu(claim, head, hash_entry) {
			backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
			if (now)
				goto purge_now;
			if (!batadv_compare_eth(claim->backbone_gw->orig,

			if (!batadv_compare_eth(backbone_gw->orig,
						primary_if->net_dev->dev_addr))
				continue;
				goto skip;

			if (!batadv_has_timed_out(claim->lasttime,
						  BATADV_BLA_CLAIM_TIMEOUT))
				continue;
				goto skip;

			batadv_dbg(BATADV_DBG_BLA, bat_priv,
				   "bla_purge_claims(): %pM, vid %d, time out\n",
@@ -1245,8 +1294,10 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,

purge_now:
			batadv_handle_unclaim(bat_priv, primary_if,
					      claim->backbone_gw->orig,
					      backbone_gw->orig,
					      claim->addr, claim->vid);
skip:
			batadv_backbone_gw_put(backbone_gw);
		}
		rcu_read_unlock();
	}
@@ -1757,9 +1808,11 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
		   unsigned short vid, bool is_bcast)
{
	struct batadv_bla_backbone_gw *backbone_gw;
	struct ethhdr *ethhdr;
	struct batadv_bla_claim search_claim, *claim = NULL;
	struct batadv_hard_iface *primary_if;
	bool own_claim;
	bool ret;

	ethhdr = eth_hdr(skb);
@@ -1794,8 +1847,12 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
	}

	/* if it is our own claim ... */
	if (batadv_compare_eth(claim->backbone_gw->orig,
			       primary_if->net_dev->dev_addr)) {
	backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
	own_claim = batadv_compare_eth(backbone_gw->orig,
				       primary_if->net_dev->dev_addr);
	batadv_backbone_gw_put(backbone_gw);

	if (own_claim) {
		/* ... allow it in any case */
		claim->lasttime = jiffies;
		goto allow;
@@ -1859,7 +1916,9 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
{
	struct ethhdr *ethhdr;
	struct batadv_bla_claim search_claim, *claim = NULL;
	struct batadv_bla_backbone_gw *backbone_gw;
	struct batadv_hard_iface *primary_if;
	bool client_roamed;
	bool ret = false;

	primary_if = batadv_primary_if_get_selected(bat_priv);
@@ -1889,8 +1948,12 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
		goto allow;

	/* check if we are responsible. */
	if (batadv_compare_eth(claim->backbone_gw->orig,
			       primary_if->net_dev->dev_addr)) {
	backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
	client_roamed = batadv_compare_eth(backbone_gw->orig,
					   primary_if->net_dev->dev_addr);
	batadv_backbone_gw_put(backbone_gw);

	if (client_roamed) {
		/* if yes, the client has roamed and we have
		 * to unclaim it.
		 */
@@ -1938,6 +2001,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
	struct net_device *net_dev = (struct net_device *)seq->private;
	struct batadv_priv *bat_priv = netdev_priv(net_dev);
	struct batadv_hashtable *hash = bat_priv->bla.claim_hash;
	struct batadv_bla_backbone_gw *backbone_gw;
	struct batadv_bla_claim *claim;
	struct batadv_hard_iface *primary_if;
	struct hlist_head *head;
@@ -1962,17 +2026,21 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)

		rcu_read_lock();
		hlist_for_each_entry_rcu(claim, head, hash_entry) {
			is_own = batadv_compare_eth(claim->backbone_gw->orig,
			backbone_gw = batadv_bla_claim_get_backbone_gw(claim);

			is_own = batadv_compare_eth(backbone_gw->orig,
						    primary_addr);

			spin_lock_bh(&claim->backbone_gw->crc_lock);
			backbone_crc = claim->backbone_gw->crc;
			spin_unlock_bh(&claim->backbone_gw->crc_lock);
			spin_lock_bh(&backbone_gw->crc_lock);
			backbone_crc = backbone_gw->crc;
			spin_unlock_bh(&backbone_gw->crc_lock);
			seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n",
				   claim->addr, BATADV_PRINT_VID(claim->vid),
				   claim->backbone_gw->orig,
				   backbone_gw->orig,
				   (is_own ? 'x' : ' '),
				   backbone_crc);

			batadv_backbone_gw_put(backbone_gw);
		}
		rcu_read_unlock();
	}
+8 −2
Original line number Diff line number Diff line
@@ -1009,9 +1009,12 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
		if (!skb_new)
			goto out;

		if (vid & BATADV_VLAN_HAS_TAG)
		if (vid & BATADV_VLAN_HAS_TAG) {
			skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
						  vid & VLAN_VID_MASK);
			if (!skb_new)
				goto out;
		}

		skb_reset_mac_header(skb_new);
		skb_new->protocol = eth_type_trans(skb_new,
@@ -1089,9 +1092,12 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
	 */
	skb_reset_mac_header(skb_new);

	if (vid & BATADV_VLAN_HAS_TAG)
	if (vid & BATADV_VLAN_HAS_TAG) {
		skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
					  vid & VLAN_VID_MASK);
		if (!skb_new)
			goto out;
	}

	/* To preserve backwards compatibility, the node has choose the outgoing
	 * format based on the incoming request packet type. The assumption is
+15 −0
Original line number Diff line number Diff line
@@ -765,6 +765,8 @@ static void batadv_orig_node_release(struct kref *ref)
	struct batadv_neigh_node *neigh_node;
	struct batadv_orig_node *orig_node;
	struct batadv_orig_ifinfo *orig_ifinfo;
	struct batadv_orig_node_vlan *vlan;
	struct batadv_orig_ifinfo *last_candidate;

	orig_node = container_of(ref, struct batadv_orig_node, refcount);

@@ -782,8 +784,21 @@ static void batadv_orig_node_release(struct kref *ref)
		hlist_del_rcu(&orig_ifinfo->list);
		batadv_orig_ifinfo_put(orig_ifinfo);
	}

	last_candidate = orig_node->last_bonding_candidate;
	orig_node->last_bonding_candidate = NULL;
	spin_unlock_bh(&orig_node->neigh_list_lock);

	if (last_candidate)
		batadv_orig_ifinfo_put(last_candidate);

	spin_lock_bh(&orig_node->vlan_list_lock);
	hlist_for_each_entry_safe(vlan, node_tmp, &orig_node->vlan_list, list) {
		hlist_del_rcu(&vlan->list);
		batadv_orig_node_vlan_put(vlan);
	}
	spin_unlock_bh(&orig_node->vlan_list_lock);

	/* Free nc_nodes */
	batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);

+39 −13
Original line number Diff line number Diff line
@@ -455,6 +455,29 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
	return 0;
}

/**
 * batadv_last_bonding_replace - Replace last_bonding_candidate of orig_node
 * @orig_node: originator node whose bonding candidates should be replaced
 * @new_candidate: new bonding candidate or NULL
 */
static void
batadv_last_bonding_replace(struct batadv_orig_node *orig_node,
			    struct batadv_orig_ifinfo *new_candidate)
{
	struct batadv_orig_ifinfo *old_candidate;

	spin_lock_bh(&orig_node->neigh_list_lock);
	old_candidate = orig_node->last_bonding_candidate;

	if (new_candidate)
		kref_get(&new_candidate->refcount);
	orig_node->last_bonding_candidate = new_candidate;
	spin_unlock_bh(&orig_node->neigh_list_lock);

	if (old_candidate)
		batadv_orig_ifinfo_put(old_candidate);
}

/**
 * batadv_find_router - find a suitable router for this originator
 * @bat_priv: the bat priv with all the soft interface information
@@ -562,10 +585,6 @@ batadv_find_router(struct batadv_priv *bat_priv,
	}
	rcu_read_unlock();

	/* last_bonding_candidate is reset below, remove the old reference. */
	if (orig_node->last_bonding_candidate)
		batadv_orig_ifinfo_put(orig_node->last_bonding_candidate);

	/* After finding candidates, handle the three cases:
	 * 1) there is a next candidate, use that
	 * 2) there is no next candidate, use the first of the list
@@ -574,21 +593,28 @@ batadv_find_router(struct batadv_priv *bat_priv,
	if (next_candidate) {
		batadv_neigh_node_put(router);

		/* remove references to first candidate, we don't need it. */
		if (first_candidate) {
			batadv_neigh_node_put(first_candidate_router);
			batadv_orig_ifinfo_put(first_candidate);
		}
		kref_get(&next_candidate_router->refcount);
		router = next_candidate_router;
		orig_node->last_bonding_candidate = next_candidate;
		batadv_last_bonding_replace(orig_node, next_candidate);
	} else if (first_candidate) {
		batadv_neigh_node_put(router);

		/* refcounting has already been done in the loop above. */
		kref_get(&first_candidate_router->refcount);
		router = first_candidate_router;
		orig_node->last_bonding_candidate = first_candidate;
		batadv_last_bonding_replace(orig_node, first_candidate);
	} else {
		orig_node->last_bonding_candidate = NULL;
		batadv_last_bonding_replace(orig_node, NULL);
	}

	/* cleanup of candidates */
	if (first_candidate) {
		batadv_neigh_node_put(first_candidate_router);
		batadv_orig_ifinfo_put(first_candidate);
	}

	if (next_candidate) {
		batadv_neigh_node_put(next_candidate_router);
		batadv_orig_ifinfo_put(next_candidate);
	}

	return router;
+2 −2
Original line number Diff line number Diff line
@@ -424,8 +424,8 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
	struct batadv_orig_node *orig_node;

	orig_node = batadv_gw_get_selected_orig(bat_priv);
	return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
				       orig_node, vid);
	return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST_4ADDR,
				       BATADV_P_DATA, orig_node, vid);
}

void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
Loading