162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) B.A.T.M.A.N. contributors:
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Marek Lindner, Simon Wunderlich, Antonio Quartulli
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "translation-table.h"
862306a36Sopenharmony_ci#include "main.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/atomic.h>
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/build_bug.h>
1362306a36Sopenharmony_ci#include <linux/byteorder/generic.h>
1462306a36Sopenharmony_ci#include <linux/cache.h>
1562306a36Sopenharmony_ci#include <linux/compiler.h>
1662306a36Sopenharmony_ci#include <linux/container_of.h>
1762306a36Sopenharmony_ci#include <linux/crc32c.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/etherdevice.h>
2062306a36Sopenharmony_ci#include <linux/gfp.h>
2162306a36Sopenharmony_ci#include <linux/if_ether.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/jhash.h>
2462306a36Sopenharmony_ci#include <linux/jiffies.h>
2562306a36Sopenharmony_ci#include <linux/kref.h>
2662306a36Sopenharmony_ci#include <linux/list.h>
2762306a36Sopenharmony_ci#include <linux/lockdep.h>
2862306a36Sopenharmony_ci#include <linux/net.h>
2962306a36Sopenharmony_ci#include <linux/netdevice.h>
3062306a36Sopenharmony_ci#include <linux/netlink.h>
3162306a36Sopenharmony_ci#include <linux/rculist.h>
3262306a36Sopenharmony_ci#include <linux/rcupdate.h>
3362306a36Sopenharmony_ci#include <linux/skbuff.h>
3462306a36Sopenharmony_ci#include <linux/slab.h>
3562306a36Sopenharmony_ci#include <linux/spinlock.h>
3662306a36Sopenharmony_ci#include <linux/stddef.h>
3762306a36Sopenharmony_ci#include <linux/string.h>
3862306a36Sopenharmony_ci#include <linux/workqueue.h>
3962306a36Sopenharmony_ci#include <net/genetlink.h>
4062306a36Sopenharmony_ci#include <net/netlink.h>
4162306a36Sopenharmony_ci#include <net/sock.h>
4262306a36Sopenharmony_ci#include <uapi/linux/batadv_packet.h>
4362306a36Sopenharmony_ci#include <uapi/linux/batman_adv.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include "bridge_loop_avoidance.h"
4662306a36Sopenharmony_ci#include "hard-interface.h"
4762306a36Sopenharmony_ci#include "hash.h"
4862306a36Sopenharmony_ci#include "log.h"
4962306a36Sopenharmony_ci#include "netlink.h"
5062306a36Sopenharmony_ci#include "originator.h"
5162306a36Sopenharmony_ci#include "soft-interface.h"
5262306a36Sopenharmony_ci#include "tvlv.h"
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic struct kmem_cache *batadv_tl_cache __read_mostly;
5562306a36Sopenharmony_cistatic struct kmem_cache *batadv_tg_cache __read_mostly;
5662306a36Sopenharmony_cistatic struct kmem_cache *batadv_tt_orig_cache __read_mostly;
5762306a36Sopenharmony_cistatic struct kmem_cache *batadv_tt_change_cache __read_mostly;
5862306a36Sopenharmony_cistatic struct kmem_cache *batadv_tt_req_cache __read_mostly;
5962306a36Sopenharmony_cistatic struct kmem_cache *batadv_tt_roam_cache __read_mostly;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* hash class keys */
6262306a36Sopenharmony_cistatic struct lock_class_key batadv_tt_local_hash_lock_class_key;
6362306a36Sopenharmony_cistatic struct lock_class_key batadv_tt_global_hash_lock_class_key;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
6662306a36Sopenharmony_ci				 unsigned short vid,
6762306a36Sopenharmony_ci				 struct batadv_orig_node *orig_node);
6862306a36Sopenharmony_cistatic void batadv_tt_purge(struct work_struct *work);
6962306a36Sopenharmony_cistatic void
7062306a36Sopenharmony_cibatadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry);
7162306a36Sopenharmony_cistatic void batadv_tt_global_del(struct batadv_priv *bat_priv,
7262306a36Sopenharmony_ci				 struct batadv_orig_node *orig_node,
7362306a36Sopenharmony_ci				 const unsigned char *addr,
7462306a36Sopenharmony_ci				 unsigned short vid, const char *message,
7562306a36Sopenharmony_ci				 bool roaming);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci * batadv_compare_tt() - check if two TT entries are the same
7962306a36Sopenharmony_ci * @node: the list element pointer of the first TT entry
8062306a36Sopenharmony_ci * @data2: pointer to the tt_common_entry of the second TT entry
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Compare the MAC address and the VLAN ID of the two TT entries and check if
8362306a36Sopenharmony_ci * they are the same TT client.
8462306a36Sopenharmony_ci * Return: true if the two TT clients are the same, false otherwise
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic bool batadv_compare_tt(const struct hlist_node *node, const void *data2)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	const void *data1 = container_of(node, struct batadv_tt_common_entry,
8962306a36Sopenharmony_ci					 hash_entry);
9062306a36Sopenharmony_ci	const struct batadv_tt_common_entry *tt1 = data1;
9162306a36Sopenharmony_ci	const struct batadv_tt_common_entry *tt2 = data2;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return (tt1->vid == tt2->vid) && batadv_compare_eth(data1, data2);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci * batadv_choose_tt() - return the index of the tt entry in the hash table
9862306a36Sopenharmony_ci * @data: pointer to the tt_common_entry object to map
9962306a36Sopenharmony_ci * @size: the size of the hash table
10062306a36Sopenharmony_ci *
10162306a36Sopenharmony_ci * Return: the hash index where the object represented by 'data' should be
10262306a36Sopenharmony_ci * stored at.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_cistatic inline u32 batadv_choose_tt(const void *data, u32 size)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	const struct batadv_tt_common_entry *tt;
10762306a36Sopenharmony_ci	u32 hash = 0;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	tt = data;
11062306a36Sopenharmony_ci	hash = jhash(&tt->addr, ETH_ALEN, hash);
11162306a36Sopenharmony_ci	hash = jhash(&tt->vid, sizeof(tt->vid), hash);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return hash % size;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/**
11762306a36Sopenharmony_ci * batadv_tt_hash_find() - look for a client in the given hash table
11862306a36Sopenharmony_ci * @hash: the hash table to search
11962306a36Sopenharmony_ci * @addr: the mac address of the client to look for
12062306a36Sopenharmony_ci * @vid: VLAN identifier
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * Return: a pointer to the tt_common struct belonging to the searched client if
12362306a36Sopenharmony_ci * found, NULL otherwise.
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistatic struct batadv_tt_common_entry *
12662306a36Sopenharmony_cibatadv_tt_hash_find(struct batadv_hashtable *hash, const u8 *addr,
12762306a36Sopenharmony_ci		    unsigned short vid)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct hlist_head *head;
13062306a36Sopenharmony_ci	struct batadv_tt_common_entry to_search, *tt, *tt_tmp = NULL;
13162306a36Sopenharmony_ci	u32 index;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!hash)
13462306a36Sopenharmony_ci		return NULL;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ether_addr_copy(to_search.addr, addr);
13762306a36Sopenharmony_ci	to_search.vid = vid;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	index = batadv_choose_tt(&to_search, hash->size);
14062306a36Sopenharmony_ci	head = &hash->table[index];
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	rcu_read_lock();
14362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(tt, head, hash_entry) {
14462306a36Sopenharmony_ci		if (!batadv_compare_eth(tt, addr))
14562306a36Sopenharmony_ci			continue;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		if (tt->vid != vid)
14862306a36Sopenharmony_ci			continue;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		if (!kref_get_unless_zero(&tt->refcount))
15162306a36Sopenharmony_ci			continue;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		tt_tmp = tt;
15462306a36Sopenharmony_ci		break;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	rcu_read_unlock();
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return tt_tmp;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/**
16262306a36Sopenharmony_ci * batadv_tt_local_hash_find() - search the local table for a given client
16362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
16462306a36Sopenharmony_ci * @addr: the mac address of the client to look for
16562306a36Sopenharmony_ci * @vid: VLAN identifier
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * Return: a pointer to the corresponding tt_local_entry struct if the client is
16862306a36Sopenharmony_ci * found, NULL otherwise.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic struct batadv_tt_local_entry *
17162306a36Sopenharmony_cibatadv_tt_local_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
17262306a36Sopenharmony_ci			  unsigned short vid)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
17562306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry = NULL;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, addr,
17862306a36Sopenharmony_ci					      vid);
17962306a36Sopenharmony_ci	if (tt_common_entry)
18062306a36Sopenharmony_ci		tt_local_entry = container_of(tt_common_entry,
18162306a36Sopenharmony_ci					      struct batadv_tt_local_entry,
18262306a36Sopenharmony_ci					      common);
18362306a36Sopenharmony_ci	return tt_local_entry;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * batadv_tt_global_hash_find() - search the global table for a given client
18862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
18962306a36Sopenharmony_ci * @addr: the mac address of the client to look for
19062306a36Sopenharmony_ci * @vid: VLAN identifier
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * Return: a pointer to the corresponding tt_global_entry struct if the client
19362306a36Sopenharmony_ci * is found, NULL otherwise.
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistruct batadv_tt_global_entry *
19662306a36Sopenharmony_cibatadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
19762306a36Sopenharmony_ci			   unsigned short vid)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
20062306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry = NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, addr,
20362306a36Sopenharmony_ci					      vid);
20462306a36Sopenharmony_ci	if (tt_common_entry)
20562306a36Sopenharmony_ci		tt_global_entry = container_of(tt_common_entry,
20662306a36Sopenharmony_ci					       struct batadv_tt_global_entry,
20762306a36Sopenharmony_ci					       common);
20862306a36Sopenharmony_ci	return tt_global_entry;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/**
21262306a36Sopenharmony_ci * batadv_tt_local_entry_free_rcu() - free the tt_local_entry
21362306a36Sopenharmony_ci * @rcu: rcu pointer of the tt_local_entry
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_cistatic void batadv_tt_local_entry_free_rcu(struct rcu_head *rcu)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	tt_local_entry = container_of(rcu, struct batadv_tt_local_entry,
22062306a36Sopenharmony_ci				      common.rcu);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	kmem_cache_free(batadv_tl_cache, tt_local_entry);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/**
22662306a36Sopenharmony_ci * batadv_tt_local_entry_release() - release tt_local_entry from lists and queue
22762306a36Sopenharmony_ci *  for free after rcu grace period
22862306a36Sopenharmony_ci * @ref: kref pointer of the nc_node
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_cistatic void batadv_tt_local_entry_release(struct kref *ref)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	tt_local_entry = container_of(ref, struct batadv_tt_local_entry,
23562306a36Sopenharmony_ci				      common.refcount);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	batadv_softif_vlan_put(tt_local_entry->vlan);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	call_rcu(&tt_local_entry->common.rcu, batadv_tt_local_entry_free_rcu);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/**
24362306a36Sopenharmony_ci * batadv_tt_local_entry_put() - decrement the tt_local_entry refcounter and
24462306a36Sopenharmony_ci *  possibly release it
24562306a36Sopenharmony_ci * @tt_local_entry: tt_local_entry to be free'd
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic void
24862306a36Sopenharmony_cibatadv_tt_local_entry_put(struct batadv_tt_local_entry *tt_local_entry)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	if (!tt_local_entry)
25162306a36Sopenharmony_ci		return;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	kref_put(&tt_local_entry->common.refcount,
25462306a36Sopenharmony_ci		 batadv_tt_local_entry_release);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/**
25862306a36Sopenharmony_ci * batadv_tt_global_entry_free_rcu() - free the tt_global_entry
25962306a36Sopenharmony_ci * @rcu: rcu pointer of the tt_global_entry
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic void batadv_tt_global_entry_free_rcu(struct rcu_head *rcu)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	tt_global_entry = container_of(rcu, struct batadv_tt_global_entry,
26662306a36Sopenharmony_ci				       common.rcu);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	kmem_cache_free(batadv_tg_cache, tt_global_entry);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/**
27262306a36Sopenharmony_ci * batadv_tt_global_entry_release() - release tt_global_entry from lists and
27362306a36Sopenharmony_ci *  queue for free after rcu grace period
27462306a36Sopenharmony_ci * @ref: kref pointer of the nc_node
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_civoid batadv_tt_global_entry_release(struct kref *ref)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	tt_global_entry = container_of(ref, struct batadv_tt_global_entry,
28162306a36Sopenharmony_ci				       common.refcount);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	batadv_tt_global_del_orig_list(tt_global_entry);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	call_rcu(&tt_global_entry->common.rcu, batadv_tt_global_entry_free_rcu);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/**
28962306a36Sopenharmony_ci * batadv_tt_global_hash_count() - count the number of orig entries
29062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
29162306a36Sopenharmony_ci * @addr: the mac address of the client to count entries for
29262306a36Sopenharmony_ci * @vid: VLAN identifier
29362306a36Sopenharmony_ci *
29462306a36Sopenharmony_ci * Return: the number of originators advertising the given address/data
29562306a36Sopenharmony_ci * (excluding our self).
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_ciint batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
29862306a36Sopenharmony_ci				const u8 *addr, unsigned short vid)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
30162306a36Sopenharmony_ci	int count;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
30462306a36Sopenharmony_ci	if (!tt_global_entry)
30562306a36Sopenharmony_ci		return 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	count = atomic_read(&tt_global_entry->orig_list_count);
30862306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global_entry);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return count;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * batadv_tt_local_size_mod() - change the size by v of the local table
31562306a36Sopenharmony_ci *  identified by vid
31662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
31762306a36Sopenharmony_ci * @vid: the VLAN identifier of the sub-table to change
31862306a36Sopenharmony_ci * @v: the amount to sum to the local table size
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_cistatic void batadv_tt_local_size_mod(struct batadv_priv *bat_priv,
32162306a36Sopenharmony_ci				     unsigned short vid, int v)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	vlan = batadv_softif_vlan_get(bat_priv, vid);
32662306a36Sopenharmony_ci	if (!vlan)
32762306a36Sopenharmony_ci		return;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	atomic_add(v, &vlan->tt.num_entries);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	batadv_softif_vlan_put(vlan);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/**
33562306a36Sopenharmony_ci * batadv_tt_local_size_inc() - increase by one the local table size for the
33662306a36Sopenharmony_ci *  given vid
33762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
33862306a36Sopenharmony_ci * @vid: the VLAN identifier
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_cistatic void batadv_tt_local_size_inc(struct batadv_priv *bat_priv,
34162306a36Sopenharmony_ci				     unsigned short vid)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	batadv_tt_local_size_mod(bat_priv, vid, 1);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * batadv_tt_local_size_dec() - decrease by one the local table size for the
34862306a36Sopenharmony_ci *  given vid
34962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
35062306a36Sopenharmony_ci * @vid: the VLAN identifier
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic void batadv_tt_local_size_dec(struct batadv_priv *bat_priv,
35362306a36Sopenharmony_ci				     unsigned short vid)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	batadv_tt_local_size_mod(bat_priv, vid, -1);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/**
35962306a36Sopenharmony_ci * batadv_tt_global_size_mod() - change the size by v of the global table
36062306a36Sopenharmony_ci *  for orig_node identified by vid
36162306a36Sopenharmony_ci * @orig_node: the originator for which the table has to be modified
36262306a36Sopenharmony_ci * @vid: the VLAN identifier
36362306a36Sopenharmony_ci * @v: the amount to sum to the global table size
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic void batadv_tt_global_size_mod(struct batadv_orig_node *orig_node,
36662306a36Sopenharmony_ci				      unsigned short vid, int v)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct batadv_orig_node_vlan *vlan;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	vlan = batadv_orig_node_vlan_new(orig_node, vid);
37162306a36Sopenharmony_ci	if (!vlan)
37262306a36Sopenharmony_ci		return;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (atomic_add_return(v, &vlan->tt.num_entries) == 0) {
37562306a36Sopenharmony_ci		spin_lock_bh(&orig_node->vlan_list_lock);
37662306a36Sopenharmony_ci		if (!hlist_unhashed(&vlan->list)) {
37762306a36Sopenharmony_ci			hlist_del_init_rcu(&vlan->list);
37862306a36Sopenharmony_ci			batadv_orig_node_vlan_put(vlan);
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci		spin_unlock_bh(&orig_node->vlan_list_lock);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	batadv_orig_node_vlan_put(vlan);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * batadv_tt_global_size_inc() - increase by one the global table size for the
38862306a36Sopenharmony_ci *  given vid
38962306a36Sopenharmony_ci * @orig_node: the originator which global table size has to be decreased
39062306a36Sopenharmony_ci * @vid: the vlan identifier
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_cistatic void batadv_tt_global_size_inc(struct batadv_orig_node *orig_node,
39362306a36Sopenharmony_ci				      unsigned short vid)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	batadv_tt_global_size_mod(orig_node, vid, 1);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/**
39962306a36Sopenharmony_ci * batadv_tt_global_size_dec() - decrease by one the global table size for the
40062306a36Sopenharmony_ci *  given vid
40162306a36Sopenharmony_ci * @orig_node: the originator which global table size has to be decreased
40262306a36Sopenharmony_ci * @vid: the vlan identifier
40362306a36Sopenharmony_ci */
40462306a36Sopenharmony_cistatic void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
40562306a36Sopenharmony_ci				      unsigned short vid)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	batadv_tt_global_size_mod(orig_node, vid, -1);
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/**
41162306a36Sopenharmony_ci * batadv_tt_orig_list_entry_free_rcu() - free the orig_entry
41262306a36Sopenharmony_ci * @rcu: rcu pointer of the orig_entry
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	kmem_cache_free(batadv_tt_orig_cache, orig_entry);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/**
42462306a36Sopenharmony_ci * batadv_tt_orig_list_entry_release() - release tt orig entry from lists and
42562306a36Sopenharmony_ci *  queue for free after rcu grace period
42662306a36Sopenharmony_ci * @ref: kref pointer of the tt orig entry
42762306a36Sopenharmony_ci */
42862306a36Sopenharmony_cistatic void batadv_tt_orig_list_entry_release(struct kref *ref)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	orig_entry = container_of(ref, struct batadv_tt_orig_list_entry,
43362306a36Sopenharmony_ci				  refcount);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	batadv_orig_node_put(orig_entry->orig_node);
43662306a36Sopenharmony_ci	call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/**
44062306a36Sopenharmony_ci * batadv_tt_orig_list_entry_put() - decrement the tt orig entry refcounter and
44162306a36Sopenharmony_ci *  possibly release it
44262306a36Sopenharmony_ci * @orig_entry: tt orig entry to be free'd
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic void
44562306a36Sopenharmony_cibatadv_tt_orig_list_entry_put(struct batadv_tt_orig_list_entry *orig_entry)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	if (!orig_entry)
44862306a36Sopenharmony_ci		return;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	kref_put(&orig_entry->refcount, batadv_tt_orig_list_entry_release);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * batadv_tt_local_event() - store a local TT event (ADD/DEL)
45562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
45662306a36Sopenharmony_ci * @tt_local_entry: the TT entry involved in the event
45762306a36Sopenharmony_ci * @event_flags: flags to store in the event structure
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic void batadv_tt_local_event(struct batadv_priv *bat_priv,
46062306a36Sopenharmony_ci				  struct batadv_tt_local_entry *tt_local_entry,
46162306a36Sopenharmony_ci				  u8 event_flags)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct batadv_tt_change_node *tt_change_node, *entry, *safe;
46462306a36Sopenharmony_ci	struct batadv_tt_common_entry *common = &tt_local_entry->common;
46562306a36Sopenharmony_ci	u8 flags = common->flags | event_flags;
46662306a36Sopenharmony_ci	bool event_removed = false;
46762306a36Sopenharmony_ci	bool del_op_requested, del_op_entry;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);
47062306a36Sopenharmony_ci	if (!tt_change_node)
47162306a36Sopenharmony_ci		return;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	tt_change_node->change.flags = flags;
47462306a36Sopenharmony_ci	memset(tt_change_node->change.reserved, 0,
47562306a36Sopenharmony_ci	       sizeof(tt_change_node->change.reserved));
47662306a36Sopenharmony_ci	ether_addr_copy(tt_change_node->change.addr, common->addr);
47762306a36Sopenharmony_ci	tt_change_node->change.vid = htons(common->vid);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	del_op_requested = flags & BATADV_TT_CLIENT_DEL;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* check for ADD+DEL or DEL+ADD events */
48262306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.changes_list_lock);
48362306a36Sopenharmony_ci	list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
48462306a36Sopenharmony_ci				 list) {
48562306a36Sopenharmony_ci		if (!batadv_compare_eth(entry->change.addr, common->addr))
48662306a36Sopenharmony_ci			continue;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		/* DEL+ADD in the same orig interval have no effect and can be
48962306a36Sopenharmony_ci		 * removed to avoid silly behaviour on the receiver side. The
49062306a36Sopenharmony_ci		 * other way around (ADD+DEL) can happen in case of roaming of
49162306a36Sopenharmony_ci		 * a client still in the NEW state. Roaming of NEW clients is
49262306a36Sopenharmony_ci		 * now possible due to automatically recognition of "temporary"
49362306a36Sopenharmony_ci		 * clients
49462306a36Sopenharmony_ci		 */
49562306a36Sopenharmony_ci		del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL;
49662306a36Sopenharmony_ci		if (!del_op_requested && del_op_entry)
49762306a36Sopenharmony_ci			goto del;
49862306a36Sopenharmony_ci		if (del_op_requested && !del_op_entry)
49962306a36Sopenharmony_ci			goto del;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		/* this is a second add in the same originator interval. It
50262306a36Sopenharmony_ci		 * means that flags have been changed: update them!
50362306a36Sopenharmony_ci		 */
50462306a36Sopenharmony_ci		if (!del_op_requested && !del_op_entry)
50562306a36Sopenharmony_ci			entry->change.flags = flags;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		continue;
50862306a36Sopenharmony_cidel:
50962306a36Sopenharmony_ci		list_del(&entry->list);
51062306a36Sopenharmony_ci		kmem_cache_free(batadv_tt_change_cache, entry);
51162306a36Sopenharmony_ci		kmem_cache_free(batadv_tt_change_cache, tt_change_node);
51262306a36Sopenharmony_ci		event_removed = true;
51362306a36Sopenharmony_ci		goto unlock;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/* track the change in the OGMinterval list */
51762306a36Sopenharmony_ci	list_add_tail(&tt_change_node->list, &bat_priv->tt.changes_list);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ciunlock:
52062306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.changes_list_lock);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (event_removed)
52362306a36Sopenharmony_ci		atomic_dec(&bat_priv->tt.local_changes);
52462306a36Sopenharmony_ci	else
52562306a36Sopenharmony_ci		atomic_inc(&bat_priv->tt.local_changes);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/**
52962306a36Sopenharmony_ci * batadv_tt_len() - compute length in bytes of given number of tt changes
53062306a36Sopenharmony_ci * @changes_num: number of tt changes
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * Return: computed length in bytes.
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_cistatic int batadv_tt_len(int changes_num)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	return changes_num * sizeof(struct batadv_tvlv_tt_change);
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci/**
54062306a36Sopenharmony_ci * batadv_tt_entries() - compute the number of entries fitting in tt_len bytes
54162306a36Sopenharmony_ci * @tt_len: available space
54262306a36Sopenharmony_ci *
54362306a36Sopenharmony_ci * Return: the number of entries.
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_cistatic u16 batadv_tt_entries(u16 tt_len)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	return tt_len / batadv_tt_len(1);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/**
55162306a36Sopenharmony_ci * batadv_tt_local_table_transmit_size() - calculates the local translation
55262306a36Sopenharmony_ci *  table size when transmitted over the air
55362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
55462306a36Sopenharmony_ci *
55562306a36Sopenharmony_ci * Return: local translation table size in bytes.
55662306a36Sopenharmony_ci */
55762306a36Sopenharmony_cistatic int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	u16 num_vlan = 0;
56062306a36Sopenharmony_ci	u16 tt_local_entries = 0;
56162306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
56262306a36Sopenharmony_ci	int hdr_size;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	rcu_read_lock();
56562306a36Sopenharmony_ci	hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
56662306a36Sopenharmony_ci		num_vlan++;
56762306a36Sopenharmony_ci		tt_local_entries += atomic_read(&vlan->tt.num_entries);
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci	rcu_read_unlock();
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* header size of tvlv encapsulated tt response payload */
57262306a36Sopenharmony_ci	hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
57362306a36Sopenharmony_ci	hdr_size += sizeof(struct batadv_tvlv_hdr);
57462306a36Sopenharmony_ci	hdr_size += sizeof(struct batadv_tvlv_tt_data);
57562306a36Sopenharmony_ci	hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return hdr_size + batadv_tt_len(tt_local_entries);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int batadv_tt_local_init(struct batadv_priv *bat_priv)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	if (bat_priv->tt.local_hash)
58362306a36Sopenharmony_ci		return 0;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	bat_priv->tt.local_hash = batadv_hash_new(1024);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (!bat_priv->tt.local_hash)
58862306a36Sopenharmony_ci		return -ENOMEM;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	batadv_hash_set_lock_class(bat_priv->tt.local_hash,
59162306a36Sopenharmony_ci				   &batadv_tt_local_hash_lock_class_key);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void batadv_tt_global_free(struct batadv_priv *bat_priv,
59762306a36Sopenharmony_ci				  struct batadv_tt_global_entry *tt_global,
59862306a36Sopenharmony_ci				  const char *message)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_removed_entry;
60162306a36Sopenharmony_ci	struct hlist_node *tt_removed_node;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
60462306a36Sopenharmony_ci		   "Deleting global tt entry %pM (vid: %d): %s\n",
60562306a36Sopenharmony_ci		   tt_global->common.addr,
60662306a36Sopenharmony_ci		   batadv_print_vid(tt_global->common.vid), message);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	tt_removed_node = batadv_hash_remove(bat_priv->tt.global_hash,
60962306a36Sopenharmony_ci					     batadv_compare_tt,
61062306a36Sopenharmony_ci					     batadv_choose_tt,
61162306a36Sopenharmony_ci					     &tt_global->common);
61262306a36Sopenharmony_ci	if (!tt_removed_node)
61362306a36Sopenharmony_ci		return;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* drop reference of remove hash entry */
61662306a36Sopenharmony_ci	tt_removed_entry = hlist_entry(tt_removed_node,
61762306a36Sopenharmony_ci				       struct batadv_tt_global_entry,
61862306a36Sopenharmony_ci				       common.hash_entry);
61962306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_removed_entry);
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci/**
62362306a36Sopenharmony_ci * batadv_tt_local_add() - add a new client to the local table or update an
62462306a36Sopenharmony_ci *  existing client
62562306a36Sopenharmony_ci * @soft_iface: netdev struct of the mesh interface
62662306a36Sopenharmony_ci * @addr: the mac address of the client to add
62762306a36Sopenharmony_ci * @vid: VLAN identifier
62862306a36Sopenharmony_ci * @ifindex: index of the interface where the client is connected to (useful to
62962306a36Sopenharmony_ci *  identify wireless clients)
63062306a36Sopenharmony_ci * @mark: the value contained in the skb->mark field of the received packet (if
63162306a36Sopenharmony_ci *  any)
63262306a36Sopenharmony_ci *
63362306a36Sopenharmony_ci * Return: true if the client was successfully added, false otherwise.
63462306a36Sopenharmony_ci */
63562306a36Sopenharmony_cibool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
63662306a36Sopenharmony_ci			 unsigned short vid, int ifindex, u32 mark)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
63962306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local;
64062306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global = NULL;
64162306a36Sopenharmony_ci	struct net *net = dev_net(soft_iface);
64262306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
64362306a36Sopenharmony_ci	struct net_device *in_dev = NULL;
64462306a36Sopenharmony_ci	struct batadv_hard_iface *in_hardif = NULL;
64562306a36Sopenharmony_ci	struct hlist_head *head;
64662306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
64762306a36Sopenharmony_ci	int hash_added, table_size, packet_size_max;
64862306a36Sopenharmony_ci	bool ret = false;
64962306a36Sopenharmony_ci	bool roamed_back = false;
65062306a36Sopenharmony_ci	u8 remote_flags;
65162306a36Sopenharmony_ci	u32 match_mark;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (ifindex != BATADV_NULL_IFINDEX)
65462306a36Sopenharmony_ci		in_dev = dev_get_by_index(net, ifindex);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (in_dev)
65762306a36Sopenharmony_ci		in_hardif = batadv_hardif_get_by_netdev(in_dev);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (!is_multicast_ether_addr(addr))
66262306a36Sopenharmony_ci		tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (tt_local) {
66562306a36Sopenharmony_ci		tt_local->last_seen = jiffies;
66662306a36Sopenharmony_ci		if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
66762306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
66862306a36Sopenharmony_ci				   "Re-adding pending client %pM (vid: %d)\n",
66962306a36Sopenharmony_ci				   addr, batadv_print_vid(vid));
67062306a36Sopenharmony_ci			/* whatever the reason why the PENDING flag was set,
67162306a36Sopenharmony_ci			 * this is a client which was enqueued to be removed in
67262306a36Sopenharmony_ci			 * this orig_interval. Since it popped up again, the
67362306a36Sopenharmony_ci			 * flag can be reset like it was never enqueued
67462306a36Sopenharmony_ci			 */
67562306a36Sopenharmony_ci			tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING;
67662306a36Sopenharmony_ci			goto add_event;
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
68062306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
68162306a36Sopenharmony_ci				   "Roaming client %pM (vid: %d) came back to its original location\n",
68262306a36Sopenharmony_ci				   addr, batadv_print_vid(vid));
68362306a36Sopenharmony_ci			/* the ROAM flag is set because this client roamed away
68462306a36Sopenharmony_ci			 * and the node got a roaming_advertisement message. Now
68562306a36Sopenharmony_ci			 * that the client popped up again at its original
68662306a36Sopenharmony_ci			 * location such flag can be unset
68762306a36Sopenharmony_ci			 */
68862306a36Sopenharmony_ci			tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM;
68962306a36Sopenharmony_ci			roamed_back = true;
69062306a36Sopenharmony_ci		}
69162306a36Sopenharmony_ci		goto check_roaming;
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/* Ignore the client if we cannot send it in a full table response. */
69562306a36Sopenharmony_ci	table_size = batadv_tt_local_table_transmit_size(bat_priv);
69662306a36Sopenharmony_ci	table_size += batadv_tt_len(1);
69762306a36Sopenharmony_ci	packet_size_max = atomic_read(&bat_priv->packet_size_max);
69862306a36Sopenharmony_ci	if (table_size > packet_size_max) {
69962306a36Sopenharmony_ci		net_ratelimited_function(batadv_info, soft_iface,
70062306a36Sopenharmony_ci					 "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
70162306a36Sopenharmony_ci					 table_size, packet_size_max, addr);
70262306a36Sopenharmony_ci		goto out;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
70662306a36Sopenharmony_ci	if (!tt_local)
70762306a36Sopenharmony_ci		goto out;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/* increase the refcounter of the related vlan */
71062306a36Sopenharmony_ci	vlan = batadv_softif_vlan_get(bat_priv, vid);
71162306a36Sopenharmony_ci	if (!vlan) {
71262306a36Sopenharmony_ci		net_ratelimited_function(batadv_info, soft_iface,
71362306a36Sopenharmony_ci					 "adding TT local entry %pM to non-existent VLAN %d\n",
71462306a36Sopenharmony_ci					 addr, batadv_print_vid(vid));
71562306a36Sopenharmony_ci		kmem_cache_free(batadv_tl_cache, tt_local);
71662306a36Sopenharmony_ci		tt_local = NULL;
71762306a36Sopenharmony_ci		goto out;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
72162306a36Sopenharmony_ci		   "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n",
72262306a36Sopenharmony_ci		   addr, batadv_print_vid(vid),
72362306a36Sopenharmony_ci		   (u8)atomic_read(&bat_priv->tt.vn));
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	ether_addr_copy(tt_local->common.addr, addr);
72662306a36Sopenharmony_ci	/* The local entry has to be marked as NEW to avoid to send it in
72762306a36Sopenharmony_ci	 * a full table response going out before the next ttvn increment
72862306a36Sopenharmony_ci	 * (consistency check)
72962306a36Sopenharmony_ci	 */
73062306a36Sopenharmony_ci	tt_local->common.flags = BATADV_TT_CLIENT_NEW;
73162306a36Sopenharmony_ci	tt_local->common.vid = vid;
73262306a36Sopenharmony_ci	if (batadv_is_wifi_hardif(in_hardif))
73362306a36Sopenharmony_ci		tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
73462306a36Sopenharmony_ci	kref_init(&tt_local->common.refcount);
73562306a36Sopenharmony_ci	tt_local->last_seen = jiffies;
73662306a36Sopenharmony_ci	tt_local->common.added_at = tt_local->last_seen;
73762306a36Sopenharmony_ci	tt_local->vlan = vlan;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* the batman interface mac and multicast addresses should never be
74062306a36Sopenharmony_ci	 * purged
74162306a36Sopenharmony_ci	 */
74262306a36Sopenharmony_ci	if (batadv_compare_eth(addr, soft_iface->dev_addr) ||
74362306a36Sopenharmony_ci	    is_multicast_ether_addr(addr))
74462306a36Sopenharmony_ci		tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	kref_get(&tt_local->common.refcount);
74762306a36Sopenharmony_ci	hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
74862306a36Sopenharmony_ci				     batadv_choose_tt, &tt_local->common,
74962306a36Sopenharmony_ci				     &tt_local->common.hash_entry);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (unlikely(hash_added != 0)) {
75262306a36Sopenharmony_ci		/* remove the reference for the hash */
75362306a36Sopenharmony_ci		batadv_tt_local_entry_put(tt_local);
75462306a36Sopenharmony_ci		goto out;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ciadd_event:
75862306a36Sopenharmony_ci	batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cicheck_roaming:
76162306a36Sopenharmony_ci	/* Check whether it is a roaming, but don't do anything if the roaming
76262306a36Sopenharmony_ci	 * process has already been handled
76362306a36Sopenharmony_ci	 */
76462306a36Sopenharmony_ci	if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) {
76562306a36Sopenharmony_ci		/* These node are probably going to update their tt table */
76662306a36Sopenharmony_ci		head = &tt_global->orig_list;
76762306a36Sopenharmony_ci		rcu_read_lock();
76862306a36Sopenharmony_ci		hlist_for_each_entry_rcu(orig_entry, head, list) {
76962306a36Sopenharmony_ci			batadv_send_roam_adv(bat_priv, tt_global->common.addr,
77062306a36Sopenharmony_ci					     tt_global->common.vid,
77162306a36Sopenharmony_ci					     orig_entry->orig_node);
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci		rcu_read_unlock();
77462306a36Sopenharmony_ci		if (roamed_back) {
77562306a36Sopenharmony_ci			batadv_tt_global_free(bat_priv, tt_global,
77662306a36Sopenharmony_ci					      "Roaming canceled");
77762306a36Sopenharmony_ci		} else {
77862306a36Sopenharmony_ci			/* The global entry has to be marked as ROAMING and
77962306a36Sopenharmony_ci			 * has to be kept for consistency purpose
78062306a36Sopenharmony_ci			 */
78162306a36Sopenharmony_ci			tt_global->common.flags |= BATADV_TT_CLIENT_ROAM;
78262306a36Sopenharmony_ci			tt_global->roam_at = jiffies;
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* store the current remote flags before altering them. This helps
78762306a36Sopenharmony_ci	 * understanding is flags are changing or not
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (batadv_is_wifi_hardif(in_hardif))
79262306a36Sopenharmony_ci		tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
79362306a36Sopenharmony_ci	else
79462306a36Sopenharmony_ci		tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* check the mark in the skb: if it's equal to the configured
79762306a36Sopenharmony_ci	 * isolation_mark, it means the packet is coming from an isolated
79862306a36Sopenharmony_ci	 * non-mesh client
79962306a36Sopenharmony_ci	 */
80062306a36Sopenharmony_ci	match_mark = (mark & bat_priv->isolation_mark_mask);
80162306a36Sopenharmony_ci	if (bat_priv->isolation_mark_mask &&
80262306a36Sopenharmony_ci	    match_mark == bat_priv->isolation_mark)
80362306a36Sopenharmony_ci		tt_local->common.flags |= BATADV_TT_CLIENT_ISOLA;
80462306a36Sopenharmony_ci	else
80562306a36Sopenharmony_ci		tt_local->common.flags &= ~BATADV_TT_CLIENT_ISOLA;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* if any "dynamic" flag has been modified, resend an ADD event for this
80862306a36Sopenharmony_ci	 * entry so that all the nodes can get the new flags
80962306a36Sopenharmony_ci	 */
81062306a36Sopenharmony_ci	if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK))
81162306a36Sopenharmony_ci		batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	ret = true;
81462306a36Sopenharmony_ciout:
81562306a36Sopenharmony_ci	batadv_hardif_put(in_hardif);
81662306a36Sopenharmony_ci	dev_put(in_dev);
81762306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local);
81862306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global);
81962306a36Sopenharmony_ci	return ret;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/**
82362306a36Sopenharmony_ci * batadv_tt_prepare_tvlv_global_data() - prepare the TVLV TT header to send
82462306a36Sopenharmony_ci *  within a TT Response directed to another node
82562306a36Sopenharmony_ci * @orig_node: originator for which the TT data has to be prepared
82662306a36Sopenharmony_ci * @tt_data: uninitialised pointer to the address of the TVLV buffer
82762306a36Sopenharmony_ci * @tt_change: uninitialised pointer to the address of the area where the TT
82862306a36Sopenharmony_ci *  changed can be stored
82962306a36Sopenharmony_ci * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
83062306a36Sopenharmony_ci *  function reserves the amount of space needed to send the entire global TT
83162306a36Sopenharmony_ci *  table. In case of success the value is updated with the real amount of
83262306a36Sopenharmony_ci *  reserved bytes
83362306a36Sopenharmony_ci * Allocate the needed amount of memory for the entire TT TVLV and write its
83462306a36Sopenharmony_ci * header made up of one tvlv_tt_data object and a series of tvlv_tt_vlan_data
83562306a36Sopenharmony_ci * objects, one per active VLAN served by the originator node.
83662306a36Sopenharmony_ci *
83762306a36Sopenharmony_ci * Return: the size of the allocated buffer or 0 in case of failure.
83862306a36Sopenharmony_ci */
83962306a36Sopenharmony_cistatic u16
84062306a36Sopenharmony_cibatadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
84162306a36Sopenharmony_ci				   struct batadv_tvlv_tt_data **tt_data,
84262306a36Sopenharmony_ci				   struct batadv_tvlv_tt_change **tt_change,
84362306a36Sopenharmony_ci				   s32 *tt_len)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	u16 num_vlan = 0;
84662306a36Sopenharmony_ci	u16 num_entries = 0;
84762306a36Sopenharmony_ci	u16 change_offset;
84862306a36Sopenharmony_ci	u16 tvlv_len;
84962306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan;
85062306a36Sopenharmony_ci	struct batadv_orig_node_vlan *vlan;
85162306a36Sopenharmony_ci	u8 *tt_change_ptr;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	spin_lock_bh(&orig_node->vlan_list_lock);
85462306a36Sopenharmony_ci	hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
85562306a36Sopenharmony_ci		num_vlan++;
85662306a36Sopenharmony_ci		num_entries += atomic_read(&vlan->tt.num_entries);
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	change_offset = sizeof(**tt_data);
86062306a36Sopenharmony_ci	change_offset += num_vlan * sizeof(*tt_vlan);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* if tt_len is negative, allocate the space needed by the full table */
86362306a36Sopenharmony_ci	if (*tt_len < 0)
86462306a36Sopenharmony_ci		*tt_len = batadv_tt_len(num_entries);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	tvlv_len = *tt_len;
86762306a36Sopenharmony_ci	tvlv_len += change_offset;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	*tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
87062306a36Sopenharmony_ci	if (!*tt_data) {
87162306a36Sopenharmony_ci		*tt_len = 0;
87262306a36Sopenharmony_ci		goto out;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	(*tt_data)->flags = BATADV_NO_FLAGS;
87662306a36Sopenharmony_ci	(*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn);
87762306a36Sopenharmony_ci	(*tt_data)->num_vlan = htons(num_vlan);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
88062306a36Sopenharmony_ci	hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
88162306a36Sopenharmony_ci		tt_vlan->vid = htons(vlan->vid);
88262306a36Sopenharmony_ci		tt_vlan->crc = htonl(vlan->tt.crc);
88362306a36Sopenharmony_ci		tt_vlan->reserved = 0;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		tt_vlan++;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	tt_change_ptr = (u8 *)*tt_data + change_offset;
88962306a36Sopenharmony_ci	*tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ciout:
89262306a36Sopenharmony_ci	spin_unlock_bh(&orig_node->vlan_list_lock);
89362306a36Sopenharmony_ci	return tvlv_len;
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/**
89762306a36Sopenharmony_ci * batadv_tt_prepare_tvlv_local_data() - allocate and prepare the TT TVLV for
89862306a36Sopenharmony_ci *  this node
89962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
90062306a36Sopenharmony_ci * @tt_data: uninitialised pointer to the address of the TVLV buffer
90162306a36Sopenharmony_ci * @tt_change: uninitialised pointer to the address of the area where the TT
90262306a36Sopenharmony_ci *  changes can be stored
90362306a36Sopenharmony_ci * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
90462306a36Sopenharmony_ci *  function reserves the amount of space needed to send the entire local TT
90562306a36Sopenharmony_ci *  table. In case of success the value is updated with the real amount of
90662306a36Sopenharmony_ci *  reserved bytes
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * Allocate the needed amount of memory for the entire TT TVLV and write its
90962306a36Sopenharmony_ci * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
91062306a36Sopenharmony_ci * objects, one per active VLAN.
91162306a36Sopenharmony_ci *
91262306a36Sopenharmony_ci * Return: the size of the allocated buffer or 0 in case of failure.
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_cistatic u16
91562306a36Sopenharmony_cibatadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
91662306a36Sopenharmony_ci				  struct batadv_tvlv_tt_data **tt_data,
91762306a36Sopenharmony_ci				  struct batadv_tvlv_tt_change **tt_change,
91862306a36Sopenharmony_ci				  s32 *tt_len)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan;
92162306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
92262306a36Sopenharmony_ci	u16 num_vlan = 0;
92362306a36Sopenharmony_ci	u16 vlan_entries = 0;
92462306a36Sopenharmony_ci	u16 total_entries = 0;
92562306a36Sopenharmony_ci	u16 tvlv_len;
92662306a36Sopenharmony_ci	u8 *tt_change_ptr;
92762306a36Sopenharmony_ci	int change_offset;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->softif_vlan_list_lock);
93062306a36Sopenharmony_ci	hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
93162306a36Sopenharmony_ci		vlan_entries = atomic_read(&vlan->tt.num_entries);
93262306a36Sopenharmony_ci		if (vlan_entries < 1)
93362306a36Sopenharmony_ci			continue;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		num_vlan++;
93662306a36Sopenharmony_ci		total_entries += vlan_entries;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	change_offset = sizeof(**tt_data);
94062306a36Sopenharmony_ci	change_offset += num_vlan * sizeof(*tt_vlan);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* if tt_len is negative, allocate the space needed by the full table */
94362306a36Sopenharmony_ci	if (*tt_len < 0)
94462306a36Sopenharmony_ci		*tt_len = batadv_tt_len(total_entries);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	tvlv_len = *tt_len;
94762306a36Sopenharmony_ci	tvlv_len += change_offset;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	*tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
95062306a36Sopenharmony_ci	if (!*tt_data) {
95162306a36Sopenharmony_ci		tvlv_len = 0;
95262306a36Sopenharmony_ci		goto out;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	(*tt_data)->flags = BATADV_NO_FLAGS;
95662306a36Sopenharmony_ci	(*tt_data)->ttvn = atomic_read(&bat_priv->tt.vn);
95762306a36Sopenharmony_ci	(*tt_data)->num_vlan = htons(num_vlan);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
96062306a36Sopenharmony_ci	hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
96162306a36Sopenharmony_ci		vlan_entries = atomic_read(&vlan->tt.num_entries);
96262306a36Sopenharmony_ci		if (vlan_entries < 1)
96362306a36Sopenharmony_ci			continue;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		tt_vlan->vid = htons(vlan->vid);
96662306a36Sopenharmony_ci		tt_vlan->crc = htonl(vlan->tt.crc);
96762306a36Sopenharmony_ci		tt_vlan->reserved = 0;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		tt_vlan++;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	tt_change_ptr = (u8 *)*tt_data + change_offset;
97362306a36Sopenharmony_ci	*tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ciout:
97662306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
97762306a36Sopenharmony_ci	return tvlv_len;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci/**
98162306a36Sopenharmony_ci * batadv_tt_tvlv_container_update() - update the translation table tvlv
98262306a36Sopenharmony_ci *  container after local tt changes have been committed
98362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
98462306a36Sopenharmony_ci */
98562306a36Sopenharmony_cistatic void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct batadv_tt_change_node *entry, *safe;
98862306a36Sopenharmony_ci	struct batadv_tvlv_tt_data *tt_data;
98962306a36Sopenharmony_ci	struct batadv_tvlv_tt_change *tt_change;
99062306a36Sopenharmony_ci	int tt_diff_len, tt_change_len = 0;
99162306a36Sopenharmony_ci	int tt_diff_entries_num = 0;
99262306a36Sopenharmony_ci	int tt_diff_entries_count = 0;
99362306a36Sopenharmony_ci	u16 tvlv_len;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	tt_diff_entries_num = atomic_read(&bat_priv->tt.local_changes);
99662306a36Sopenharmony_ci	tt_diff_len = batadv_tt_len(tt_diff_entries_num);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* if we have too many changes for one packet don't send any
99962306a36Sopenharmony_ci	 * and wait for the tt table request which will be fragmented
100062306a36Sopenharmony_ci	 */
100162306a36Sopenharmony_ci	if (tt_diff_len > bat_priv->soft_iface->mtu)
100262306a36Sopenharmony_ci		tt_diff_len = 0;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data,
100562306a36Sopenharmony_ci						     &tt_change, &tt_diff_len);
100662306a36Sopenharmony_ci	if (!tvlv_len)
100762306a36Sopenharmony_ci		return;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	tt_data->flags = BATADV_TT_OGM_DIFF;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (tt_diff_len == 0)
101262306a36Sopenharmony_ci		goto container_register;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.changes_list_lock);
101562306a36Sopenharmony_ci	atomic_set(&bat_priv->tt.local_changes, 0);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
101862306a36Sopenharmony_ci				 list) {
101962306a36Sopenharmony_ci		if (tt_diff_entries_count < tt_diff_entries_num) {
102062306a36Sopenharmony_ci			memcpy(tt_change + tt_diff_entries_count,
102162306a36Sopenharmony_ci			       &entry->change,
102262306a36Sopenharmony_ci			       sizeof(struct batadv_tvlv_tt_change));
102362306a36Sopenharmony_ci			tt_diff_entries_count++;
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci		list_del(&entry->list);
102662306a36Sopenharmony_ci		kmem_cache_free(batadv_tt_change_cache, entry);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.changes_list_lock);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* Keep the buffer for possible tt_request */
103162306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.last_changeset_lock);
103262306a36Sopenharmony_ci	kfree(bat_priv->tt.last_changeset);
103362306a36Sopenharmony_ci	bat_priv->tt.last_changeset_len = 0;
103462306a36Sopenharmony_ci	bat_priv->tt.last_changeset = NULL;
103562306a36Sopenharmony_ci	tt_change_len = batadv_tt_len(tt_diff_entries_count);
103662306a36Sopenharmony_ci	/* check whether this new OGM has no changes due to size problems */
103762306a36Sopenharmony_ci	if (tt_diff_entries_count > 0) {
103862306a36Sopenharmony_ci		/* if kmalloc() fails we will reply with the full table
103962306a36Sopenharmony_ci		 * instead of providing the diff
104062306a36Sopenharmony_ci		 */
104162306a36Sopenharmony_ci		bat_priv->tt.last_changeset = kzalloc(tt_diff_len, GFP_ATOMIC);
104262306a36Sopenharmony_ci		if (bat_priv->tt.last_changeset) {
104362306a36Sopenharmony_ci			memcpy(bat_priv->tt.last_changeset,
104462306a36Sopenharmony_ci			       tt_change, tt_change_len);
104562306a36Sopenharmony_ci			bat_priv->tt.last_changeset_len = tt_diff_len;
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cicontainer_register:
105162306a36Sopenharmony_ci	batadv_tvlv_container_register(bat_priv, BATADV_TVLV_TT, 1, tt_data,
105262306a36Sopenharmony_ci				       tvlv_len);
105362306a36Sopenharmony_ci	kfree(tt_data);
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci/**
105762306a36Sopenharmony_ci * batadv_tt_local_dump_entry() - Dump one TT local entry into a message
105862306a36Sopenharmony_ci * @msg :Netlink message to dump into
105962306a36Sopenharmony_ci * @portid: Port making netlink request
106062306a36Sopenharmony_ci * @cb: Control block containing additional options
106162306a36Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
106262306a36Sopenharmony_ci * @common: tt local & tt global common data
106362306a36Sopenharmony_ci *
106462306a36Sopenharmony_ci * Return: Error code, or 0 on success
106562306a36Sopenharmony_ci */
106662306a36Sopenharmony_cistatic int
106762306a36Sopenharmony_cibatadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
106862306a36Sopenharmony_ci			   struct netlink_callback *cb,
106962306a36Sopenharmony_ci			   struct batadv_priv *bat_priv,
107062306a36Sopenharmony_ci			   struct batadv_tt_common_entry *common)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	void *hdr;
107362306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
107462306a36Sopenharmony_ci	struct batadv_tt_local_entry *local;
107562306a36Sopenharmony_ci	unsigned int last_seen_msecs;
107662306a36Sopenharmony_ci	u32 crc;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	local = container_of(common, struct batadv_tt_local_entry, common);
107962306a36Sopenharmony_ci	last_seen_msecs = jiffies_to_msecs(jiffies - local->last_seen);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	vlan = batadv_softif_vlan_get(bat_priv, common->vid);
108262306a36Sopenharmony_ci	if (!vlan)
108362306a36Sopenharmony_ci		return 0;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	crc = vlan->tt.crc;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	batadv_softif_vlan_put(vlan);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
109062306a36Sopenharmony_ci			  &batadv_netlink_family,  NLM_F_MULTI,
109162306a36Sopenharmony_ci			  BATADV_CMD_GET_TRANSTABLE_LOCAL);
109262306a36Sopenharmony_ci	if (!hdr)
109362306a36Sopenharmony_ci		return -ENOBUFS;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	genl_dump_check_consistent(cb, hdr);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
109862306a36Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
109962306a36Sopenharmony_ci	    nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
110062306a36Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
110162306a36Sopenharmony_ci		goto nla_put_failure;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	if (!(common->flags & BATADV_TT_CLIENT_NOPURGE) &&
110462306a36Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, last_seen_msecs))
110562306a36Sopenharmony_ci		goto nla_put_failure;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
110862306a36Sopenharmony_ci	return 0;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci nla_put_failure:
111162306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
111262306a36Sopenharmony_ci	return -EMSGSIZE;
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci/**
111662306a36Sopenharmony_ci * batadv_tt_local_dump_bucket() - Dump one TT local bucket into a message
111762306a36Sopenharmony_ci * @msg: Netlink message to dump into
111862306a36Sopenharmony_ci * @portid: Port making netlink request
111962306a36Sopenharmony_ci * @cb: Control block containing additional options
112062306a36Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
112162306a36Sopenharmony_ci * @hash: hash to dump
112262306a36Sopenharmony_ci * @bucket: bucket index to dump
112362306a36Sopenharmony_ci * @idx_s: Number of entries to skip
112462306a36Sopenharmony_ci *
112562306a36Sopenharmony_ci * Return: Error code, or 0 on success
112662306a36Sopenharmony_ci */
112762306a36Sopenharmony_cistatic int
112862306a36Sopenharmony_cibatadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid,
112962306a36Sopenharmony_ci			    struct netlink_callback *cb,
113062306a36Sopenharmony_ci			    struct batadv_priv *bat_priv,
113162306a36Sopenharmony_ci			    struct batadv_hashtable *hash, unsigned int bucket,
113262306a36Sopenharmony_ci			    int *idx_s)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct batadv_tt_common_entry *common;
113562306a36Sopenharmony_ci	int idx = 0;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	spin_lock_bh(&hash->list_locks[bucket]);
113862306a36Sopenharmony_ci	cb->seq = atomic_read(&hash->generation) << 1 | 1;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	hlist_for_each_entry(common, &hash->table[bucket], hash_entry) {
114162306a36Sopenharmony_ci		if (idx++ < *idx_s)
114262306a36Sopenharmony_ci			continue;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		if (batadv_tt_local_dump_entry(msg, portid, cb, bat_priv,
114562306a36Sopenharmony_ci					       common)) {
114662306a36Sopenharmony_ci			spin_unlock_bh(&hash->list_locks[bucket]);
114762306a36Sopenharmony_ci			*idx_s = idx - 1;
114862306a36Sopenharmony_ci			return -EMSGSIZE;
114962306a36Sopenharmony_ci		}
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci	spin_unlock_bh(&hash->list_locks[bucket]);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	*idx_s = 0;
115462306a36Sopenharmony_ci	return 0;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci/**
115862306a36Sopenharmony_ci * batadv_tt_local_dump() - Dump TT local entries into a message
115962306a36Sopenharmony_ci * @msg: Netlink message to dump into
116062306a36Sopenharmony_ci * @cb: Parameters from query
116162306a36Sopenharmony_ci *
116262306a36Sopenharmony_ci * Return: Error code, or 0 on success
116362306a36Sopenharmony_ci */
116462306a36Sopenharmony_ciint batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
116762306a36Sopenharmony_ci	struct net_device *soft_iface;
116862306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
116962306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
117062306a36Sopenharmony_ci	struct batadv_hashtable *hash;
117162306a36Sopenharmony_ci	int ret;
117262306a36Sopenharmony_ci	int ifindex;
117362306a36Sopenharmony_ci	int bucket = cb->args[0];
117462306a36Sopenharmony_ci	int idx = cb->args[1];
117562306a36Sopenharmony_ci	int portid = NETLINK_CB(cb->skb).portid;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
117862306a36Sopenharmony_ci	if (!ifindex)
117962306a36Sopenharmony_ci		return -EINVAL;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	soft_iface = dev_get_by_index(net, ifindex);
118262306a36Sopenharmony_ci	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
118362306a36Sopenharmony_ci		ret = -ENODEV;
118462306a36Sopenharmony_ci		goto out;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	bat_priv = netdev_priv(soft_iface);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
119062306a36Sopenharmony_ci	if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
119162306a36Sopenharmony_ci		ret = -ENOENT;
119262306a36Sopenharmony_ci		goto out;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	hash = bat_priv->tt.local_hash;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	while (bucket < hash->size) {
119862306a36Sopenharmony_ci		if (batadv_tt_local_dump_bucket(msg, portid, cb, bat_priv,
119962306a36Sopenharmony_ci						hash, bucket, &idx))
120062306a36Sopenharmony_ci			break;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		bucket++;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	ret = msg->len;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci out:
120862306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
120962306a36Sopenharmony_ci	dev_put(soft_iface);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	cb->args[0] = bucket;
121262306a36Sopenharmony_ci	cb->args[1] = idx;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	return ret;
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic void
121862306a36Sopenharmony_cibatadv_tt_local_set_pending(struct batadv_priv *bat_priv,
121962306a36Sopenharmony_ci			    struct batadv_tt_local_entry *tt_local_entry,
122062306a36Sopenharmony_ci			    u16 flags, const char *message)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	batadv_tt_local_event(bat_priv, tt_local_entry, flags);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/* The local client has to be marked as "pending to be removed" but has
122562306a36Sopenharmony_ci	 * to be kept in the table in order to send it in a full table
122662306a36Sopenharmony_ci	 * response issued before the net ttvn increment (consistency check)
122762306a36Sopenharmony_ci	 */
122862306a36Sopenharmony_ci	tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
123162306a36Sopenharmony_ci		   "Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
123262306a36Sopenharmony_ci		   tt_local_entry->common.addr,
123362306a36Sopenharmony_ci		   batadv_print_vid(tt_local_entry->common.vid), message);
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/**
123762306a36Sopenharmony_ci * batadv_tt_local_remove() - logically remove an entry from the local table
123862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
123962306a36Sopenharmony_ci * @addr: the MAC address of the client to remove
124062306a36Sopenharmony_ci * @vid: VLAN identifier
124162306a36Sopenharmony_ci * @message: message to append to the log on deletion
124262306a36Sopenharmony_ci * @roaming: true if the deletion is due to a roaming event
124362306a36Sopenharmony_ci *
124462306a36Sopenharmony_ci * Return: the flags assigned to the local entry before being deleted
124562306a36Sopenharmony_ci */
124662306a36Sopenharmony_ciu16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
124762306a36Sopenharmony_ci			   unsigned short vid, const char *message,
124862306a36Sopenharmony_ci			   bool roaming)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_removed_entry;
125162306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
125262306a36Sopenharmony_ci	u16 flags, curr_flags = BATADV_NO_FLAGS;
125362306a36Sopenharmony_ci	struct hlist_node *tt_removed_node;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
125662306a36Sopenharmony_ci	if (!tt_local_entry)
125762306a36Sopenharmony_ci		goto out;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	curr_flags = tt_local_entry->common.flags;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	flags = BATADV_TT_CLIENT_DEL;
126262306a36Sopenharmony_ci	/* if this global entry addition is due to a roaming, the node has to
126362306a36Sopenharmony_ci	 * mark the local entry as "roamed" in order to correctly reroute
126462306a36Sopenharmony_ci	 * packets later
126562306a36Sopenharmony_ci	 */
126662306a36Sopenharmony_ci	if (roaming) {
126762306a36Sopenharmony_ci		flags |= BATADV_TT_CLIENT_ROAM;
126862306a36Sopenharmony_ci		/* mark the local client as ROAMed */
126962306a36Sopenharmony_ci		tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) {
127362306a36Sopenharmony_ci		batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
127462306a36Sopenharmony_ci					    message);
127562306a36Sopenharmony_ci		goto out;
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci	/* if this client has been added right now, it is possible to
127862306a36Sopenharmony_ci	 * immediately purge it
127962306a36Sopenharmony_ci	 */
128062306a36Sopenharmony_ci	batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash,
128362306a36Sopenharmony_ci					     batadv_compare_tt,
128462306a36Sopenharmony_ci					     batadv_choose_tt,
128562306a36Sopenharmony_ci					     &tt_local_entry->common);
128662306a36Sopenharmony_ci	if (!tt_removed_node)
128762306a36Sopenharmony_ci		goto out;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	/* drop reference of remove hash entry */
129062306a36Sopenharmony_ci	tt_removed_entry = hlist_entry(tt_removed_node,
129162306a36Sopenharmony_ci				       struct batadv_tt_local_entry,
129262306a36Sopenharmony_ci				       common.hash_entry);
129362306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_removed_entry);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ciout:
129662306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local_entry);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	return curr_flags;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci/**
130262306a36Sopenharmony_ci * batadv_tt_local_purge_list() - purge inactive tt local entries
130362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
130462306a36Sopenharmony_ci * @head: pointer to the list containing the local tt entries
130562306a36Sopenharmony_ci * @timeout: parameter deciding whether a given tt local entry is considered
130662306a36Sopenharmony_ci *  inactive or not
130762306a36Sopenharmony_ci */
130862306a36Sopenharmony_cistatic void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
130962306a36Sopenharmony_ci				       struct hlist_head *head,
131062306a36Sopenharmony_ci				       int timeout)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
131362306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
131462306a36Sopenharmony_ci	struct hlist_node *node_tmp;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	hlist_for_each_entry_safe(tt_common_entry, node_tmp, head,
131762306a36Sopenharmony_ci				  hash_entry) {
131862306a36Sopenharmony_ci		tt_local_entry = container_of(tt_common_entry,
131962306a36Sopenharmony_ci					      struct batadv_tt_local_entry,
132062306a36Sopenharmony_ci					      common);
132162306a36Sopenharmony_ci		if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE)
132262306a36Sopenharmony_ci			continue;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		/* entry already marked for deletion */
132562306a36Sopenharmony_ci		if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
132662306a36Sopenharmony_ci			continue;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci		if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
132962306a36Sopenharmony_ci			continue;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci		batadv_tt_local_set_pending(bat_priv, tt_local_entry,
133262306a36Sopenharmony_ci					    BATADV_TT_CLIENT_DEL, "timed out");
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci/**
133762306a36Sopenharmony_ci * batadv_tt_local_purge() - purge inactive tt local entries
133862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
133962306a36Sopenharmony_ci * @timeout: parameter deciding whether a given tt local entry is considered
134062306a36Sopenharmony_ci *  inactive or not
134162306a36Sopenharmony_ci */
134262306a36Sopenharmony_cistatic void batadv_tt_local_purge(struct batadv_priv *bat_priv,
134362306a36Sopenharmony_ci				  int timeout)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
134662306a36Sopenharmony_ci	struct hlist_head *head;
134762306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
134862306a36Sopenharmony_ci	u32 i;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
135162306a36Sopenharmony_ci		head = &hash->table[i];
135262306a36Sopenharmony_ci		list_lock = &hash->list_locks[i];
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci		spin_lock_bh(list_lock);
135562306a36Sopenharmony_ci		batadv_tt_local_purge_list(bat_priv, head, timeout);
135662306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct batadv_hashtable *hash;
136362306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
136462306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
136562306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local;
136662306a36Sopenharmony_ci	struct hlist_node *node_tmp;
136762306a36Sopenharmony_ci	struct hlist_head *head;
136862306a36Sopenharmony_ci	u32 i;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	if (!bat_priv->tt.local_hash)
137162306a36Sopenharmony_ci		return;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	hash = bat_priv->tt.local_hash;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
137662306a36Sopenharmony_ci		head = &hash->table[i];
137762306a36Sopenharmony_ci		list_lock = &hash->list_locks[i];
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci		spin_lock_bh(list_lock);
138062306a36Sopenharmony_ci		hlist_for_each_entry_safe(tt_common_entry, node_tmp,
138162306a36Sopenharmony_ci					  head, hash_entry) {
138262306a36Sopenharmony_ci			hlist_del_rcu(&tt_common_entry->hash_entry);
138362306a36Sopenharmony_ci			tt_local = container_of(tt_common_entry,
138462306a36Sopenharmony_ci						struct batadv_tt_local_entry,
138562306a36Sopenharmony_ci						common);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci			batadv_tt_local_entry_put(tt_local);
138862306a36Sopenharmony_ci		}
138962306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	batadv_hash_destroy(hash);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	bat_priv->tt.local_hash = NULL;
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cistatic int batadv_tt_global_init(struct batadv_priv *bat_priv)
139862306a36Sopenharmony_ci{
139962306a36Sopenharmony_ci	if (bat_priv->tt.global_hash)
140062306a36Sopenharmony_ci		return 0;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	bat_priv->tt.global_hash = batadv_hash_new(1024);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (!bat_priv->tt.global_hash)
140562306a36Sopenharmony_ci		return -ENOMEM;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	batadv_hash_set_lock_class(bat_priv->tt.global_hash,
140862306a36Sopenharmony_ci				   &batadv_tt_global_hash_lock_class_key);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	return 0;
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic void batadv_tt_changes_list_free(struct batadv_priv *bat_priv)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct batadv_tt_change_node *entry, *safe;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.changes_list_lock);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
142062306a36Sopenharmony_ci				 list) {
142162306a36Sopenharmony_ci		list_del(&entry->list);
142262306a36Sopenharmony_ci		kmem_cache_free(batadv_tt_change_cache, entry);
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	atomic_set(&bat_priv->tt.local_changes, 0);
142662306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.changes_list_lock);
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci/**
143062306a36Sopenharmony_ci * batadv_tt_global_orig_entry_find() - find a TT orig_list_entry
143162306a36Sopenharmony_ci * @entry: the TT global entry where the orig_list_entry has to be
143262306a36Sopenharmony_ci *  extracted from
143362306a36Sopenharmony_ci * @orig_node: the originator for which the orig_list_entry has to be found
143462306a36Sopenharmony_ci *
143562306a36Sopenharmony_ci * retrieve the orig_tt_list_entry belonging to orig_node from the
143662306a36Sopenharmony_ci * batadv_tt_global_entry list
143762306a36Sopenharmony_ci *
143862306a36Sopenharmony_ci * Return: it with an increased refcounter, NULL if not found
143962306a36Sopenharmony_ci */
144062306a36Sopenharmony_cistatic struct batadv_tt_orig_list_entry *
144162306a36Sopenharmony_cibatadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry *entry,
144262306a36Sopenharmony_ci				 const struct batadv_orig_node *orig_node)
144362306a36Sopenharmony_ci{
144462306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *tmp_orig_entry, *orig_entry = NULL;
144562306a36Sopenharmony_ci	const struct hlist_head *head;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	rcu_read_lock();
144862306a36Sopenharmony_ci	head = &entry->orig_list;
144962306a36Sopenharmony_ci	hlist_for_each_entry_rcu(tmp_orig_entry, head, list) {
145062306a36Sopenharmony_ci		if (tmp_orig_entry->orig_node != orig_node)
145162306a36Sopenharmony_ci			continue;
145262306a36Sopenharmony_ci		if (!kref_get_unless_zero(&tmp_orig_entry->refcount))
145362306a36Sopenharmony_ci			continue;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		orig_entry = tmp_orig_entry;
145662306a36Sopenharmony_ci		break;
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci	rcu_read_unlock();
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	return orig_entry;
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci/**
146462306a36Sopenharmony_ci * batadv_tt_global_entry_has_orig() - check if a TT global entry is also
146562306a36Sopenharmony_ci *  handled by a given originator
146662306a36Sopenharmony_ci * @entry: the TT global entry to check
146762306a36Sopenharmony_ci * @orig_node: the originator to search in the list
146862306a36Sopenharmony_ci * @flags: a pointer to store TT flags for the given @entry received
146962306a36Sopenharmony_ci *  from @orig_node
147062306a36Sopenharmony_ci *
147162306a36Sopenharmony_ci * find out if an orig_node is already in the list of a tt_global_entry.
147262306a36Sopenharmony_ci *
147362306a36Sopenharmony_ci * Return: true if found, false otherwise
147462306a36Sopenharmony_ci */
147562306a36Sopenharmony_cistatic bool
147662306a36Sopenharmony_cibatadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry *entry,
147762306a36Sopenharmony_ci				const struct batadv_orig_node *orig_node,
147862306a36Sopenharmony_ci				u8 *flags)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
148162306a36Sopenharmony_ci	bool found = false;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	orig_entry = batadv_tt_global_orig_entry_find(entry, orig_node);
148462306a36Sopenharmony_ci	if (orig_entry) {
148562306a36Sopenharmony_ci		found = true;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		if (flags)
148862306a36Sopenharmony_ci			*flags = orig_entry->flags;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci		batadv_tt_orig_list_entry_put(orig_entry);
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return found;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci/**
149762306a36Sopenharmony_ci * batadv_tt_global_sync_flags() - update TT sync flags
149862306a36Sopenharmony_ci * @tt_global: the TT global entry to update sync flags in
149962306a36Sopenharmony_ci *
150062306a36Sopenharmony_ci * Updates the sync flag bits in the tt_global flag attribute with a logical
150162306a36Sopenharmony_ci * OR of all sync flags from any of its TT orig entries.
150262306a36Sopenharmony_ci */
150362306a36Sopenharmony_cistatic void
150462306a36Sopenharmony_cibatadv_tt_global_sync_flags(struct batadv_tt_global_entry *tt_global)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
150762306a36Sopenharmony_ci	const struct hlist_head *head;
150862306a36Sopenharmony_ci	u16 flags = BATADV_NO_FLAGS;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	rcu_read_lock();
151162306a36Sopenharmony_ci	head = &tt_global->orig_list;
151262306a36Sopenharmony_ci	hlist_for_each_entry_rcu(orig_entry, head, list)
151362306a36Sopenharmony_ci		flags |= orig_entry->flags;
151462306a36Sopenharmony_ci	rcu_read_unlock();
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	flags |= tt_global->common.flags & (~BATADV_TT_SYNC_MASK);
151762306a36Sopenharmony_ci	tt_global->common.flags = flags;
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci/**
152162306a36Sopenharmony_ci * batadv_tt_global_orig_entry_add() - add or update a TT orig entry
152262306a36Sopenharmony_ci * @tt_global: the TT global entry to add an orig entry in
152362306a36Sopenharmony_ci * @orig_node: the originator to add an orig entry for
152462306a36Sopenharmony_ci * @ttvn: translation table version number of this changeset
152562306a36Sopenharmony_ci * @flags: TT sync flags
152662306a36Sopenharmony_ci */
152762306a36Sopenharmony_cistatic void
152862306a36Sopenharmony_cibatadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
152962306a36Sopenharmony_ci				struct batadv_orig_node *orig_node, int ttvn,
153062306a36Sopenharmony_ci				u8 flags)
153162306a36Sopenharmony_ci{
153262306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	spin_lock_bh(&tt_global->list_lock);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	orig_entry = batadv_tt_global_orig_entry_find(tt_global, orig_node);
153762306a36Sopenharmony_ci	if (orig_entry) {
153862306a36Sopenharmony_ci		/* refresh the ttvn: the current value could be a bogus one that
153962306a36Sopenharmony_ci		 * was added during a "temporary client detection"
154062306a36Sopenharmony_ci		 */
154162306a36Sopenharmony_ci		orig_entry->ttvn = ttvn;
154262306a36Sopenharmony_ci		orig_entry->flags = flags;
154362306a36Sopenharmony_ci		goto sync_flags;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	orig_entry = kmem_cache_zalloc(batadv_tt_orig_cache, GFP_ATOMIC);
154762306a36Sopenharmony_ci	if (!orig_entry)
154862306a36Sopenharmony_ci		goto out;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	INIT_HLIST_NODE(&orig_entry->list);
155162306a36Sopenharmony_ci	kref_get(&orig_node->refcount);
155262306a36Sopenharmony_ci	batadv_tt_global_size_inc(orig_node, tt_global->common.vid);
155362306a36Sopenharmony_ci	orig_entry->orig_node = orig_node;
155462306a36Sopenharmony_ci	orig_entry->ttvn = ttvn;
155562306a36Sopenharmony_ci	orig_entry->flags = flags;
155662306a36Sopenharmony_ci	kref_init(&orig_entry->refcount);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	kref_get(&orig_entry->refcount);
155962306a36Sopenharmony_ci	hlist_add_head_rcu(&orig_entry->list,
156062306a36Sopenharmony_ci			   &tt_global->orig_list);
156162306a36Sopenharmony_ci	atomic_inc(&tt_global->orig_list_count);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cisync_flags:
156462306a36Sopenharmony_ci	batadv_tt_global_sync_flags(tt_global);
156562306a36Sopenharmony_ciout:
156662306a36Sopenharmony_ci	batadv_tt_orig_list_entry_put(orig_entry);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	spin_unlock_bh(&tt_global->list_lock);
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci/**
157262306a36Sopenharmony_ci * batadv_tt_global_add() - add a new TT global entry or update an existing one
157362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
157462306a36Sopenharmony_ci * @orig_node: the originator announcing the client
157562306a36Sopenharmony_ci * @tt_addr: the mac address of the non-mesh client
157662306a36Sopenharmony_ci * @vid: VLAN identifier
157762306a36Sopenharmony_ci * @flags: TT flags that have to be set for this non-mesh client
157862306a36Sopenharmony_ci * @ttvn: the tt version number ever announcing this non-mesh client
157962306a36Sopenharmony_ci *
158062306a36Sopenharmony_ci * Add a new TT global entry for the given originator. If the entry already
158162306a36Sopenharmony_ci * exists add a new reference to the given originator (a global entry can have
158262306a36Sopenharmony_ci * references to multiple originators) and adjust the flags attribute to reflect
158362306a36Sopenharmony_ci * the function argument.
158462306a36Sopenharmony_ci * If a TT local entry exists for this non-mesh client remove it.
158562306a36Sopenharmony_ci *
158662306a36Sopenharmony_ci * The caller must hold the orig_node refcount.
158762306a36Sopenharmony_ci *
158862306a36Sopenharmony_ci * Return: true if the new entry has been added, false otherwise
158962306a36Sopenharmony_ci */
159062306a36Sopenharmony_cistatic bool batadv_tt_global_add(struct batadv_priv *bat_priv,
159162306a36Sopenharmony_ci				 struct batadv_orig_node *orig_node,
159262306a36Sopenharmony_ci				 const unsigned char *tt_addr,
159362306a36Sopenharmony_ci				 unsigned short vid, u16 flags, u8 ttvn)
159462306a36Sopenharmony_ci{
159562306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
159662306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
159762306a36Sopenharmony_ci	bool ret = false;
159862306a36Sopenharmony_ci	int hash_added;
159962306a36Sopenharmony_ci	struct batadv_tt_common_entry *common;
160062306a36Sopenharmony_ci	u16 local_flags;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	/* ignore global entries from backbone nodes */
160362306a36Sopenharmony_ci	if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid))
160462306a36Sopenharmony_ci		return true;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr, vid);
160762306a36Sopenharmony_ci	tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr, vid);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* if the node already has a local client for this entry, it has to wait
161062306a36Sopenharmony_ci	 * for a roaming advertisement instead of manually messing up the global
161162306a36Sopenharmony_ci	 * table
161262306a36Sopenharmony_ci	 */
161362306a36Sopenharmony_ci	if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry &&
161462306a36Sopenharmony_ci	    !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW))
161562306a36Sopenharmony_ci		goto out;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	if (!tt_global_entry) {
161862306a36Sopenharmony_ci		tt_global_entry = kmem_cache_zalloc(batadv_tg_cache,
161962306a36Sopenharmony_ci						    GFP_ATOMIC);
162062306a36Sopenharmony_ci		if (!tt_global_entry)
162162306a36Sopenharmony_ci			goto out;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci		common = &tt_global_entry->common;
162462306a36Sopenharmony_ci		ether_addr_copy(common->addr, tt_addr);
162562306a36Sopenharmony_ci		common->vid = vid;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci		if (!is_multicast_ether_addr(common->addr))
162862306a36Sopenharmony_ci			common->flags = flags & (~BATADV_TT_SYNC_MASK);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci		tt_global_entry->roam_at = 0;
163162306a36Sopenharmony_ci		/* node must store current time in case of roaming. This is
163262306a36Sopenharmony_ci		 * needed to purge this entry out on timeout (if nobody claims
163362306a36Sopenharmony_ci		 * it)
163462306a36Sopenharmony_ci		 */
163562306a36Sopenharmony_ci		if (flags & BATADV_TT_CLIENT_ROAM)
163662306a36Sopenharmony_ci			tt_global_entry->roam_at = jiffies;
163762306a36Sopenharmony_ci		kref_init(&common->refcount);
163862306a36Sopenharmony_ci		common->added_at = jiffies;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci		INIT_HLIST_HEAD(&tt_global_entry->orig_list);
164162306a36Sopenharmony_ci		atomic_set(&tt_global_entry->orig_list_count, 0);
164262306a36Sopenharmony_ci		spin_lock_init(&tt_global_entry->list_lock);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci		kref_get(&common->refcount);
164562306a36Sopenharmony_ci		hash_added = batadv_hash_add(bat_priv->tt.global_hash,
164662306a36Sopenharmony_ci					     batadv_compare_tt,
164762306a36Sopenharmony_ci					     batadv_choose_tt, common,
164862306a36Sopenharmony_ci					     &common->hash_entry);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci		if (unlikely(hash_added != 0)) {
165162306a36Sopenharmony_ci			/* remove the reference for the hash */
165262306a36Sopenharmony_ci			batadv_tt_global_entry_put(tt_global_entry);
165362306a36Sopenharmony_ci			goto out_remove;
165462306a36Sopenharmony_ci		}
165562306a36Sopenharmony_ci	} else {
165662306a36Sopenharmony_ci		common = &tt_global_entry->common;
165762306a36Sopenharmony_ci		/* If there is already a global entry, we can use this one for
165862306a36Sopenharmony_ci		 * our processing.
165962306a36Sopenharmony_ci		 * But if we are trying to add a temporary client then here are
166062306a36Sopenharmony_ci		 * two options at this point:
166162306a36Sopenharmony_ci		 * 1) the global client is not a temporary client: the global
166262306a36Sopenharmony_ci		 *    client has to be left as it is, temporary information
166362306a36Sopenharmony_ci		 *    should never override any already known client state
166462306a36Sopenharmony_ci		 * 2) the global client is a temporary client: purge the
166562306a36Sopenharmony_ci		 *    originator list and add the new one orig_entry
166662306a36Sopenharmony_ci		 */
166762306a36Sopenharmony_ci		if (flags & BATADV_TT_CLIENT_TEMP) {
166862306a36Sopenharmony_ci			if (!(common->flags & BATADV_TT_CLIENT_TEMP))
166962306a36Sopenharmony_ci				goto out;
167062306a36Sopenharmony_ci			if (batadv_tt_global_entry_has_orig(tt_global_entry,
167162306a36Sopenharmony_ci							    orig_node, NULL))
167262306a36Sopenharmony_ci				goto out_remove;
167362306a36Sopenharmony_ci			batadv_tt_global_del_orig_list(tt_global_entry);
167462306a36Sopenharmony_ci			goto add_orig_entry;
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci		/* if the client was temporary added before receiving the first
167862306a36Sopenharmony_ci		 * OGM announcing it, we have to clear the TEMP flag. Also,
167962306a36Sopenharmony_ci		 * remove the previous temporary orig node and re-add it
168062306a36Sopenharmony_ci		 * if required. If the orig entry changed, the new one which
168162306a36Sopenharmony_ci		 * is a non-temporary entry is preferred.
168262306a36Sopenharmony_ci		 */
168362306a36Sopenharmony_ci		if (common->flags & BATADV_TT_CLIENT_TEMP) {
168462306a36Sopenharmony_ci			batadv_tt_global_del_orig_list(tt_global_entry);
168562306a36Sopenharmony_ci			common->flags &= ~BATADV_TT_CLIENT_TEMP;
168662306a36Sopenharmony_ci		}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci		/* the change can carry possible "attribute" flags like the
168962306a36Sopenharmony_ci		 * TT_CLIENT_TEMP, therefore they have to be copied in the
169062306a36Sopenharmony_ci		 * client entry
169162306a36Sopenharmony_ci		 */
169262306a36Sopenharmony_ci		if (!is_multicast_ether_addr(common->addr))
169362306a36Sopenharmony_ci			common->flags |= flags & (~BATADV_TT_SYNC_MASK);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci		/* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
169662306a36Sopenharmony_ci		 * one originator left in the list and we previously received a
169762306a36Sopenharmony_ci		 * delete + roaming change for this originator.
169862306a36Sopenharmony_ci		 *
169962306a36Sopenharmony_ci		 * We should first delete the old originator before adding the
170062306a36Sopenharmony_ci		 * new one.
170162306a36Sopenharmony_ci		 */
170262306a36Sopenharmony_ci		if (common->flags & BATADV_TT_CLIENT_ROAM) {
170362306a36Sopenharmony_ci			batadv_tt_global_del_orig_list(tt_global_entry);
170462306a36Sopenharmony_ci			common->flags &= ~BATADV_TT_CLIENT_ROAM;
170562306a36Sopenharmony_ci			tt_global_entry->roam_at = 0;
170662306a36Sopenharmony_ci		}
170762306a36Sopenharmony_ci	}
170862306a36Sopenharmony_ciadd_orig_entry:
170962306a36Sopenharmony_ci	/* add the new orig_entry (if needed) or update it */
171062306a36Sopenharmony_ci	batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn,
171162306a36Sopenharmony_ci					flags & BATADV_TT_SYNC_MASK);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
171462306a36Sopenharmony_ci		   "Creating new global tt entry: %pM (vid: %d, via %pM)\n",
171562306a36Sopenharmony_ci		   common->addr, batadv_print_vid(common->vid),
171662306a36Sopenharmony_ci		   orig_node->orig);
171762306a36Sopenharmony_ci	ret = true;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ciout_remove:
172062306a36Sopenharmony_ci	/* Do not remove multicast addresses from the local hash on
172162306a36Sopenharmony_ci	 * global additions
172262306a36Sopenharmony_ci	 */
172362306a36Sopenharmony_ci	if (is_multicast_ether_addr(tt_addr))
172462306a36Sopenharmony_ci		goto out;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* remove address from local hash if present */
172762306a36Sopenharmony_ci	local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid,
172862306a36Sopenharmony_ci					     "global tt received",
172962306a36Sopenharmony_ci					     flags & BATADV_TT_CLIENT_ROAM);
173062306a36Sopenharmony_ci	tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	if (!(flags & BATADV_TT_CLIENT_ROAM))
173362306a36Sopenharmony_ci		/* this is a normal global add. Therefore the client is not in a
173462306a36Sopenharmony_ci		 * roaming state anymore.
173562306a36Sopenharmony_ci		 */
173662306a36Sopenharmony_ci		tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ciout:
173962306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global_entry);
174062306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local_entry);
174162306a36Sopenharmony_ci	return ret;
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci/**
174562306a36Sopenharmony_ci * batadv_transtable_best_orig() - Get best originator list entry from tt entry
174662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
174762306a36Sopenharmony_ci * @tt_global_entry: global translation table entry to be analyzed
174862306a36Sopenharmony_ci *
174962306a36Sopenharmony_ci * This function assumes the caller holds rcu_read_lock().
175062306a36Sopenharmony_ci * Return: best originator list entry or NULL on errors.
175162306a36Sopenharmony_ci */
175262306a36Sopenharmony_cistatic struct batadv_tt_orig_list_entry *
175362306a36Sopenharmony_cibatadv_transtable_best_orig(struct batadv_priv *bat_priv,
175462306a36Sopenharmony_ci			    struct batadv_tt_global_entry *tt_global_entry)
175562306a36Sopenharmony_ci{
175662306a36Sopenharmony_ci	struct batadv_neigh_node *router, *best_router = NULL;
175762306a36Sopenharmony_ci	struct batadv_algo_ops *bao = bat_priv->algo_ops;
175862306a36Sopenharmony_ci	struct hlist_head *head;
175962306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	head = &tt_global_entry->orig_list;
176262306a36Sopenharmony_ci	hlist_for_each_entry_rcu(orig_entry, head, list) {
176362306a36Sopenharmony_ci		router = batadv_orig_router_get(orig_entry->orig_node,
176462306a36Sopenharmony_ci						BATADV_IF_DEFAULT);
176562306a36Sopenharmony_ci		if (!router)
176662306a36Sopenharmony_ci			continue;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci		if (best_router &&
176962306a36Sopenharmony_ci		    bao->neigh.cmp(router, BATADV_IF_DEFAULT, best_router,
177062306a36Sopenharmony_ci				   BATADV_IF_DEFAULT) <= 0) {
177162306a36Sopenharmony_ci			batadv_neigh_node_put(router);
177262306a36Sopenharmony_ci			continue;
177362306a36Sopenharmony_ci		}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		/* release the refcount for the "old" best */
177662306a36Sopenharmony_ci		batadv_neigh_node_put(best_router);
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci		best_entry = orig_entry;
177962306a36Sopenharmony_ci		best_router = router;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	batadv_neigh_node_put(best_router);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	return best_entry;
178562306a36Sopenharmony_ci}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci/**
178862306a36Sopenharmony_ci * batadv_tt_global_dump_subentry() - Dump all TT local entries into a message
178962306a36Sopenharmony_ci * @msg: Netlink message to dump into
179062306a36Sopenharmony_ci * @portid: Port making netlink request
179162306a36Sopenharmony_ci * @seq: Sequence number of netlink message
179262306a36Sopenharmony_ci * @common: tt local & tt global common data
179362306a36Sopenharmony_ci * @orig: Originator node announcing a non-mesh client
179462306a36Sopenharmony_ci * @best: Is the best originator for the TT entry
179562306a36Sopenharmony_ci *
179662306a36Sopenharmony_ci * Return: Error code, or 0 on success
179762306a36Sopenharmony_ci */
179862306a36Sopenharmony_cistatic int
179962306a36Sopenharmony_cibatadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
180062306a36Sopenharmony_ci			       struct batadv_tt_common_entry *common,
180162306a36Sopenharmony_ci			       struct batadv_tt_orig_list_entry *orig,
180262306a36Sopenharmony_ci			       bool best)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	u16 flags = (common->flags & (~BATADV_TT_SYNC_MASK)) | orig->flags;
180562306a36Sopenharmony_ci	void *hdr;
180662306a36Sopenharmony_ci	struct batadv_orig_node_vlan *vlan;
180762306a36Sopenharmony_ci	u8 last_ttvn;
180862306a36Sopenharmony_ci	u32 crc;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	vlan = batadv_orig_node_vlan_get(orig->orig_node,
181162306a36Sopenharmony_ci					 common->vid);
181262306a36Sopenharmony_ci	if (!vlan)
181362306a36Sopenharmony_ci		return 0;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	crc = vlan->tt.crc;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	batadv_orig_node_vlan_put(vlan);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
182062306a36Sopenharmony_ci			  NLM_F_MULTI,
182162306a36Sopenharmony_ci			  BATADV_CMD_GET_TRANSTABLE_GLOBAL);
182262306a36Sopenharmony_ci	if (!hdr)
182362306a36Sopenharmony_ci		return -ENOBUFS;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	last_ttvn = atomic_read(&orig->orig_node->last_ttvn);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
182862306a36Sopenharmony_ci	    nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
182962306a36Sopenharmony_ci		    orig->orig_node->orig) ||
183062306a36Sopenharmony_ci	    nla_put_u8(msg, BATADV_ATTR_TT_TTVN, orig->ttvn) ||
183162306a36Sopenharmony_ci	    nla_put_u8(msg, BATADV_ATTR_TT_LAST_TTVN, last_ttvn) ||
183262306a36Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
183362306a36Sopenharmony_ci	    nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
183462306a36Sopenharmony_ci	    nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, flags))
183562306a36Sopenharmony_ci		goto nla_put_failure;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	if (best && nla_put_flag(msg, BATADV_ATTR_FLAG_BEST))
183862306a36Sopenharmony_ci		goto nla_put_failure;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
184162306a36Sopenharmony_ci	return 0;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci nla_put_failure:
184462306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
184562306a36Sopenharmony_ci	return -EMSGSIZE;
184662306a36Sopenharmony_ci}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci/**
184962306a36Sopenharmony_ci * batadv_tt_global_dump_entry() - Dump one TT global entry into a message
185062306a36Sopenharmony_ci * @msg: Netlink message to dump into
185162306a36Sopenharmony_ci * @portid: Port making netlink request
185262306a36Sopenharmony_ci * @seq: Sequence number of netlink message
185362306a36Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
185462306a36Sopenharmony_ci * @common: tt local & tt global common data
185562306a36Sopenharmony_ci * @sub_s: Number of entries to skip
185662306a36Sopenharmony_ci *
185762306a36Sopenharmony_ci * This function assumes the caller holds rcu_read_lock().
185862306a36Sopenharmony_ci *
185962306a36Sopenharmony_ci * Return: Error code, or 0 on success
186062306a36Sopenharmony_ci */
186162306a36Sopenharmony_cistatic int
186262306a36Sopenharmony_cibatadv_tt_global_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
186362306a36Sopenharmony_ci			    struct batadv_priv *bat_priv,
186462306a36Sopenharmony_ci			    struct batadv_tt_common_entry *common, int *sub_s)
186562306a36Sopenharmony_ci{
186662306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
186762306a36Sopenharmony_ci	struct batadv_tt_global_entry *global;
186862306a36Sopenharmony_ci	struct hlist_head *head;
186962306a36Sopenharmony_ci	int sub = 0;
187062306a36Sopenharmony_ci	bool best;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	global = container_of(common, struct batadv_tt_global_entry, common);
187362306a36Sopenharmony_ci	best_entry = batadv_transtable_best_orig(bat_priv, global);
187462306a36Sopenharmony_ci	head = &global->orig_list;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	hlist_for_each_entry_rcu(orig_entry, head, list) {
187762306a36Sopenharmony_ci		if (sub++ < *sub_s)
187862306a36Sopenharmony_ci			continue;
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci		best = (orig_entry == best_entry);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci		if (batadv_tt_global_dump_subentry(msg, portid, seq, common,
188362306a36Sopenharmony_ci						   orig_entry, best)) {
188462306a36Sopenharmony_ci			*sub_s = sub - 1;
188562306a36Sopenharmony_ci			return -EMSGSIZE;
188662306a36Sopenharmony_ci		}
188762306a36Sopenharmony_ci	}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	*sub_s = 0;
189062306a36Sopenharmony_ci	return 0;
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci/**
189462306a36Sopenharmony_ci * batadv_tt_global_dump_bucket() - Dump one TT local bucket into a message
189562306a36Sopenharmony_ci * @msg: Netlink message to dump into
189662306a36Sopenharmony_ci * @portid: Port making netlink request
189762306a36Sopenharmony_ci * @seq: Sequence number of netlink message
189862306a36Sopenharmony_ci * @bat_priv: The bat priv with all the soft interface information
189962306a36Sopenharmony_ci * @head: Pointer to the list containing the global tt entries
190062306a36Sopenharmony_ci * @idx_s: Number of entries to skip
190162306a36Sopenharmony_ci * @sub: Number of entries to skip
190262306a36Sopenharmony_ci *
190362306a36Sopenharmony_ci * Return: Error code, or 0 on success
190462306a36Sopenharmony_ci */
190562306a36Sopenharmony_cistatic int
190662306a36Sopenharmony_cibatadv_tt_global_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
190762306a36Sopenharmony_ci			     struct batadv_priv *bat_priv,
190862306a36Sopenharmony_ci			     struct hlist_head *head, int *idx_s, int *sub)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	struct batadv_tt_common_entry *common;
191162306a36Sopenharmony_ci	int idx = 0;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	rcu_read_lock();
191462306a36Sopenharmony_ci	hlist_for_each_entry_rcu(common, head, hash_entry) {
191562306a36Sopenharmony_ci		if (idx++ < *idx_s)
191662306a36Sopenharmony_ci			continue;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci		if (batadv_tt_global_dump_entry(msg, portid, seq, bat_priv,
191962306a36Sopenharmony_ci						common, sub)) {
192062306a36Sopenharmony_ci			rcu_read_unlock();
192162306a36Sopenharmony_ci			*idx_s = idx - 1;
192262306a36Sopenharmony_ci			return -EMSGSIZE;
192362306a36Sopenharmony_ci		}
192462306a36Sopenharmony_ci	}
192562306a36Sopenharmony_ci	rcu_read_unlock();
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	*idx_s = 0;
192862306a36Sopenharmony_ci	*sub = 0;
192962306a36Sopenharmony_ci	return 0;
193062306a36Sopenharmony_ci}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci/**
193362306a36Sopenharmony_ci * batadv_tt_global_dump() -  Dump TT global entries into a message
193462306a36Sopenharmony_ci * @msg: Netlink message to dump into
193562306a36Sopenharmony_ci * @cb: Parameters from query
193662306a36Sopenharmony_ci *
193762306a36Sopenharmony_ci * Return: Error code, or length of message on success
193862306a36Sopenharmony_ci */
193962306a36Sopenharmony_ciint batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb)
194062306a36Sopenharmony_ci{
194162306a36Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
194262306a36Sopenharmony_ci	struct net_device *soft_iface;
194362306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
194462306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
194562306a36Sopenharmony_ci	struct batadv_hashtable *hash;
194662306a36Sopenharmony_ci	struct hlist_head *head;
194762306a36Sopenharmony_ci	int ret;
194862306a36Sopenharmony_ci	int ifindex;
194962306a36Sopenharmony_ci	int bucket = cb->args[0];
195062306a36Sopenharmony_ci	int idx = cb->args[1];
195162306a36Sopenharmony_ci	int sub = cb->args[2];
195262306a36Sopenharmony_ci	int portid = NETLINK_CB(cb->skb).portid;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
195562306a36Sopenharmony_ci	if (!ifindex)
195662306a36Sopenharmony_ci		return -EINVAL;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	soft_iface = dev_get_by_index(net, ifindex);
195962306a36Sopenharmony_ci	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
196062306a36Sopenharmony_ci		ret = -ENODEV;
196162306a36Sopenharmony_ci		goto out;
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	bat_priv = netdev_priv(soft_iface);
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
196762306a36Sopenharmony_ci	if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
196862306a36Sopenharmony_ci		ret = -ENOENT;
196962306a36Sopenharmony_ci		goto out;
197062306a36Sopenharmony_ci	}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	hash = bat_priv->tt.global_hash;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	while (bucket < hash->size) {
197562306a36Sopenharmony_ci		head = &hash->table[bucket];
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		if (batadv_tt_global_dump_bucket(msg, portid,
197862306a36Sopenharmony_ci						 cb->nlh->nlmsg_seq, bat_priv,
197962306a36Sopenharmony_ci						 head, &idx, &sub))
198062306a36Sopenharmony_ci			break;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci		bucket++;
198362306a36Sopenharmony_ci	}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	ret = msg->len;
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci out:
198862306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
198962306a36Sopenharmony_ci	dev_put(soft_iface);
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	cb->args[0] = bucket;
199262306a36Sopenharmony_ci	cb->args[1] = idx;
199362306a36Sopenharmony_ci	cb->args[2] = sub;
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	return ret;
199662306a36Sopenharmony_ci}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci/**
199962306a36Sopenharmony_ci * _batadv_tt_global_del_orig_entry() - remove and free an orig_entry
200062306a36Sopenharmony_ci * @tt_global_entry: the global entry to remove the orig_entry from
200162306a36Sopenharmony_ci * @orig_entry: the orig entry to remove and free
200262306a36Sopenharmony_ci *
200362306a36Sopenharmony_ci * Remove an orig_entry from its list in the given tt_global_entry and
200462306a36Sopenharmony_ci * free this orig_entry afterwards.
200562306a36Sopenharmony_ci *
200662306a36Sopenharmony_ci * Caller must hold tt_global_entry->list_lock and ensure orig_entry->list is
200762306a36Sopenharmony_ci * part of a list.
200862306a36Sopenharmony_ci */
200962306a36Sopenharmony_cistatic void
201062306a36Sopenharmony_ci_batadv_tt_global_del_orig_entry(struct batadv_tt_global_entry *tt_global_entry,
201162306a36Sopenharmony_ci				 struct batadv_tt_orig_list_entry *orig_entry)
201262306a36Sopenharmony_ci{
201362306a36Sopenharmony_ci	lockdep_assert_held(&tt_global_entry->list_lock);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	batadv_tt_global_size_dec(orig_entry->orig_node,
201662306a36Sopenharmony_ci				  tt_global_entry->common.vid);
201762306a36Sopenharmony_ci	atomic_dec(&tt_global_entry->orig_list_count);
201862306a36Sopenharmony_ci	/* requires holding tt_global_entry->list_lock and orig_entry->list
201962306a36Sopenharmony_ci	 * being part of a list
202062306a36Sopenharmony_ci	 */
202162306a36Sopenharmony_ci	hlist_del_rcu(&orig_entry->list);
202262306a36Sopenharmony_ci	batadv_tt_orig_list_entry_put(orig_entry);
202362306a36Sopenharmony_ci}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci/* deletes the orig list of a tt_global_entry */
202662306a36Sopenharmony_cistatic void
202762306a36Sopenharmony_cibatadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
202862306a36Sopenharmony_ci{
202962306a36Sopenharmony_ci	struct hlist_head *head;
203062306a36Sopenharmony_ci	struct hlist_node *safe;
203162306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	spin_lock_bh(&tt_global_entry->list_lock);
203462306a36Sopenharmony_ci	head = &tt_global_entry->orig_list;
203562306a36Sopenharmony_ci	hlist_for_each_entry_safe(orig_entry, safe, head, list)
203662306a36Sopenharmony_ci		_batadv_tt_global_del_orig_entry(tt_global_entry, orig_entry);
203762306a36Sopenharmony_ci	spin_unlock_bh(&tt_global_entry->list_lock);
203862306a36Sopenharmony_ci}
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci/**
204162306a36Sopenharmony_ci * batadv_tt_global_del_orig_node() - remove orig_node from a global tt entry
204262306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
204362306a36Sopenharmony_ci * @tt_global_entry: the global entry to remove the orig_node from
204462306a36Sopenharmony_ci * @orig_node: the originator announcing the client
204562306a36Sopenharmony_ci * @message: message to append to the log on deletion
204662306a36Sopenharmony_ci *
204762306a36Sopenharmony_ci * Remove the given orig_node and its according orig_entry from the given
204862306a36Sopenharmony_ci * global tt entry.
204962306a36Sopenharmony_ci */
205062306a36Sopenharmony_cistatic void
205162306a36Sopenharmony_cibatadv_tt_global_del_orig_node(struct batadv_priv *bat_priv,
205262306a36Sopenharmony_ci			       struct batadv_tt_global_entry *tt_global_entry,
205362306a36Sopenharmony_ci			       struct batadv_orig_node *orig_node,
205462306a36Sopenharmony_ci			       const char *message)
205562306a36Sopenharmony_ci{
205662306a36Sopenharmony_ci	struct hlist_head *head;
205762306a36Sopenharmony_ci	struct hlist_node *safe;
205862306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
205962306a36Sopenharmony_ci	unsigned short vid;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	spin_lock_bh(&tt_global_entry->list_lock);
206262306a36Sopenharmony_ci	head = &tt_global_entry->orig_list;
206362306a36Sopenharmony_ci	hlist_for_each_entry_safe(orig_entry, safe, head, list) {
206462306a36Sopenharmony_ci		if (orig_entry->orig_node == orig_node) {
206562306a36Sopenharmony_ci			vid = tt_global_entry->common.vid;
206662306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
206762306a36Sopenharmony_ci				   "Deleting %pM from global tt entry %pM (vid: %d): %s\n",
206862306a36Sopenharmony_ci				   orig_node->orig,
206962306a36Sopenharmony_ci				   tt_global_entry->common.addr,
207062306a36Sopenharmony_ci				   batadv_print_vid(vid), message);
207162306a36Sopenharmony_ci			_batadv_tt_global_del_orig_entry(tt_global_entry,
207262306a36Sopenharmony_ci							 orig_entry);
207362306a36Sopenharmony_ci		}
207462306a36Sopenharmony_ci	}
207562306a36Sopenharmony_ci	spin_unlock_bh(&tt_global_entry->list_lock);
207662306a36Sopenharmony_ci}
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci/* If the client is to be deleted, we check if it is the last origantor entry
207962306a36Sopenharmony_ci * within tt_global entry. If yes, we set the BATADV_TT_CLIENT_ROAM flag and the
208062306a36Sopenharmony_ci * timer, otherwise we simply remove the originator scheduled for deletion.
208162306a36Sopenharmony_ci */
208262306a36Sopenharmony_cistatic void
208362306a36Sopenharmony_cibatadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
208462306a36Sopenharmony_ci			     struct batadv_tt_global_entry *tt_global_entry,
208562306a36Sopenharmony_ci			     struct batadv_orig_node *orig_node,
208662306a36Sopenharmony_ci			     const char *message)
208762306a36Sopenharmony_ci{
208862306a36Sopenharmony_ci	bool last_entry = true;
208962306a36Sopenharmony_ci	struct hlist_head *head;
209062306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *orig_entry;
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	/* no local entry exists, case 1:
209362306a36Sopenharmony_ci	 * Check if this is the last one or if other entries exist.
209462306a36Sopenharmony_ci	 */
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	rcu_read_lock();
209762306a36Sopenharmony_ci	head = &tt_global_entry->orig_list;
209862306a36Sopenharmony_ci	hlist_for_each_entry_rcu(orig_entry, head, list) {
209962306a36Sopenharmony_ci		if (orig_entry->orig_node != orig_node) {
210062306a36Sopenharmony_ci			last_entry = false;
210162306a36Sopenharmony_ci			break;
210262306a36Sopenharmony_ci		}
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci	rcu_read_unlock();
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	if (last_entry) {
210762306a36Sopenharmony_ci		/* its the last one, mark for roaming. */
210862306a36Sopenharmony_ci		tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
210962306a36Sopenharmony_ci		tt_global_entry->roam_at = jiffies;
211062306a36Sopenharmony_ci	} else {
211162306a36Sopenharmony_ci		/* there is another entry, we can simply delete this
211262306a36Sopenharmony_ci		 * one and can still use the other one.
211362306a36Sopenharmony_ci		 */
211462306a36Sopenharmony_ci		batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
211562306a36Sopenharmony_ci					       orig_node, message);
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci}
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci/**
212062306a36Sopenharmony_ci * batadv_tt_global_del() - remove a client from the global table
212162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
212262306a36Sopenharmony_ci * @orig_node: an originator serving this client
212362306a36Sopenharmony_ci * @addr: the mac address of the client
212462306a36Sopenharmony_ci * @vid: VLAN identifier
212562306a36Sopenharmony_ci * @message: a message explaining the reason for deleting the client to print
212662306a36Sopenharmony_ci *  for debugging purpose
212762306a36Sopenharmony_ci * @roaming: true if the deletion has been triggered by a roaming event
212862306a36Sopenharmony_ci */
212962306a36Sopenharmony_cistatic void batadv_tt_global_del(struct batadv_priv *bat_priv,
213062306a36Sopenharmony_ci				 struct batadv_orig_node *orig_node,
213162306a36Sopenharmony_ci				 const unsigned char *addr, unsigned short vid,
213262306a36Sopenharmony_ci				 const char *message, bool roaming)
213362306a36Sopenharmony_ci{
213462306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
213562306a36Sopenharmony_ci	struct batadv_tt_local_entry *local_entry = NULL;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
213862306a36Sopenharmony_ci	if (!tt_global_entry)
213962306a36Sopenharmony_ci		goto out;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	if (!roaming) {
214262306a36Sopenharmony_ci		batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
214362306a36Sopenharmony_ci					       orig_node, message);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci		if (hlist_empty(&tt_global_entry->orig_list))
214662306a36Sopenharmony_ci			batadv_tt_global_free(bat_priv, tt_global_entry,
214762306a36Sopenharmony_ci					      message);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci		goto out;
215062306a36Sopenharmony_ci	}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	/* if we are deleting a global entry due to a roam
215362306a36Sopenharmony_ci	 * event, there are two possibilities:
215462306a36Sopenharmony_ci	 * 1) the client roamed from node A to node B => if there
215562306a36Sopenharmony_ci	 *    is only one originator left for this client, we mark
215662306a36Sopenharmony_ci	 *    it with BATADV_TT_CLIENT_ROAM, we start a timer and we
215762306a36Sopenharmony_ci	 *    wait for node B to claim it. In case of timeout
215862306a36Sopenharmony_ci	 *    the entry is purged.
215962306a36Sopenharmony_ci	 *
216062306a36Sopenharmony_ci	 *    If there are other originators left, we directly delete
216162306a36Sopenharmony_ci	 *    the originator.
216262306a36Sopenharmony_ci	 * 2) the client roamed to us => we can directly delete
216362306a36Sopenharmony_ci	 *    the global entry, since it is useless now.
216462306a36Sopenharmony_ci	 */
216562306a36Sopenharmony_ci	local_entry = batadv_tt_local_hash_find(bat_priv,
216662306a36Sopenharmony_ci						tt_global_entry->common.addr,
216762306a36Sopenharmony_ci						vid);
216862306a36Sopenharmony_ci	if (local_entry) {
216962306a36Sopenharmony_ci		/* local entry exists, case 2: client roamed to us. */
217062306a36Sopenharmony_ci		batadv_tt_global_del_orig_list(tt_global_entry);
217162306a36Sopenharmony_ci		batadv_tt_global_free(bat_priv, tt_global_entry, message);
217262306a36Sopenharmony_ci	} else {
217362306a36Sopenharmony_ci		/* no local entry exists, case 1: check for roaming */
217462306a36Sopenharmony_ci		batadv_tt_global_del_roaming(bat_priv, tt_global_entry,
217562306a36Sopenharmony_ci					     orig_node, message);
217662306a36Sopenharmony_ci	}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ciout:
217962306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global_entry);
218062306a36Sopenharmony_ci	batadv_tt_local_entry_put(local_entry);
218162306a36Sopenharmony_ci}
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci/**
218462306a36Sopenharmony_ci * batadv_tt_global_del_orig() - remove all the TT global entries belonging to
218562306a36Sopenharmony_ci *  the given originator matching the provided vid
218662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
218762306a36Sopenharmony_ci * @orig_node: the originator owning the entries to remove
218862306a36Sopenharmony_ci * @match_vid: the VLAN identifier to match. If negative all the entries will be
218962306a36Sopenharmony_ci *  removed
219062306a36Sopenharmony_ci * @message: debug message to print as "reason"
219162306a36Sopenharmony_ci */
219262306a36Sopenharmony_civoid batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
219362306a36Sopenharmony_ci			       struct batadv_orig_node *orig_node,
219462306a36Sopenharmony_ci			       s32 match_vid,
219562306a36Sopenharmony_ci			       const char *message)
219662306a36Sopenharmony_ci{
219762306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global;
219862306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
219962306a36Sopenharmony_ci	u32 i;
220062306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.global_hash;
220162306a36Sopenharmony_ci	struct hlist_node *safe;
220262306a36Sopenharmony_ci	struct hlist_head *head;
220362306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
220462306a36Sopenharmony_ci	unsigned short vid;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	if (!hash)
220762306a36Sopenharmony_ci		return;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
221062306a36Sopenharmony_ci		head = &hash->table[i];
221162306a36Sopenharmony_ci		list_lock = &hash->list_locks[i];
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci		spin_lock_bh(list_lock);
221462306a36Sopenharmony_ci		hlist_for_each_entry_safe(tt_common_entry, safe,
221562306a36Sopenharmony_ci					  head, hash_entry) {
221662306a36Sopenharmony_ci			/* remove only matching entries */
221762306a36Sopenharmony_ci			if (match_vid >= 0 && tt_common_entry->vid != match_vid)
221862306a36Sopenharmony_ci				continue;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci			tt_global = container_of(tt_common_entry,
222162306a36Sopenharmony_ci						 struct batadv_tt_global_entry,
222262306a36Sopenharmony_ci						 common);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci			batadv_tt_global_del_orig_node(bat_priv, tt_global,
222562306a36Sopenharmony_ci						       orig_node, message);
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci			if (hlist_empty(&tt_global->orig_list)) {
222862306a36Sopenharmony_ci				vid = tt_global->common.vid;
222962306a36Sopenharmony_ci				batadv_dbg(BATADV_DBG_TT, bat_priv,
223062306a36Sopenharmony_ci					   "Deleting global tt entry %pM (vid: %d): %s\n",
223162306a36Sopenharmony_ci					   tt_global->common.addr,
223262306a36Sopenharmony_ci					   batadv_print_vid(vid), message);
223362306a36Sopenharmony_ci				hlist_del_rcu(&tt_common_entry->hash_entry);
223462306a36Sopenharmony_ci				batadv_tt_global_entry_put(tt_global);
223562306a36Sopenharmony_ci			}
223662306a36Sopenharmony_ci		}
223762306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
223862306a36Sopenharmony_ci	}
223962306a36Sopenharmony_ci	clear_bit(BATADV_ORIG_CAPA_HAS_TT, &orig_node->capa_initialized);
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_cistatic bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global,
224362306a36Sopenharmony_ci				      char **msg)
224462306a36Sopenharmony_ci{
224562306a36Sopenharmony_ci	bool purge = false;
224662306a36Sopenharmony_ci	unsigned long roam_timeout = BATADV_TT_CLIENT_ROAM_TIMEOUT;
224762306a36Sopenharmony_ci	unsigned long temp_timeout = BATADV_TT_CLIENT_TEMP_TIMEOUT;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	if ((tt_global->common.flags & BATADV_TT_CLIENT_ROAM) &&
225062306a36Sopenharmony_ci	    batadv_has_timed_out(tt_global->roam_at, roam_timeout)) {
225162306a36Sopenharmony_ci		purge = true;
225262306a36Sopenharmony_ci		*msg = "Roaming timeout\n";
225362306a36Sopenharmony_ci	}
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	if ((tt_global->common.flags & BATADV_TT_CLIENT_TEMP) &&
225662306a36Sopenharmony_ci	    batadv_has_timed_out(tt_global->common.added_at, temp_timeout)) {
225762306a36Sopenharmony_ci		purge = true;
225862306a36Sopenharmony_ci		*msg = "Temporary client timeout\n";
225962306a36Sopenharmony_ci	}
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	return purge;
226262306a36Sopenharmony_ci}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_cistatic void batadv_tt_global_purge(struct batadv_priv *bat_priv)
226562306a36Sopenharmony_ci{
226662306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.global_hash;
226762306a36Sopenharmony_ci	struct hlist_head *head;
226862306a36Sopenharmony_ci	struct hlist_node *node_tmp;
226962306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
227062306a36Sopenharmony_ci	u32 i;
227162306a36Sopenharmony_ci	char *msg = NULL;
227262306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common;
227362306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global;
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
227662306a36Sopenharmony_ci		head = &hash->table[i];
227762306a36Sopenharmony_ci		list_lock = &hash->list_locks[i];
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci		spin_lock_bh(list_lock);
228062306a36Sopenharmony_ci		hlist_for_each_entry_safe(tt_common, node_tmp, head,
228162306a36Sopenharmony_ci					  hash_entry) {
228262306a36Sopenharmony_ci			tt_global = container_of(tt_common,
228362306a36Sopenharmony_ci						 struct batadv_tt_global_entry,
228462306a36Sopenharmony_ci						 common);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci			if (!batadv_tt_global_to_purge(tt_global, &msg))
228762306a36Sopenharmony_ci				continue;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
229062306a36Sopenharmony_ci				   "Deleting global tt entry %pM (vid: %d): %s\n",
229162306a36Sopenharmony_ci				   tt_global->common.addr,
229262306a36Sopenharmony_ci				   batadv_print_vid(tt_global->common.vid),
229362306a36Sopenharmony_ci				   msg);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci			hlist_del_rcu(&tt_common->hash_entry);
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci			batadv_tt_global_entry_put(tt_global);
229862306a36Sopenharmony_ci		}
229962306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
230062306a36Sopenharmony_ci	}
230162306a36Sopenharmony_ci}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_cistatic void batadv_tt_global_table_free(struct batadv_priv *bat_priv)
230462306a36Sopenharmony_ci{
230562306a36Sopenharmony_ci	struct batadv_hashtable *hash;
230662306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
230762306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
230862306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global;
230962306a36Sopenharmony_ci	struct hlist_node *node_tmp;
231062306a36Sopenharmony_ci	struct hlist_head *head;
231162306a36Sopenharmony_ci	u32 i;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	if (!bat_priv->tt.global_hash)
231462306a36Sopenharmony_ci		return;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	hash = bat_priv->tt.global_hash;
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
231962306a36Sopenharmony_ci		head = &hash->table[i];
232062306a36Sopenharmony_ci		list_lock = &hash->list_locks[i];
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci		spin_lock_bh(list_lock);
232362306a36Sopenharmony_ci		hlist_for_each_entry_safe(tt_common_entry, node_tmp,
232462306a36Sopenharmony_ci					  head, hash_entry) {
232562306a36Sopenharmony_ci			hlist_del_rcu(&tt_common_entry->hash_entry);
232662306a36Sopenharmony_ci			tt_global = container_of(tt_common_entry,
232762306a36Sopenharmony_ci						 struct batadv_tt_global_entry,
232862306a36Sopenharmony_ci						 common);
232962306a36Sopenharmony_ci			batadv_tt_global_entry_put(tt_global);
233062306a36Sopenharmony_ci		}
233162306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
233262306a36Sopenharmony_ci	}
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	batadv_hash_destroy(hash);
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	bat_priv->tt.global_hash = NULL;
233762306a36Sopenharmony_ci}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_cistatic bool
234062306a36Sopenharmony_ci_batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry,
234162306a36Sopenharmony_ci		       struct batadv_tt_global_entry *tt_global_entry)
234262306a36Sopenharmony_ci{
234362306a36Sopenharmony_ci	if (tt_local_entry->common.flags & BATADV_TT_CLIENT_WIFI &&
234462306a36Sopenharmony_ci	    tt_global_entry->common.flags & BATADV_TT_CLIENT_WIFI)
234562306a36Sopenharmony_ci		return true;
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	/* check if the two clients are marked as isolated */
234862306a36Sopenharmony_ci	if (tt_local_entry->common.flags & BATADV_TT_CLIENT_ISOLA &&
234962306a36Sopenharmony_ci	    tt_global_entry->common.flags & BATADV_TT_CLIENT_ISOLA)
235062306a36Sopenharmony_ci		return true;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	return false;
235362306a36Sopenharmony_ci}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci/**
235662306a36Sopenharmony_ci * batadv_transtable_search() - get the mesh destination for a given client
235762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
235862306a36Sopenharmony_ci * @src: mac address of the source client
235962306a36Sopenharmony_ci * @addr: mac address of the destination client
236062306a36Sopenharmony_ci * @vid: VLAN identifier
236162306a36Sopenharmony_ci *
236262306a36Sopenharmony_ci * Return: a pointer to the originator that was selected as destination in the
236362306a36Sopenharmony_ci * mesh for contacting the client 'addr', NULL otherwise.
236462306a36Sopenharmony_ci * In case of multiple originators serving the same client, the function returns
236562306a36Sopenharmony_ci * the best one (best in terms of metric towards the destination node).
236662306a36Sopenharmony_ci *
236762306a36Sopenharmony_ci * If the two clients are AP isolated the function returns NULL.
236862306a36Sopenharmony_ci */
236962306a36Sopenharmony_cistruct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
237062306a36Sopenharmony_ci						  const u8 *src,
237162306a36Sopenharmony_ci						  const u8 *addr,
237262306a36Sopenharmony_ci						  unsigned short vid)
237362306a36Sopenharmony_ci{
237462306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry = NULL;
237562306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry = NULL;
237662306a36Sopenharmony_ci	struct batadv_orig_node *orig_node = NULL;
237762306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *best_entry;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	if (src && batadv_vlan_ap_isola_get(bat_priv, vid)) {
238062306a36Sopenharmony_ci		tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid);
238162306a36Sopenharmony_ci		if (!tt_local_entry ||
238262306a36Sopenharmony_ci		    (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
238362306a36Sopenharmony_ci			goto out;
238462306a36Sopenharmony_ci	}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
238762306a36Sopenharmony_ci	if (!tt_global_entry)
238862306a36Sopenharmony_ci		goto out;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	/* check whether the clients should not communicate due to AP
239162306a36Sopenharmony_ci	 * isolation
239262306a36Sopenharmony_ci	 */
239362306a36Sopenharmony_ci	if (tt_local_entry &&
239462306a36Sopenharmony_ci	    _batadv_is_ap_isolated(tt_local_entry, tt_global_entry))
239562306a36Sopenharmony_ci		goto out;
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	rcu_read_lock();
239862306a36Sopenharmony_ci	best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
239962306a36Sopenharmony_ci	/* found anything? */
240062306a36Sopenharmony_ci	if (best_entry)
240162306a36Sopenharmony_ci		orig_node = best_entry->orig_node;
240262306a36Sopenharmony_ci	if (orig_node && !kref_get_unless_zero(&orig_node->refcount))
240362306a36Sopenharmony_ci		orig_node = NULL;
240462306a36Sopenharmony_ci	rcu_read_unlock();
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ciout:
240762306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global_entry);
240862306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local_entry);
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	return orig_node;
241162306a36Sopenharmony_ci}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci/**
241462306a36Sopenharmony_ci * batadv_tt_global_crc() - calculates the checksum of the local table belonging
241562306a36Sopenharmony_ci *  to the given orig_node
241662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
241762306a36Sopenharmony_ci * @orig_node: originator for which the CRC should be computed
241862306a36Sopenharmony_ci * @vid: VLAN identifier for which the CRC32 has to be computed
241962306a36Sopenharmony_ci *
242062306a36Sopenharmony_ci * This function computes the checksum for the global table corresponding to a
242162306a36Sopenharmony_ci * specific originator. In particular, the checksum is computed as follows: For
242262306a36Sopenharmony_ci * each client connected to the originator the CRC32C of the MAC address and the
242362306a36Sopenharmony_ci * VID is computed and then all the CRC32Cs of the various clients are xor'ed
242462306a36Sopenharmony_ci * together.
242562306a36Sopenharmony_ci *
242662306a36Sopenharmony_ci * The idea behind is that CRC32C should be used as much as possible in order to
242762306a36Sopenharmony_ci * produce a unique hash of the table, but since the order which is used to feed
242862306a36Sopenharmony_ci * the CRC32C function affects the result and since every node in the network
242962306a36Sopenharmony_ci * probably sorts the clients differently, the hash function cannot be directly
243062306a36Sopenharmony_ci * computed over the entire table. Hence the CRC32C is used only on
243162306a36Sopenharmony_ci * the single client entry, while all the results are then xor'ed together
243262306a36Sopenharmony_ci * because the XOR operation can combine them all while trying to reduce the
243362306a36Sopenharmony_ci * noise as much as possible.
243462306a36Sopenharmony_ci *
243562306a36Sopenharmony_ci * Return: the checksum of the global table of a given originator.
243662306a36Sopenharmony_ci */
243762306a36Sopenharmony_cistatic u32 batadv_tt_global_crc(struct batadv_priv *bat_priv,
243862306a36Sopenharmony_ci				struct batadv_orig_node *orig_node,
243962306a36Sopenharmony_ci				unsigned short vid)
244062306a36Sopenharmony_ci{
244162306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.global_hash;
244262306a36Sopenharmony_ci	struct batadv_tt_orig_list_entry *tt_orig;
244362306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common;
244462306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global;
244562306a36Sopenharmony_ci	struct hlist_head *head;
244662306a36Sopenharmony_ci	u32 i, crc_tmp, crc = 0;
244762306a36Sopenharmony_ci	u8 flags;
244862306a36Sopenharmony_ci	__be16 tmp_vid;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
245162306a36Sopenharmony_ci		head = &hash->table[i];
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci		rcu_read_lock();
245462306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tt_common, head, hash_entry) {
245562306a36Sopenharmony_ci			tt_global = container_of(tt_common,
245662306a36Sopenharmony_ci						 struct batadv_tt_global_entry,
245762306a36Sopenharmony_ci						 common);
245862306a36Sopenharmony_ci			/* compute the CRC only for entries belonging to the
245962306a36Sopenharmony_ci			 * VLAN identified by the vid passed as parameter
246062306a36Sopenharmony_ci			 */
246162306a36Sopenharmony_ci			if (tt_common->vid != vid)
246262306a36Sopenharmony_ci				continue;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci			/* Roaming clients are in the global table for
246562306a36Sopenharmony_ci			 * consistency only. They don't have to be
246662306a36Sopenharmony_ci			 * taken into account while computing the
246762306a36Sopenharmony_ci			 * global crc
246862306a36Sopenharmony_ci			 */
246962306a36Sopenharmony_ci			if (tt_common->flags & BATADV_TT_CLIENT_ROAM)
247062306a36Sopenharmony_ci				continue;
247162306a36Sopenharmony_ci			/* Temporary clients have not been announced yet, so
247262306a36Sopenharmony_ci			 * they have to be skipped while computing the global
247362306a36Sopenharmony_ci			 * crc
247462306a36Sopenharmony_ci			 */
247562306a36Sopenharmony_ci			if (tt_common->flags & BATADV_TT_CLIENT_TEMP)
247662306a36Sopenharmony_ci				continue;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci			/* find out if this global entry is announced by this
247962306a36Sopenharmony_ci			 * originator
248062306a36Sopenharmony_ci			 */
248162306a36Sopenharmony_ci			tt_orig = batadv_tt_global_orig_entry_find(tt_global,
248262306a36Sopenharmony_ci								   orig_node);
248362306a36Sopenharmony_ci			if (!tt_orig)
248462306a36Sopenharmony_ci				continue;
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci			/* use network order to read the VID: this ensures that
248762306a36Sopenharmony_ci			 * every node reads the bytes in the same order.
248862306a36Sopenharmony_ci			 */
248962306a36Sopenharmony_ci			tmp_vid = htons(tt_common->vid);
249062306a36Sopenharmony_ci			crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid));
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci			/* compute the CRC on flags that have to be kept in sync
249362306a36Sopenharmony_ci			 * among nodes
249462306a36Sopenharmony_ci			 */
249562306a36Sopenharmony_ci			flags = tt_orig->flags;
249662306a36Sopenharmony_ci			crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci			crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci			batadv_tt_orig_list_entry_put(tt_orig);
250162306a36Sopenharmony_ci		}
250262306a36Sopenharmony_ci		rcu_read_unlock();
250362306a36Sopenharmony_ci	}
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	return crc;
250662306a36Sopenharmony_ci}
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci/**
250962306a36Sopenharmony_ci * batadv_tt_local_crc() - calculates the checksum of the local table
251062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
251162306a36Sopenharmony_ci * @vid: VLAN identifier for which the CRC32 has to be computed
251262306a36Sopenharmony_ci *
251362306a36Sopenharmony_ci * For details about the computation, please refer to the documentation for
251462306a36Sopenharmony_ci * batadv_tt_global_crc().
251562306a36Sopenharmony_ci *
251662306a36Sopenharmony_ci * Return: the checksum of the local table
251762306a36Sopenharmony_ci */
251862306a36Sopenharmony_cistatic u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
251962306a36Sopenharmony_ci			       unsigned short vid)
252062306a36Sopenharmony_ci{
252162306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
252262306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common;
252362306a36Sopenharmony_ci	struct hlist_head *head;
252462306a36Sopenharmony_ci	u32 i, crc_tmp, crc = 0;
252562306a36Sopenharmony_ci	u8 flags;
252662306a36Sopenharmony_ci	__be16 tmp_vid;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
252962306a36Sopenharmony_ci		head = &hash->table[i];
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci		rcu_read_lock();
253262306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tt_common, head, hash_entry) {
253362306a36Sopenharmony_ci			/* compute the CRC only for entries belonging to the
253462306a36Sopenharmony_ci			 * VLAN identified by vid
253562306a36Sopenharmony_ci			 */
253662306a36Sopenharmony_ci			if (tt_common->vid != vid)
253762306a36Sopenharmony_ci				continue;
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci			/* not yet committed clients have not to be taken into
254062306a36Sopenharmony_ci			 * account while computing the CRC
254162306a36Sopenharmony_ci			 */
254262306a36Sopenharmony_ci			if (tt_common->flags & BATADV_TT_CLIENT_NEW)
254362306a36Sopenharmony_ci				continue;
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci			/* use network order to read the VID: this ensures that
254662306a36Sopenharmony_ci			 * every node reads the bytes in the same order.
254762306a36Sopenharmony_ci			 */
254862306a36Sopenharmony_ci			tmp_vid = htons(tt_common->vid);
254962306a36Sopenharmony_ci			crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid));
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci			/* compute the CRC on flags that have to be kept in sync
255262306a36Sopenharmony_ci			 * among nodes
255362306a36Sopenharmony_ci			 */
255462306a36Sopenharmony_ci			flags = tt_common->flags & BATADV_TT_SYNC_MASK;
255562306a36Sopenharmony_ci			crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci			crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
255862306a36Sopenharmony_ci		}
255962306a36Sopenharmony_ci		rcu_read_unlock();
256062306a36Sopenharmony_ci	}
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci	return crc;
256362306a36Sopenharmony_ci}
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci/**
256662306a36Sopenharmony_ci * batadv_tt_req_node_release() - free tt_req node entry
256762306a36Sopenharmony_ci * @ref: kref pointer of the tt req_node entry
256862306a36Sopenharmony_ci */
256962306a36Sopenharmony_cistatic void batadv_tt_req_node_release(struct kref *ref)
257062306a36Sopenharmony_ci{
257162306a36Sopenharmony_ci	struct batadv_tt_req_node *tt_req_node;
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	tt_req_node = container_of(ref, struct batadv_tt_req_node, refcount);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	kmem_cache_free(batadv_tt_req_cache, tt_req_node);
257662306a36Sopenharmony_ci}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci/**
257962306a36Sopenharmony_ci * batadv_tt_req_node_put() - decrement the tt_req_node refcounter and
258062306a36Sopenharmony_ci *  possibly release it
258162306a36Sopenharmony_ci * @tt_req_node: tt_req_node to be free'd
258262306a36Sopenharmony_ci */
258362306a36Sopenharmony_cistatic void batadv_tt_req_node_put(struct batadv_tt_req_node *tt_req_node)
258462306a36Sopenharmony_ci{
258562306a36Sopenharmony_ci	if (!tt_req_node)
258662306a36Sopenharmony_ci		return;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	kref_put(&tt_req_node->refcount, batadv_tt_req_node_release);
258962306a36Sopenharmony_ci}
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_cistatic void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
259262306a36Sopenharmony_ci{
259362306a36Sopenharmony_ci	struct batadv_tt_req_node *node;
259462306a36Sopenharmony_ci	struct hlist_node *safe;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.req_list_lock);
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
259962306a36Sopenharmony_ci		hlist_del_init(&node->list);
260062306a36Sopenharmony_ci		batadv_tt_req_node_put(node);
260162306a36Sopenharmony_ci	}
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.req_list_lock);
260462306a36Sopenharmony_ci}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_cistatic void batadv_tt_save_orig_buffer(struct batadv_priv *bat_priv,
260762306a36Sopenharmony_ci				       struct batadv_orig_node *orig_node,
260862306a36Sopenharmony_ci				       const void *tt_buff,
260962306a36Sopenharmony_ci				       u16 tt_buff_len)
261062306a36Sopenharmony_ci{
261162306a36Sopenharmony_ci	/* Replace the old buffer only if I received something in the
261262306a36Sopenharmony_ci	 * last OGM (the OGM could carry no changes)
261362306a36Sopenharmony_ci	 */
261462306a36Sopenharmony_ci	spin_lock_bh(&orig_node->tt_buff_lock);
261562306a36Sopenharmony_ci	if (tt_buff_len > 0) {
261662306a36Sopenharmony_ci		kfree(orig_node->tt_buff);
261762306a36Sopenharmony_ci		orig_node->tt_buff_len = 0;
261862306a36Sopenharmony_ci		orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);
261962306a36Sopenharmony_ci		if (orig_node->tt_buff) {
262062306a36Sopenharmony_ci			memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);
262162306a36Sopenharmony_ci			orig_node->tt_buff_len = tt_buff_len;
262262306a36Sopenharmony_ci		}
262362306a36Sopenharmony_ci	}
262462306a36Sopenharmony_ci	spin_unlock_bh(&orig_node->tt_buff_lock);
262562306a36Sopenharmony_ci}
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_cistatic void batadv_tt_req_purge(struct batadv_priv *bat_priv)
262862306a36Sopenharmony_ci{
262962306a36Sopenharmony_ci	struct batadv_tt_req_node *node;
263062306a36Sopenharmony_ci	struct hlist_node *safe;
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.req_list_lock);
263362306a36Sopenharmony_ci	hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
263462306a36Sopenharmony_ci		if (batadv_has_timed_out(node->issued_at,
263562306a36Sopenharmony_ci					 BATADV_TT_REQUEST_TIMEOUT)) {
263662306a36Sopenharmony_ci			hlist_del_init(&node->list);
263762306a36Sopenharmony_ci			batadv_tt_req_node_put(node);
263862306a36Sopenharmony_ci		}
263962306a36Sopenharmony_ci	}
264062306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.req_list_lock);
264162306a36Sopenharmony_ci}
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci/**
264462306a36Sopenharmony_ci * batadv_tt_req_node_new() - search and possibly create a tt_req_node object
264562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
264662306a36Sopenharmony_ci * @orig_node: orig node this request is being issued for
264762306a36Sopenharmony_ci *
264862306a36Sopenharmony_ci * Return: the pointer to the new tt_req_node struct if no request
264962306a36Sopenharmony_ci * has already been issued for this orig_node, NULL otherwise.
265062306a36Sopenharmony_ci */
265162306a36Sopenharmony_cistatic struct batadv_tt_req_node *
265262306a36Sopenharmony_cibatadv_tt_req_node_new(struct batadv_priv *bat_priv,
265362306a36Sopenharmony_ci		       struct batadv_orig_node *orig_node)
265462306a36Sopenharmony_ci{
265562306a36Sopenharmony_ci	struct batadv_tt_req_node *tt_req_node_tmp, *tt_req_node = NULL;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.req_list_lock);
265862306a36Sopenharmony_ci	hlist_for_each_entry(tt_req_node_tmp, &bat_priv->tt.req_list, list) {
265962306a36Sopenharmony_ci		if (batadv_compare_eth(tt_req_node_tmp, orig_node) &&
266062306a36Sopenharmony_ci		    !batadv_has_timed_out(tt_req_node_tmp->issued_at,
266162306a36Sopenharmony_ci					  BATADV_TT_REQUEST_TIMEOUT))
266262306a36Sopenharmony_ci			goto unlock;
266362306a36Sopenharmony_ci	}
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	tt_req_node = kmem_cache_alloc(batadv_tt_req_cache, GFP_ATOMIC);
266662306a36Sopenharmony_ci	if (!tt_req_node)
266762306a36Sopenharmony_ci		goto unlock;
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	kref_init(&tt_req_node->refcount);
267062306a36Sopenharmony_ci	ether_addr_copy(tt_req_node->addr, orig_node->orig);
267162306a36Sopenharmony_ci	tt_req_node->issued_at = jiffies;
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	kref_get(&tt_req_node->refcount);
267462306a36Sopenharmony_ci	hlist_add_head(&tt_req_node->list, &bat_priv->tt.req_list);
267562306a36Sopenharmony_ciunlock:
267662306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.req_list_lock);
267762306a36Sopenharmony_ci	return tt_req_node;
267862306a36Sopenharmony_ci}
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci/**
268162306a36Sopenharmony_ci * batadv_tt_local_valid() - verify local tt entry and get flags
268262306a36Sopenharmony_ci * @entry_ptr: to be checked local tt entry
268362306a36Sopenharmony_ci * @data_ptr: not used but definition required to satisfy the callback prototype
268462306a36Sopenharmony_ci * @flags: a pointer to store TT flags for this client to
268562306a36Sopenharmony_ci *
268662306a36Sopenharmony_ci * Checks the validity of the given local TT entry. If it is, then the provided
268762306a36Sopenharmony_ci * flags pointer is updated.
268862306a36Sopenharmony_ci *
268962306a36Sopenharmony_ci * Return: true if the entry is a valid, false otherwise.
269062306a36Sopenharmony_ci */
269162306a36Sopenharmony_cistatic bool batadv_tt_local_valid(const void *entry_ptr,
269262306a36Sopenharmony_ci				  const void *data_ptr,
269362306a36Sopenharmony_ci				  u8 *flags)
269462306a36Sopenharmony_ci{
269562306a36Sopenharmony_ci	const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	if (tt_common_entry->flags & BATADV_TT_CLIENT_NEW)
269862306a36Sopenharmony_ci		return false;
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci	if (flags)
270162306a36Sopenharmony_ci		*flags = tt_common_entry->flags;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	return true;
270462306a36Sopenharmony_ci}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci/**
270762306a36Sopenharmony_ci * batadv_tt_global_valid() - verify global tt entry and get flags
270862306a36Sopenharmony_ci * @entry_ptr: to be checked global tt entry
270962306a36Sopenharmony_ci * @data_ptr: an orig_node object (may be NULL)
271062306a36Sopenharmony_ci * @flags: a pointer to store TT flags for this client to
271162306a36Sopenharmony_ci *
271262306a36Sopenharmony_ci * Checks the validity of the given global TT entry. If it is, then the provided
271362306a36Sopenharmony_ci * flags pointer is updated either with the common (summed) TT flags if data_ptr
271462306a36Sopenharmony_ci * is NULL or the specific, per originator TT flags otherwise.
271562306a36Sopenharmony_ci *
271662306a36Sopenharmony_ci * Return: true if the entry is a valid, false otherwise.
271762306a36Sopenharmony_ci */
271862306a36Sopenharmony_cistatic bool batadv_tt_global_valid(const void *entry_ptr,
271962306a36Sopenharmony_ci				   const void *data_ptr,
272062306a36Sopenharmony_ci				   u8 *flags)
272162306a36Sopenharmony_ci{
272262306a36Sopenharmony_ci	const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
272362306a36Sopenharmony_ci	const struct batadv_tt_global_entry *tt_global_entry;
272462306a36Sopenharmony_ci	const struct batadv_orig_node *orig_node = data_ptr;
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	if (tt_common_entry->flags & BATADV_TT_CLIENT_ROAM ||
272762306a36Sopenharmony_ci	    tt_common_entry->flags & BATADV_TT_CLIENT_TEMP)
272862306a36Sopenharmony_ci		return false;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	tt_global_entry = container_of(tt_common_entry,
273162306a36Sopenharmony_ci				       struct batadv_tt_global_entry,
273262306a36Sopenharmony_ci				       common);
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	return batadv_tt_global_entry_has_orig(tt_global_entry, orig_node,
273562306a36Sopenharmony_ci					       flags);
273662306a36Sopenharmony_ci}
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci/**
273962306a36Sopenharmony_ci * batadv_tt_tvlv_generate() - fill the tvlv buff with the tt entries from the
274062306a36Sopenharmony_ci *  specified tt hash
274162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
274262306a36Sopenharmony_ci * @hash: hash table containing the tt entries
274362306a36Sopenharmony_ci * @tt_len: expected tvlv tt data buffer length in number of bytes
274462306a36Sopenharmony_ci * @tvlv_buff: pointer to the buffer to fill with the TT data
274562306a36Sopenharmony_ci * @valid_cb: function to filter tt change entries and to return TT flags
274662306a36Sopenharmony_ci * @cb_data: data passed to the filter function as argument
274762306a36Sopenharmony_ci *
274862306a36Sopenharmony_ci * Fills the tvlv buff with the tt entries from the specified hash. If valid_cb
274962306a36Sopenharmony_ci * is not provided then this becomes a no-op.
275062306a36Sopenharmony_ci */
275162306a36Sopenharmony_cistatic void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv,
275262306a36Sopenharmony_ci				    struct batadv_hashtable *hash,
275362306a36Sopenharmony_ci				    void *tvlv_buff, u16 tt_len,
275462306a36Sopenharmony_ci				    bool (*valid_cb)(const void *,
275562306a36Sopenharmony_ci						     const void *,
275662306a36Sopenharmony_ci						     u8 *flags),
275762306a36Sopenharmony_ci				    void *cb_data)
275862306a36Sopenharmony_ci{
275962306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
276062306a36Sopenharmony_ci	struct batadv_tvlv_tt_change *tt_change;
276162306a36Sopenharmony_ci	struct hlist_head *head;
276262306a36Sopenharmony_ci	u16 tt_tot, tt_num_entries = 0;
276362306a36Sopenharmony_ci	u8 flags;
276462306a36Sopenharmony_ci	bool ret;
276562306a36Sopenharmony_ci	u32 i;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	tt_tot = batadv_tt_entries(tt_len);
276862306a36Sopenharmony_ci	tt_change = tvlv_buff;
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	if (!valid_cb)
277162306a36Sopenharmony_ci		return;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	rcu_read_lock();
277462306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
277562306a36Sopenharmony_ci		head = &hash->table[i];
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tt_common_entry,
277862306a36Sopenharmony_ci					 head, hash_entry) {
277962306a36Sopenharmony_ci			if (tt_tot == tt_num_entries)
278062306a36Sopenharmony_ci				break;
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci			ret = valid_cb(tt_common_entry, cb_data, &flags);
278362306a36Sopenharmony_ci			if (!ret)
278462306a36Sopenharmony_ci				continue;
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci			ether_addr_copy(tt_change->addr, tt_common_entry->addr);
278762306a36Sopenharmony_ci			tt_change->flags = flags;
278862306a36Sopenharmony_ci			tt_change->vid = htons(tt_common_entry->vid);
278962306a36Sopenharmony_ci			memset(tt_change->reserved, 0,
279062306a36Sopenharmony_ci			       sizeof(tt_change->reserved));
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci			tt_num_entries++;
279362306a36Sopenharmony_ci			tt_change++;
279462306a36Sopenharmony_ci		}
279562306a36Sopenharmony_ci	}
279662306a36Sopenharmony_ci	rcu_read_unlock();
279762306a36Sopenharmony_ci}
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci/**
280062306a36Sopenharmony_ci * batadv_tt_global_check_crc() - check if all the CRCs are correct
280162306a36Sopenharmony_ci * @orig_node: originator for which the CRCs have to be checked
280262306a36Sopenharmony_ci * @tt_vlan: pointer to the first tvlv VLAN entry
280362306a36Sopenharmony_ci * @num_vlan: number of tvlv VLAN entries
280462306a36Sopenharmony_ci *
280562306a36Sopenharmony_ci * Return: true if all the received CRCs match the locally stored ones, false
280662306a36Sopenharmony_ci * otherwise
280762306a36Sopenharmony_ci */
280862306a36Sopenharmony_cistatic bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
280962306a36Sopenharmony_ci				       struct batadv_tvlv_tt_vlan_data *tt_vlan,
281062306a36Sopenharmony_ci				       u16 num_vlan)
281162306a36Sopenharmony_ci{
281262306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
281362306a36Sopenharmony_ci	struct batadv_orig_node_vlan *vlan;
281462306a36Sopenharmony_ci	int i, orig_num_vlan;
281562306a36Sopenharmony_ci	u32 crc;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	/* check if each received CRC matches the locally stored one */
281862306a36Sopenharmony_ci	for (i = 0; i < num_vlan; i++) {
281962306a36Sopenharmony_ci		tt_vlan_tmp = tt_vlan + i;
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci		/* if orig_node is a backbone node for this VLAN, don't check
282262306a36Sopenharmony_ci		 * the CRC as we ignore all the global entries over it
282362306a36Sopenharmony_ci		 */
282462306a36Sopenharmony_ci		if (batadv_bla_is_backbone_gw_orig(orig_node->bat_priv,
282562306a36Sopenharmony_ci						   orig_node->orig,
282662306a36Sopenharmony_ci						   ntohs(tt_vlan_tmp->vid)))
282762306a36Sopenharmony_ci			continue;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci		vlan = batadv_orig_node_vlan_get(orig_node,
283062306a36Sopenharmony_ci						 ntohs(tt_vlan_tmp->vid));
283162306a36Sopenharmony_ci		if (!vlan)
283262306a36Sopenharmony_ci			return false;
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_ci		crc = vlan->tt.crc;
283562306a36Sopenharmony_ci		batadv_orig_node_vlan_put(vlan);
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci		if (crc != ntohl(tt_vlan_tmp->crc))
283862306a36Sopenharmony_ci			return false;
283962306a36Sopenharmony_ci	}
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	/* check if any excess VLANs exist locally for the originator
284262306a36Sopenharmony_ci	 * which are not mentioned in the TVLV from the originator.
284362306a36Sopenharmony_ci	 */
284462306a36Sopenharmony_ci	rcu_read_lock();
284562306a36Sopenharmony_ci	orig_num_vlan = 0;
284662306a36Sopenharmony_ci	hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list)
284762306a36Sopenharmony_ci		orig_num_vlan++;
284862306a36Sopenharmony_ci	rcu_read_unlock();
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	if (orig_num_vlan > num_vlan)
285162306a36Sopenharmony_ci		return false;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	return true;
285462306a36Sopenharmony_ci}
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci/**
285762306a36Sopenharmony_ci * batadv_tt_local_update_crc() - update all the local CRCs
285862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
285962306a36Sopenharmony_ci */
286062306a36Sopenharmony_cistatic void batadv_tt_local_update_crc(struct batadv_priv *bat_priv)
286162306a36Sopenharmony_ci{
286262306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	/* recompute the global CRC for each VLAN */
286562306a36Sopenharmony_ci	rcu_read_lock();
286662306a36Sopenharmony_ci	hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
286762306a36Sopenharmony_ci		vlan->tt.crc = batadv_tt_local_crc(bat_priv, vlan->vid);
286862306a36Sopenharmony_ci	}
286962306a36Sopenharmony_ci	rcu_read_unlock();
287062306a36Sopenharmony_ci}
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci/**
287362306a36Sopenharmony_ci * batadv_tt_global_update_crc() - update all the global CRCs for this orig_node
287462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
287562306a36Sopenharmony_ci * @orig_node: the orig_node for which the CRCs have to be updated
287662306a36Sopenharmony_ci */
287762306a36Sopenharmony_cistatic void batadv_tt_global_update_crc(struct batadv_priv *bat_priv,
287862306a36Sopenharmony_ci					struct batadv_orig_node *orig_node)
287962306a36Sopenharmony_ci{
288062306a36Sopenharmony_ci	struct batadv_orig_node_vlan *vlan;
288162306a36Sopenharmony_ci	u32 crc;
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	/* recompute the global CRC for each VLAN */
288462306a36Sopenharmony_ci	rcu_read_lock();
288562306a36Sopenharmony_ci	hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
288662306a36Sopenharmony_ci		/* if orig_node is a backbone node for this VLAN, don't compute
288762306a36Sopenharmony_ci		 * the CRC as we ignore all the global entries over it
288862306a36Sopenharmony_ci		 */
288962306a36Sopenharmony_ci		if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig,
289062306a36Sopenharmony_ci						   vlan->vid))
289162306a36Sopenharmony_ci			continue;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci		crc = batadv_tt_global_crc(bat_priv, orig_node, vlan->vid);
289462306a36Sopenharmony_ci		vlan->tt.crc = crc;
289562306a36Sopenharmony_ci	}
289662306a36Sopenharmony_ci	rcu_read_unlock();
289762306a36Sopenharmony_ci}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci/**
290062306a36Sopenharmony_ci * batadv_send_tt_request() - send a TT Request message to a given node
290162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
290262306a36Sopenharmony_ci * @dst_orig_node: the destination of the message
290362306a36Sopenharmony_ci * @ttvn: the version number that the source of the message is looking for
290462306a36Sopenharmony_ci * @tt_vlan: pointer to the first tvlv VLAN object to request
290562306a36Sopenharmony_ci * @num_vlan: number of tvlv VLAN entries
290662306a36Sopenharmony_ci * @full_table: ask for the entire translation table if true, while only for the
290762306a36Sopenharmony_ci *  last TT diff otherwise
290862306a36Sopenharmony_ci *
290962306a36Sopenharmony_ci * Return: true if the TT Request was sent, false otherwise
291062306a36Sopenharmony_ci */
291162306a36Sopenharmony_cistatic bool batadv_send_tt_request(struct batadv_priv *bat_priv,
291262306a36Sopenharmony_ci				   struct batadv_orig_node *dst_orig_node,
291362306a36Sopenharmony_ci				   u8 ttvn,
291462306a36Sopenharmony_ci				   struct batadv_tvlv_tt_vlan_data *tt_vlan,
291562306a36Sopenharmony_ci				   u16 num_vlan, bool full_table)
291662306a36Sopenharmony_ci{
291762306a36Sopenharmony_ci	struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
291862306a36Sopenharmony_ci	struct batadv_tt_req_node *tt_req_node = NULL;
291962306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan_req;
292062306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if;
292162306a36Sopenharmony_ci	bool ret = false;
292262306a36Sopenharmony_ci	int i, size;
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
292562306a36Sopenharmony_ci	if (!primary_if)
292662306a36Sopenharmony_ci		goto out;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	/* The new tt_req will be issued only if I'm not waiting for a
292962306a36Sopenharmony_ci	 * reply from the same orig_node yet
293062306a36Sopenharmony_ci	 */
293162306a36Sopenharmony_ci	tt_req_node = batadv_tt_req_node_new(bat_priv, dst_orig_node);
293262306a36Sopenharmony_ci	if (!tt_req_node)
293362306a36Sopenharmony_ci		goto out;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	size = sizeof(*tvlv_tt_data) + sizeof(*tt_vlan_req) * num_vlan;
293662306a36Sopenharmony_ci	tvlv_tt_data = kzalloc(size, GFP_ATOMIC);
293762306a36Sopenharmony_ci	if (!tvlv_tt_data)
293862306a36Sopenharmony_ci		goto out;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	tvlv_tt_data->flags = BATADV_TT_REQUEST;
294162306a36Sopenharmony_ci	tvlv_tt_data->ttvn = ttvn;
294262306a36Sopenharmony_ci	tvlv_tt_data->num_vlan = htons(num_vlan);
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	/* send all the CRCs within the request. This is needed by intermediate
294562306a36Sopenharmony_ci	 * nodes to ensure they have the correct table before replying
294662306a36Sopenharmony_ci	 */
294762306a36Sopenharmony_ci	tt_vlan_req = (struct batadv_tvlv_tt_vlan_data *)(tvlv_tt_data + 1);
294862306a36Sopenharmony_ci	for (i = 0; i < num_vlan; i++) {
294962306a36Sopenharmony_ci		tt_vlan_req->vid = tt_vlan->vid;
295062306a36Sopenharmony_ci		tt_vlan_req->crc = tt_vlan->crc;
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci		tt_vlan_req++;
295362306a36Sopenharmony_ci		tt_vlan++;
295462306a36Sopenharmony_ci	}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	if (full_table)
295762306a36Sopenharmony_ci		tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n",
296062306a36Sopenharmony_ci		   dst_orig_node->orig, full_table ? 'F' : '.');
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci	batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
296362306a36Sopenharmony_ci	batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
296462306a36Sopenharmony_ci				 dst_orig_node->orig, BATADV_TVLV_TT, 1,
296562306a36Sopenharmony_ci				 tvlv_tt_data, size);
296662306a36Sopenharmony_ci	ret = true;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ciout:
296962306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	if (ret && tt_req_node) {
297262306a36Sopenharmony_ci		spin_lock_bh(&bat_priv->tt.req_list_lock);
297362306a36Sopenharmony_ci		if (!hlist_unhashed(&tt_req_node->list)) {
297462306a36Sopenharmony_ci			hlist_del_init(&tt_req_node->list);
297562306a36Sopenharmony_ci			batadv_tt_req_node_put(tt_req_node);
297662306a36Sopenharmony_ci		}
297762306a36Sopenharmony_ci		spin_unlock_bh(&bat_priv->tt.req_list_lock);
297862306a36Sopenharmony_ci	}
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	batadv_tt_req_node_put(tt_req_node);
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	kfree(tvlv_tt_data);
298362306a36Sopenharmony_ci	return ret;
298462306a36Sopenharmony_ci}
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci/**
298762306a36Sopenharmony_ci * batadv_send_other_tt_response() - send reply to tt request concerning another
298862306a36Sopenharmony_ci *  node's translation table
298962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
299062306a36Sopenharmony_ci * @tt_data: tt data containing the tt request information
299162306a36Sopenharmony_ci * @req_src: mac address of tt request sender
299262306a36Sopenharmony_ci * @req_dst: mac address of tt request recipient
299362306a36Sopenharmony_ci *
299462306a36Sopenharmony_ci * Return: true if tt request reply was sent, false otherwise.
299562306a36Sopenharmony_ci */
299662306a36Sopenharmony_cistatic bool batadv_send_other_tt_response(struct batadv_priv *bat_priv,
299762306a36Sopenharmony_ci					  struct batadv_tvlv_tt_data *tt_data,
299862306a36Sopenharmony_ci					  u8 *req_src, u8 *req_dst)
299962306a36Sopenharmony_ci{
300062306a36Sopenharmony_ci	struct batadv_orig_node *req_dst_orig_node;
300162306a36Sopenharmony_ci	struct batadv_orig_node *res_dst_orig_node = NULL;
300262306a36Sopenharmony_ci	struct batadv_tvlv_tt_change *tt_change;
300362306a36Sopenharmony_ci	struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
300462306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan;
300562306a36Sopenharmony_ci	bool ret = false, full_table;
300662306a36Sopenharmony_ci	u8 orig_ttvn, req_ttvn;
300762306a36Sopenharmony_ci	u16 tvlv_len;
300862306a36Sopenharmony_ci	s32 tt_len;
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
301162306a36Sopenharmony_ci		   "Received TT_REQUEST from %pM for ttvn: %u (%pM) [%c]\n",
301262306a36Sopenharmony_ci		   req_src, tt_data->ttvn, req_dst,
301362306a36Sopenharmony_ci		   ((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	/* Let's get the orig node of the REAL destination */
301662306a36Sopenharmony_ci	req_dst_orig_node = batadv_orig_hash_find(bat_priv, req_dst);
301762306a36Sopenharmony_ci	if (!req_dst_orig_node)
301862306a36Sopenharmony_ci		goto out;
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	res_dst_orig_node = batadv_orig_hash_find(bat_priv, req_src);
302162306a36Sopenharmony_ci	if (!res_dst_orig_node)
302262306a36Sopenharmony_ci		goto out;
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	orig_ttvn = (u8)atomic_read(&req_dst_orig_node->last_ttvn);
302562306a36Sopenharmony_ci	req_ttvn = tt_data->ttvn;
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
302862306a36Sopenharmony_ci	/* this node doesn't have the requested data */
302962306a36Sopenharmony_ci	if (orig_ttvn != req_ttvn ||
303062306a36Sopenharmony_ci	    !batadv_tt_global_check_crc(req_dst_orig_node, tt_vlan,
303162306a36Sopenharmony_ci					ntohs(tt_data->num_vlan)))
303262306a36Sopenharmony_ci		goto out;
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	/* If the full table has been explicitly requested */
303562306a36Sopenharmony_ci	if (tt_data->flags & BATADV_TT_FULL_TABLE ||
303662306a36Sopenharmony_ci	    !req_dst_orig_node->tt_buff)
303762306a36Sopenharmony_ci		full_table = true;
303862306a36Sopenharmony_ci	else
303962306a36Sopenharmony_ci		full_table = false;
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	/* TT fragmentation hasn't been implemented yet, so send as many
304262306a36Sopenharmony_ci	 * TT entries fit a single packet as possible only
304362306a36Sopenharmony_ci	 */
304462306a36Sopenharmony_ci	if (!full_table) {
304562306a36Sopenharmony_ci		spin_lock_bh(&req_dst_orig_node->tt_buff_lock);
304662306a36Sopenharmony_ci		tt_len = req_dst_orig_node->tt_buff_len;
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci		tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
304962306a36Sopenharmony_ci							      &tvlv_tt_data,
305062306a36Sopenharmony_ci							      &tt_change,
305162306a36Sopenharmony_ci							      &tt_len);
305262306a36Sopenharmony_ci		if (!tt_len)
305362306a36Sopenharmony_ci			goto unlock;
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ci		/* Copy the last orig_node's OGM buffer */
305662306a36Sopenharmony_ci		memcpy(tt_change, req_dst_orig_node->tt_buff,
305762306a36Sopenharmony_ci		       req_dst_orig_node->tt_buff_len);
305862306a36Sopenharmony_ci		spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
305962306a36Sopenharmony_ci	} else {
306062306a36Sopenharmony_ci		/* allocate the tvlv, put the tt_data and all the tt_vlan_data
306162306a36Sopenharmony_ci		 * in the initial part
306262306a36Sopenharmony_ci		 */
306362306a36Sopenharmony_ci		tt_len = -1;
306462306a36Sopenharmony_ci		tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
306562306a36Sopenharmony_ci							      &tvlv_tt_data,
306662306a36Sopenharmony_ci							      &tt_change,
306762306a36Sopenharmony_ci							      &tt_len);
306862306a36Sopenharmony_ci		if (!tt_len)
306962306a36Sopenharmony_ci			goto out;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci		/* fill the rest of the tvlv with the real TT entries */
307262306a36Sopenharmony_ci		batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.global_hash,
307362306a36Sopenharmony_ci					tt_change, tt_len,
307462306a36Sopenharmony_ci					batadv_tt_global_valid,
307562306a36Sopenharmony_ci					req_dst_orig_node);
307662306a36Sopenharmony_ci	}
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	/* Don't send the response, if larger than fragmented packet. */
307962306a36Sopenharmony_ci	tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len;
308062306a36Sopenharmony_ci	if (tt_len > atomic_read(&bat_priv->packet_size_max)) {
308162306a36Sopenharmony_ci		net_ratelimited_function(batadv_info, bat_priv->soft_iface,
308262306a36Sopenharmony_ci					 "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n",
308362306a36Sopenharmony_ci					 res_dst_orig_node->orig);
308462306a36Sopenharmony_ci		goto out;
308562306a36Sopenharmony_ci	}
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci	tvlv_tt_data->flags = BATADV_TT_RESPONSE;
308862306a36Sopenharmony_ci	tvlv_tt_data->ttvn = req_ttvn;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	if (full_table)
309162306a36Sopenharmony_ci		tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
309462306a36Sopenharmony_ci		   "Sending TT_RESPONSE %pM for %pM [%c] (ttvn: %u)\n",
309562306a36Sopenharmony_ci		   res_dst_orig_node->orig, req_dst_orig_node->orig,
309662306a36Sopenharmony_ci		   full_table ? 'F' : '.', req_ttvn);
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci	batadv_tvlv_unicast_send(bat_priv, req_dst_orig_node->orig,
310162306a36Sopenharmony_ci				 req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
310262306a36Sopenharmony_ci				 tvlv_len);
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	ret = true;
310562306a36Sopenharmony_ci	goto out;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ciunlock:
310862306a36Sopenharmony_ci	spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ciout:
311162306a36Sopenharmony_ci	batadv_orig_node_put(res_dst_orig_node);
311262306a36Sopenharmony_ci	batadv_orig_node_put(req_dst_orig_node);
311362306a36Sopenharmony_ci	kfree(tvlv_tt_data);
311462306a36Sopenharmony_ci	return ret;
311562306a36Sopenharmony_ci}
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci/**
311862306a36Sopenharmony_ci * batadv_send_my_tt_response() - send reply to tt request concerning this
311962306a36Sopenharmony_ci *  node's translation table
312062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
312162306a36Sopenharmony_ci * @tt_data: tt data containing the tt request information
312262306a36Sopenharmony_ci * @req_src: mac address of tt request sender
312362306a36Sopenharmony_ci *
312462306a36Sopenharmony_ci * Return: true if tt request reply was sent, false otherwise.
312562306a36Sopenharmony_ci */
312662306a36Sopenharmony_cistatic bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
312762306a36Sopenharmony_ci				       struct batadv_tvlv_tt_data *tt_data,
312862306a36Sopenharmony_ci				       u8 *req_src)
312962306a36Sopenharmony_ci{
313062306a36Sopenharmony_ci	struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
313162306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if = NULL;
313262306a36Sopenharmony_ci	struct batadv_tvlv_tt_change *tt_change;
313362306a36Sopenharmony_ci	struct batadv_orig_node *orig_node;
313462306a36Sopenharmony_ci	u8 my_ttvn, req_ttvn;
313562306a36Sopenharmony_ci	u16 tvlv_len;
313662306a36Sopenharmony_ci	bool full_table;
313762306a36Sopenharmony_ci	s32 tt_len;
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
314062306a36Sopenharmony_ci		   "Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n",
314162306a36Sopenharmony_ci		   req_src, tt_data->ttvn,
314262306a36Sopenharmony_ci		   ((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.commit_lock);
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci	my_ttvn = (u8)atomic_read(&bat_priv->tt.vn);
314762306a36Sopenharmony_ci	req_ttvn = tt_data->ttvn;
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	orig_node = batadv_orig_hash_find(bat_priv, req_src);
315062306a36Sopenharmony_ci	if (!orig_node)
315162306a36Sopenharmony_ci		goto out;
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
315462306a36Sopenharmony_ci	if (!primary_if)
315562306a36Sopenharmony_ci		goto out;
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	/* If the full table has been explicitly requested or the gap
315862306a36Sopenharmony_ci	 * is too big send the whole local translation table
315962306a36Sopenharmony_ci	 */
316062306a36Sopenharmony_ci	if (tt_data->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn ||
316162306a36Sopenharmony_ci	    !bat_priv->tt.last_changeset)
316262306a36Sopenharmony_ci		full_table = true;
316362306a36Sopenharmony_ci	else
316462306a36Sopenharmony_ci		full_table = false;
316562306a36Sopenharmony_ci
316662306a36Sopenharmony_ci	/* TT fragmentation hasn't been implemented yet, so send as many
316762306a36Sopenharmony_ci	 * TT entries fit a single packet as possible only
316862306a36Sopenharmony_ci	 */
316962306a36Sopenharmony_ci	if (!full_table) {
317062306a36Sopenharmony_ci		spin_lock_bh(&bat_priv->tt.last_changeset_lock);
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci		tt_len = bat_priv->tt.last_changeset_len;
317362306a36Sopenharmony_ci		tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
317462306a36Sopenharmony_ci							     &tvlv_tt_data,
317562306a36Sopenharmony_ci							     &tt_change,
317662306a36Sopenharmony_ci							     &tt_len);
317762306a36Sopenharmony_ci		if (!tt_len || !tvlv_len)
317862306a36Sopenharmony_ci			goto unlock;
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci		/* Copy the last orig_node's OGM buffer */
318162306a36Sopenharmony_ci		memcpy(tt_change, bat_priv->tt.last_changeset,
318262306a36Sopenharmony_ci		       bat_priv->tt.last_changeset_len);
318362306a36Sopenharmony_ci		spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
318462306a36Sopenharmony_ci	} else {
318562306a36Sopenharmony_ci		req_ttvn = (u8)atomic_read(&bat_priv->tt.vn);
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci		/* allocate the tvlv, put the tt_data and all the tt_vlan_data
318862306a36Sopenharmony_ci		 * in the initial part
318962306a36Sopenharmony_ci		 */
319062306a36Sopenharmony_ci		tt_len = -1;
319162306a36Sopenharmony_ci		tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
319262306a36Sopenharmony_ci							     &tvlv_tt_data,
319362306a36Sopenharmony_ci							     &tt_change,
319462306a36Sopenharmony_ci							     &tt_len);
319562306a36Sopenharmony_ci		if (!tt_len || !tvlv_len)
319662306a36Sopenharmony_ci			goto out;
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci		/* fill the rest of the tvlv with the real TT entries */
319962306a36Sopenharmony_ci		batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.local_hash,
320062306a36Sopenharmony_ci					tt_change, tt_len,
320162306a36Sopenharmony_ci					batadv_tt_local_valid, NULL);
320262306a36Sopenharmony_ci	}
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	tvlv_tt_data->flags = BATADV_TT_RESPONSE;
320562306a36Sopenharmony_ci	tvlv_tt_data->ttvn = req_ttvn;
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	if (full_table)
320862306a36Sopenharmony_ci		tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
321162306a36Sopenharmony_ci		   "Sending TT_RESPONSE to %pM [%c] (ttvn: %u)\n",
321262306a36Sopenharmony_ci		   orig_node->orig, full_table ? 'F' : '.', req_ttvn);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci	batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
321762306a36Sopenharmony_ci				 req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
321862306a36Sopenharmony_ci				 tvlv_len);
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	goto out;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ciunlock:
322362306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
322462306a36Sopenharmony_ciout:
322562306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.commit_lock);
322662306a36Sopenharmony_ci	batadv_orig_node_put(orig_node);
322762306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
322862306a36Sopenharmony_ci	kfree(tvlv_tt_data);
322962306a36Sopenharmony_ci	/* The packet was for this host, so it doesn't need to be re-routed */
323062306a36Sopenharmony_ci	return true;
323162306a36Sopenharmony_ci}
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci/**
323462306a36Sopenharmony_ci * batadv_send_tt_response() - send reply to tt request
323562306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
323662306a36Sopenharmony_ci * @tt_data: tt data containing the tt request information
323762306a36Sopenharmony_ci * @req_src: mac address of tt request sender
323862306a36Sopenharmony_ci * @req_dst: mac address of tt request recipient
323962306a36Sopenharmony_ci *
324062306a36Sopenharmony_ci * Return: true if tt request reply was sent, false otherwise.
324162306a36Sopenharmony_ci */
324262306a36Sopenharmony_cistatic bool batadv_send_tt_response(struct batadv_priv *bat_priv,
324362306a36Sopenharmony_ci				    struct batadv_tvlv_tt_data *tt_data,
324462306a36Sopenharmony_ci				    u8 *req_src, u8 *req_dst)
324562306a36Sopenharmony_ci{
324662306a36Sopenharmony_ci	if (batadv_is_my_mac(bat_priv, req_dst))
324762306a36Sopenharmony_ci		return batadv_send_my_tt_response(bat_priv, tt_data, req_src);
324862306a36Sopenharmony_ci	return batadv_send_other_tt_response(bat_priv, tt_data, req_src,
324962306a36Sopenharmony_ci					     req_dst);
325062306a36Sopenharmony_ci}
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_cistatic void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
325362306a36Sopenharmony_ci				      struct batadv_orig_node *orig_node,
325462306a36Sopenharmony_ci				      struct batadv_tvlv_tt_change *tt_change,
325562306a36Sopenharmony_ci				      u16 tt_num_changes, u8 ttvn)
325662306a36Sopenharmony_ci{
325762306a36Sopenharmony_ci	int i;
325862306a36Sopenharmony_ci	int roams;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	for (i = 0; i < tt_num_changes; i++) {
326162306a36Sopenharmony_ci		if ((tt_change + i)->flags & BATADV_TT_CLIENT_DEL) {
326262306a36Sopenharmony_ci			roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM;
326362306a36Sopenharmony_ci			batadv_tt_global_del(bat_priv, orig_node,
326462306a36Sopenharmony_ci					     (tt_change + i)->addr,
326562306a36Sopenharmony_ci					     ntohs((tt_change + i)->vid),
326662306a36Sopenharmony_ci					     "tt removed by changes",
326762306a36Sopenharmony_ci					     roams);
326862306a36Sopenharmony_ci		} else {
326962306a36Sopenharmony_ci			if (!batadv_tt_global_add(bat_priv, orig_node,
327062306a36Sopenharmony_ci						  (tt_change + i)->addr,
327162306a36Sopenharmony_ci						  ntohs((tt_change + i)->vid),
327262306a36Sopenharmony_ci						  (tt_change + i)->flags, ttvn))
327362306a36Sopenharmony_ci				/* In case of problem while storing a
327462306a36Sopenharmony_ci				 * global_entry, we stop the updating
327562306a36Sopenharmony_ci				 * procedure without committing the
327662306a36Sopenharmony_ci				 * ttvn change. This will avoid to send
327762306a36Sopenharmony_ci				 * corrupted data on tt_request
327862306a36Sopenharmony_ci				 */
327962306a36Sopenharmony_ci				return;
328062306a36Sopenharmony_ci		}
328162306a36Sopenharmony_ci	}
328262306a36Sopenharmony_ci	set_bit(BATADV_ORIG_CAPA_HAS_TT, &orig_node->capa_initialized);
328362306a36Sopenharmony_ci}
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_cistatic void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
328662306a36Sopenharmony_ci				  struct batadv_tvlv_tt_change *tt_change,
328762306a36Sopenharmony_ci				  u8 ttvn, u8 *resp_src,
328862306a36Sopenharmony_ci				  u16 num_entries)
328962306a36Sopenharmony_ci{
329062306a36Sopenharmony_ci	struct batadv_orig_node *orig_node;
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	orig_node = batadv_orig_hash_find(bat_priv, resp_src);
329362306a36Sopenharmony_ci	if (!orig_node)
329462306a36Sopenharmony_ci		goto out;
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci	/* Purge the old table first.. */
329762306a36Sopenharmony_ci	batadv_tt_global_del_orig(bat_priv, orig_node, -1,
329862306a36Sopenharmony_ci				  "Received full table");
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	_batadv_tt_update_changes(bat_priv, orig_node, tt_change, num_entries,
330162306a36Sopenharmony_ci				  ttvn);
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	spin_lock_bh(&orig_node->tt_buff_lock);
330462306a36Sopenharmony_ci	kfree(orig_node->tt_buff);
330562306a36Sopenharmony_ci	orig_node->tt_buff_len = 0;
330662306a36Sopenharmony_ci	orig_node->tt_buff = NULL;
330762306a36Sopenharmony_ci	spin_unlock_bh(&orig_node->tt_buff_lock);
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	atomic_set(&orig_node->last_ttvn, ttvn);
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ciout:
331262306a36Sopenharmony_ci	batadv_orig_node_put(orig_node);
331362306a36Sopenharmony_ci}
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_cistatic void batadv_tt_update_changes(struct batadv_priv *bat_priv,
331662306a36Sopenharmony_ci				     struct batadv_orig_node *orig_node,
331762306a36Sopenharmony_ci				     u16 tt_num_changes, u8 ttvn,
331862306a36Sopenharmony_ci				     struct batadv_tvlv_tt_change *tt_change)
331962306a36Sopenharmony_ci{
332062306a36Sopenharmony_ci	_batadv_tt_update_changes(bat_priv, orig_node, tt_change,
332162306a36Sopenharmony_ci				  tt_num_changes, ttvn);
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	batadv_tt_save_orig_buffer(bat_priv, orig_node, tt_change,
332462306a36Sopenharmony_ci				   batadv_tt_len(tt_num_changes));
332562306a36Sopenharmony_ci	atomic_set(&orig_node->last_ttvn, ttvn);
332662306a36Sopenharmony_ci}
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci/**
332962306a36Sopenharmony_ci * batadv_is_my_client() - check if a client is served by the local node
333062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
333162306a36Sopenharmony_ci * @addr: the mac address of the client to check
333262306a36Sopenharmony_ci * @vid: VLAN identifier
333362306a36Sopenharmony_ci *
333462306a36Sopenharmony_ci * Return: true if the client is served by this node, false otherwise.
333562306a36Sopenharmony_ci */
333662306a36Sopenharmony_cibool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
333762306a36Sopenharmony_ci			 unsigned short vid)
333862306a36Sopenharmony_ci{
333962306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
334062306a36Sopenharmony_ci	bool ret = false;
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
334362306a36Sopenharmony_ci	if (!tt_local_entry)
334462306a36Sopenharmony_ci		goto out;
334562306a36Sopenharmony_ci	/* Check if the client has been logically deleted (but is kept for
334662306a36Sopenharmony_ci	 * consistency purpose)
334762306a36Sopenharmony_ci	 */
334862306a36Sopenharmony_ci	if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) ||
334962306a36Sopenharmony_ci	    (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM))
335062306a36Sopenharmony_ci		goto out;
335162306a36Sopenharmony_ci	ret = true;
335262306a36Sopenharmony_ciout:
335362306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local_entry);
335462306a36Sopenharmony_ci	return ret;
335562306a36Sopenharmony_ci}
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci/**
335862306a36Sopenharmony_ci * batadv_handle_tt_response() - process incoming tt reply
335962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
336062306a36Sopenharmony_ci * @tt_data: tt data containing the tt request information
336162306a36Sopenharmony_ci * @resp_src: mac address of tt reply sender
336262306a36Sopenharmony_ci * @num_entries: number of tt change entries appended to the tt data
336362306a36Sopenharmony_ci */
336462306a36Sopenharmony_cistatic void batadv_handle_tt_response(struct batadv_priv *bat_priv,
336562306a36Sopenharmony_ci				      struct batadv_tvlv_tt_data *tt_data,
336662306a36Sopenharmony_ci				      u8 *resp_src, u16 num_entries)
336762306a36Sopenharmony_ci{
336862306a36Sopenharmony_ci	struct batadv_tt_req_node *node;
336962306a36Sopenharmony_ci	struct hlist_node *safe;
337062306a36Sopenharmony_ci	struct batadv_orig_node *orig_node = NULL;
337162306a36Sopenharmony_ci	struct batadv_tvlv_tt_change *tt_change;
337262306a36Sopenharmony_ci	u8 *tvlv_ptr = (u8 *)tt_data;
337362306a36Sopenharmony_ci	u16 change_offset;
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
337662306a36Sopenharmony_ci		   "Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n",
337762306a36Sopenharmony_ci		   resp_src, tt_data->ttvn, num_entries,
337862306a36Sopenharmony_ci		   ((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	orig_node = batadv_orig_hash_find(bat_priv, resp_src);
338162306a36Sopenharmony_ci	if (!orig_node)
338262306a36Sopenharmony_ci		goto out;
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	spin_lock_bh(&orig_node->tt_lock);
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	change_offset = sizeof(struct batadv_tvlv_tt_vlan_data);
338762306a36Sopenharmony_ci	change_offset *= ntohs(tt_data->num_vlan);
338862306a36Sopenharmony_ci	change_offset += sizeof(*tt_data);
338962306a36Sopenharmony_ci	tvlv_ptr += change_offset;
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	tt_change = (struct batadv_tvlv_tt_change *)tvlv_ptr;
339262306a36Sopenharmony_ci	if (tt_data->flags & BATADV_TT_FULL_TABLE) {
339362306a36Sopenharmony_ci		batadv_tt_fill_gtable(bat_priv, tt_change, tt_data->ttvn,
339462306a36Sopenharmony_ci				      resp_src, num_entries);
339562306a36Sopenharmony_ci	} else {
339662306a36Sopenharmony_ci		batadv_tt_update_changes(bat_priv, orig_node, num_entries,
339762306a36Sopenharmony_ci					 tt_data->ttvn, tt_change);
339862306a36Sopenharmony_ci	}
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	/* Recalculate the CRC for this orig_node and store it */
340162306a36Sopenharmony_ci	batadv_tt_global_update_crc(bat_priv, orig_node);
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci	spin_unlock_bh(&orig_node->tt_lock);
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	/* Delete the tt_req_node from pending tt_requests list */
340662306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.req_list_lock);
340762306a36Sopenharmony_ci	hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
340862306a36Sopenharmony_ci		if (!batadv_compare_eth(node->addr, resp_src))
340962306a36Sopenharmony_ci			continue;
341062306a36Sopenharmony_ci		hlist_del_init(&node->list);
341162306a36Sopenharmony_ci		batadv_tt_req_node_put(node);
341262306a36Sopenharmony_ci	}
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.req_list_lock);
341562306a36Sopenharmony_ciout:
341662306a36Sopenharmony_ci	batadv_orig_node_put(orig_node);
341762306a36Sopenharmony_ci}
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_cistatic void batadv_tt_roam_list_free(struct batadv_priv *bat_priv)
342062306a36Sopenharmony_ci{
342162306a36Sopenharmony_ci	struct batadv_tt_roam_node *node, *safe;
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.roam_list_lock);
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	list_for_each_entry_safe(node, safe, &bat_priv->tt.roam_list, list) {
342662306a36Sopenharmony_ci		list_del(&node->list);
342762306a36Sopenharmony_ci		kmem_cache_free(batadv_tt_roam_cache, node);
342862306a36Sopenharmony_ci	}
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.roam_list_lock);
343162306a36Sopenharmony_ci}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_cistatic void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
343462306a36Sopenharmony_ci{
343562306a36Sopenharmony_ci	struct batadv_tt_roam_node *node, *safe;
343662306a36Sopenharmony_ci
343762306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.roam_list_lock);
343862306a36Sopenharmony_ci	list_for_each_entry_safe(node, safe, &bat_priv->tt.roam_list, list) {
343962306a36Sopenharmony_ci		if (!batadv_has_timed_out(node->first_time,
344062306a36Sopenharmony_ci					  BATADV_ROAMING_MAX_TIME))
344162306a36Sopenharmony_ci			continue;
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci		list_del(&node->list);
344462306a36Sopenharmony_ci		kmem_cache_free(batadv_tt_roam_cache, node);
344562306a36Sopenharmony_ci	}
344662306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.roam_list_lock);
344762306a36Sopenharmony_ci}
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci/**
345062306a36Sopenharmony_ci * batadv_tt_check_roam_count() - check if a client has roamed too frequently
345162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
345262306a36Sopenharmony_ci * @client: mac address of the roaming client
345362306a36Sopenharmony_ci *
345462306a36Sopenharmony_ci * This function checks whether the client already reached the
345562306a36Sopenharmony_ci * maximum number of possible roaming phases. In this case the ROAMING_ADV
345662306a36Sopenharmony_ci * will not be sent.
345762306a36Sopenharmony_ci *
345862306a36Sopenharmony_ci * Return: true if the ROAMING_ADV can be sent, false otherwise
345962306a36Sopenharmony_ci */
346062306a36Sopenharmony_cistatic bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
346162306a36Sopenharmony_ci{
346262306a36Sopenharmony_ci	struct batadv_tt_roam_node *tt_roam_node;
346362306a36Sopenharmony_ci	bool ret = false;
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.roam_list_lock);
346662306a36Sopenharmony_ci	/* The new tt_req will be issued only if I'm not waiting for a
346762306a36Sopenharmony_ci	 * reply from the same orig_node yet
346862306a36Sopenharmony_ci	 */
346962306a36Sopenharmony_ci	list_for_each_entry(tt_roam_node, &bat_priv->tt.roam_list, list) {
347062306a36Sopenharmony_ci		if (!batadv_compare_eth(tt_roam_node->addr, client))
347162306a36Sopenharmony_ci			continue;
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci		if (batadv_has_timed_out(tt_roam_node->first_time,
347462306a36Sopenharmony_ci					 BATADV_ROAMING_MAX_TIME))
347562306a36Sopenharmony_ci			continue;
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci		if (!batadv_atomic_dec_not_zero(&tt_roam_node->counter))
347862306a36Sopenharmony_ci			/* Sorry, you roamed too many times! */
347962306a36Sopenharmony_ci			goto unlock;
348062306a36Sopenharmony_ci		ret = true;
348162306a36Sopenharmony_ci		break;
348262306a36Sopenharmony_ci	}
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci	if (!ret) {
348562306a36Sopenharmony_ci		tt_roam_node = kmem_cache_alloc(batadv_tt_roam_cache,
348662306a36Sopenharmony_ci						GFP_ATOMIC);
348762306a36Sopenharmony_ci		if (!tt_roam_node)
348862306a36Sopenharmony_ci			goto unlock;
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci		tt_roam_node->first_time = jiffies;
349162306a36Sopenharmony_ci		atomic_set(&tt_roam_node->counter,
349262306a36Sopenharmony_ci			   BATADV_ROAMING_MAX_COUNT - 1);
349362306a36Sopenharmony_ci		ether_addr_copy(tt_roam_node->addr, client);
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci		list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
349662306a36Sopenharmony_ci		ret = true;
349762306a36Sopenharmony_ci	}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ciunlock:
350062306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.roam_list_lock);
350162306a36Sopenharmony_ci	return ret;
350262306a36Sopenharmony_ci}
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_ci/**
350562306a36Sopenharmony_ci * batadv_send_roam_adv() - send a roaming advertisement message
350662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
350762306a36Sopenharmony_ci * @client: mac address of the roaming client
350862306a36Sopenharmony_ci * @vid: VLAN identifier
350962306a36Sopenharmony_ci * @orig_node: message destination
351062306a36Sopenharmony_ci *
351162306a36Sopenharmony_ci * Send a ROAMING_ADV message to the node which was previously serving this
351262306a36Sopenharmony_ci * client. This is done to inform the node that from now on all traffic destined
351362306a36Sopenharmony_ci * for this particular roamed client has to be forwarded to the sender of the
351462306a36Sopenharmony_ci * roaming message.
351562306a36Sopenharmony_ci */
351662306a36Sopenharmony_cistatic void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
351762306a36Sopenharmony_ci				 unsigned short vid,
351862306a36Sopenharmony_ci				 struct batadv_orig_node *orig_node)
351962306a36Sopenharmony_ci{
352062306a36Sopenharmony_ci	struct batadv_hard_iface *primary_if;
352162306a36Sopenharmony_ci	struct batadv_tvlv_roam_adv tvlv_roam;
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	primary_if = batadv_primary_if_get_selected(bat_priv);
352462306a36Sopenharmony_ci	if (!primary_if)
352562306a36Sopenharmony_ci		goto out;
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	/* before going on we have to check whether the client has
352862306a36Sopenharmony_ci	 * already roamed to us too many times
352962306a36Sopenharmony_ci	 */
353062306a36Sopenharmony_ci	if (!batadv_tt_check_roam_count(bat_priv, client))
353162306a36Sopenharmony_ci		goto out;
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
353462306a36Sopenharmony_ci		   "Sending ROAMING_ADV to %pM (client %pM, vid: %d)\n",
353562306a36Sopenharmony_ci		   orig_node->orig, client, batadv_print_vid(vid));
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
353862306a36Sopenharmony_ci
353962306a36Sopenharmony_ci	memcpy(tvlv_roam.client, client, sizeof(tvlv_roam.client));
354062306a36Sopenharmony_ci	tvlv_roam.vid = htons(vid);
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci	batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
354362306a36Sopenharmony_ci				 orig_node->orig, BATADV_TVLV_ROAM, 1,
354462306a36Sopenharmony_ci				 &tvlv_roam, sizeof(tvlv_roam));
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ciout:
354762306a36Sopenharmony_ci	batadv_hardif_put(primary_if);
354862306a36Sopenharmony_ci}
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_cistatic void batadv_tt_purge(struct work_struct *work)
355162306a36Sopenharmony_ci{
355262306a36Sopenharmony_ci	struct delayed_work *delayed_work;
355362306a36Sopenharmony_ci	struct batadv_priv_tt *priv_tt;
355462306a36Sopenharmony_ci	struct batadv_priv *bat_priv;
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	delayed_work = to_delayed_work(work);
355762306a36Sopenharmony_ci	priv_tt = container_of(delayed_work, struct batadv_priv_tt, work);
355862306a36Sopenharmony_ci	bat_priv = container_of(priv_tt, struct batadv_priv, tt);
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	batadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT);
356162306a36Sopenharmony_ci	batadv_tt_global_purge(bat_priv);
356262306a36Sopenharmony_ci	batadv_tt_req_purge(bat_priv);
356362306a36Sopenharmony_ci	batadv_tt_roam_purge(bat_priv);
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
356662306a36Sopenharmony_ci			   msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
356762306a36Sopenharmony_ci}
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci/**
357062306a36Sopenharmony_ci * batadv_tt_free() - Free translation table of soft interface
357162306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
357262306a36Sopenharmony_ci */
357362306a36Sopenharmony_civoid batadv_tt_free(struct batadv_priv *bat_priv)
357462306a36Sopenharmony_ci{
357562306a36Sopenharmony_ci	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_ROAM, 1);
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_TT, 1);
357862306a36Sopenharmony_ci	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_TT, 1);
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci	cancel_delayed_work_sync(&bat_priv->tt.work);
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci	batadv_tt_local_table_free(bat_priv);
358362306a36Sopenharmony_ci	batadv_tt_global_table_free(bat_priv);
358462306a36Sopenharmony_ci	batadv_tt_req_list_free(bat_priv);
358562306a36Sopenharmony_ci	batadv_tt_changes_list_free(bat_priv);
358662306a36Sopenharmony_ci	batadv_tt_roam_list_free(bat_priv);
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci	kfree(bat_priv->tt.last_changeset);
358962306a36Sopenharmony_ci}
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci/**
359262306a36Sopenharmony_ci * batadv_tt_local_set_flags() - set or unset the specified flags on the local
359362306a36Sopenharmony_ci *  table and possibly count them in the TT size
359462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
359562306a36Sopenharmony_ci * @flags: the flag to switch
359662306a36Sopenharmony_ci * @enable: whether to set or unset the flag
359762306a36Sopenharmony_ci * @count: whether to increase the TT size by the number of changed entries
359862306a36Sopenharmony_ci */
359962306a36Sopenharmony_cistatic void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags,
360062306a36Sopenharmony_ci				      bool enable, bool count)
360162306a36Sopenharmony_ci{
360262306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
360362306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common_entry;
360462306a36Sopenharmony_ci	struct hlist_head *head;
360562306a36Sopenharmony_ci	u32 i;
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	if (!hash)
360862306a36Sopenharmony_ci		return;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
361162306a36Sopenharmony_ci		head = &hash->table[i];
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci		rcu_read_lock();
361462306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tt_common_entry,
361562306a36Sopenharmony_ci					 head, hash_entry) {
361662306a36Sopenharmony_ci			if (enable) {
361762306a36Sopenharmony_ci				if ((tt_common_entry->flags & flags) == flags)
361862306a36Sopenharmony_ci					continue;
361962306a36Sopenharmony_ci				tt_common_entry->flags |= flags;
362062306a36Sopenharmony_ci			} else {
362162306a36Sopenharmony_ci				if (!(tt_common_entry->flags & flags))
362262306a36Sopenharmony_ci					continue;
362362306a36Sopenharmony_ci				tt_common_entry->flags &= ~flags;
362462306a36Sopenharmony_ci			}
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_ci			if (!count)
362762306a36Sopenharmony_ci				continue;
362862306a36Sopenharmony_ci
362962306a36Sopenharmony_ci			batadv_tt_local_size_inc(bat_priv,
363062306a36Sopenharmony_ci						 tt_common_entry->vid);
363162306a36Sopenharmony_ci		}
363262306a36Sopenharmony_ci		rcu_read_unlock();
363362306a36Sopenharmony_ci	}
363462306a36Sopenharmony_ci}
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci/* Purge out all the tt local entries marked with BATADV_TT_CLIENT_PENDING */
363762306a36Sopenharmony_cistatic void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
363862306a36Sopenharmony_ci{
363962306a36Sopenharmony_ci	struct batadv_hashtable *hash = bat_priv->tt.local_hash;
364062306a36Sopenharmony_ci	struct batadv_tt_common_entry *tt_common;
364162306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local;
364262306a36Sopenharmony_ci	struct hlist_node *node_tmp;
364362306a36Sopenharmony_ci	struct hlist_head *head;
364462306a36Sopenharmony_ci	spinlock_t *list_lock; /* protects write access to the hash lists */
364562306a36Sopenharmony_ci	u32 i;
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci	if (!hash)
364862306a36Sopenharmony_ci		return;
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci	for (i = 0; i < hash->size; i++) {
365162306a36Sopenharmony_ci		head = &hash->table[i];
365262306a36Sopenharmony_ci		list_lock = &hash->list_locks[i];
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci		spin_lock_bh(list_lock);
365562306a36Sopenharmony_ci		hlist_for_each_entry_safe(tt_common, node_tmp, head,
365662306a36Sopenharmony_ci					  hash_entry) {
365762306a36Sopenharmony_ci			if (!(tt_common->flags & BATADV_TT_CLIENT_PENDING))
365862306a36Sopenharmony_ci				continue;
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
366162306a36Sopenharmony_ci				   "Deleting local tt entry (%pM, vid: %d): pending\n",
366262306a36Sopenharmony_ci				   tt_common->addr,
366362306a36Sopenharmony_ci				   batadv_print_vid(tt_common->vid));
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci			batadv_tt_local_size_dec(bat_priv, tt_common->vid);
366662306a36Sopenharmony_ci			hlist_del_rcu(&tt_common->hash_entry);
366762306a36Sopenharmony_ci			tt_local = container_of(tt_common,
366862306a36Sopenharmony_ci						struct batadv_tt_local_entry,
366962306a36Sopenharmony_ci						common);
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci			batadv_tt_local_entry_put(tt_local);
367262306a36Sopenharmony_ci		}
367362306a36Sopenharmony_ci		spin_unlock_bh(list_lock);
367462306a36Sopenharmony_ci	}
367562306a36Sopenharmony_ci}
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci/**
367862306a36Sopenharmony_ci * batadv_tt_local_commit_changes_nolock() - commit all pending local tt changes
367962306a36Sopenharmony_ci *  which have been queued in the time since the last commit
368062306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
368162306a36Sopenharmony_ci *
368262306a36Sopenharmony_ci * Caller must hold tt->commit_lock.
368362306a36Sopenharmony_ci */
368462306a36Sopenharmony_cistatic void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
368562306a36Sopenharmony_ci{
368662306a36Sopenharmony_ci	lockdep_assert_held(&bat_priv->tt.commit_lock);
368762306a36Sopenharmony_ci
368862306a36Sopenharmony_ci	if (atomic_read(&bat_priv->tt.local_changes) < 1) {
368962306a36Sopenharmony_ci		if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
369062306a36Sopenharmony_ci			batadv_tt_tvlv_container_update(bat_priv);
369162306a36Sopenharmony_ci		return;
369262306a36Sopenharmony_ci	}
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci	batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true);
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci	batadv_tt_local_purge_pending_clients(bat_priv);
369762306a36Sopenharmony_ci	batadv_tt_local_update_crc(bat_priv);
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	/* Increment the TTVN only once per OGM interval */
370062306a36Sopenharmony_ci	atomic_inc(&bat_priv->tt.vn);
370162306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
370262306a36Sopenharmony_ci		   "Local changes committed, updating to ttvn %u\n",
370362306a36Sopenharmony_ci		   (u8)atomic_read(&bat_priv->tt.vn));
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci	/* reset the sending counter */
370662306a36Sopenharmony_ci	atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
370762306a36Sopenharmony_ci	batadv_tt_tvlv_container_update(bat_priv);
370862306a36Sopenharmony_ci}
370962306a36Sopenharmony_ci
371062306a36Sopenharmony_ci/**
371162306a36Sopenharmony_ci * batadv_tt_local_commit_changes() - commit all pending local tt changes which
371262306a36Sopenharmony_ci *  have been queued in the time since the last commit
371362306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
371462306a36Sopenharmony_ci */
371562306a36Sopenharmony_civoid batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
371662306a36Sopenharmony_ci{
371762306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.commit_lock);
371862306a36Sopenharmony_ci	batadv_tt_local_commit_changes_nolock(bat_priv);
371962306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.commit_lock);
372062306a36Sopenharmony_ci}
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci/**
372362306a36Sopenharmony_ci * batadv_is_ap_isolated() - Check if packet from upper layer should be dropped
372462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
372562306a36Sopenharmony_ci * @src: source mac address of packet
372662306a36Sopenharmony_ci * @dst: destination mac address of packet
372762306a36Sopenharmony_ci * @vid: vlan id of packet
372862306a36Sopenharmony_ci *
372962306a36Sopenharmony_ci * Return: true when src+dst(+vid) pair should be isolated, false otherwise
373062306a36Sopenharmony_ci */
373162306a36Sopenharmony_cibool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
373262306a36Sopenharmony_ci			   unsigned short vid)
373362306a36Sopenharmony_ci{
373462306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
373562306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
373662306a36Sopenharmony_ci	struct batadv_softif_vlan *vlan;
373762306a36Sopenharmony_ci	bool ret = false;
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci	vlan = batadv_softif_vlan_get(bat_priv, vid);
374062306a36Sopenharmony_ci	if (!vlan)
374162306a36Sopenharmony_ci		return false;
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci	if (!atomic_read(&vlan->ap_isolation))
374462306a36Sopenharmony_ci		goto vlan_put;
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid);
374762306a36Sopenharmony_ci	if (!tt_local_entry)
374862306a36Sopenharmony_ci		goto vlan_put;
374962306a36Sopenharmony_ci
375062306a36Sopenharmony_ci	tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid);
375162306a36Sopenharmony_ci	if (!tt_global_entry)
375262306a36Sopenharmony_ci		goto local_entry_put;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	if (_batadv_is_ap_isolated(tt_local_entry, tt_global_entry))
375562306a36Sopenharmony_ci		ret = true;
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global_entry);
375862306a36Sopenharmony_cilocal_entry_put:
375962306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local_entry);
376062306a36Sopenharmony_civlan_put:
376162306a36Sopenharmony_ci	batadv_softif_vlan_put(vlan);
376262306a36Sopenharmony_ci	return ret;
376362306a36Sopenharmony_ci}
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci/**
376662306a36Sopenharmony_ci * batadv_tt_update_orig() - update global translation table with new tt
376762306a36Sopenharmony_ci *  information received via ogms
376862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
376962306a36Sopenharmony_ci * @orig_node: the orig_node of the ogm
377062306a36Sopenharmony_ci * @tt_buff: pointer to the first tvlv VLAN entry
377162306a36Sopenharmony_ci * @tt_num_vlan: number of tvlv VLAN entries
377262306a36Sopenharmony_ci * @tt_change: pointer to the first entry in the TT buffer
377362306a36Sopenharmony_ci * @tt_num_changes: number of tt changes inside the tt buffer
377462306a36Sopenharmony_ci * @ttvn: translation table version number of this changeset
377562306a36Sopenharmony_ci */
377662306a36Sopenharmony_cistatic void batadv_tt_update_orig(struct batadv_priv *bat_priv,
377762306a36Sopenharmony_ci				  struct batadv_orig_node *orig_node,
377862306a36Sopenharmony_ci				  const void *tt_buff, u16 tt_num_vlan,
377962306a36Sopenharmony_ci				  struct batadv_tvlv_tt_change *tt_change,
378062306a36Sopenharmony_ci				  u16 tt_num_changes, u8 ttvn)
378162306a36Sopenharmony_ci{
378262306a36Sopenharmony_ci	u8 orig_ttvn = (u8)atomic_read(&orig_node->last_ttvn);
378362306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan;
378462306a36Sopenharmony_ci	bool full_table = true;
378562306a36Sopenharmony_ci	bool has_tt_init;
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci	tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff;
378862306a36Sopenharmony_ci	has_tt_init = test_bit(BATADV_ORIG_CAPA_HAS_TT,
378962306a36Sopenharmony_ci			       &orig_node->capa_initialized);
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci	/* orig table not initialised AND first diff is in the OGM OR the ttvn
379262306a36Sopenharmony_ci	 * increased by one -> we can apply the attached changes
379362306a36Sopenharmony_ci	 */
379462306a36Sopenharmony_ci	if ((!has_tt_init && ttvn == 1) || ttvn - orig_ttvn == 1) {
379562306a36Sopenharmony_ci		/* the OGM could not contain the changes due to their size or
379662306a36Sopenharmony_ci		 * because they have already been sent BATADV_TT_OGM_APPEND_MAX
379762306a36Sopenharmony_ci		 * times.
379862306a36Sopenharmony_ci		 * In this case send a tt request
379962306a36Sopenharmony_ci		 */
380062306a36Sopenharmony_ci		if (!tt_num_changes) {
380162306a36Sopenharmony_ci			full_table = false;
380262306a36Sopenharmony_ci			goto request_table;
380362306a36Sopenharmony_ci		}
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci		spin_lock_bh(&orig_node->tt_lock);
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci		batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes,
380862306a36Sopenharmony_ci					 ttvn, tt_change);
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci		/* Even if we received the precomputed crc with the OGM, we
381162306a36Sopenharmony_ci		 * prefer to recompute it to spot any possible inconsistency
381262306a36Sopenharmony_ci		 * in the global table
381362306a36Sopenharmony_ci		 */
381462306a36Sopenharmony_ci		batadv_tt_global_update_crc(bat_priv, orig_node);
381562306a36Sopenharmony_ci
381662306a36Sopenharmony_ci		spin_unlock_bh(&orig_node->tt_lock);
381762306a36Sopenharmony_ci
381862306a36Sopenharmony_ci		/* The ttvn alone is not enough to guarantee consistency
381962306a36Sopenharmony_ci		 * because a single value could represent different states
382062306a36Sopenharmony_ci		 * (due to the wrap around). Thus a node has to check whether
382162306a36Sopenharmony_ci		 * the resulting table (after applying the changes) is still
382262306a36Sopenharmony_ci		 * consistent or not. E.g. a node could disconnect while its
382362306a36Sopenharmony_ci		 * ttvn is X and reconnect on ttvn = X + TTVN_MAX: in this case
382462306a36Sopenharmony_ci		 * checking the CRC value is mandatory to detect the
382562306a36Sopenharmony_ci		 * inconsistency
382662306a36Sopenharmony_ci		 */
382762306a36Sopenharmony_ci		if (!batadv_tt_global_check_crc(orig_node, tt_vlan,
382862306a36Sopenharmony_ci						tt_num_vlan))
382962306a36Sopenharmony_ci			goto request_table;
383062306a36Sopenharmony_ci	} else {
383162306a36Sopenharmony_ci		/* if we missed more than one change or our tables are not
383262306a36Sopenharmony_ci		 * in sync anymore -> request fresh tt data
383362306a36Sopenharmony_ci		 */
383462306a36Sopenharmony_ci		if (!has_tt_init || ttvn != orig_ttvn ||
383562306a36Sopenharmony_ci		    !batadv_tt_global_check_crc(orig_node, tt_vlan,
383662306a36Sopenharmony_ci						tt_num_vlan)) {
383762306a36Sopenharmony_cirequest_table:
383862306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
383962306a36Sopenharmony_ci				   "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u num_changes: %u)\n",
384062306a36Sopenharmony_ci				   orig_node->orig, ttvn, orig_ttvn,
384162306a36Sopenharmony_ci				   tt_num_changes);
384262306a36Sopenharmony_ci			batadv_send_tt_request(bat_priv, orig_node, ttvn,
384362306a36Sopenharmony_ci					       tt_vlan, tt_num_vlan,
384462306a36Sopenharmony_ci					       full_table);
384562306a36Sopenharmony_ci			return;
384662306a36Sopenharmony_ci		}
384762306a36Sopenharmony_ci	}
384862306a36Sopenharmony_ci}
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci/**
385162306a36Sopenharmony_ci * batadv_tt_global_client_is_roaming() - check if a client is marked as roaming
385262306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
385362306a36Sopenharmony_ci * @addr: the mac address of the client to check
385462306a36Sopenharmony_ci * @vid: VLAN identifier
385562306a36Sopenharmony_ci *
385662306a36Sopenharmony_ci * Return: true if we know that the client has moved from its old originator
385762306a36Sopenharmony_ci * to another one. This entry is still kept for consistency purposes and will be
385862306a36Sopenharmony_ci * deleted later by a DEL or because of timeout
385962306a36Sopenharmony_ci */
386062306a36Sopenharmony_cibool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
386162306a36Sopenharmony_ci					u8 *addr, unsigned short vid)
386262306a36Sopenharmony_ci{
386362306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt_global_entry;
386462306a36Sopenharmony_ci	bool ret = false;
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci	tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
386762306a36Sopenharmony_ci	if (!tt_global_entry)
386862306a36Sopenharmony_ci		goto out;
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci	ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
387162306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt_global_entry);
387262306a36Sopenharmony_ciout:
387362306a36Sopenharmony_ci	return ret;
387462306a36Sopenharmony_ci}
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci/**
387762306a36Sopenharmony_ci * batadv_tt_local_client_is_roaming() - tells whether the client is roaming
387862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
387962306a36Sopenharmony_ci * @addr: the mac address of the local client to query
388062306a36Sopenharmony_ci * @vid: VLAN identifier
388162306a36Sopenharmony_ci *
388262306a36Sopenharmony_ci * Return: true if the local client is known to be roaming (it is not served by
388362306a36Sopenharmony_ci * this node anymore) or not. If yes, the client is still present in the table
388462306a36Sopenharmony_ci * to keep the latter consistent with the node TTVN
388562306a36Sopenharmony_ci */
388662306a36Sopenharmony_cibool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
388762306a36Sopenharmony_ci				       u8 *addr, unsigned short vid)
388862306a36Sopenharmony_ci{
388962306a36Sopenharmony_ci	struct batadv_tt_local_entry *tt_local_entry;
389062306a36Sopenharmony_ci	bool ret = false;
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
389362306a36Sopenharmony_ci	if (!tt_local_entry)
389462306a36Sopenharmony_ci		goto out;
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci	ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM;
389762306a36Sopenharmony_ci	batadv_tt_local_entry_put(tt_local_entry);
389862306a36Sopenharmony_ciout:
389962306a36Sopenharmony_ci	return ret;
390062306a36Sopenharmony_ci}
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci/**
390362306a36Sopenharmony_ci * batadv_tt_add_temporary_global_entry() - Add temporary entry to global TT
390462306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
390562306a36Sopenharmony_ci * @orig_node: orig node which the temporary entry should be associated with
390662306a36Sopenharmony_ci * @addr: mac address of the client
390762306a36Sopenharmony_ci * @vid: VLAN id of the new temporary global translation table
390862306a36Sopenharmony_ci *
390962306a36Sopenharmony_ci * Return: true when temporary tt entry could be added, false otherwise
391062306a36Sopenharmony_ci */
391162306a36Sopenharmony_cibool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
391262306a36Sopenharmony_ci					  struct batadv_orig_node *orig_node,
391362306a36Sopenharmony_ci					  const unsigned char *addr,
391462306a36Sopenharmony_ci					  unsigned short vid)
391562306a36Sopenharmony_ci{
391662306a36Sopenharmony_ci	/* ignore loop detect macs, they are not supposed to be in the tt local
391762306a36Sopenharmony_ci	 * data as well.
391862306a36Sopenharmony_ci	 */
391962306a36Sopenharmony_ci	if (batadv_bla_is_loopdetect_mac(addr))
392062306a36Sopenharmony_ci		return false;
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci	if (!batadv_tt_global_add(bat_priv, orig_node, addr, vid,
392362306a36Sopenharmony_ci				  BATADV_TT_CLIENT_TEMP,
392462306a36Sopenharmony_ci				  atomic_read(&orig_node->last_ttvn)))
392562306a36Sopenharmony_ci		return false;
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
392862306a36Sopenharmony_ci		   "Added temporary global client (addr: %pM, vid: %d, orig: %pM)\n",
392962306a36Sopenharmony_ci		   addr, batadv_print_vid(vid), orig_node->orig);
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci	return true;
393262306a36Sopenharmony_ci}
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci/**
393562306a36Sopenharmony_ci * batadv_tt_local_resize_to_mtu() - resize the local translation table fit the
393662306a36Sopenharmony_ci *  maximum packet size that can be transported through the mesh
393762306a36Sopenharmony_ci * @soft_iface: netdev struct of the mesh interface
393862306a36Sopenharmony_ci *
393962306a36Sopenharmony_ci * Remove entries older than 'timeout' and half timeout if more entries need
394062306a36Sopenharmony_ci * to be removed.
394162306a36Sopenharmony_ci */
394262306a36Sopenharmony_civoid batadv_tt_local_resize_to_mtu(struct net_device *soft_iface)
394362306a36Sopenharmony_ci{
394462306a36Sopenharmony_ci	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
394562306a36Sopenharmony_ci	int packet_size_max = atomic_read(&bat_priv->packet_size_max);
394662306a36Sopenharmony_ci	int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2;
394762306a36Sopenharmony_ci	bool reduced = false;
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	spin_lock_bh(&bat_priv->tt.commit_lock);
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci	while (true) {
395262306a36Sopenharmony_ci		table_size = batadv_tt_local_table_transmit_size(bat_priv);
395362306a36Sopenharmony_ci		if (packet_size_max >= table_size)
395462306a36Sopenharmony_ci			break;
395562306a36Sopenharmony_ci
395662306a36Sopenharmony_ci		batadv_tt_local_purge(bat_priv, timeout);
395762306a36Sopenharmony_ci		batadv_tt_local_purge_pending_clients(bat_priv);
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci		timeout /= 2;
396062306a36Sopenharmony_ci		reduced = true;
396162306a36Sopenharmony_ci		net_ratelimited_function(batadv_info, soft_iface,
396262306a36Sopenharmony_ci					 "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n",
396362306a36Sopenharmony_ci					 packet_size_max);
396462306a36Sopenharmony_ci	}
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_ci	/* commit these changes immediately, to avoid synchronization problem
396762306a36Sopenharmony_ci	 * with the TTVN
396862306a36Sopenharmony_ci	 */
396962306a36Sopenharmony_ci	if (reduced)
397062306a36Sopenharmony_ci		batadv_tt_local_commit_changes_nolock(bat_priv);
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci	spin_unlock_bh(&bat_priv->tt.commit_lock);
397362306a36Sopenharmony_ci}
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_ci/**
397662306a36Sopenharmony_ci * batadv_tt_tvlv_ogm_handler_v1() - process incoming tt tvlv container
397762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
397862306a36Sopenharmony_ci * @orig: the orig_node of the ogm
397962306a36Sopenharmony_ci * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
398062306a36Sopenharmony_ci * @tvlv_value: tvlv buffer containing the gateway data
398162306a36Sopenharmony_ci * @tvlv_value_len: tvlv buffer length
398262306a36Sopenharmony_ci */
398362306a36Sopenharmony_cistatic void batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
398462306a36Sopenharmony_ci					  struct batadv_orig_node *orig,
398562306a36Sopenharmony_ci					  u8 flags, void *tvlv_value,
398662306a36Sopenharmony_ci					  u16 tvlv_value_len)
398762306a36Sopenharmony_ci{
398862306a36Sopenharmony_ci	struct batadv_tvlv_tt_vlan_data *tt_vlan;
398962306a36Sopenharmony_ci	struct batadv_tvlv_tt_change *tt_change;
399062306a36Sopenharmony_ci	struct batadv_tvlv_tt_data *tt_data;
399162306a36Sopenharmony_ci	u16 num_entries, num_vlan;
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci	if (tvlv_value_len < sizeof(*tt_data))
399462306a36Sopenharmony_ci		return;
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_ci	tt_data = tvlv_value;
399762306a36Sopenharmony_ci	tvlv_value_len -= sizeof(*tt_data);
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_ci	num_vlan = ntohs(tt_data->num_vlan);
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci	if (tvlv_value_len < sizeof(*tt_vlan) * num_vlan)
400262306a36Sopenharmony_ci		return;
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_ci	tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
400562306a36Sopenharmony_ci	tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan);
400662306a36Sopenharmony_ci	tvlv_value_len -= sizeof(*tt_vlan) * num_vlan;
400762306a36Sopenharmony_ci
400862306a36Sopenharmony_ci	num_entries = batadv_tt_entries(tvlv_value_len);
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	batadv_tt_update_orig(bat_priv, orig, tt_vlan, num_vlan, tt_change,
401162306a36Sopenharmony_ci			      num_entries, tt_data->ttvn);
401262306a36Sopenharmony_ci}
401362306a36Sopenharmony_ci
401462306a36Sopenharmony_ci/**
401562306a36Sopenharmony_ci * batadv_tt_tvlv_unicast_handler_v1() - process incoming (unicast) tt tvlv
401662306a36Sopenharmony_ci *  container
401762306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
401862306a36Sopenharmony_ci * @src: mac address of tt tvlv sender
401962306a36Sopenharmony_ci * @dst: mac address of tt tvlv recipient
402062306a36Sopenharmony_ci * @tvlv_value: tvlv buffer containing the tt data
402162306a36Sopenharmony_ci * @tvlv_value_len: tvlv buffer length
402262306a36Sopenharmony_ci *
402362306a36Sopenharmony_ci * Return: NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS
402462306a36Sopenharmony_ci * otherwise.
402562306a36Sopenharmony_ci */
402662306a36Sopenharmony_cistatic int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
402762306a36Sopenharmony_ci					     u8 *src, u8 *dst,
402862306a36Sopenharmony_ci					     void *tvlv_value,
402962306a36Sopenharmony_ci					     u16 tvlv_value_len)
403062306a36Sopenharmony_ci{
403162306a36Sopenharmony_ci	struct batadv_tvlv_tt_data *tt_data;
403262306a36Sopenharmony_ci	u16 tt_vlan_len, tt_num_entries;
403362306a36Sopenharmony_ci	char tt_flag;
403462306a36Sopenharmony_ci	bool ret;
403562306a36Sopenharmony_ci
403662306a36Sopenharmony_ci	if (tvlv_value_len < sizeof(*tt_data))
403762306a36Sopenharmony_ci		return NET_RX_SUCCESS;
403862306a36Sopenharmony_ci
403962306a36Sopenharmony_ci	tt_data = tvlv_value;
404062306a36Sopenharmony_ci	tvlv_value_len -= sizeof(*tt_data);
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci	tt_vlan_len = sizeof(struct batadv_tvlv_tt_vlan_data);
404362306a36Sopenharmony_ci	tt_vlan_len *= ntohs(tt_data->num_vlan);
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci	if (tvlv_value_len < tt_vlan_len)
404662306a36Sopenharmony_ci		return NET_RX_SUCCESS;
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci	tvlv_value_len -= tt_vlan_len;
404962306a36Sopenharmony_ci	tt_num_entries = batadv_tt_entries(tvlv_value_len);
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci	switch (tt_data->flags & BATADV_TT_DATA_TYPE_MASK) {
405262306a36Sopenharmony_ci	case BATADV_TT_REQUEST:
405362306a36Sopenharmony_ci		batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_ci		/* If this node cannot provide a TT response the tt_request is
405662306a36Sopenharmony_ci		 * forwarded
405762306a36Sopenharmony_ci		 */
405862306a36Sopenharmony_ci		ret = batadv_send_tt_response(bat_priv, tt_data, src, dst);
405962306a36Sopenharmony_ci		if (!ret) {
406062306a36Sopenharmony_ci			if (tt_data->flags & BATADV_TT_FULL_TABLE)
406162306a36Sopenharmony_ci				tt_flag = 'F';
406262306a36Sopenharmony_ci			else
406362306a36Sopenharmony_ci				tt_flag = '.';
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_ci			batadv_dbg(BATADV_DBG_TT, bat_priv,
406662306a36Sopenharmony_ci				   "Routing TT_REQUEST to %pM [%c]\n",
406762306a36Sopenharmony_ci				   dst, tt_flag);
406862306a36Sopenharmony_ci			/* tvlv API will re-route the packet */
406962306a36Sopenharmony_ci			return NET_RX_DROP;
407062306a36Sopenharmony_ci		}
407162306a36Sopenharmony_ci		break;
407262306a36Sopenharmony_ci	case BATADV_TT_RESPONSE:
407362306a36Sopenharmony_ci		batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci		if (batadv_is_my_mac(bat_priv, dst)) {
407662306a36Sopenharmony_ci			batadv_handle_tt_response(bat_priv, tt_data,
407762306a36Sopenharmony_ci						  src, tt_num_entries);
407862306a36Sopenharmony_ci			return NET_RX_SUCCESS;
407962306a36Sopenharmony_ci		}
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_ci		if (tt_data->flags & BATADV_TT_FULL_TABLE)
408262306a36Sopenharmony_ci			tt_flag =  'F';
408362306a36Sopenharmony_ci		else
408462306a36Sopenharmony_ci			tt_flag = '.';
408562306a36Sopenharmony_ci
408662306a36Sopenharmony_ci		batadv_dbg(BATADV_DBG_TT, bat_priv,
408762306a36Sopenharmony_ci			   "Routing TT_RESPONSE to %pM [%c]\n", dst, tt_flag);
408862306a36Sopenharmony_ci
408962306a36Sopenharmony_ci		/* tvlv API will re-route the packet */
409062306a36Sopenharmony_ci		return NET_RX_DROP;
409162306a36Sopenharmony_ci	}
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci	return NET_RX_SUCCESS;
409462306a36Sopenharmony_ci}
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci/**
409762306a36Sopenharmony_ci * batadv_roam_tvlv_unicast_handler_v1() - process incoming tt roam tvlv
409862306a36Sopenharmony_ci *  container
409962306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
410062306a36Sopenharmony_ci * @src: mac address of tt tvlv sender
410162306a36Sopenharmony_ci * @dst: mac address of tt tvlv recipient
410262306a36Sopenharmony_ci * @tvlv_value: tvlv buffer containing the tt data
410362306a36Sopenharmony_ci * @tvlv_value_len: tvlv buffer length
410462306a36Sopenharmony_ci *
410562306a36Sopenharmony_ci * Return: NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS
410662306a36Sopenharmony_ci * otherwise.
410762306a36Sopenharmony_ci */
410862306a36Sopenharmony_cistatic int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
410962306a36Sopenharmony_ci					       u8 *src, u8 *dst,
411062306a36Sopenharmony_ci					       void *tvlv_value,
411162306a36Sopenharmony_ci					       u16 tvlv_value_len)
411262306a36Sopenharmony_ci{
411362306a36Sopenharmony_ci	struct batadv_tvlv_roam_adv *roaming_adv;
411462306a36Sopenharmony_ci	struct batadv_orig_node *orig_node = NULL;
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	/* If this node is not the intended recipient of the
411762306a36Sopenharmony_ci	 * roaming advertisement the packet is forwarded
411862306a36Sopenharmony_ci	 * (the tvlv API will re-route the packet).
411962306a36Sopenharmony_ci	 */
412062306a36Sopenharmony_ci	if (!batadv_is_my_mac(bat_priv, dst))
412162306a36Sopenharmony_ci		return NET_RX_DROP;
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci	if (tvlv_value_len < sizeof(*roaming_adv))
412462306a36Sopenharmony_ci		goto out;
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_ci	orig_node = batadv_orig_hash_find(bat_priv, src);
412762306a36Sopenharmony_ci	if (!orig_node)
412862306a36Sopenharmony_ci		goto out;
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_ci	batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
413162306a36Sopenharmony_ci	roaming_adv = tvlv_value;
413262306a36Sopenharmony_ci
413362306a36Sopenharmony_ci	batadv_dbg(BATADV_DBG_TT, bat_priv,
413462306a36Sopenharmony_ci		   "Received ROAMING_ADV from %pM (client %pM)\n",
413562306a36Sopenharmony_ci		   src, roaming_adv->client);
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_ci	batadv_tt_global_add(bat_priv, orig_node, roaming_adv->client,
413862306a36Sopenharmony_ci			     ntohs(roaming_adv->vid), BATADV_TT_CLIENT_ROAM,
413962306a36Sopenharmony_ci			     atomic_read(&orig_node->last_ttvn) + 1);
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ciout:
414262306a36Sopenharmony_ci	batadv_orig_node_put(orig_node);
414362306a36Sopenharmony_ci	return NET_RX_SUCCESS;
414462306a36Sopenharmony_ci}
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci/**
414762306a36Sopenharmony_ci * batadv_tt_init() - initialise the translation table internals
414862306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
414962306a36Sopenharmony_ci *
415062306a36Sopenharmony_ci * Return: 0 on success or negative error number in case of failure.
415162306a36Sopenharmony_ci */
415262306a36Sopenharmony_ciint batadv_tt_init(struct batadv_priv *bat_priv)
415362306a36Sopenharmony_ci{
415462306a36Sopenharmony_ci	int ret;
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci	/* synchronized flags must be remote */
415762306a36Sopenharmony_ci	BUILD_BUG_ON(!(BATADV_TT_SYNC_MASK & BATADV_TT_REMOTE_MASK));
415862306a36Sopenharmony_ci
415962306a36Sopenharmony_ci	ret = batadv_tt_local_init(bat_priv);
416062306a36Sopenharmony_ci	if (ret < 0)
416162306a36Sopenharmony_ci		return ret;
416262306a36Sopenharmony_ci
416362306a36Sopenharmony_ci	ret = batadv_tt_global_init(bat_priv);
416462306a36Sopenharmony_ci	if (ret < 0) {
416562306a36Sopenharmony_ci		batadv_tt_local_table_free(bat_priv);
416662306a36Sopenharmony_ci		return ret;
416762306a36Sopenharmony_ci	}
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_ci	batadv_tvlv_handler_register(bat_priv, batadv_tt_tvlv_ogm_handler_v1,
417062306a36Sopenharmony_ci				     batadv_tt_tvlv_unicast_handler_v1, NULL,
417162306a36Sopenharmony_ci				     BATADV_TVLV_TT, 1, BATADV_NO_FLAGS);
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ci	batadv_tvlv_handler_register(bat_priv, NULL,
417462306a36Sopenharmony_ci				     batadv_roam_tvlv_unicast_handler_v1, NULL,
417562306a36Sopenharmony_ci				     BATADV_TVLV_ROAM, 1, BATADV_NO_FLAGS);
417662306a36Sopenharmony_ci
417762306a36Sopenharmony_ci	INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge);
417862306a36Sopenharmony_ci	queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
417962306a36Sopenharmony_ci			   msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	return 1;
418262306a36Sopenharmony_ci}
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_ci/**
418562306a36Sopenharmony_ci * batadv_tt_global_is_isolated() - check if a client is marked as isolated
418662306a36Sopenharmony_ci * @bat_priv: the bat priv with all the soft interface information
418762306a36Sopenharmony_ci * @addr: the mac address of the client
418862306a36Sopenharmony_ci * @vid: the identifier of the VLAN where this client is connected
418962306a36Sopenharmony_ci *
419062306a36Sopenharmony_ci * Return: true if the client is marked with the TT_CLIENT_ISOLA flag, false
419162306a36Sopenharmony_ci * otherwise
419262306a36Sopenharmony_ci */
419362306a36Sopenharmony_cibool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
419462306a36Sopenharmony_ci				  const u8 *addr, unsigned short vid)
419562306a36Sopenharmony_ci{
419662306a36Sopenharmony_ci	struct batadv_tt_global_entry *tt;
419762306a36Sopenharmony_ci	bool ret;
419862306a36Sopenharmony_ci
419962306a36Sopenharmony_ci	tt = batadv_tt_global_hash_find(bat_priv, addr, vid);
420062306a36Sopenharmony_ci	if (!tt)
420162306a36Sopenharmony_ci		return false;
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA;
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	batadv_tt_global_entry_put(tt);
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	return ret;
420862306a36Sopenharmony_ci}
420962306a36Sopenharmony_ci
421062306a36Sopenharmony_ci/**
421162306a36Sopenharmony_ci * batadv_tt_cache_init() - Initialize tt memory object cache
421262306a36Sopenharmony_ci *
421362306a36Sopenharmony_ci * Return: 0 on success or negative error number in case of failure.
421462306a36Sopenharmony_ci */
421562306a36Sopenharmony_ciint __init batadv_tt_cache_init(void)
421662306a36Sopenharmony_ci{
421762306a36Sopenharmony_ci	size_t tl_size = sizeof(struct batadv_tt_local_entry);
421862306a36Sopenharmony_ci	size_t tg_size = sizeof(struct batadv_tt_global_entry);
421962306a36Sopenharmony_ci	size_t tt_orig_size = sizeof(struct batadv_tt_orig_list_entry);
422062306a36Sopenharmony_ci	size_t tt_change_size = sizeof(struct batadv_tt_change_node);
422162306a36Sopenharmony_ci	size_t tt_req_size = sizeof(struct batadv_tt_req_node);
422262306a36Sopenharmony_ci	size_t tt_roam_size = sizeof(struct batadv_tt_roam_node);
422362306a36Sopenharmony_ci
422462306a36Sopenharmony_ci	batadv_tl_cache = kmem_cache_create("batadv_tl_cache", tl_size, 0,
422562306a36Sopenharmony_ci					    SLAB_HWCACHE_ALIGN, NULL);
422662306a36Sopenharmony_ci	if (!batadv_tl_cache)
422762306a36Sopenharmony_ci		return -ENOMEM;
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci	batadv_tg_cache = kmem_cache_create("batadv_tg_cache", tg_size, 0,
423062306a36Sopenharmony_ci					    SLAB_HWCACHE_ALIGN, NULL);
423162306a36Sopenharmony_ci	if (!batadv_tg_cache)
423262306a36Sopenharmony_ci		goto err_tt_tl_destroy;
423362306a36Sopenharmony_ci
423462306a36Sopenharmony_ci	batadv_tt_orig_cache = kmem_cache_create("batadv_tt_orig_cache",
423562306a36Sopenharmony_ci						 tt_orig_size, 0,
423662306a36Sopenharmony_ci						 SLAB_HWCACHE_ALIGN, NULL);
423762306a36Sopenharmony_ci	if (!batadv_tt_orig_cache)
423862306a36Sopenharmony_ci		goto err_tt_tg_destroy;
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_ci	batadv_tt_change_cache = kmem_cache_create("batadv_tt_change_cache",
424162306a36Sopenharmony_ci						   tt_change_size, 0,
424262306a36Sopenharmony_ci						   SLAB_HWCACHE_ALIGN, NULL);
424362306a36Sopenharmony_ci	if (!batadv_tt_change_cache)
424462306a36Sopenharmony_ci		goto err_tt_orig_destroy;
424562306a36Sopenharmony_ci
424662306a36Sopenharmony_ci	batadv_tt_req_cache = kmem_cache_create("batadv_tt_req_cache",
424762306a36Sopenharmony_ci						tt_req_size, 0,
424862306a36Sopenharmony_ci						SLAB_HWCACHE_ALIGN, NULL);
424962306a36Sopenharmony_ci	if (!batadv_tt_req_cache)
425062306a36Sopenharmony_ci		goto err_tt_change_destroy;
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci	batadv_tt_roam_cache = kmem_cache_create("batadv_tt_roam_cache",
425362306a36Sopenharmony_ci						 tt_roam_size, 0,
425462306a36Sopenharmony_ci						 SLAB_HWCACHE_ALIGN, NULL);
425562306a36Sopenharmony_ci	if (!batadv_tt_roam_cache)
425662306a36Sopenharmony_ci		goto err_tt_req_destroy;
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_ci	return 0;
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_cierr_tt_req_destroy:
426162306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_req_cache);
426262306a36Sopenharmony_ci	batadv_tt_req_cache = NULL;
426362306a36Sopenharmony_cierr_tt_change_destroy:
426462306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_change_cache);
426562306a36Sopenharmony_ci	batadv_tt_change_cache = NULL;
426662306a36Sopenharmony_cierr_tt_orig_destroy:
426762306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_orig_cache);
426862306a36Sopenharmony_ci	batadv_tt_orig_cache = NULL;
426962306a36Sopenharmony_cierr_tt_tg_destroy:
427062306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tg_cache);
427162306a36Sopenharmony_ci	batadv_tg_cache = NULL;
427262306a36Sopenharmony_cierr_tt_tl_destroy:
427362306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tl_cache);
427462306a36Sopenharmony_ci	batadv_tl_cache = NULL;
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	return -ENOMEM;
427762306a36Sopenharmony_ci}
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci/**
428062306a36Sopenharmony_ci * batadv_tt_cache_destroy() - Destroy tt memory object cache
428162306a36Sopenharmony_ci */
428262306a36Sopenharmony_civoid batadv_tt_cache_destroy(void)
428362306a36Sopenharmony_ci{
428462306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tl_cache);
428562306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tg_cache);
428662306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_orig_cache);
428762306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_change_cache);
428862306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_req_cache);
428962306a36Sopenharmony_ci	kmem_cache_destroy(batadv_tt_roam_cache);
429062306a36Sopenharmony_ci}
4291