Commit 850717ef authored by David S. Miller's avatar David S. Miller

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

Antonio Quartulli says:

====================
pull request [net]: batman-adv 20140721

here you have two fixes that we have been testing for quite some time
(this is why they arrived a bit late in the rc cycle).

Patch 1) ensures that BLA packets get dropped and not forwarded to the
mesh even if they reach batman-adv within QinQ frames. Forwarding them
into the mesh means messing up with the TT database of other nodes which
can generate all kind of unexpected behaviours during route computation.

Patch 2) avoids a couple of race conditions triggered upon fast VLAN
deletion-addition. Such race conditions are pretty dangerous because
they not only create inconsistencies in the TT database of the nodes
in the network, but such scenario is also unrecoverable (unless
nodes are rebooted).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 10ec9472 35df3b29
...@@ -800,11 +800,6 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv, ...@@ -800,11 +800,6 @@ static int batadv_check_claim_group(struct batadv_priv *bat_priv,
bla_dst = (struct batadv_bla_claim_dst *)hw_dst; bla_dst = (struct batadv_bla_claim_dst *)hw_dst;
bla_dst_own = &bat_priv->bla.claim_dest; bla_dst_own = &bat_priv->bla.claim_dest;
/* check if it is a claim packet in general */
if (memcmp(bla_dst->magic, bla_dst_own->magic,
sizeof(bla_dst->magic)) != 0)
return 0;
/* if announcement packet, use the source, /* if announcement packet, use the source,
* otherwise assume it is in the hw_src * otherwise assume it is in the hw_src
*/ */
...@@ -866,12 +861,13 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, ...@@ -866,12 +861,13 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if, struct batadv_hard_iface *primary_if,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct batadv_bla_claim_dst *bla_dst; struct batadv_bla_claim_dst *bla_dst, *bla_dst_own;
uint8_t *hw_src, *hw_dst; uint8_t *hw_src, *hw_dst;
struct vlan_ethhdr *vhdr; struct vlan_hdr *vhdr, vhdr_buf;
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
struct arphdr *arphdr; struct arphdr *arphdr;
unsigned short vid; unsigned short vid;
int vlan_depth = 0;
__be16 proto; __be16 proto;
int headlen; int headlen;
int ret; int ret;
...@@ -882,9 +878,24 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, ...@@ -882,9 +878,24 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
proto = ethhdr->h_proto; proto = ethhdr->h_proto;
headlen = ETH_HLEN; headlen = ETH_HLEN;
if (vid & BATADV_VLAN_HAS_TAG) { if (vid & BATADV_VLAN_HAS_TAG) {
vhdr = vlan_eth_hdr(skb); /* Traverse the VLAN/Ethertypes.
proto = vhdr->h_vlan_encapsulated_proto; *
headlen += VLAN_HLEN; * At this point it is known that the first protocol is a VLAN
* header, so start checking at the encapsulated protocol.
*
* The depth of the VLAN headers is recorded to drop BLA claim
* frames encapsulated into multiple VLAN headers (QinQ).
*/
do {
vhdr = skb_header_pointer(skb, headlen, VLAN_HLEN,
&vhdr_buf);
if (!vhdr)
return 0;
proto = vhdr->h_vlan_encapsulated_proto;
headlen += VLAN_HLEN;
vlan_depth++;
} while (proto == htons(ETH_P_8021Q));
} }
if (proto != htons(ETH_P_ARP)) if (proto != htons(ETH_P_ARP))
...@@ -914,6 +925,19 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, ...@@ -914,6 +925,19 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
hw_src = (uint8_t *)arphdr + sizeof(struct arphdr); hw_src = (uint8_t *)arphdr + sizeof(struct arphdr);
hw_dst = hw_src + ETH_ALEN + 4; hw_dst = hw_src + ETH_ALEN + 4;
bla_dst = (struct batadv_bla_claim_dst *)hw_dst; bla_dst = (struct batadv_bla_claim_dst *)hw_dst;
bla_dst_own = &bat_priv->bla.claim_dest;
/* check if it is a claim frame in general */
if (memcmp(bla_dst->magic, bla_dst_own->magic,
sizeof(bla_dst->magic)) != 0)
return 0;
/* check if there is a claim frame encapsulated deeper in (QinQ) and
* drop that, as this is not supported by BLA but should also not be
* sent via the mesh.
*/
if (vlan_depth > 1)
return 1;
/* check if it is a claim frame. */ /* check if it is a claim frame. */
ret = batadv_check_claim_group(bat_priv, primary_if, hw_src, hw_dst, ret = batadv_check_claim_group(bat_priv, primary_if, hw_src, hw_dst,
......
...@@ -448,10 +448,15 @@ void batadv_interface_rx(struct net_device *soft_iface, ...@@ -448,10 +448,15 @@ void batadv_interface_rx(struct net_device *soft_iface,
* possibly free it * possibly free it
* @softif_vlan: the vlan object to release * @softif_vlan: the vlan object to release
*/ */
void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan) void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *vlan)
{ {
if (atomic_dec_and_test(&softif_vlan->refcount)) if (atomic_dec_and_test(&vlan->refcount)) {
kfree_rcu(softif_vlan, rcu); spin_lock_bh(&vlan->bat_priv->softif_vlan_list_lock);
hlist_del_rcu(&vlan->list);
spin_unlock_bh(&vlan->bat_priv->softif_vlan_list_lock);
kfree_rcu(vlan, rcu);
}
} }
/** /**
...@@ -505,6 +510,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) ...@@ -505,6 +510,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
if (!vlan) if (!vlan)
return -ENOMEM; return -ENOMEM;
vlan->bat_priv = bat_priv;
vlan->vid = vid; vlan->vid = vid;
atomic_set(&vlan->refcount, 1); atomic_set(&vlan->refcount, 1);
...@@ -516,6 +522,10 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) ...@@ -516,6 +522,10 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
return err; return err;
} }
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
/* add a new TT local entry. This one will be marked with the NOPURGE /* add a new TT local entry. This one will be marked with the NOPURGE
* flag * flag
*/ */
...@@ -523,10 +533,6 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) ...@@ -523,10 +533,6 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
bat_priv->soft_iface->dev_addr, vid, bat_priv->soft_iface->dev_addr, vid,
BATADV_NULL_IFINDEX, BATADV_NO_MARK); BATADV_NULL_IFINDEX, BATADV_NO_MARK);
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
return 0; return 0;
} }
...@@ -538,18 +544,13 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) ...@@ -538,18 +544,13 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv, static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
struct batadv_softif_vlan *vlan) struct batadv_softif_vlan *vlan)
{ {
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
hlist_del_rcu(&vlan->list);
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
batadv_sysfs_del_vlan(bat_priv, vlan);
/* explicitly remove the associated TT local entry because it is marked /* explicitly remove the associated TT local entry because it is marked
* with the NOPURGE flag * with the NOPURGE flag
*/ */
batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr, batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr,
vlan->vid, "vlan interface destroyed", false); vlan->vid, "vlan interface destroyed", false);
batadv_sysfs_del_vlan(bat_priv, vlan);
batadv_softif_vlan_free_ref(vlan); batadv_softif_vlan_free_ref(vlan);
} }
...@@ -567,6 +568,8 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto, ...@@ -567,6 +568,8 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
unsigned short vid) unsigned short vid)
{ {
struct batadv_priv *bat_priv = netdev_priv(dev); struct batadv_priv *bat_priv = netdev_priv(dev);
struct batadv_softif_vlan *vlan;
int ret;
/* only 802.1Q vlans are supported. /* only 802.1Q vlans are supported.
* batman-adv does not know how to handle other types * batman-adv does not know how to handle other types
...@@ -576,7 +579,36 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto, ...@@ -576,7 +579,36 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
vid |= BATADV_VLAN_HAS_TAG; vid |= BATADV_VLAN_HAS_TAG;
return batadv_softif_create_vlan(bat_priv, vid); /* if a new vlan is getting created and it already exists, it means that
* it was not deleted yet. batadv_softif_vlan_get() increases the
* refcount in order to revive the object.
*
* if it does not exist then create it.
*/
vlan = batadv_softif_vlan_get(bat_priv, vid);
if (!vlan)
return batadv_softif_create_vlan(bat_priv, vid);
/* recreate the sysfs object if it was already destroyed (and it should
* be since we received a kill_vid() for this vlan
*/
if (!vlan->kobj) {
ret = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan);
if (ret) {
batadv_softif_vlan_free_ref(vlan);
return ret;
}
}
/* add a new TT local entry. This one will be marked with the NOPURGE
* flag. This must be added again, even if the vlan object already
* exists, because the entry was deleted by kill_vid()
*/
batadv_tt_local_add(bat_priv->soft_iface,
bat_priv->soft_iface->dev_addr, vid,
BATADV_NULL_IFINDEX, BATADV_NO_MARK);
return 0;
} }
/** /**
......
...@@ -511,6 +511,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, ...@@ -511,6 +511,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_priv *bat_priv = netdev_priv(soft_iface);
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_tt_global_entry *tt_global = NULL; struct batadv_tt_global_entry *tt_global = NULL;
struct batadv_softif_vlan *vlan;
struct net_device *in_dev = NULL; struct net_device *in_dev = NULL;
struct hlist_head *head; struct hlist_head *head;
struct batadv_tt_orig_list_entry *orig_entry; struct batadv_tt_orig_list_entry *orig_entry;
...@@ -572,6 +573,9 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, ...@@ -572,6 +573,9 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
if (!tt_local) if (!tt_local)
goto out; goto out;
/* increase the refcounter of the related vlan */
vlan = batadv_softif_vlan_get(bat_priv, vid);
batadv_dbg(BATADV_DBG_TT, bat_priv, batadv_dbg(BATADV_DBG_TT, bat_priv,
"Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n", "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n",
addr, BATADV_PRINT_VID(vid), addr, BATADV_PRINT_VID(vid),
...@@ -604,6 +608,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, ...@@ -604,6 +608,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
if (unlikely(hash_added != 0)) { if (unlikely(hash_added != 0)) {
/* remove the reference for the hash */ /* remove the reference for the hash */
batadv_tt_local_entry_free_ref(tt_local); batadv_tt_local_entry_free_ref(tt_local);
batadv_softif_vlan_free_ref(vlan);
goto out; goto out;
} }
...@@ -1009,6 +1014,7 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, ...@@ -1009,6 +1014,7 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
{ {
struct batadv_tt_local_entry *tt_local_entry; struct batadv_tt_local_entry *tt_local_entry;
uint16_t flags, curr_flags = BATADV_NO_FLAGS; uint16_t flags, curr_flags = BATADV_NO_FLAGS;
struct batadv_softif_vlan *vlan;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!tt_local_entry) if (!tt_local_entry)
...@@ -1039,6 +1045,11 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, ...@@ -1039,6 +1045,11 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
hlist_del_rcu(&tt_local_entry->common.hash_entry); hlist_del_rcu(&tt_local_entry->common.hash_entry);
batadv_tt_local_entry_free_ref(tt_local_entry); batadv_tt_local_entry_free_ref(tt_local_entry);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv, vid);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
out: out:
if (tt_local_entry) if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry); batadv_tt_local_entry_free_ref(tt_local_entry);
...@@ -1111,6 +1122,7 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv) ...@@ -1111,6 +1122,7 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
spinlock_t *list_lock; /* protects write access to the hash lists */ spinlock_t *list_lock; /* protects write access to the hash lists */
struct batadv_tt_common_entry *tt_common_entry; struct batadv_tt_common_entry *tt_common_entry;
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_softif_vlan *vlan;
struct hlist_node *node_tmp; struct hlist_node *node_tmp;
struct hlist_head *head; struct hlist_head *head;
uint32_t i; uint32_t i;
...@@ -1131,6 +1143,13 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv) ...@@ -1131,6 +1143,13 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
tt_local = container_of(tt_common_entry, tt_local = container_of(tt_common_entry,
struct batadv_tt_local_entry, struct batadv_tt_local_entry,
common); common);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv,
tt_common_entry->vid);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_tt_local_entry_free_ref(tt_local); batadv_tt_local_entry_free_ref(tt_local);
} }
spin_unlock_bh(list_lock); spin_unlock_bh(list_lock);
...@@ -3139,6 +3158,7 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) ...@@ -3139,6 +3158,7 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct batadv_hashtable *hash = bat_priv->tt.local_hash;
struct batadv_tt_common_entry *tt_common; struct batadv_tt_common_entry *tt_common;
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_softif_vlan *vlan;
struct hlist_node *node_tmp; struct hlist_node *node_tmp;
struct hlist_head *head; struct hlist_head *head;
spinlock_t *list_lock; /* protects write access to the hash lists */ spinlock_t *list_lock; /* protects write access to the hash lists */
...@@ -3167,6 +3187,12 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) ...@@ -3167,6 +3187,12 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
tt_local = container_of(tt_common, tt_local = container_of(tt_common,
struct batadv_tt_local_entry, struct batadv_tt_local_entry,
common); common);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv, tt_common->vid);
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
batadv_tt_local_entry_free_ref(tt_local); batadv_tt_local_entry_free_ref(tt_local);
} }
spin_unlock_bh(list_lock); spin_unlock_bh(list_lock);
......
...@@ -687,6 +687,7 @@ struct batadv_priv_nc { ...@@ -687,6 +687,7 @@ struct batadv_priv_nc {
/** /**
* struct batadv_softif_vlan - per VLAN attributes set * struct batadv_softif_vlan - per VLAN attributes set
* @bat_priv: pointer to the mesh object
* @vid: VLAN identifier * @vid: VLAN identifier
* @kobj: kobject for sysfs vlan subdirectory * @kobj: kobject for sysfs vlan subdirectory
* @ap_isolation: AP isolation state * @ap_isolation: AP isolation state
...@@ -696,6 +697,7 @@ struct batadv_priv_nc { ...@@ -696,6 +697,7 @@ struct batadv_priv_nc {
* @rcu: struct used for freeing in a RCU-safe manner * @rcu: struct used for freeing in a RCU-safe manner
*/ */
struct batadv_softif_vlan { struct batadv_softif_vlan {
struct batadv_priv *bat_priv;
unsigned short vid; unsigned short vid;
struct kobject *kobj; struct kobject *kobj;
atomic_t ap_isolation; /* boolean */ atomic_t ap_isolation; /* boolean */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment