mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-22 20:23:57 +08:00
23721387c4
This second version of the bridge loop avoidance for batman-adv avoids loops between the mesh and a backbone (usually a LAN). By connecting multiple batman-adv mesh nodes to the same ethernet segment a loop can be created when the soft-interface is bridged into that ethernet segment. A simple visualization of the loop involving the most common case - a LAN as ethernet segment: node1 <-- LAN --> node2 | | wifi <-- mesh --> wifi Packets from the LAN (e.g. ARP broadcasts) will circle forever from node1 or node2 over the mesh back into the LAN. With this patch, batman recognizes backbone gateways, nodes which are part of the mesh and backbone/LAN at the same time. Each backbone gateway "claims" clients from within the mesh to handle them exclusively. By restricting that only responsible backbone gateways may handle their claimed clients traffic, loops are effectively avoided. Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de> Signed-off-by: Antonio Quartulli <ordex@autistici.org>
645 lines
16 KiB
C
645 lines
16 KiB
C
/*
|
|
* Copyright (C) 2009-2012 B.A.T.M.A.N. contributors:
|
|
*
|
|
* Marek Lindner, Simon Wunderlich
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA
|
|
*
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include "originator.h"
|
|
#include "hash.h"
|
|
#include "translation-table.h"
|
|
#include "routing.h"
|
|
#include "gateway_client.h"
|
|
#include "hard-interface.h"
|
|
#include "unicast.h"
|
|
#include "soft-interface.h"
|
|
#include "bridge_loop_avoidance.h"
|
|
|
|
static void purge_orig(struct work_struct *work);
|
|
|
|
static void start_purge_timer(struct bat_priv *bat_priv)
|
|
{
|
|
INIT_DELAYED_WORK(&bat_priv->orig_work, purge_orig);
|
|
queue_delayed_work(bat_event_workqueue, &bat_priv->orig_work, 1 * HZ);
|
|
}
|
|
|
|
/* returns 1 if they are the same originator */
|
|
static int compare_orig(const struct hlist_node *node, const void *data2)
|
|
{
|
|
const void *data1 = container_of(node, struct orig_node, hash_entry);
|
|
|
|
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
|
|
}
|
|
|
|
int originator_init(struct bat_priv *bat_priv)
|
|
{
|
|
if (bat_priv->orig_hash)
|
|
return 1;
|
|
|
|
bat_priv->orig_hash = hash_new(1024);
|
|
|
|
if (!bat_priv->orig_hash)
|
|
goto err;
|
|
|
|
start_purge_timer(bat_priv);
|
|
return 1;
|
|
|
|
err:
|
|
return 0;
|
|
}
|
|
|
|
void neigh_node_free_ref(struct neigh_node *neigh_node)
|
|
{
|
|
if (atomic_dec_and_test(&neigh_node->refcount))
|
|
kfree_rcu(neigh_node, rcu);
|
|
}
|
|
|
|
/* increases the refcounter of a found router */
|
|
struct neigh_node *orig_node_get_router(struct orig_node *orig_node)
|
|
{
|
|
struct neigh_node *router;
|
|
|
|
rcu_read_lock();
|
|
router = rcu_dereference(orig_node->router);
|
|
|
|
if (router && !atomic_inc_not_zero(&router->refcount))
|
|
router = NULL;
|
|
|
|
rcu_read_unlock();
|
|
return router;
|
|
}
|
|
|
|
struct neigh_node *create_neighbor(struct orig_node *orig_node,
|
|
struct orig_node *orig_neigh_node,
|
|
const uint8_t *neigh,
|
|
struct hard_iface *if_incoming)
|
|
{
|
|
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
|
|
struct neigh_node *neigh_node;
|
|
|
|
bat_dbg(DBG_BATMAN, bat_priv,
|
|
"Creating new last-hop neighbor of originator\n");
|
|
|
|
neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
|
|
if (!neigh_node)
|
|
return NULL;
|
|
|
|
INIT_HLIST_NODE(&neigh_node->list);
|
|
INIT_LIST_HEAD(&neigh_node->bonding_list);
|
|
spin_lock_init(&neigh_node->tq_lock);
|
|
|
|
memcpy(neigh_node->addr, neigh, ETH_ALEN);
|
|
neigh_node->orig_node = orig_neigh_node;
|
|
neigh_node->if_incoming = if_incoming;
|
|
|
|
/* extra reference for return */
|
|
atomic_set(&neigh_node->refcount, 2);
|
|
|
|
spin_lock_bh(&orig_node->neigh_list_lock);
|
|
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
|
|
spin_unlock_bh(&orig_node->neigh_list_lock);
|
|
return neigh_node;
|
|
}
|
|
|
|
static void orig_node_free_rcu(struct rcu_head *rcu)
|
|
{
|
|
struct hlist_node *node, *node_tmp;
|
|
struct neigh_node *neigh_node, *tmp_neigh_node;
|
|
struct orig_node *orig_node;
|
|
|
|
orig_node = container_of(rcu, struct orig_node, rcu);
|
|
|
|
spin_lock_bh(&orig_node->neigh_list_lock);
|
|
|
|
/* for all bonding members ... */
|
|
list_for_each_entry_safe(neigh_node, tmp_neigh_node,
|
|
&orig_node->bond_list, bonding_list) {
|
|
list_del_rcu(&neigh_node->bonding_list);
|
|
neigh_node_free_ref(neigh_node);
|
|
}
|
|
|
|
/* for all neighbors towards this originator ... */
|
|
hlist_for_each_entry_safe(neigh_node, node, node_tmp,
|
|
&orig_node->neigh_list, list) {
|
|
hlist_del_rcu(&neigh_node->list);
|
|
neigh_node_free_ref(neigh_node);
|
|
}
|
|
|
|
spin_unlock_bh(&orig_node->neigh_list_lock);
|
|
|
|
frag_list_free(&orig_node->frag_list);
|
|
tt_global_del_orig(orig_node->bat_priv, orig_node,
|
|
"originator timed out");
|
|
|
|
kfree(orig_node->tt_buff);
|
|
kfree(orig_node->bcast_own);
|
|
kfree(orig_node->bcast_own_sum);
|
|
kfree(orig_node);
|
|
}
|
|
|
|
void orig_node_free_ref(struct orig_node *orig_node)
|
|
{
|
|
if (atomic_dec_and_test(&orig_node->refcount))
|
|
call_rcu(&orig_node->rcu, orig_node_free_rcu);
|
|
}
|
|
|
|
void originator_free(struct bat_priv *bat_priv)
|
|
{
|
|
struct hashtable_t *hash = bat_priv->orig_hash;
|
|
struct hlist_node *node, *node_tmp;
|
|
struct hlist_head *head;
|
|
spinlock_t *list_lock; /* spinlock to protect write access */
|
|
struct orig_node *orig_node;
|
|
uint32_t i;
|
|
|
|
if (!hash)
|
|
return;
|
|
|
|
cancel_delayed_work_sync(&bat_priv->orig_work);
|
|
|
|
bat_priv->orig_hash = NULL;
|
|
|
|
for (i = 0; i < hash->size; i++) {
|
|
head = &hash->table[i];
|
|
list_lock = &hash->list_locks[i];
|
|
|
|
spin_lock_bh(list_lock);
|
|
hlist_for_each_entry_safe(orig_node, node, node_tmp,
|
|
head, hash_entry) {
|
|
|
|
hlist_del_rcu(node);
|
|
orig_node_free_ref(orig_node);
|
|
}
|
|
spin_unlock_bh(list_lock);
|
|
}
|
|
|
|
hash_destroy(hash);
|
|
}
|
|
|
|
/* this function finds or creates an originator entry for the given
|
|
* address if it does not exits */
|
|
struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr)
|
|
{
|
|
struct orig_node *orig_node;
|
|
int size;
|
|
int hash_added;
|
|
|
|
orig_node = orig_hash_find(bat_priv, addr);
|
|
if (orig_node)
|
|
return orig_node;
|
|
|
|
bat_dbg(DBG_BATMAN, bat_priv,
|
|
"Creating new originator: %pM\n", addr);
|
|
|
|
orig_node = kzalloc(sizeof(*orig_node), GFP_ATOMIC);
|
|
if (!orig_node)
|
|
return NULL;
|
|
|
|
INIT_HLIST_HEAD(&orig_node->neigh_list);
|
|
INIT_LIST_HEAD(&orig_node->bond_list);
|
|
spin_lock_init(&orig_node->ogm_cnt_lock);
|
|
spin_lock_init(&orig_node->bcast_seqno_lock);
|
|
spin_lock_init(&orig_node->neigh_list_lock);
|
|
spin_lock_init(&orig_node->tt_buff_lock);
|
|
|
|
/* extra reference for return */
|
|
atomic_set(&orig_node->refcount, 2);
|
|
|
|
orig_node->tt_initialised = false;
|
|
orig_node->tt_poss_change = false;
|
|
orig_node->bat_priv = bat_priv;
|
|
memcpy(orig_node->orig, addr, ETH_ALEN);
|
|
orig_node->router = NULL;
|
|
orig_node->tt_crc = 0;
|
|
atomic_set(&orig_node->last_ttvn, 0);
|
|
orig_node->tt_buff = NULL;
|
|
orig_node->tt_buff_len = 0;
|
|
atomic_set(&orig_node->tt_size, 0);
|
|
orig_node->bcast_seqno_reset = jiffies - 1
|
|
- msecs_to_jiffies(RESET_PROTECTION_MS);
|
|
orig_node->batman_seqno_reset = jiffies - 1
|
|
- msecs_to_jiffies(RESET_PROTECTION_MS);
|
|
|
|
atomic_set(&orig_node->bond_candidates, 0);
|
|
|
|
size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS;
|
|
|
|
orig_node->bcast_own = kzalloc(size, GFP_ATOMIC);
|
|
if (!orig_node->bcast_own)
|
|
goto free_orig_node;
|
|
|
|
size = bat_priv->num_ifaces * sizeof(uint8_t);
|
|
orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
|
|
|
|
INIT_LIST_HEAD(&orig_node->frag_list);
|
|
orig_node->last_frag_packet = 0;
|
|
|
|
if (!orig_node->bcast_own_sum)
|
|
goto free_bcast_own;
|
|
|
|
hash_added = hash_add(bat_priv->orig_hash, compare_orig,
|
|
choose_orig, orig_node, &orig_node->hash_entry);
|
|
if (hash_added != 0)
|
|
goto free_bcast_own_sum;
|
|
|
|
return orig_node;
|
|
free_bcast_own_sum:
|
|
kfree(orig_node->bcast_own_sum);
|
|
free_bcast_own:
|
|
kfree(orig_node->bcast_own);
|
|
free_orig_node:
|
|
kfree(orig_node);
|
|
return NULL;
|
|
}
|
|
|
|
static bool purge_orig_neighbors(struct bat_priv *bat_priv,
|
|
struct orig_node *orig_node,
|
|
struct neigh_node **best_neigh_node)
|
|
{
|
|
struct hlist_node *node, *node_tmp;
|
|
struct neigh_node *neigh_node;
|
|
bool neigh_purged = false;
|
|
|
|
*best_neigh_node = NULL;
|
|
|
|
spin_lock_bh(&orig_node->neigh_list_lock);
|
|
|
|
/* for all neighbors towards this originator ... */
|
|
hlist_for_each_entry_safe(neigh_node, node, node_tmp,
|
|
&orig_node->neigh_list, list) {
|
|
|
|
if ((has_timed_out(neigh_node->last_valid, PURGE_TIMEOUT)) ||
|
|
(neigh_node->if_incoming->if_status == IF_INACTIVE) ||
|
|
(neigh_node->if_incoming->if_status == IF_NOT_IN_USE) ||
|
|
(neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) {
|
|
|
|
if ((neigh_node->if_incoming->if_status ==
|
|
IF_INACTIVE) ||
|
|
(neigh_node->if_incoming->if_status ==
|
|
IF_NOT_IN_USE) ||
|
|
(neigh_node->if_incoming->if_status ==
|
|
IF_TO_BE_REMOVED))
|
|
bat_dbg(DBG_BATMAN, bat_priv,
|
|
"neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",
|
|
orig_node->orig, neigh_node->addr,
|
|
neigh_node->if_incoming->net_dev->name);
|
|
else
|
|
bat_dbg(DBG_BATMAN, bat_priv,
|
|
"neighbor timeout: originator %pM, neighbor: %pM, last_valid: %lu\n",
|
|
orig_node->orig, neigh_node->addr,
|
|
(neigh_node->last_valid / HZ));
|
|
|
|
neigh_purged = true;
|
|
|
|
hlist_del_rcu(&neigh_node->list);
|
|
bonding_candidate_del(orig_node, neigh_node);
|
|
neigh_node_free_ref(neigh_node);
|
|
} else {
|
|
if ((!*best_neigh_node) ||
|
|
(neigh_node->tq_avg > (*best_neigh_node)->tq_avg))
|
|
*best_neigh_node = neigh_node;
|
|
}
|
|
}
|
|
|
|
spin_unlock_bh(&orig_node->neigh_list_lock);
|
|
return neigh_purged;
|
|
}
|
|
|
|
static bool purge_orig_node(struct bat_priv *bat_priv,
|
|
struct orig_node *orig_node)
|
|
{
|
|
struct neigh_node *best_neigh_node;
|
|
|
|
if (has_timed_out(orig_node->last_valid, 2 * PURGE_TIMEOUT)) {
|
|
bat_dbg(DBG_BATMAN, bat_priv,
|
|
"Originator timeout: originator %pM, last_valid %lu\n",
|
|
orig_node->orig, (orig_node->last_valid / HZ));
|
|
return true;
|
|
} else {
|
|
if (purge_orig_neighbors(bat_priv, orig_node,
|
|
&best_neigh_node))
|
|
update_route(bat_priv, orig_node, best_neigh_node);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void _purge_orig(struct bat_priv *bat_priv)
|
|
{
|
|
struct hashtable_t *hash = bat_priv->orig_hash;
|
|
struct hlist_node *node, *node_tmp;
|
|
struct hlist_head *head;
|
|
spinlock_t *list_lock; /* spinlock to protect write access */
|
|
struct orig_node *orig_node;
|
|
uint32_t i;
|
|
|
|
if (!hash)
|
|
return;
|
|
|
|
/* for all origins... */
|
|
for (i = 0; i < hash->size; i++) {
|
|
head = &hash->table[i];
|
|
list_lock = &hash->list_locks[i];
|
|
|
|
spin_lock_bh(list_lock);
|
|
hlist_for_each_entry_safe(orig_node, node, node_tmp,
|
|
head, hash_entry) {
|
|
if (purge_orig_node(bat_priv, orig_node)) {
|
|
if (orig_node->gw_flags)
|
|
gw_node_delete(bat_priv, orig_node);
|
|
hlist_del_rcu(node);
|
|
orig_node_free_ref(orig_node);
|
|
continue;
|
|
}
|
|
|
|
if (has_timed_out(orig_node->last_frag_packet,
|
|
FRAG_TIMEOUT))
|
|
frag_list_free(&orig_node->frag_list);
|
|
}
|
|
spin_unlock_bh(list_lock);
|
|
}
|
|
|
|
gw_node_purge(bat_priv);
|
|
gw_election(bat_priv);
|
|
}
|
|
|
|
static void purge_orig(struct work_struct *work)
|
|
{
|
|
struct delayed_work *delayed_work =
|
|
container_of(work, struct delayed_work, work);
|
|
struct bat_priv *bat_priv =
|
|
container_of(delayed_work, struct bat_priv, orig_work);
|
|
|
|
_purge_orig(bat_priv);
|
|
start_purge_timer(bat_priv);
|
|
}
|
|
|
|
void purge_orig_ref(struct bat_priv *bat_priv)
|
|
{
|
|
_purge_orig(bat_priv);
|
|
}
|
|
|
|
int orig_seq_print_text(struct seq_file *seq, void *offset)
|
|
{
|
|
struct net_device *net_dev = (struct net_device *)seq->private;
|
|
struct bat_priv *bat_priv = netdev_priv(net_dev);
|
|
struct hashtable_t *hash = bat_priv->orig_hash;
|
|
struct hlist_node *node, *node_tmp;
|
|
struct hlist_head *head;
|
|
struct hard_iface *primary_if;
|
|
struct orig_node *orig_node;
|
|
struct neigh_node *neigh_node, *neigh_node_tmp;
|
|
int batman_count = 0;
|
|
int last_seen_secs;
|
|
int last_seen_msecs;
|
|
uint32_t i;
|
|
int ret = 0;
|
|
|
|
primary_if = primary_if_get_selected(bat_priv);
|
|
|
|
if (!primary_if) {
|
|
ret = seq_printf(seq,
|
|
"BATMAN mesh %s disabled - please specify interfaces to enable it\n",
|
|
net_dev->name);
|
|
goto out;
|
|
}
|
|
|
|
if (primary_if->if_status != IF_ACTIVE) {
|
|
ret = seq_printf(seq,
|
|
"BATMAN mesh %s disabled - primary interface not active\n",
|
|
net_dev->name);
|
|
goto out;
|
|
}
|
|
|
|
seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
|
|
SOURCE_VERSION, primary_if->net_dev->name,
|
|
primary_if->net_dev->dev_addr, net_dev->name);
|
|
seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
|
|
"Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
|
|
"outgoingIF", "Potential nexthops");
|
|
|
|
for (i = 0; i < hash->size; i++) {
|
|
head = &hash->table[i];
|
|
|
|
rcu_read_lock();
|
|
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
|
|
neigh_node = orig_node_get_router(orig_node);
|
|
if (!neigh_node)
|
|
continue;
|
|
|
|
if (neigh_node->tq_avg == 0)
|
|
goto next;
|
|
|
|
last_seen_secs = jiffies_to_msecs(jiffies -
|
|
orig_node->last_valid) / 1000;
|
|
last_seen_msecs = jiffies_to_msecs(jiffies -
|
|
orig_node->last_valid) % 1000;
|
|
|
|
seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:",
|
|
orig_node->orig, last_seen_secs,
|
|
last_seen_msecs, neigh_node->tq_avg,
|
|
neigh_node->addr,
|
|
neigh_node->if_incoming->net_dev->name);
|
|
|
|
hlist_for_each_entry_rcu(neigh_node_tmp, node_tmp,
|
|
&orig_node->neigh_list, list) {
|
|
seq_printf(seq, " %pM (%3i)",
|
|
neigh_node_tmp->addr,
|
|
neigh_node_tmp->tq_avg);
|
|
}
|
|
|
|
seq_printf(seq, "\n");
|
|
batman_count++;
|
|
|
|
next:
|
|
neigh_node_free_ref(neigh_node);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
if (batman_count == 0)
|
|
seq_printf(seq, "No batman nodes in range ...\n");
|
|
|
|
out:
|
|
if (primary_if)
|
|
hardif_free_ref(primary_if);
|
|
return ret;
|
|
}
|
|
|
|
static int orig_node_add_if(struct orig_node *orig_node, int max_if_num)
|
|
{
|
|
void *data_ptr;
|
|
|
|
data_ptr = kmalloc(max_if_num * sizeof(unsigned long) * NUM_WORDS,
|
|
GFP_ATOMIC);
|
|
if (!data_ptr)
|
|
return -1;
|
|
|
|
memcpy(data_ptr, orig_node->bcast_own,
|
|
(max_if_num - 1) * sizeof(unsigned long) * NUM_WORDS);
|
|
kfree(orig_node->bcast_own);
|
|
orig_node->bcast_own = data_ptr;
|
|
|
|
data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
|
|
if (!data_ptr)
|
|
return -1;
|
|
|
|
memcpy(data_ptr, orig_node->bcast_own_sum,
|
|
(max_if_num - 1) * sizeof(uint8_t));
|
|
kfree(orig_node->bcast_own_sum);
|
|
orig_node->bcast_own_sum = data_ptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num)
|
|
{
|
|
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
|
struct hashtable_t *hash = bat_priv->orig_hash;
|
|
struct hlist_node *node;
|
|
struct hlist_head *head;
|
|
struct orig_node *orig_node;
|
|
uint32_t i;
|
|
int ret;
|
|
|
|
/* resize all orig nodes because orig_node->bcast_own(_sum) depend on
|
|
* if_num */
|
|
for (i = 0; i < hash->size; i++) {
|
|
head = &hash->table[i];
|
|
|
|
rcu_read_lock();
|
|
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
|
|
spin_lock_bh(&orig_node->ogm_cnt_lock);
|
|
ret = orig_node_add_if(orig_node, max_if_num);
|
|
spin_unlock_bh(&orig_node->ogm_cnt_lock);
|
|
|
|
if (ret == -1)
|
|
goto err;
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
rcu_read_unlock();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int orig_node_del_if(struct orig_node *orig_node,
|
|
int max_if_num, int del_if_num)
|
|
{
|
|
void *data_ptr = NULL;
|
|
int chunk_size;
|
|
|
|
/* last interface was removed */
|
|
if (max_if_num == 0)
|
|
goto free_bcast_own;
|
|
|
|
chunk_size = sizeof(unsigned long) * NUM_WORDS;
|
|
data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
|
|
if (!data_ptr)
|
|
return -1;
|
|
|
|
/* copy first part */
|
|
memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size);
|
|
|
|
/* copy second part */
|
|
memcpy((char *)data_ptr + del_if_num * chunk_size,
|
|
orig_node->bcast_own + ((del_if_num + 1) * chunk_size),
|
|
(max_if_num - del_if_num) * chunk_size);
|
|
|
|
free_bcast_own:
|
|
kfree(orig_node->bcast_own);
|
|
orig_node->bcast_own = data_ptr;
|
|
|
|
if (max_if_num == 0)
|
|
goto free_own_sum;
|
|
|
|
data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
|
|
if (!data_ptr)
|
|
return -1;
|
|
|
|
memcpy(data_ptr, orig_node->bcast_own_sum,
|
|
del_if_num * sizeof(uint8_t));
|
|
|
|
memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t),
|
|
orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)),
|
|
(max_if_num - del_if_num) * sizeof(uint8_t));
|
|
|
|
free_own_sum:
|
|
kfree(orig_node->bcast_own_sum);
|
|
orig_node->bcast_own_sum = data_ptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num)
|
|
{
|
|
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
|
|
struct hashtable_t *hash = bat_priv->orig_hash;
|
|
struct hlist_node *node;
|
|
struct hlist_head *head;
|
|
struct hard_iface *hard_iface_tmp;
|
|
struct orig_node *orig_node;
|
|
uint32_t i;
|
|
int ret;
|
|
|
|
/* resize all orig nodes because orig_node->bcast_own(_sum) depend on
|
|
* if_num */
|
|
for (i = 0; i < hash->size; i++) {
|
|
head = &hash->table[i];
|
|
|
|
rcu_read_lock();
|
|
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
|
|
spin_lock_bh(&orig_node->ogm_cnt_lock);
|
|
ret = orig_node_del_if(orig_node, max_if_num,
|
|
hard_iface->if_num);
|
|
spin_unlock_bh(&orig_node->ogm_cnt_lock);
|
|
|
|
if (ret == -1)
|
|
goto err;
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
/* renumber remaining batman interfaces _inside_ of orig_hash_lock */
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(hard_iface_tmp, &hardif_list, list) {
|
|
if (hard_iface_tmp->if_status == IF_NOT_IN_USE)
|
|
continue;
|
|
|
|
if (hard_iface == hard_iface_tmp)
|
|
continue;
|
|
|
|
if (hard_iface->soft_iface != hard_iface_tmp->soft_iface)
|
|
continue;
|
|
|
|
if (hard_iface_tmp->if_num > hard_iface->if_num)
|
|
hard_iface_tmp->if_num--;
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
hard_iface->if_num = -1;
|
|
return 0;
|
|
|
|
err:
|
|
rcu_read_unlock();
|
|
return -ENOMEM;
|
|
}
|