Skip to content

Commit 35df3b2

Browse files
committed
batman-adv: fix TT VLAN inconsistency on VLAN re-add
When a VLAN interface (on top of batX) is removed and re-added within a short timeframe TT does not have enough time to properly cleanup. This creates an internal TT state mismatch as the newly created softif_vlan will be initialized from scratch with a TT client count of zero (even if TT entries for this VLAN still exist). The resulting TT messages are bogus due to the counter / tt client listing mismatch, thus creating inconsistencies on every node in the network To fix this issue destroy_vlan() has to not free the VLAN object immediately but it has to be kept alive until all the TT entries for this VLAN have been removed. destroy_vlan() still removes the sysfs folder so that the user has the feeling that everything went fine. If the same VLAN is re-added before the old object is free'd, then the latter is resurrected and re-used. Implement such behaviour by increasing the reference counter of a softif_vlan object every time a new local TT entry for such VLAN is created and remove the object from the list only when all the TT entries have been destroyed. Signed-off-by: Antonio Quartulli <antonio@open-mesh.com> Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
1 parent d46b6bf commit 35df3b2

File tree

3 files changed

+74
-14
lines changed

3 files changed

+74
-14
lines changed

net/batman-adv/soft-interface.c

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -448,10 +448,15 @@ void batadv_interface_rx(struct net_device *soft_iface,
448448
* possibly free it
449449
* @softif_vlan: the vlan object to release
450450
*/
451-
void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan)
451+
void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *vlan)
452452
{
453-
if (atomic_dec_and_test(&softif_vlan->refcount))
454-
kfree_rcu(softif_vlan, rcu);
453+
if (atomic_dec_and_test(&vlan->refcount)) {
454+
spin_lock_bh(&vlan->bat_priv->softif_vlan_list_lock);
455+
hlist_del_rcu(&vlan->list);
456+
spin_unlock_bh(&vlan->bat_priv->softif_vlan_list_lock);
457+
458+
kfree_rcu(vlan, rcu);
459+
}
455460
}
456461

457462
/**
@@ -505,6 +510,7 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
505510
if (!vlan)
506511
return -ENOMEM;
507512

513+
vlan->bat_priv = bat_priv;
508514
vlan->vid = vid;
509515
atomic_set(&vlan->refcount, 1);
510516

@@ -516,17 +522,17 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
516522
return err;
517523
}
518524

525+
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
526+
hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
527+
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
528+
519529
/* add a new TT local entry. This one will be marked with the NOPURGE
520530
* flag
521531
*/
522532
batadv_tt_local_add(bat_priv->soft_iface,
523533
bat_priv->soft_iface->dev_addr, vid,
524534
BATADV_NULL_IFINDEX, BATADV_NO_MARK);
525535

526-
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
527-
hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
528-
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
529-
530536
return 0;
531537
}
532538

@@ -538,18 +544,13 @@ int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
538544
static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
539545
struct batadv_softif_vlan *vlan)
540546
{
541-
spin_lock_bh(&bat_priv->softif_vlan_list_lock);
542-
hlist_del_rcu(&vlan->list);
543-
spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
544-
545-
batadv_sysfs_del_vlan(bat_priv, vlan);
546-
547547
/* explicitly remove the associated TT local entry because it is marked
548548
* with the NOPURGE flag
549549
*/
550550
batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr,
551551
vlan->vid, "vlan interface destroyed", false);
552552

553+
batadv_sysfs_del_vlan(bat_priv, vlan);
553554
batadv_softif_vlan_free_ref(vlan);
554555
}
555556

@@ -567,6 +568,8 @@ static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
567568
unsigned short vid)
568569
{
569570
struct batadv_priv *bat_priv = netdev_priv(dev);
571+
struct batadv_softif_vlan *vlan;
572+
int ret;
570573

571574
/* only 802.1Q vlans are supported.
572575
* 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,
576579

577580
vid |= BATADV_VLAN_HAS_TAG;
578581

579-
return batadv_softif_create_vlan(bat_priv, vid);
582+
/* if a new vlan is getting created and it already exists, it means that
583+
* it was not deleted yet. batadv_softif_vlan_get() increases the
584+
* refcount in order to revive the object.
585+
*
586+
* if it does not exist then create it.
587+
*/
588+
vlan = batadv_softif_vlan_get(bat_priv, vid);
589+
if (!vlan)
590+
return batadv_softif_create_vlan(bat_priv, vid);
591+
592+
/* recreate the sysfs object if it was already destroyed (and it should
593+
* be since we received a kill_vid() for this vlan
594+
*/
595+
if (!vlan->kobj) {
596+
ret = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan);
597+
if (ret) {
598+
batadv_softif_vlan_free_ref(vlan);
599+
return ret;
600+
}
601+
}
602+
603+
/* add a new TT local entry. This one will be marked with the NOPURGE
604+
* flag. This must be added again, even if the vlan object already
605+
* exists, because the entry was deleted by kill_vid()
606+
*/
607+
batadv_tt_local_add(bat_priv->soft_iface,
608+
bat_priv->soft_iface->dev_addr, vid,
609+
BATADV_NULL_IFINDEX, BATADV_NO_MARK);
610+
611+
return 0;
580612
}
581613

582614
/**

net/batman-adv/translation-table.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
511511
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
512512
struct batadv_tt_local_entry *tt_local;
513513
struct batadv_tt_global_entry *tt_global = NULL;
514+
struct batadv_softif_vlan *vlan;
514515
struct net_device *in_dev = NULL;
515516
struct hlist_head *head;
516517
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,
572573
if (!tt_local)
573574
goto out;
574575

576+
/* increase the refcounter of the related vlan */
577+
vlan = batadv_softif_vlan_get(bat_priv, vid);
578+
575579
batadv_dbg(BATADV_DBG_TT, bat_priv,
576580
"Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n",
577581
addr, BATADV_PRINT_VID(vid),
@@ -604,6 +608,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
604608
if (unlikely(hash_added != 0)) {
605609
/* remove the reference for the hash */
606610
batadv_tt_local_entry_free_ref(tt_local);
611+
batadv_softif_vlan_free_ref(vlan);
607612
goto out;
608613
}
609614

@@ -1009,6 +1014,7 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
10091014
{
10101015
struct batadv_tt_local_entry *tt_local_entry;
10111016
uint16_t flags, curr_flags = BATADV_NO_FLAGS;
1017+
struct batadv_softif_vlan *vlan;
10121018

10131019
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
10141020
if (!tt_local_entry)
@@ -1039,6 +1045,11 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
10391045
hlist_del_rcu(&tt_local_entry->common.hash_entry);
10401046
batadv_tt_local_entry_free_ref(tt_local_entry);
10411047

1048+
/* decrease the reference held for this vlan */
1049+
vlan = batadv_softif_vlan_get(bat_priv, vid);
1050+
batadv_softif_vlan_free_ref(vlan);
1051+
batadv_softif_vlan_free_ref(vlan);
1052+
10421053
out:
10431054
if (tt_local_entry)
10441055
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)
11111122
spinlock_t *list_lock; /* protects write access to the hash lists */
11121123
struct batadv_tt_common_entry *tt_common_entry;
11131124
struct batadv_tt_local_entry *tt_local;
1125+
struct batadv_softif_vlan *vlan;
11141126
struct hlist_node *node_tmp;
11151127
struct hlist_head *head;
11161128
uint32_t i;
@@ -1131,6 +1143,13 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
11311143
tt_local = container_of(tt_common_entry,
11321144
struct batadv_tt_local_entry,
11331145
common);
1146+
1147+
/* decrease the reference held for this vlan */
1148+
vlan = batadv_softif_vlan_get(bat_priv,
1149+
tt_common_entry->vid);
1150+
batadv_softif_vlan_free_ref(vlan);
1151+
batadv_softif_vlan_free_ref(vlan);
1152+
11341153
batadv_tt_local_entry_free_ref(tt_local);
11351154
}
11361155
spin_unlock_bh(list_lock);
@@ -3139,6 +3158,7 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
31393158
struct batadv_hashtable *hash = bat_priv->tt.local_hash;
31403159
struct batadv_tt_common_entry *tt_common;
31413160
struct batadv_tt_local_entry *tt_local;
3161+
struct batadv_softif_vlan *vlan;
31423162
struct hlist_node *node_tmp;
31433163
struct hlist_head *head;
31443164
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)
31673187
tt_local = container_of(tt_common,
31683188
struct batadv_tt_local_entry,
31693189
common);
3190+
3191+
/* decrease the reference held for this vlan */
3192+
vlan = batadv_softif_vlan_get(bat_priv, tt_common->vid);
3193+
batadv_softif_vlan_free_ref(vlan);
3194+
batadv_softif_vlan_free_ref(vlan);
3195+
31703196
batadv_tt_local_entry_free_ref(tt_local);
31713197
}
31723198
spin_unlock_bh(list_lock);

net/batman-adv/types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,7 @@ struct batadv_priv_nc {
687687

688688
/**
689689
* struct batadv_softif_vlan - per VLAN attributes set
690+
* @bat_priv: pointer to the mesh object
690691
* @vid: VLAN identifier
691692
* @kobj: kobject for sysfs vlan subdirectory
692693
* @ap_isolation: AP isolation state
@@ -696,6 +697,7 @@ struct batadv_priv_nc {
696697
* @rcu: struct used for freeing in a RCU-safe manner
697698
*/
698699
struct batadv_softif_vlan {
700+
struct batadv_priv *bat_priv;
699701
unsigned short vid;
700702
struct kobject *kobj;
701703
atomic_t ap_isolation; /* boolean */

0 commit comments

Comments
 (0)