diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 074936f2d30abe1b5a166bc6b65d97d2e145e898..d82766bfd2649acc39334c88c222d90367af23b0 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -157,7 +157,9 @@ batadv_tt_orig_list_entry_free_ref(struct tt_orig_list_entry *orig_entry) static void batadv_tt_local_event(struct bat_priv *bat_priv, const uint8_t *addr, uint8_t flags) { - struct tt_change_node *tt_change_node; + struct tt_change_node *tt_change_node, *entry, *safe; + bool event_removed = false; + bool del_op_requested, del_op_entry; tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC); @@ -167,13 +169,45 @@ static void batadv_tt_local_event(struct bat_priv *bat_priv, tt_change_node->change.flags = flags; memcpy(tt_change_node->change.addr, addr, ETH_ALEN); + del_op_requested = flags & TT_CLIENT_DEL; + + /* check for ADD+DEL or DEL+ADD events */ spin_lock_bh(&bat_priv->tt_changes_list_lock); + list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list, + list) { + if (!batadv_compare_eth(entry->change.addr, addr)) + continue; + + /* DEL+ADD in the same orig interval have no effect and can be + * removed to avoid silly behaviour on the receiver side. The + * other way around (ADD+DEL) can happen in case of roaming of + * a client still in the NEW state. Roaming of NEW clients is + * now possible due to automatically recognition of "temporary" + * clients + */ + del_op_entry = entry->change.flags & TT_CLIENT_DEL; + if (!del_op_requested && del_op_entry) + goto del; + if (del_op_requested && !del_op_entry) + goto del; + continue; +del: + list_del(&entry->list); + kfree(entry); + event_removed = true; + goto unlock; + } + /* track the change in the OGMinterval list */ list_add_tail(&tt_change_node->list, &bat_priv->tt_changes_list); - atomic_inc(&bat_priv->tt_local_changes); + +unlock: spin_unlock_bh(&bat_priv->tt_changes_list_lock); - atomic_set(&bat_priv->tt_ogm_append_cnt, 0); + if (event_removed) + atomic_dec(&bat_priv->tt_local_changes); + else + atomic_inc(&bat_priv->tt_local_changes); } int batadv_tt_len(int changes_num)