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