Skip to content

Commit

Permalink
Merge tag 'batman-adv-for-davem' of git://git.open-mesh.org/linux-merge
Browse files Browse the repository at this point in the history
Antonio Quartulli says:

====================
Included changes:
- avoid integer overflow in GW selection routine
- prevent race condition by making capability bit changes atomic (use
  clear/set/test_bit)
- fix synchronization issue in mcast tvlv handler
- fix crash on double list removal of TT Request objects
- fix leak by puring packets enqueued for sending upon iface removal
- ensure network header pointer is set in skb
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 17, 2015
2 parents 2bd736f + 53cf037 commit c1f066d
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 48 deletions.
7 changes: 4 additions & 3 deletions net/batman-adv/distributed-arp-table.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "main.h"

#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
Expand Down Expand Up @@ -453,7 +454,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
int j;

/* check if orig node candidate is running DAT */
if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT))
if (!test_bit(BATADV_ORIG_CAPA_HAS_DAT, &candidate->capabilities))
goto out;

/* Check if this node has already been selected... */
Expand Down Expand Up @@ -713,9 +714,9 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
uint16_t tvlv_value_len)
{
if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT;
clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
else
orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT;
set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
}

/**
Expand Down
8 changes: 2 additions & 6 deletions net/batman-adv/gateway_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,11 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
struct batadv_neigh_node *router;
struct batadv_neigh_ifinfo *router_ifinfo;
struct batadv_gw_node *gw_node, *curr_gw = NULL;
uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
uint32_t gw_divisor;
uint64_t max_gw_factor = 0, tmp_gw_factor = 0;
uint8_t max_tq = 0;
uint8_t tq_avg;
struct batadv_orig_node *orig_node;

gw_divisor = BATADV_TQ_LOCAL_WINDOW_SIZE * BATADV_TQ_LOCAL_WINDOW_SIZE;
gw_divisor *= 64;

rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
if (gw_node->deleted)
Expand All @@ -187,7 +183,7 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
tmp_gw_factor = tq_avg * tq_avg;
tmp_gw_factor *= gw_node->bandwidth_down;
tmp_gw_factor *= 100 * 100;
tmp_gw_factor /= gw_divisor;
tmp_gw_factor >>= 18;

if ((tmp_gw_factor > max_gw_factor) ||
((tmp_gw_factor == max_gw_factor) &&
Expand Down
81 changes: 60 additions & 21 deletions net/batman-adv/multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "main.h"

#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/byteorder/generic.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
Expand Down Expand Up @@ -588,27 +590,37 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
*
* If the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag of this originator,
* orig, has toggled then this method updates counter and list accordingly.
*
* Caller needs to hold orig->mcast_handler_lock.
*/
static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
uint8_t mcast_flags)
{
struct hlist_node *node = &orig->mcast_want_all_unsnoopables_node;
struct hlist_head *head = &bat_priv->mcast.want_all_unsnoopables_list;

/* switched from flag unset to set */
if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES &&
!(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) {
atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables);

spin_lock_bh(&bat_priv->mcast.want_lists_lock);
hlist_add_head_rcu(&orig->mcast_want_all_unsnoopables_node,
&bat_priv->mcast.want_all_unsnoopables_list);
/* flag checks above + mcast_handler_lock prevents this */
WARN_ON(!hlist_unhashed(node));

hlist_add_head_rcu(node, head);
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
/* switched from flag set to unset */
} else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) &&
orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) {
atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables);

spin_lock_bh(&bat_priv->mcast.want_lists_lock);
hlist_del_rcu(&orig->mcast_want_all_unsnoopables_node);
/* flag checks above + mcast_handler_lock prevents this */
WARN_ON(hlist_unhashed(node));

hlist_del_init_rcu(node);
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
}
}
Expand All @@ -621,27 +633,37 @@ static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv,
*
* If the BATADV_MCAST_WANT_ALL_IPV4 flag of this originator, orig, has
* toggled then this method updates counter and list accordingly.
*
* Caller needs to hold orig->mcast_handler_lock.
*/
static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
uint8_t mcast_flags)
{
struct hlist_node *node = &orig->mcast_want_all_ipv4_node;
struct hlist_head *head = &bat_priv->mcast.want_all_ipv4_list;

/* switched from flag unset to set */
if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 &&
!(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) {
atomic_inc(&bat_priv->mcast.num_want_all_ipv4);

spin_lock_bh(&bat_priv->mcast.want_lists_lock);
hlist_add_head_rcu(&orig->mcast_want_all_ipv4_node,
&bat_priv->mcast.want_all_ipv4_list);
/* flag checks above + mcast_handler_lock prevents this */
WARN_ON(!hlist_unhashed(node));

hlist_add_head_rcu(node, head);
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
/* switched from flag set to unset */
} else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) &&
orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) {
atomic_dec(&bat_priv->mcast.num_want_all_ipv4);

spin_lock_bh(&bat_priv->mcast.want_lists_lock);
hlist_del_rcu(&orig->mcast_want_all_ipv4_node);
/* flag checks above + mcast_handler_lock prevents this */
WARN_ON(hlist_unhashed(node));

hlist_del_init_rcu(node);
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
}
}
Expand All @@ -654,27 +676,37 @@ static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv,
*
* If the BATADV_MCAST_WANT_ALL_IPV6 flag of this originator, orig, has
* toggled then this method updates counter and list accordingly.
*
* Caller needs to hold orig->mcast_handler_lock.
*/
static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
uint8_t mcast_flags)
{
struct hlist_node *node = &orig->mcast_want_all_ipv6_node;
struct hlist_head *head = &bat_priv->mcast.want_all_ipv6_list;

/* switched from flag unset to set */
if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 &&
!(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) {
atomic_inc(&bat_priv->mcast.num_want_all_ipv6);

spin_lock_bh(&bat_priv->mcast.want_lists_lock);
hlist_add_head_rcu(&orig->mcast_want_all_ipv6_node,
&bat_priv->mcast.want_all_ipv6_list);
/* flag checks above + mcast_handler_lock prevents this */
WARN_ON(!hlist_unhashed(node));

hlist_add_head_rcu(node, head);
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
/* switched from flag set to unset */
} else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) &&
orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) {
atomic_dec(&bat_priv->mcast.num_want_all_ipv6);

spin_lock_bh(&bat_priv->mcast.want_lists_lock);
hlist_del_rcu(&orig->mcast_want_all_ipv6_node);
/* flag checks above + mcast_handler_lock prevents this */
WARN_ON(hlist_unhashed(node));

hlist_del_init_rcu(node);
spin_unlock_bh(&bat_priv->mcast.want_lists_lock);
}
}
Expand All @@ -697,39 +729,42 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
uint8_t mcast_flags = BATADV_NO_FLAGS;
bool orig_initialized;

orig_initialized = orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST;
if (orig_mcast_enabled && tvlv_value &&
(tvlv_value_len >= sizeof(mcast_flags)))
mcast_flags = *(uint8_t *)tvlv_value;

spin_lock_bh(&orig->mcast_handler_lock);
orig_initialized = test_bit(BATADV_ORIG_CAPA_HAS_MCAST,
&orig->capa_initialized);

/* If mcast support is turned on decrease the disabled mcast node
* counter only if we had increased it for this node before. If this
* is a completely new orig_node no need to decrease the counter.
*/
if (orig_mcast_enabled &&
!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) {
!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities)) {
if (orig_initialized)
atomic_dec(&bat_priv->mcast.num_disabled);
orig->capabilities |= BATADV_ORIG_CAPA_HAS_MCAST;
set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities);
/* If mcast support is being switched off or if this is an initial
* OGM without mcast support then increase the disabled mcast
* node counter.
*/
} else if (!orig_mcast_enabled &&
(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST ||
(test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities) ||
!orig_initialized)) {
atomic_inc(&bat_priv->mcast.num_disabled);
orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_MCAST;
clear_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities);
}

orig->capa_initialized |= BATADV_ORIG_CAPA_HAS_MCAST;

if (orig_mcast_enabled && tvlv_value &&
(tvlv_value_len >= sizeof(mcast_flags)))
mcast_flags = *(uint8_t *)tvlv_value;
set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capa_initialized);

batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags);
batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags);
batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags);

orig->mcast_flags = mcast_flags;
spin_unlock_bh(&orig->mcast_handler_lock);
}

/**
Expand Down Expand Up @@ -763,11 +798,15 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
{
struct batadv_priv *bat_priv = orig->bat_priv;

if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST) &&
orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST)
spin_lock_bh(&orig->mcast_handler_lock);

if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities) &&
test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capa_initialized))
atomic_dec(&bat_priv->mcast.num_disabled);

batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS);
batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS);
batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS);

spin_unlock_bh(&orig->mcast_handler_lock);
}
7 changes: 4 additions & 3 deletions net/batman-adv/network-coding.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "main.h"

#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/byteorder/generic.h>
#include <linux/compiler.h>
#include <linux/debugfs.h>
Expand Down Expand Up @@ -134,9 +135,9 @@ static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
uint16_t tvlv_value_len)
{
if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_NC;
clear_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities);
else
orig->capabilities |= BATADV_ORIG_CAPA_HAS_NC;
set_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities);
}

/**
Expand Down Expand Up @@ -894,7 +895,7 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
goto out;

/* check if orig node is network coding enabled */
if (!(orig_node->capabilities & BATADV_ORIG_CAPA_HAS_NC))
if (!test_bit(BATADV_ORIG_CAPA_HAS_NC, &orig_node->capabilities))
goto out;

/* accept ogms from 'good' neighbors and single hop neighbors */
Expand Down
5 changes: 5 additions & 0 deletions net/batman-adv/originator.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,8 +696,13 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
orig_node->last_seen = jiffies;
reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
orig_node->bcast_seqno_reset = reset_time;

#ifdef CONFIG_BATMAN_ADV_MCAST
orig_node->mcast_flags = BATADV_NO_FLAGS;
INIT_HLIST_NODE(&orig_node->mcast_want_all_unsnoopables_node);
INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv4_node);
INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv6_node);
spin_lock_init(&orig_node->mcast_handler_lock);
#endif

/* create a vlan object for the "untagged" LAN */
Expand Down
3 changes: 2 additions & 1 deletion net/batman-adv/send.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,8 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
* we delete only packets belonging to the given interface
*/
if ((hard_iface) &&
(forw_packet->if_incoming != hard_iface))
(forw_packet->if_incoming != hard_iface) &&
(forw_packet->if_outgoing != hard_iface))
continue;

spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
Expand Down
7 changes: 6 additions & 1 deletion net/batman-adv/soft-interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
int gw_mode;
enum batadv_forw_mode forw_mode;
struct batadv_orig_node *mcast_single_orig = NULL;
int network_offset = ETH_HLEN;

if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
Expand All @@ -214,14 +215,18 @@ static int batadv_interface_tx(struct sk_buff *skb,
case ETH_P_8021Q:
vhdr = vlan_eth_hdr(skb);

if (vhdr->h_vlan_encapsulated_proto != ethertype)
if (vhdr->h_vlan_encapsulated_proto != ethertype) {
network_offset += VLAN_HLEN;
break;
}

/* fall through */
case ETH_P_BATMAN:
goto dropped;
}

skb_set_network_header(skb, network_offset);

if (batadv_bla_tx(bat_priv, skb, vid))
goto dropped;

Expand Down
Loading

0 comments on commit c1f066d

Please sign in to comment.