1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Copyright (C) 2007-2020 B.A.T.M.A.N. contributors:
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Marek Lindner, Simon Wunderlich, Antonio Quartulli
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include "translation-table.h"
8*4882a593Smuzhiyun #include "main.h"
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/atomic.h>
11*4882a593Smuzhiyun #include <linux/bitops.h>
12*4882a593Smuzhiyun #include <linux/build_bug.h>
13*4882a593Smuzhiyun #include <linux/byteorder/generic.h>
14*4882a593Smuzhiyun #include <linux/cache.h>
15*4882a593Smuzhiyun #include <linux/compiler.h>
16*4882a593Smuzhiyun #include <linux/crc32c.h>
17*4882a593Smuzhiyun #include <linux/errno.h>
18*4882a593Smuzhiyun #include <linux/etherdevice.h>
19*4882a593Smuzhiyun #include <linux/gfp.h>
20*4882a593Smuzhiyun #include <linux/if_ether.h>
21*4882a593Smuzhiyun #include <linux/init.h>
22*4882a593Smuzhiyun #include <linux/jhash.h>
23*4882a593Smuzhiyun #include <linux/jiffies.h>
24*4882a593Smuzhiyun #include <linux/kernel.h>
25*4882a593Smuzhiyun #include <linux/kref.h>
26*4882a593Smuzhiyun #include <linux/list.h>
27*4882a593Smuzhiyun #include <linux/lockdep.h>
28*4882a593Smuzhiyun #include <linux/net.h>
29*4882a593Smuzhiyun #include <linux/netdevice.h>
30*4882a593Smuzhiyun #include <linux/netlink.h>
31*4882a593Smuzhiyun #include <linux/rculist.h>
32*4882a593Smuzhiyun #include <linux/rcupdate.h>
33*4882a593Smuzhiyun #include <linux/seq_file.h>
34*4882a593Smuzhiyun #include <linux/skbuff.h>
35*4882a593Smuzhiyun #include <linux/slab.h>
36*4882a593Smuzhiyun #include <linux/spinlock.h>
37*4882a593Smuzhiyun #include <linux/stddef.h>
38*4882a593Smuzhiyun #include <linux/string.h>
39*4882a593Smuzhiyun #include <linux/workqueue.h>
40*4882a593Smuzhiyun #include <net/genetlink.h>
41*4882a593Smuzhiyun #include <net/netlink.h>
42*4882a593Smuzhiyun #include <net/sock.h>
43*4882a593Smuzhiyun #include <uapi/linux/batadv_packet.h>
44*4882a593Smuzhiyun #include <uapi/linux/batman_adv.h>
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun #include "bridge_loop_avoidance.h"
47*4882a593Smuzhiyun #include "hard-interface.h"
48*4882a593Smuzhiyun #include "hash.h"
49*4882a593Smuzhiyun #include "log.h"
50*4882a593Smuzhiyun #include "netlink.h"
51*4882a593Smuzhiyun #include "originator.h"
52*4882a593Smuzhiyun #include "soft-interface.h"
53*4882a593Smuzhiyun #include "tvlv.h"
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun static struct kmem_cache *batadv_tl_cache __read_mostly;
56*4882a593Smuzhiyun static struct kmem_cache *batadv_tg_cache __read_mostly;
57*4882a593Smuzhiyun static struct kmem_cache *batadv_tt_orig_cache __read_mostly;
58*4882a593Smuzhiyun static struct kmem_cache *batadv_tt_change_cache __read_mostly;
59*4882a593Smuzhiyun static struct kmem_cache *batadv_tt_req_cache __read_mostly;
60*4882a593Smuzhiyun static struct kmem_cache *batadv_tt_roam_cache __read_mostly;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /* hash class keys */
63*4882a593Smuzhiyun static struct lock_class_key batadv_tt_local_hash_lock_class_key;
64*4882a593Smuzhiyun static struct lock_class_key batadv_tt_global_hash_lock_class_key;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
67*4882a593Smuzhiyun unsigned short vid,
68*4882a593Smuzhiyun struct batadv_orig_node *orig_node);
69*4882a593Smuzhiyun static void batadv_tt_purge(struct work_struct *work);
70*4882a593Smuzhiyun static void
71*4882a593Smuzhiyun batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry);
72*4882a593Smuzhiyun static void batadv_tt_global_del(struct batadv_priv *bat_priv,
73*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
74*4882a593Smuzhiyun const unsigned char *addr,
75*4882a593Smuzhiyun unsigned short vid, const char *message,
76*4882a593Smuzhiyun bool roaming);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun /**
79*4882a593Smuzhiyun * batadv_compare_tt() - check if two TT entries are the same
80*4882a593Smuzhiyun * @node: the list element pointer of the first TT entry
81*4882a593Smuzhiyun * @data2: pointer to the tt_common_entry of the second TT entry
82*4882a593Smuzhiyun *
83*4882a593Smuzhiyun * Compare the MAC address and the VLAN ID of the two TT entries and check if
84*4882a593Smuzhiyun * they are the same TT client.
85*4882a593Smuzhiyun * Return: true if the two TT clients are the same, false otherwise
86*4882a593Smuzhiyun */
batadv_compare_tt(const struct hlist_node * node,const void * data2)87*4882a593Smuzhiyun static bool batadv_compare_tt(const struct hlist_node *node, const void *data2)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun const void *data1 = container_of(node, struct batadv_tt_common_entry,
90*4882a593Smuzhiyun hash_entry);
91*4882a593Smuzhiyun const struct batadv_tt_common_entry *tt1 = data1;
92*4882a593Smuzhiyun const struct batadv_tt_common_entry *tt2 = data2;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return (tt1->vid == tt2->vid) && batadv_compare_eth(data1, data2);
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun /**
98*4882a593Smuzhiyun * batadv_choose_tt() - return the index of the tt entry in the hash table
99*4882a593Smuzhiyun * @data: pointer to the tt_common_entry object to map
100*4882a593Smuzhiyun * @size: the size of the hash table
101*4882a593Smuzhiyun *
102*4882a593Smuzhiyun * Return: the hash index where the object represented by 'data' should be
103*4882a593Smuzhiyun * stored at.
104*4882a593Smuzhiyun */
batadv_choose_tt(const void * data,u32 size)105*4882a593Smuzhiyun static inline u32 batadv_choose_tt(const void *data, u32 size)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun struct batadv_tt_common_entry *tt;
108*4882a593Smuzhiyun u32 hash = 0;
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun tt = (struct batadv_tt_common_entry *)data;
111*4882a593Smuzhiyun hash = jhash(&tt->addr, ETH_ALEN, hash);
112*4882a593Smuzhiyun hash = jhash(&tt->vid, sizeof(tt->vid), hash);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun return hash % size;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun /**
118*4882a593Smuzhiyun * batadv_tt_hash_find() - look for a client in the given hash table
119*4882a593Smuzhiyun * @hash: the hash table to search
120*4882a593Smuzhiyun * @addr: the mac address of the client to look for
121*4882a593Smuzhiyun * @vid: VLAN identifier
122*4882a593Smuzhiyun *
123*4882a593Smuzhiyun * Return: a pointer to the tt_common struct belonging to the searched client if
124*4882a593Smuzhiyun * found, NULL otherwise.
125*4882a593Smuzhiyun */
126*4882a593Smuzhiyun static struct batadv_tt_common_entry *
batadv_tt_hash_find(struct batadv_hashtable * hash,const u8 * addr,unsigned short vid)127*4882a593Smuzhiyun batadv_tt_hash_find(struct batadv_hashtable *hash, const u8 *addr,
128*4882a593Smuzhiyun unsigned short vid)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun struct hlist_head *head;
131*4882a593Smuzhiyun struct batadv_tt_common_entry to_search, *tt, *tt_tmp = NULL;
132*4882a593Smuzhiyun u32 index;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun if (!hash)
135*4882a593Smuzhiyun return NULL;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun ether_addr_copy(to_search.addr, addr);
138*4882a593Smuzhiyun to_search.vid = vid;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun index = batadv_choose_tt(&to_search, hash->size);
141*4882a593Smuzhiyun head = &hash->table[index];
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun rcu_read_lock();
144*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt, head, hash_entry) {
145*4882a593Smuzhiyun if (!batadv_compare_eth(tt, addr))
146*4882a593Smuzhiyun continue;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (tt->vid != vid)
149*4882a593Smuzhiyun continue;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun if (!kref_get_unless_zero(&tt->refcount))
152*4882a593Smuzhiyun continue;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun tt_tmp = tt;
155*4882a593Smuzhiyun break;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun rcu_read_unlock();
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun return tt_tmp;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun /**
163*4882a593Smuzhiyun * batadv_tt_local_hash_find() - search the local table for a given client
164*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
165*4882a593Smuzhiyun * @addr: the mac address of the client to look for
166*4882a593Smuzhiyun * @vid: VLAN identifier
167*4882a593Smuzhiyun *
168*4882a593Smuzhiyun * Return: a pointer to the corresponding tt_local_entry struct if the client is
169*4882a593Smuzhiyun * found, NULL otherwise.
170*4882a593Smuzhiyun */
171*4882a593Smuzhiyun static struct batadv_tt_local_entry *
batadv_tt_local_hash_find(struct batadv_priv * bat_priv,const u8 * addr,unsigned short vid)172*4882a593Smuzhiyun batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
173*4882a593Smuzhiyun unsigned short vid)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
176*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry = NULL;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, addr,
179*4882a593Smuzhiyun vid);
180*4882a593Smuzhiyun if (tt_common_entry)
181*4882a593Smuzhiyun tt_local_entry = container_of(tt_common_entry,
182*4882a593Smuzhiyun struct batadv_tt_local_entry,
183*4882a593Smuzhiyun common);
184*4882a593Smuzhiyun return tt_local_entry;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun /**
188*4882a593Smuzhiyun * batadv_tt_global_hash_find() - search the global table for a given client
189*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
190*4882a593Smuzhiyun * @addr: the mac address of the client to look for
191*4882a593Smuzhiyun * @vid: VLAN identifier
192*4882a593Smuzhiyun *
193*4882a593Smuzhiyun * Return: a pointer to the corresponding tt_global_entry struct if the client
194*4882a593Smuzhiyun * is found, NULL otherwise.
195*4882a593Smuzhiyun */
196*4882a593Smuzhiyun struct batadv_tt_global_entry *
batadv_tt_global_hash_find(struct batadv_priv * bat_priv,const u8 * addr,unsigned short vid)197*4882a593Smuzhiyun batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
198*4882a593Smuzhiyun unsigned short vid)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
201*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry = NULL;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, addr,
204*4882a593Smuzhiyun vid);
205*4882a593Smuzhiyun if (tt_common_entry)
206*4882a593Smuzhiyun tt_global_entry = container_of(tt_common_entry,
207*4882a593Smuzhiyun struct batadv_tt_global_entry,
208*4882a593Smuzhiyun common);
209*4882a593Smuzhiyun return tt_global_entry;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun /**
213*4882a593Smuzhiyun * batadv_tt_local_entry_free_rcu() - free the tt_local_entry
214*4882a593Smuzhiyun * @rcu: rcu pointer of the tt_local_entry
215*4882a593Smuzhiyun */
batadv_tt_local_entry_free_rcu(struct rcu_head * rcu)216*4882a593Smuzhiyun static void batadv_tt_local_entry_free_rcu(struct rcu_head *rcu)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun tt_local_entry = container_of(rcu, struct batadv_tt_local_entry,
221*4882a593Smuzhiyun common.rcu);
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun kmem_cache_free(batadv_tl_cache, tt_local_entry);
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun /**
227*4882a593Smuzhiyun * batadv_tt_local_entry_release() - release tt_local_entry from lists and queue
228*4882a593Smuzhiyun * for free after rcu grace period
229*4882a593Smuzhiyun * @ref: kref pointer of the nc_node
230*4882a593Smuzhiyun */
batadv_tt_local_entry_release(struct kref * ref)231*4882a593Smuzhiyun static void batadv_tt_local_entry_release(struct kref *ref)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun tt_local_entry = container_of(ref, struct batadv_tt_local_entry,
236*4882a593Smuzhiyun common.refcount);
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun batadv_softif_vlan_put(tt_local_entry->vlan);
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun call_rcu(&tt_local_entry->common.rcu, batadv_tt_local_entry_free_rcu);
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun /**
244*4882a593Smuzhiyun * batadv_tt_local_entry_put() - decrement the tt_local_entry refcounter and
245*4882a593Smuzhiyun * possibly release it
246*4882a593Smuzhiyun * @tt_local_entry: tt_local_entry to be free'd
247*4882a593Smuzhiyun */
248*4882a593Smuzhiyun static void
batadv_tt_local_entry_put(struct batadv_tt_local_entry * tt_local_entry)249*4882a593Smuzhiyun batadv_tt_local_entry_put(struct batadv_tt_local_entry *tt_local_entry)
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun if (!tt_local_entry)
252*4882a593Smuzhiyun return;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun kref_put(&tt_local_entry->common.refcount,
255*4882a593Smuzhiyun batadv_tt_local_entry_release);
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun /**
259*4882a593Smuzhiyun * batadv_tt_global_entry_free_rcu() - free the tt_global_entry
260*4882a593Smuzhiyun * @rcu: rcu pointer of the tt_global_entry
261*4882a593Smuzhiyun */
batadv_tt_global_entry_free_rcu(struct rcu_head * rcu)262*4882a593Smuzhiyun static void batadv_tt_global_entry_free_rcu(struct rcu_head *rcu)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun tt_global_entry = container_of(rcu, struct batadv_tt_global_entry,
267*4882a593Smuzhiyun common.rcu);
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun kmem_cache_free(batadv_tg_cache, tt_global_entry);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun /**
273*4882a593Smuzhiyun * batadv_tt_global_entry_release() - release tt_global_entry from lists and
274*4882a593Smuzhiyun * queue for free after rcu grace period
275*4882a593Smuzhiyun * @ref: kref pointer of the nc_node
276*4882a593Smuzhiyun */
batadv_tt_global_entry_release(struct kref * ref)277*4882a593Smuzhiyun void batadv_tt_global_entry_release(struct kref *ref)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun tt_global_entry = container_of(ref, struct batadv_tt_global_entry,
282*4882a593Smuzhiyun common.refcount);
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun batadv_tt_global_del_orig_list(tt_global_entry);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun call_rcu(&tt_global_entry->common.rcu, batadv_tt_global_entry_free_rcu);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun /**
290*4882a593Smuzhiyun * batadv_tt_global_hash_count() - count the number of orig entries
291*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
292*4882a593Smuzhiyun * @addr: the mac address of the client to count entries for
293*4882a593Smuzhiyun * @vid: VLAN identifier
294*4882a593Smuzhiyun *
295*4882a593Smuzhiyun * Return: the number of originators advertising the given address/data
296*4882a593Smuzhiyun * (excluding our self).
297*4882a593Smuzhiyun */
batadv_tt_global_hash_count(struct batadv_priv * bat_priv,const u8 * addr,unsigned short vid)298*4882a593Smuzhiyun int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
299*4882a593Smuzhiyun const u8 *addr, unsigned short vid)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
302*4882a593Smuzhiyun int count;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
305*4882a593Smuzhiyun if (!tt_global_entry)
306*4882a593Smuzhiyun return 0;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun count = atomic_read(&tt_global_entry->orig_list_count);
309*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun return count;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun /**
315*4882a593Smuzhiyun * batadv_tt_local_size_mod() - change the size by v of the local table
316*4882a593Smuzhiyun * identified by vid
317*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
318*4882a593Smuzhiyun * @vid: the VLAN identifier of the sub-table to change
319*4882a593Smuzhiyun * @v: the amount to sum to the local table size
320*4882a593Smuzhiyun */
batadv_tt_local_size_mod(struct batadv_priv * bat_priv,unsigned short vid,int v)321*4882a593Smuzhiyun static void batadv_tt_local_size_mod(struct batadv_priv *bat_priv,
322*4882a593Smuzhiyun unsigned short vid, int v)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun vlan = batadv_softif_vlan_get(bat_priv, vid);
327*4882a593Smuzhiyun if (!vlan)
328*4882a593Smuzhiyun return;
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun atomic_add(v, &vlan->tt.num_entries);
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun batadv_softif_vlan_put(vlan);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun /**
336*4882a593Smuzhiyun * batadv_tt_local_size_inc() - increase by one the local table size for the
337*4882a593Smuzhiyun * given vid
338*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
339*4882a593Smuzhiyun * @vid: the VLAN identifier
340*4882a593Smuzhiyun */
batadv_tt_local_size_inc(struct batadv_priv * bat_priv,unsigned short vid)341*4882a593Smuzhiyun static void batadv_tt_local_size_inc(struct batadv_priv *bat_priv,
342*4882a593Smuzhiyun unsigned short vid)
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun batadv_tt_local_size_mod(bat_priv, vid, 1);
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun /**
348*4882a593Smuzhiyun * batadv_tt_local_size_dec() - decrease by one the local table size for the
349*4882a593Smuzhiyun * given vid
350*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
351*4882a593Smuzhiyun * @vid: the VLAN identifier
352*4882a593Smuzhiyun */
batadv_tt_local_size_dec(struct batadv_priv * bat_priv,unsigned short vid)353*4882a593Smuzhiyun static void batadv_tt_local_size_dec(struct batadv_priv *bat_priv,
354*4882a593Smuzhiyun unsigned short vid)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun batadv_tt_local_size_mod(bat_priv, vid, -1);
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun /**
360*4882a593Smuzhiyun * batadv_tt_global_size_mod() - change the size by v of the global table
361*4882a593Smuzhiyun * for orig_node identified by vid
362*4882a593Smuzhiyun * @orig_node: the originator for which the table has to be modified
363*4882a593Smuzhiyun * @vid: the VLAN identifier
364*4882a593Smuzhiyun * @v: the amount to sum to the global table size
365*4882a593Smuzhiyun */
batadv_tt_global_size_mod(struct batadv_orig_node * orig_node,unsigned short vid,int v)366*4882a593Smuzhiyun static void batadv_tt_global_size_mod(struct batadv_orig_node *orig_node,
367*4882a593Smuzhiyun unsigned short vid, int v)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun struct batadv_orig_node_vlan *vlan;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun vlan = batadv_orig_node_vlan_new(orig_node, vid);
372*4882a593Smuzhiyun if (!vlan)
373*4882a593Smuzhiyun return;
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun if (atomic_add_return(v, &vlan->tt.num_entries) == 0) {
376*4882a593Smuzhiyun spin_lock_bh(&orig_node->vlan_list_lock);
377*4882a593Smuzhiyun if (!hlist_unhashed(&vlan->list)) {
378*4882a593Smuzhiyun hlist_del_init_rcu(&vlan->list);
379*4882a593Smuzhiyun batadv_orig_node_vlan_put(vlan);
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun spin_unlock_bh(&orig_node->vlan_list_lock);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun batadv_orig_node_vlan_put(vlan);
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun /**
388*4882a593Smuzhiyun * batadv_tt_global_size_inc() - increase by one the global table size for the
389*4882a593Smuzhiyun * given vid
390*4882a593Smuzhiyun * @orig_node: the originator which global table size has to be decreased
391*4882a593Smuzhiyun * @vid: the vlan identifier
392*4882a593Smuzhiyun */
batadv_tt_global_size_inc(struct batadv_orig_node * orig_node,unsigned short vid)393*4882a593Smuzhiyun static void batadv_tt_global_size_inc(struct batadv_orig_node *orig_node,
394*4882a593Smuzhiyun unsigned short vid)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun batadv_tt_global_size_mod(orig_node, vid, 1);
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun /**
400*4882a593Smuzhiyun * batadv_tt_global_size_dec() - decrease by one the global table size for the
401*4882a593Smuzhiyun * given vid
402*4882a593Smuzhiyun * @orig_node: the originator which global table size has to be decreased
403*4882a593Smuzhiyun * @vid: the vlan identifier
404*4882a593Smuzhiyun */
batadv_tt_global_size_dec(struct batadv_orig_node * orig_node,unsigned short vid)405*4882a593Smuzhiyun static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
406*4882a593Smuzhiyun unsigned short vid)
407*4882a593Smuzhiyun {
408*4882a593Smuzhiyun batadv_tt_global_size_mod(orig_node, vid, -1);
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun /**
412*4882a593Smuzhiyun * batadv_tt_orig_list_entry_free_rcu() - free the orig_entry
413*4882a593Smuzhiyun * @rcu: rcu pointer of the orig_entry
414*4882a593Smuzhiyun */
batadv_tt_orig_list_entry_free_rcu(struct rcu_head * rcu)415*4882a593Smuzhiyun static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu);
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun kmem_cache_free(batadv_tt_orig_cache, orig_entry);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun /**
425*4882a593Smuzhiyun * batadv_tt_orig_list_entry_release() - release tt orig entry from lists and
426*4882a593Smuzhiyun * queue for free after rcu grace period
427*4882a593Smuzhiyun * @ref: kref pointer of the tt orig entry
428*4882a593Smuzhiyun */
batadv_tt_orig_list_entry_release(struct kref * ref)429*4882a593Smuzhiyun static void batadv_tt_orig_list_entry_release(struct kref *ref)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun orig_entry = container_of(ref, struct batadv_tt_orig_list_entry,
434*4882a593Smuzhiyun refcount);
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun batadv_orig_node_put(orig_entry->orig_node);
437*4882a593Smuzhiyun call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu);
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun /**
441*4882a593Smuzhiyun * batadv_tt_orig_list_entry_put() - decrement the tt orig entry refcounter and
442*4882a593Smuzhiyun * possibly release it
443*4882a593Smuzhiyun * @orig_entry: tt orig entry to be free'd
444*4882a593Smuzhiyun */
445*4882a593Smuzhiyun static void
batadv_tt_orig_list_entry_put(struct batadv_tt_orig_list_entry * orig_entry)446*4882a593Smuzhiyun batadv_tt_orig_list_entry_put(struct batadv_tt_orig_list_entry *orig_entry)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun if (!orig_entry)
449*4882a593Smuzhiyun return;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun kref_put(&orig_entry->refcount, batadv_tt_orig_list_entry_release);
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun /**
455*4882a593Smuzhiyun * batadv_tt_local_event() - store a local TT event (ADD/DEL)
456*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
457*4882a593Smuzhiyun * @tt_local_entry: the TT entry involved in the event
458*4882a593Smuzhiyun * @event_flags: flags to store in the event structure
459*4882a593Smuzhiyun */
batadv_tt_local_event(struct batadv_priv * bat_priv,struct batadv_tt_local_entry * tt_local_entry,u8 event_flags)460*4882a593Smuzhiyun static void batadv_tt_local_event(struct batadv_priv *bat_priv,
461*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry,
462*4882a593Smuzhiyun u8 event_flags)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun struct batadv_tt_change_node *tt_change_node, *entry, *safe;
465*4882a593Smuzhiyun struct batadv_tt_common_entry *common = &tt_local_entry->common;
466*4882a593Smuzhiyun u8 flags = common->flags | event_flags;
467*4882a593Smuzhiyun bool event_removed = false;
468*4882a593Smuzhiyun bool del_op_requested, del_op_entry;
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun tt_change_node = kmem_cache_alloc(batadv_tt_change_cache, GFP_ATOMIC);
471*4882a593Smuzhiyun if (!tt_change_node)
472*4882a593Smuzhiyun return;
473*4882a593Smuzhiyun
474*4882a593Smuzhiyun tt_change_node->change.flags = flags;
475*4882a593Smuzhiyun memset(tt_change_node->change.reserved, 0,
476*4882a593Smuzhiyun sizeof(tt_change_node->change.reserved));
477*4882a593Smuzhiyun ether_addr_copy(tt_change_node->change.addr, common->addr);
478*4882a593Smuzhiyun tt_change_node->change.vid = htons(common->vid);
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun del_op_requested = flags & BATADV_TT_CLIENT_DEL;
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun /* check for ADD+DEL or DEL+ADD events */
483*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.changes_list_lock);
484*4882a593Smuzhiyun list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
485*4882a593Smuzhiyun list) {
486*4882a593Smuzhiyun if (!batadv_compare_eth(entry->change.addr, common->addr))
487*4882a593Smuzhiyun continue;
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun /* DEL+ADD in the same orig interval have no effect and can be
490*4882a593Smuzhiyun * removed to avoid silly behaviour on the receiver side. The
491*4882a593Smuzhiyun * other way around (ADD+DEL) can happen in case of roaming of
492*4882a593Smuzhiyun * a client still in the NEW state. Roaming of NEW clients is
493*4882a593Smuzhiyun * now possible due to automatically recognition of "temporary"
494*4882a593Smuzhiyun * clients
495*4882a593Smuzhiyun */
496*4882a593Smuzhiyun del_op_entry = entry->change.flags & BATADV_TT_CLIENT_DEL;
497*4882a593Smuzhiyun if (!del_op_requested && del_op_entry)
498*4882a593Smuzhiyun goto del;
499*4882a593Smuzhiyun if (del_op_requested && !del_op_entry)
500*4882a593Smuzhiyun goto del;
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun /* this is a second add in the same originator interval. It
503*4882a593Smuzhiyun * means that flags have been changed: update them!
504*4882a593Smuzhiyun */
505*4882a593Smuzhiyun if (!del_op_requested && !del_op_entry)
506*4882a593Smuzhiyun entry->change.flags = flags;
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun continue;
509*4882a593Smuzhiyun del:
510*4882a593Smuzhiyun list_del(&entry->list);
511*4882a593Smuzhiyun kmem_cache_free(batadv_tt_change_cache, entry);
512*4882a593Smuzhiyun kmem_cache_free(batadv_tt_change_cache, tt_change_node);
513*4882a593Smuzhiyun event_removed = true;
514*4882a593Smuzhiyun goto unlock;
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun /* track the change in the OGMinterval list */
518*4882a593Smuzhiyun list_add_tail(&tt_change_node->list, &bat_priv->tt.changes_list);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun unlock:
521*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.changes_list_lock);
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun if (event_removed)
524*4882a593Smuzhiyun atomic_dec(&bat_priv->tt.local_changes);
525*4882a593Smuzhiyun else
526*4882a593Smuzhiyun atomic_inc(&bat_priv->tt.local_changes);
527*4882a593Smuzhiyun }
528*4882a593Smuzhiyun
529*4882a593Smuzhiyun /**
530*4882a593Smuzhiyun * batadv_tt_len() - compute length in bytes of given number of tt changes
531*4882a593Smuzhiyun * @changes_num: number of tt changes
532*4882a593Smuzhiyun *
533*4882a593Smuzhiyun * Return: computed length in bytes.
534*4882a593Smuzhiyun */
batadv_tt_len(int changes_num)535*4882a593Smuzhiyun static int batadv_tt_len(int changes_num)
536*4882a593Smuzhiyun {
537*4882a593Smuzhiyun return changes_num * sizeof(struct batadv_tvlv_tt_change);
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun /**
541*4882a593Smuzhiyun * batadv_tt_entries() - compute the number of entries fitting in tt_len bytes
542*4882a593Smuzhiyun * @tt_len: available space
543*4882a593Smuzhiyun *
544*4882a593Smuzhiyun * Return: the number of entries.
545*4882a593Smuzhiyun */
batadv_tt_entries(u16 tt_len)546*4882a593Smuzhiyun static u16 batadv_tt_entries(u16 tt_len)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun return tt_len / batadv_tt_len(1);
549*4882a593Smuzhiyun }
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun /**
552*4882a593Smuzhiyun * batadv_tt_local_table_transmit_size() - calculates the local translation
553*4882a593Smuzhiyun * table size when transmitted over the air
554*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
555*4882a593Smuzhiyun *
556*4882a593Smuzhiyun * Return: local translation table size in bytes.
557*4882a593Smuzhiyun */
batadv_tt_local_table_transmit_size(struct batadv_priv * bat_priv)558*4882a593Smuzhiyun static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
559*4882a593Smuzhiyun {
560*4882a593Smuzhiyun u16 num_vlan = 0;
561*4882a593Smuzhiyun u16 tt_local_entries = 0;
562*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
563*4882a593Smuzhiyun int hdr_size;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun rcu_read_lock();
566*4882a593Smuzhiyun hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
567*4882a593Smuzhiyun num_vlan++;
568*4882a593Smuzhiyun tt_local_entries += atomic_read(&vlan->tt.num_entries);
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun rcu_read_unlock();
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun /* header size of tvlv encapsulated tt response payload */
573*4882a593Smuzhiyun hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
574*4882a593Smuzhiyun hdr_size += sizeof(struct batadv_tvlv_hdr);
575*4882a593Smuzhiyun hdr_size += sizeof(struct batadv_tvlv_tt_data);
576*4882a593Smuzhiyun hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun return hdr_size + batadv_tt_len(tt_local_entries);
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun
batadv_tt_local_init(struct batadv_priv * bat_priv)581*4882a593Smuzhiyun static int batadv_tt_local_init(struct batadv_priv *bat_priv)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun if (bat_priv->tt.local_hash)
584*4882a593Smuzhiyun return 0;
585*4882a593Smuzhiyun
586*4882a593Smuzhiyun bat_priv->tt.local_hash = batadv_hash_new(1024);
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun if (!bat_priv->tt.local_hash)
589*4882a593Smuzhiyun return -ENOMEM;
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun batadv_hash_set_lock_class(bat_priv->tt.local_hash,
592*4882a593Smuzhiyun &batadv_tt_local_hash_lock_class_key);
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun return 0;
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun
batadv_tt_global_free(struct batadv_priv * bat_priv,struct batadv_tt_global_entry * tt_global,const char * message)597*4882a593Smuzhiyun static void batadv_tt_global_free(struct batadv_priv *bat_priv,
598*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global,
599*4882a593Smuzhiyun const char *message)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_removed_entry;
602*4882a593Smuzhiyun struct hlist_node *tt_removed_node;
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
605*4882a593Smuzhiyun "Deleting global tt entry %pM (vid: %d): %s\n",
606*4882a593Smuzhiyun tt_global->common.addr,
607*4882a593Smuzhiyun batadv_print_vid(tt_global->common.vid), message);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun tt_removed_node = batadv_hash_remove(bat_priv->tt.global_hash,
610*4882a593Smuzhiyun batadv_compare_tt,
611*4882a593Smuzhiyun batadv_choose_tt,
612*4882a593Smuzhiyun &tt_global->common);
613*4882a593Smuzhiyun if (!tt_removed_node)
614*4882a593Smuzhiyun return;
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun /* drop reference of remove hash entry */
617*4882a593Smuzhiyun tt_removed_entry = hlist_entry(tt_removed_node,
618*4882a593Smuzhiyun struct batadv_tt_global_entry,
619*4882a593Smuzhiyun common.hash_entry);
620*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_removed_entry);
621*4882a593Smuzhiyun }
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun /**
624*4882a593Smuzhiyun * batadv_tt_local_add() - add a new client to the local table or update an
625*4882a593Smuzhiyun * existing client
626*4882a593Smuzhiyun * @soft_iface: netdev struct of the mesh interface
627*4882a593Smuzhiyun * @addr: the mac address of the client to add
628*4882a593Smuzhiyun * @vid: VLAN identifier
629*4882a593Smuzhiyun * @ifindex: index of the interface where the client is connected to (useful to
630*4882a593Smuzhiyun * identify wireless clients)
631*4882a593Smuzhiyun * @mark: the value contained in the skb->mark field of the received packet (if
632*4882a593Smuzhiyun * any)
633*4882a593Smuzhiyun *
634*4882a593Smuzhiyun * Return: true if the client was successfully added, false otherwise.
635*4882a593Smuzhiyun */
batadv_tt_local_add(struct net_device * soft_iface,const u8 * addr,unsigned short vid,int ifindex,u32 mark)636*4882a593Smuzhiyun bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
637*4882a593Smuzhiyun unsigned short vid, int ifindex, u32 mark)
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun struct batadv_priv *bat_priv = netdev_priv(soft_iface);
640*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local;
641*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global = NULL;
642*4882a593Smuzhiyun struct net *net = dev_net(soft_iface);
643*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
644*4882a593Smuzhiyun struct net_device *in_dev = NULL;
645*4882a593Smuzhiyun struct batadv_hard_iface *in_hardif = NULL;
646*4882a593Smuzhiyun struct hlist_head *head;
647*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
648*4882a593Smuzhiyun int hash_added, table_size, packet_size_max;
649*4882a593Smuzhiyun bool ret = false;
650*4882a593Smuzhiyun bool roamed_back = false;
651*4882a593Smuzhiyun u8 remote_flags;
652*4882a593Smuzhiyun u32 match_mark;
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun if (ifindex != BATADV_NULL_IFINDEX)
655*4882a593Smuzhiyun in_dev = dev_get_by_index(net, ifindex);
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun if (in_dev)
658*4882a593Smuzhiyun in_hardif = batadv_hardif_get_by_netdev(in_dev);
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun if (!is_multicast_ether_addr(addr))
663*4882a593Smuzhiyun tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun if (tt_local) {
666*4882a593Smuzhiyun tt_local->last_seen = jiffies;
667*4882a593Smuzhiyun if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
668*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
669*4882a593Smuzhiyun "Re-adding pending client %pM (vid: %d)\n",
670*4882a593Smuzhiyun addr, batadv_print_vid(vid));
671*4882a593Smuzhiyun /* whatever the reason why the PENDING flag was set,
672*4882a593Smuzhiyun * this is a client which was enqueued to be removed in
673*4882a593Smuzhiyun * this orig_interval. Since it popped up again, the
674*4882a593Smuzhiyun * flag can be reset like it was never enqueued
675*4882a593Smuzhiyun */
676*4882a593Smuzhiyun tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING;
677*4882a593Smuzhiyun goto add_event;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
681*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
682*4882a593Smuzhiyun "Roaming client %pM (vid: %d) came back to its original location\n",
683*4882a593Smuzhiyun addr, batadv_print_vid(vid));
684*4882a593Smuzhiyun /* the ROAM flag is set because this client roamed away
685*4882a593Smuzhiyun * and the node got a roaming_advertisement message. Now
686*4882a593Smuzhiyun * that the client popped up again at its original
687*4882a593Smuzhiyun * location such flag can be unset
688*4882a593Smuzhiyun */
689*4882a593Smuzhiyun tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM;
690*4882a593Smuzhiyun roamed_back = true;
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun goto check_roaming;
693*4882a593Smuzhiyun }
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun /* Ignore the client if we cannot send it in a full table response. */
696*4882a593Smuzhiyun table_size = batadv_tt_local_table_transmit_size(bat_priv);
697*4882a593Smuzhiyun table_size += batadv_tt_len(1);
698*4882a593Smuzhiyun packet_size_max = atomic_read(&bat_priv->packet_size_max);
699*4882a593Smuzhiyun if (table_size > packet_size_max) {
700*4882a593Smuzhiyun net_ratelimited_function(batadv_info, soft_iface,
701*4882a593Smuzhiyun "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
702*4882a593Smuzhiyun table_size, packet_size_max, addr);
703*4882a593Smuzhiyun goto out;
704*4882a593Smuzhiyun }
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
707*4882a593Smuzhiyun if (!tt_local)
708*4882a593Smuzhiyun goto out;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun /* increase the refcounter of the related vlan */
711*4882a593Smuzhiyun vlan = batadv_softif_vlan_get(bat_priv, vid);
712*4882a593Smuzhiyun if (!vlan) {
713*4882a593Smuzhiyun net_ratelimited_function(batadv_info, soft_iface,
714*4882a593Smuzhiyun "adding TT local entry %pM to non-existent VLAN %d\n",
715*4882a593Smuzhiyun addr, batadv_print_vid(vid));
716*4882a593Smuzhiyun kmem_cache_free(batadv_tl_cache, tt_local);
717*4882a593Smuzhiyun tt_local = NULL;
718*4882a593Smuzhiyun goto out;
719*4882a593Smuzhiyun }
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
722*4882a593Smuzhiyun "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n",
723*4882a593Smuzhiyun addr, batadv_print_vid(vid),
724*4882a593Smuzhiyun (u8)atomic_read(&bat_priv->tt.vn));
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun ether_addr_copy(tt_local->common.addr, addr);
727*4882a593Smuzhiyun /* The local entry has to be marked as NEW to avoid to send it in
728*4882a593Smuzhiyun * a full table response going out before the next ttvn increment
729*4882a593Smuzhiyun * (consistency check)
730*4882a593Smuzhiyun */
731*4882a593Smuzhiyun tt_local->common.flags = BATADV_TT_CLIENT_NEW;
732*4882a593Smuzhiyun tt_local->common.vid = vid;
733*4882a593Smuzhiyun if (batadv_is_wifi_hardif(in_hardif))
734*4882a593Smuzhiyun tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
735*4882a593Smuzhiyun kref_init(&tt_local->common.refcount);
736*4882a593Smuzhiyun tt_local->last_seen = jiffies;
737*4882a593Smuzhiyun tt_local->common.added_at = tt_local->last_seen;
738*4882a593Smuzhiyun tt_local->vlan = vlan;
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun /* the batman interface mac and multicast addresses should never be
741*4882a593Smuzhiyun * purged
742*4882a593Smuzhiyun */
743*4882a593Smuzhiyun if (batadv_compare_eth(addr, soft_iface->dev_addr) ||
744*4882a593Smuzhiyun is_multicast_ether_addr(addr))
745*4882a593Smuzhiyun tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun kref_get(&tt_local->common.refcount);
748*4882a593Smuzhiyun hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
749*4882a593Smuzhiyun batadv_choose_tt, &tt_local->common,
750*4882a593Smuzhiyun &tt_local->common.hash_entry);
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun if (unlikely(hash_added != 0)) {
753*4882a593Smuzhiyun /* remove the reference for the hash */
754*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local);
755*4882a593Smuzhiyun goto out;
756*4882a593Smuzhiyun }
757*4882a593Smuzhiyun
758*4882a593Smuzhiyun add_event:
759*4882a593Smuzhiyun batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun check_roaming:
762*4882a593Smuzhiyun /* Check whether it is a roaming, but don't do anything if the roaming
763*4882a593Smuzhiyun * process has already been handled
764*4882a593Smuzhiyun */
765*4882a593Smuzhiyun if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) {
766*4882a593Smuzhiyun /* These node are probably going to update their tt table */
767*4882a593Smuzhiyun head = &tt_global->orig_list;
768*4882a593Smuzhiyun rcu_read_lock();
769*4882a593Smuzhiyun hlist_for_each_entry_rcu(orig_entry, head, list) {
770*4882a593Smuzhiyun batadv_send_roam_adv(bat_priv, tt_global->common.addr,
771*4882a593Smuzhiyun tt_global->common.vid,
772*4882a593Smuzhiyun orig_entry->orig_node);
773*4882a593Smuzhiyun }
774*4882a593Smuzhiyun rcu_read_unlock();
775*4882a593Smuzhiyun if (roamed_back) {
776*4882a593Smuzhiyun batadv_tt_global_free(bat_priv, tt_global,
777*4882a593Smuzhiyun "Roaming canceled");
778*4882a593Smuzhiyun tt_global = NULL;
779*4882a593Smuzhiyun } else {
780*4882a593Smuzhiyun /* The global entry has to be marked as ROAMING and
781*4882a593Smuzhiyun * has to be kept for consistency purpose
782*4882a593Smuzhiyun */
783*4882a593Smuzhiyun tt_global->common.flags |= BATADV_TT_CLIENT_ROAM;
784*4882a593Smuzhiyun tt_global->roam_at = jiffies;
785*4882a593Smuzhiyun }
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun /* store the current remote flags before altering them. This helps
789*4882a593Smuzhiyun * understanding is flags are changing or not
790*4882a593Smuzhiyun */
791*4882a593Smuzhiyun remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK;
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun if (batadv_is_wifi_hardif(in_hardif))
794*4882a593Smuzhiyun tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
795*4882a593Smuzhiyun else
796*4882a593Smuzhiyun tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI;
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun /* check the mark in the skb: if it's equal to the configured
799*4882a593Smuzhiyun * isolation_mark, it means the packet is coming from an isolated
800*4882a593Smuzhiyun * non-mesh client
801*4882a593Smuzhiyun */
802*4882a593Smuzhiyun match_mark = (mark & bat_priv->isolation_mark_mask);
803*4882a593Smuzhiyun if (bat_priv->isolation_mark_mask &&
804*4882a593Smuzhiyun match_mark == bat_priv->isolation_mark)
805*4882a593Smuzhiyun tt_local->common.flags |= BATADV_TT_CLIENT_ISOLA;
806*4882a593Smuzhiyun else
807*4882a593Smuzhiyun tt_local->common.flags &= ~BATADV_TT_CLIENT_ISOLA;
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun /* if any "dynamic" flag has been modified, resend an ADD event for this
810*4882a593Smuzhiyun * entry so that all the nodes can get the new flags
811*4882a593Smuzhiyun */
812*4882a593Smuzhiyun if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK))
813*4882a593Smuzhiyun batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun ret = true;
816*4882a593Smuzhiyun out:
817*4882a593Smuzhiyun if (in_hardif)
818*4882a593Smuzhiyun batadv_hardif_put(in_hardif);
819*4882a593Smuzhiyun if (in_dev)
820*4882a593Smuzhiyun dev_put(in_dev);
821*4882a593Smuzhiyun if (tt_local)
822*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local);
823*4882a593Smuzhiyun if (tt_global)
824*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global);
825*4882a593Smuzhiyun return ret;
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun
828*4882a593Smuzhiyun /**
829*4882a593Smuzhiyun * batadv_tt_prepare_tvlv_global_data() - prepare the TVLV TT header to send
830*4882a593Smuzhiyun * within a TT Response directed to another node
831*4882a593Smuzhiyun * @orig_node: originator for which the TT data has to be prepared
832*4882a593Smuzhiyun * @tt_data: uninitialised pointer to the address of the TVLV buffer
833*4882a593Smuzhiyun * @tt_change: uninitialised pointer to the address of the area where the TT
834*4882a593Smuzhiyun * changed can be stored
835*4882a593Smuzhiyun * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
836*4882a593Smuzhiyun * function reserves the amount of space needed to send the entire global TT
837*4882a593Smuzhiyun * table. In case of success the value is updated with the real amount of
838*4882a593Smuzhiyun * reserved bytes
839*4882a593Smuzhiyun * Allocate the needed amount of memory for the entire TT TVLV and write its
840*4882a593Smuzhiyun * header made up of one tvlv_tt_data object and a series of tvlv_tt_vlan_data
841*4882a593Smuzhiyun * objects, one per active VLAN served by the originator node.
842*4882a593Smuzhiyun *
843*4882a593Smuzhiyun * Return: the size of the allocated buffer or 0 in case of failure.
844*4882a593Smuzhiyun */
845*4882a593Smuzhiyun static u16
batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node * orig_node,struct batadv_tvlv_tt_data ** tt_data,struct batadv_tvlv_tt_change ** tt_change,s32 * tt_len)846*4882a593Smuzhiyun batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
847*4882a593Smuzhiyun struct batadv_tvlv_tt_data **tt_data,
848*4882a593Smuzhiyun struct batadv_tvlv_tt_change **tt_change,
849*4882a593Smuzhiyun s32 *tt_len)
850*4882a593Smuzhiyun {
851*4882a593Smuzhiyun u16 num_vlan = 0;
852*4882a593Smuzhiyun u16 num_entries = 0;
853*4882a593Smuzhiyun u16 change_offset;
854*4882a593Smuzhiyun u16 tvlv_len;
855*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan;
856*4882a593Smuzhiyun struct batadv_orig_node_vlan *vlan;
857*4882a593Smuzhiyun u8 *tt_change_ptr;
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun spin_lock_bh(&orig_node->vlan_list_lock);
860*4882a593Smuzhiyun hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
861*4882a593Smuzhiyun num_vlan++;
862*4882a593Smuzhiyun num_entries += atomic_read(&vlan->tt.num_entries);
863*4882a593Smuzhiyun }
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun change_offset = sizeof(**tt_data);
866*4882a593Smuzhiyun change_offset += num_vlan * sizeof(*tt_vlan);
867*4882a593Smuzhiyun
868*4882a593Smuzhiyun /* if tt_len is negative, allocate the space needed by the full table */
869*4882a593Smuzhiyun if (*tt_len < 0)
870*4882a593Smuzhiyun *tt_len = batadv_tt_len(num_entries);
871*4882a593Smuzhiyun
872*4882a593Smuzhiyun tvlv_len = *tt_len;
873*4882a593Smuzhiyun tvlv_len += change_offset;
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
876*4882a593Smuzhiyun if (!*tt_data) {
877*4882a593Smuzhiyun *tt_len = 0;
878*4882a593Smuzhiyun goto out;
879*4882a593Smuzhiyun }
880*4882a593Smuzhiyun
881*4882a593Smuzhiyun (*tt_data)->flags = BATADV_NO_FLAGS;
882*4882a593Smuzhiyun (*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn);
883*4882a593Smuzhiyun (*tt_data)->num_vlan = htons(num_vlan);
884*4882a593Smuzhiyun
885*4882a593Smuzhiyun tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
886*4882a593Smuzhiyun hlist_for_each_entry(vlan, &orig_node->vlan_list, list) {
887*4882a593Smuzhiyun tt_vlan->vid = htons(vlan->vid);
888*4882a593Smuzhiyun tt_vlan->crc = htonl(vlan->tt.crc);
889*4882a593Smuzhiyun tt_vlan->reserved = 0;
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun tt_vlan++;
892*4882a593Smuzhiyun }
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun tt_change_ptr = (u8 *)*tt_data + change_offset;
895*4882a593Smuzhiyun *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun out:
898*4882a593Smuzhiyun spin_unlock_bh(&orig_node->vlan_list_lock);
899*4882a593Smuzhiyun return tvlv_len;
900*4882a593Smuzhiyun }
901*4882a593Smuzhiyun
902*4882a593Smuzhiyun /**
903*4882a593Smuzhiyun * batadv_tt_prepare_tvlv_local_data() - allocate and prepare the TT TVLV for
904*4882a593Smuzhiyun * this node
905*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
906*4882a593Smuzhiyun * @tt_data: uninitialised pointer to the address of the TVLV buffer
907*4882a593Smuzhiyun * @tt_change: uninitialised pointer to the address of the area where the TT
908*4882a593Smuzhiyun * changes can be stored
909*4882a593Smuzhiyun * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
910*4882a593Smuzhiyun * function reserves the amount of space needed to send the entire local TT
911*4882a593Smuzhiyun * table. In case of success the value is updated with the real amount of
912*4882a593Smuzhiyun * reserved bytes
913*4882a593Smuzhiyun *
914*4882a593Smuzhiyun * Allocate the needed amount of memory for the entire TT TVLV and write its
915*4882a593Smuzhiyun * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
916*4882a593Smuzhiyun * objects, one per active VLAN.
917*4882a593Smuzhiyun *
918*4882a593Smuzhiyun * Return: the size of the allocated buffer or 0 in case of failure.
919*4882a593Smuzhiyun */
920*4882a593Smuzhiyun static u16
batadv_tt_prepare_tvlv_local_data(struct batadv_priv * bat_priv,struct batadv_tvlv_tt_data ** tt_data,struct batadv_tvlv_tt_change ** tt_change,s32 * tt_len)921*4882a593Smuzhiyun batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
922*4882a593Smuzhiyun struct batadv_tvlv_tt_data **tt_data,
923*4882a593Smuzhiyun struct batadv_tvlv_tt_change **tt_change,
924*4882a593Smuzhiyun s32 *tt_len)
925*4882a593Smuzhiyun {
926*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan;
927*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
928*4882a593Smuzhiyun u16 num_vlan = 0;
929*4882a593Smuzhiyun u16 vlan_entries = 0;
930*4882a593Smuzhiyun u16 total_entries = 0;
931*4882a593Smuzhiyun u16 tvlv_len;
932*4882a593Smuzhiyun u8 *tt_change_ptr;
933*4882a593Smuzhiyun int change_offset;
934*4882a593Smuzhiyun
935*4882a593Smuzhiyun spin_lock_bh(&bat_priv->softif_vlan_list_lock);
936*4882a593Smuzhiyun hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
937*4882a593Smuzhiyun vlan_entries = atomic_read(&vlan->tt.num_entries);
938*4882a593Smuzhiyun if (vlan_entries < 1)
939*4882a593Smuzhiyun continue;
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun num_vlan++;
942*4882a593Smuzhiyun total_entries += vlan_entries;
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun change_offset = sizeof(**tt_data);
946*4882a593Smuzhiyun change_offset += num_vlan * sizeof(*tt_vlan);
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun /* if tt_len is negative, allocate the space needed by the full table */
949*4882a593Smuzhiyun if (*tt_len < 0)
950*4882a593Smuzhiyun *tt_len = batadv_tt_len(total_entries);
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun tvlv_len = *tt_len;
953*4882a593Smuzhiyun tvlv_len += change_offset;
954*4882a593Smuzhiyun
955*4882a593Smuzhiyun *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
956*4882a593Smuzhiyun if (!*tt_data) {
957*4882a593Smuzhiyun tvlv_len = 0;
958*4882a593Smuzhiyun goto out;
959*4882a593Smuzhiyun }
960*4882a593Smuzhiyun
961*4882a593Smuzhiyun (*tt_data)->flags = BATADV_NO_FLAGS;
962*4882a593Smuzhiyun (*tt_data)->ttvn = atomic_read(&bat_priv->tt.vn);
963*4882a593Smuzhiyun (*tt_data)->num_vlan = htons(num_vlan);
964*4882a593Smuzhiyun
965*4882a593Smuzhiyun tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
966*4882a593Smuzhiyun hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) {
967*4882a593Smuzhiyun vlan_entries = atomic_read(&vlan->tt.num_entries);
968*4882a593Smuzhiyun if (vlan_entries < 1)
969*4882a593Smuzhiyun continue;
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun tt_vlan->vid = htons(vlan->vid);
972*4882a593Smuzhiyun tt_vlan->crc = htonl(vlan->tt.crc);
973*4882a593Smuzhiyun tt_vlan->reserved = 0;
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun tt_vlan++;
976*4882a593Smuzhiyun }
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun tt_change_ptr = (u8 *)*tt_data + change_offset;
979*4882a593Smuzhiyun *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
980*4882a593Smuzhiyun
981*4882a593Smuzhiyun out:
982*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
983*4882a593Smuzhiyun return tvlv_len;
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun /**
987*4882a593Smuzhiyun * batadv_tt_tvlv_container_update() - update the translation table tvlv
988*4882a593Smuzhiyun * container after local tt changes have been committed
989*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
990*4882a593Smuzhiyun */
batadv_tt_tvlv_container_update(struct batadv_priv * bat_priv)991*4882a593Smuzhiyun static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
992*4882a593Smuzhiyun {
993*4882a593Smuzhiyun struct batadv_tt_change_node *entry, *safe;
994*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data;
995*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change;
996*4882a593Smuzhiyun int tt_diff_len, tt_change_len = 0;
997*4882a593Smuzhiyun int tt_diff_entries_num = 0;
998*4882a593Smuzhiyun int tt_diff_entries_count = 0;
999*4882a593Smuzhiyun u16 tvlv_len;
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun tt_diff_entries_num = atomic_read(&bat_priv->tt.local_changes);
1002*4882a593Smuzhiyun tt_diff_len = batadv_tt_len(tt_diff_entries_num);
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun /* if we have too many changes for one packet don't send any
1005*4882a593Smuzhiyun * and wait for the tt table request which will be fragmented
1006*4882a593Smuzhiyun */
1007*4882a593Smuzhiyun if (tt_diff_len > bat_priv->soft_iface->mtu)
1008*4882a593Smuzhiyun tt_diff_len = 0;
1009*4882a593Smuzhiyun
1010*4882a593Smuzhiyun tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data,
1011*4882a593Smuzhiyun &tt_change, &tt_diff_len);
1012*4882a593Smuzhiyun if (!tvlv_len)
1013*4882a593Smuzhiyun return;
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun tt_data->flags = BATADV_TT_OGM_DIFF;
1016*4882a593Smuzhiyun
1017*4882a593Smuzhiyun if (tt_diff_len == 0)
1018*4882a593Smuzhiyun goto container_register;
1019*4882a593Smuzhiyun
1020*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.changes_list_lock);
1021*4882a593Smuzhiyun atomic_set(&bat_priv->tt.local_changes, 0);
1022*4882a593Smuzhiyun
1023*4882a593Smuzhiyun list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
1024*4882a593Smuzhiyun list) {
1025*4882a593Smuzhiyun if (tt_diff_entries_count < tt_diff_entries_num) {
1026*4882a593Smuzhiyun memcpy(tt_change + tt_diff_entries_count,
1027*4882a593Smuzhiyun &entry->change,
1028*4882a593Smuzhiyun sizeof(struct batadv_tvlv_tt_change));
1029*4882a593Smuzhiyun tt_diff_entries_count++;
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun list_del(&entry->list);
1032*4882a593Smuzhiyun kmem_cache_free(batadv_tt_change_cache, entry);
1033*4882a593Smuzhiyun }
1034*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.changes_list_lock);
1035*4882a593Smuzhiyun
1036*4882a593Smuzhiyun /* Keep the buffer for possible tt_request */
1037*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.last_changeset_lock);
1038*4882a593Smuzhiyun kfree(bat_priv->tt.last_changeset);
1039*4882a593Smuzhiyun bat_priv->tt.last_changeset_len = 0;
1040*4882a593Smuzhiyun bat_priv->tt.last_changeset = NULL;
1041*4882a593Smuzhiyun tt_change_len = batadv_tt_len(tt_diff_entries_count);
1042*4882a593Smuzhiyun /* check whether this new OGM has no changes due to size problems */
1043*4882a593Smuzhiyun if (tt_diff_entries_count > 0) {
1044*4882a593Smuzhiyun /* if kmalloc() fails we will reply with the full table
1045*4882a593Smuzhiyun * instead of providing the diff
1046*4882a593Smuzhiyun */
1047*4882a593Smuzhiyun bat_priv->tt.last_changeset = kzalloc(tt_diff_len, GFP_ATOMIC);
1048*4882a593Smuzhiyun if (bat_priv->tt.last_changeset) {
1049*4882a593Smuzhiyun memcpy(bat_priv->tt.last_changeset,
1050*4882a593Smuzhiyun tt_change, tt_change_len);
1051*4882a593Smuzhiyun bat_priv->tt.last_changeset_len = tt_diff_len;
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun }
1054*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
1055*4882a593Smuzhiyun
1056*4882a593Smuzhiyun container_register:
1057*4882a593Smuzhiyun batadv_tvlv_container_register(bat_priv, BATADV_TVLV_TT, 1, tt_data,
1058*4882a593Smuzhiyun tvlv_len);
1059*4882a593Smuzhiyun kfree(tt_data);
1060*4882a593Smuzhiyun }
1061*4882a593Smuzhiyun
1062*4882a593Smuzhiyun #ifdef CONFIG_BATMAN_ADV_DEBUGFS
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun /**
1065*4882a593Smuzhiyun * batadv_tt_local_seq_print_text() - Print the local tt table in a seq file
1066*4882a593Smuzhiyun * @seq: seq file to print on
1067*4882a593Smuzhiyun * @offset: not used
1068*4882a593Smuzhiyun *
1069*4882a593Smuzhiyun * Return: always 0
1070*4882a593Smuzhiyun */
batadv_tt_local_seq_print_text(struct seq_file * seq,void * offset)1071*4882a593Smuzhiyun int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
1072*4882a593Smuzhiyun {
1073*4882a593Smuzhiyun struct net_device *net_dev = (struct net_device *)seq->private;
1074*4882a593Smuzhiyun struct batadv_priv *bat_priv = netdev_priv(net_dev);
1075*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.local_hash;
1076*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
1077*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local;
1078*4882a593Smuzhiyun struct batadv_hard_iface *primary_if;
1079*4882a593Smuzhiyun struct hlist_head *head;
1080*4882a593Smuzhiyun u32 i;
1081*4882a593Smuzhiyun int last_seen_secs;
1082*4882a593Smuzhiyun int last_seen_msecs;
1083*4882a593Smuzhiyun unsigned long last_seen_jiffies;
1084*4882a593Smuzhiyun bool no_purge;
1085*4882a593Smuzhiyun u16 np_flag = BATADV_TT_CLIENT_NOPURGE;
1086*4882a593Smuzhiyun
1087*4882a593Smuzhiyun primary_if = batadv_seq_print_text_primary_if_get(seq);
1088*4882a593Smuzhiyun if (!primary_if)
1089*4882a593Smuzhiyun goto out;
1090*4882a593Smuzhiyun
1091*4882a593Smuzhiyun seq_printf(seq,
1092*4882a593Smuzhiyun "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n",
1093*4882a593Smuzhiyun net_dev->name, (u8)atomic_read(&bat_priv->tt.vn));
1094*4882a593Smuzhiyun seq_puts(seq,
1095*4882a593Smuzhiyun " Client VID Flags Last seen (CRC )\n");
1096*4882a593Smuzhiyun
1097*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
1098*4882a593Smuzhiyun head = &hash->table[i];
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun rcu_read_lock();
1101*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt_common_entry,
1102*4882a593Smuzhiyun head, hash_entry) {
1103*4882a593Smuzhiyun tt_local = container_of(tt_common_entry,
1104*4882a593Smuzhiyun struct batadv_tt_local_entry,
1105*4882a593Smuzhiyun common);
1106*4882a593Smuzhiyun last_seen_jiffies = jiffies - tt_local->last_seen;
1107*4882a593Smuzhiyun last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
1108*4882a593Smuzhiyun last_seen_secs = last_seen_msecs / 1000;
1109*4882a593Smuzhiyun last_seen_msecs = last_seen_msecs % 1000;
1110*4882a593Smuzhiyun
1111*4882a593Smuzhiyun no_purge = tt_common_entry->flags & np_flag;
1112*4882a593Smuzhiyun seq_printf(seq,
1113*4882a593Smuzhiyun " * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n",
1114*4882a593Smuzhiyun tt_common_entry->addr,
1115*4882a593Smuzhiyun batadv_print_vid(tt_common_entry->vid),
1116*4882a593Smuzhiyun ((tt_common_entry->flags &
1117*4882a593Smuzhiyun BATADV_TT_CLIENT_ROAM) ? 'R' : '.'),
1118*4882a593Smuzhiyun no_purge ? 'P' : '.',
1119*4882a593Smuzhiyun ((tt_common_entry->flags &
1120*4882a593Smuzhiyun BATADV_TT_CLIENT_NEW) ? 'N' : '.'),
1121*4882a593Smuzhiyun ((tt_common_entry->flags &
1122*4882a593Smuzhiyun BATADV_TT_CLIENT_PENDING) ? 'X' : '.'),
1123*4882a593Smuzhiyun ((tt_common_entry->flags &
1124*4882a593Smuzhiyun BATADV_TT_CLIENT_WIFI) ? 'W' : '.'),
1125*4882a593Smuzhiyun ((tt_common_entry->flags &
1126*4882a593Smuzhiyun BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
1127*4882a593Smuzhiyun no_purge ? 0 : last_seen_secs,
1128*4882a593Smuzhiyun no_purge ? 0 : last_seen_msecs,
1129*4882a593Smuzhiyun tt_local->vlan->tt.crc);
1130*4882a593Smuzhiyun }
1131*4882a593Smuzhiyun rcu_read_unlock();
1132*4882a593Smuzhiyun }
1133*4882a593Smuzhiyun out:
1134*4882a593Smuzhiyun if (primary_if)
1135*4882a593Smuzhiyun batadv_hardif_put(primary_if);
1136*4882a593Smuzhiyun return 0;
1137*4882a593Smuzhiyun }
1138*4882a593Smuzhiyun #endif
1139*4882a593Smuzhiyun
1140*4882a593Smuzhiyun /**
1141*4882a593Smuzhiyun * batadv_tt_local_dump_entry() - Dump one TT local entry into a message
1142*4882a593Smuzhiyun * @msg :Netlink message to dump into
1143*4882a593Smuzhiyun * @portid: Port making netlink request
1144*4882a593Smuzhiyun * @cb: Control block containing additional options
1145*4882a593Smuzhiyun * @bat_priv: The bat priv with all the soft interface information
1146*4882a593Smuzhiyun * @common: tt local & tt global common data
1147*4882a593Smuzhiyun *
1148*4882a593Smuzhiyun * Return: Error code, or 0 on success
1149*4882a593Smuzhiyun */
1150*4882a593Smuzhiyun static int
batadv_tt_local_dump_entry(struct sk_buff * msg,u32 portid,struct netlink_callback * cb,struct batadv_priv * bat_priv,struct batadv_tt_common_entry * common)1151*4882a593Smuzhiyun batadv_tt_local_dump_entry(struct sk_buff *msg, u32 portid,
1152*4882a593Smuzhiyun struct netlink_callback *cb,
1153*4882a593Smuzhiyun struct batadv_priv *bat_priv,
1154*4882a593Smuzhiyun struct batadv_tt_common_entry *common)
1155*4882a593Smuzhiyun {
1156*4882a593Smuzhiyun void *hdr;
1157*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
1158*4882a593Smuzhiyun struct batadv_tt_local_entry *local;
1159*4882a593Smuzhiyun unsigned int last_seen_msecs;
1160*4882a593Smuzhiyun u32 crc;
1161*4882a593Smuzhiyun
1162*4882a593Smuzhiyun local = container_of(common, struct batadv_tt_local_entry, common);
1163*4882a593Smuzhiyun last_seen_msecs = jiffies_to_msecs(jiffies - local->last_seen);
1164*4882a593Smuzhiyun
1165*4882a593Smuzhiyun vlan = batadv_softif_vlan_get(bat_priv, common->vid);
1166*4882a593Smuzhiyun if (!vlan)
1167*4882a593Smuzhiyun return 0;
1168*4882a593Smuzhiyun
1169*4882a593Smuzhiyun crc = vlan->tt.crc;
1170*4882a593Smuzhiyun
1171*4882a593Smuzhiyun batadv_softif_vlan_put(vlan);
1172*4882a593Smuzhiyun
1173*4882a593Smuzhiyun hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
1174*4882a593Smuzhiyun &batadv_netlink_family, NLM_F_MULTI,
1175*4882a593Smuzhiyun BATADV_CMD_GET_TRANSTABLE_LOCAL);
1176*4882a593Smuzhiyun if (!hdr)
1177*4882a593Smuzhiyun return -ENOBUFS;
1178*4882a593Smuzhiyun
1179*4882a593Smuzhiyun genl_dump_check_consistent(cb, hdr);
1180*4882a593Smuzhiyun
1181*4882a593Smuzhiyun if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
1182*4882a593Smuzhiyun nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
1183*4882a593Smuzhiyun nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
1184*4882a593Smuzhiyun nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, common->flags))
1185*4882a593Smuzhiyun goto nla_put_failure;
1186*4882a593Smuzhiyun
1187*4882a593Smuzhiyun if (!(common->flags & BATADV_TT_CLIENT_NOPURGE) &&
1188*4882a593Smuzhiyun nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS, last_seen_msecs))
1189*4882a593Smuzhiyun goto nla_put_failure;
1190*4882a593Smuzhiyun
1191*4882a593Smuzhiyun genlmsg_end(msg, hdr);
1192*4882a593Smuzhiyun return 0;
1193*4882a593Smuzhiyun
1194*4882a593Smuzhiyun nla_put_failure:
1195*4882a593Smuzhiyun genlmsg_cancel(msg, hdr);
1196*4882a593Smuzhiyun return -EMSGSIZE;
1197*4882a593Smuzhiyun }
1198*4882a593Smuzhiyun
1199*4882a593Smuzhiyun /**
1200*4882a593Smuzhiyun * batadv_tt_local_dump_bucket() - Dump one TT local bucket into a message
1201*4882a593Smuzhiyun * @msg: Netlink message to dump into
1202*4882a593Smuzhiyun * @portid: Port making netlink request
1203*4882a593Smuzhiyun * @cb: Control block containing additional options
1204*4882a593Smuzhiyun * @bat_priv: The bat priv with all the soft interface information
1205*4882a593Smuzhiyun * @hash: hash to dump
1206*4882a593Smuzhiyun * @bucket: bucket index to dump
1207*4882a593Smuzhiyun * @idx_s: Number of entries to skip
1208*4882a593Smuzhiyun *
1209*4882a593Smuzhiyun * Return: Error code, or 0 on success
1210*4882a593Smuzhiyun */
1211*4882a593Smuzhiyun static int
batadv_tt_local_dump_bucket(struct sk_buff * msg,u32 portid,struct netlink_callback * cb,struct batadv_priv * bat_priv,struct batadv_hashtable * hash,unsigned int bucket,int * idx_s)1212*4882a593Smuzhiyun batadv_tt_local_dump_bucket(struct sk_buff *msg, u32 portid,
1213*4882a593Smuzhiyun struct netlink_callback *cb,
1214*4882a593Smuzhiyun struct batadv_priv *bat_priv,
1215*4882a593Smuzhiyun struct batadv_hashtable *hash, unsigned int bucket,
1216*4882a593Smuzhiyun int *idx_s)
1217*4882a593Smuzhiyun {
1218*4882a593Smuzhiyun struct batadv_tt_common_entry *common;
1219*4882a593Smuzhiyun int idx = 0;
1220*4882a593Smuzhiyun
1221*4882a593Smuzhiyun spin_lock_bh(&hash->list_locks[bucket]);
1222*4882a593Smuzhiyun cb->seq = atomic_read(&hash->generation) << 1 | 1;
1223*4882a593Smuzhiyun
1224*4882a593Smuzhiyun hlist_for_each_entry(common, &hash->table[bucket], hash_entry) {
1225*4882a593Smuzhiyun if (idx++ < *idx_s)
1226*4882a593Smuzhiyun continue;
1227*4882a593Smuzhiyun
1228*4882a593Smuzhiyun if (batadv_tt_local_dump_entry(msg, portid, cb, bat_priv,
1229*4882a593Smuzhiyun common)) {
1230*4882a593Smuzhiyun spin_unlock_bh(&hash->list_locks[bucket]);
1231*4882a593Smuzhiyun *idx_s = idx - 1;
1232*4882a593Smuzhiyun return -EMSGSIZE;
1233*4882a593Smuzhiyun }
1234*4882a593Smuzhiyun }
1235*4882a593Smuzhiyun spin_unlock_bh(&hash->list_locks[bucket]);
1236*4882a593Smuzhiyun
1237*4882a593Smuzhiyun *idx_s = 0;
1238*4882a593Smuzhiyun return 0;
1239*4882a593Smuzhiyun }
1240*4882a593Smuzhiyun
1241*4882a593Smuzhiyun /**
1242*4882a593Smuzhiyun * batadv_tt_local_dump() - Dump TT local entries into a message
1243*4882a593Smuzhiyun * @msg: Netlink message to dump into
1244*4882a593Smuzhiyun * @cb: Parameters from query
1245*4882a593Smuzhiyun *
1246*4882a593Smuzhiyun * Return: Error code, or 0 on success
1247*4882a593Smuzhiyun */
batadv_tt_local_dump(struct sk_buff * msg,struct netlink_callback * cb)1248*4882a593Smuzhiyun int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
1249*4882a593Smuzhiyun {
1250*4882a593Smuzhiyun struct net *net = sock_net(cb->skb->sk);
1251*4882a593Smuzhiyun struct net_device *soft_iface;
1252*4882a593Smuzhiyun struct batadv_priv *bat_priv;
1253*4882a593Smuzhiyun struct batadv_hard_iface *primary_if = NULL;
1254*4882a593Smuzhiyun struct batadv_hashtable *hash;
1255*4882a593Smuzhiyun int ret;
1256*4882a593Smuzhiyun int ifindex;
1257*4882a593Smuzhiyun int bucket = cb->args[0];
1258*4882a593Smuzhiyun int idx = cb->args[1];
1259*4882a593Smuzhiyun int portid = NETLINK_CB(cb->skb).portid;
1260*4882a593Smuzhiyun
1261*4882a593Smuzhiyun ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
1262*4882a593Smuzhiyun if (!ifindex)
1263*4882a593Smuzhiyun return -EINVAL;
1264*4882a593Smuzhiyun
1265*4882a593Smuzhiyun soft_iface = dev_get_by_index(net, ifindex);
1266*4882a593Smuzhiyun if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
1267*4882a593Smuzhiyun ret = -ENODEV;
1268*4882a593Smuzhiyun goto out;
1269*4882a593Smuzhiyun }
1270*4882a593Smuzhiyun
1271*4882a593Smuzhiyun bat_priv = netdev_priv(soft_iface);
1272*4882a593Smuzhiyun
1273*4882a593Smuzhiyun primary_if = batadv_primary_if_get_selected(bat_priv);
1274*4882a593Smuzhiyun if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
1275*4882a593Smuzhiyun ret = -ENOENT;
1276*4882a593Smuzhiyun goto out;
1277*4882a593Smuzhiyun }
1278*4882a593Smuzhiyun
1279*4882a593Smuzhiyun hash = bat_priv->tt.local_hash;
1280*4882a593Smuzhiyun
1281*4882a593Smuzhiyun while (bucket < hash->size) {
1282*4882a593Smuzhiyun if (batadv_tt_local_dump_bucket(msg, portid, cb, bat_priv,
1283*4882a593Smuzhiyun hash, bucket, &idx))
1284*4882a593Smuzhiyun break;
1285*4882a593Smuzhiyun
1286*4882a593Smuzhiyun bucket++;
1287*4882a593Smuzhiyun }
1288*4882a593Smuzhiyun
1289*4882a593Smuzhiyun ret = msg->len;
1290*4882a593Smuzhiyun
1291*4882a593Smuzhiyun out:
1292*4882a593Smuzhiyun if (primary_if)
1293*4882a593Smuzhiyun batadv_hardif_put(primary_if);
1294*4882a593Smuzhiyun if (soft_iface)
1295*4882a593Smuzhiyun dev_put(soft_iface);
1296*4882a593Smuzhiyun
1297*4882a593Smuzhiyun cb->args[0] = bucket;
1298*4882a593Smuzhiyun cb->args[1] = idx;
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun return ret;
1301*4882a593Smuzhiyun }
1302*4882a593Smuzhiyun
1303*4882a593Smuzhiyun static void
batadv_tt_local_set_pending(struct batadv_priv * bat_priv,struct batadv_tt_local_entry * tt_local_entry,u16 flags,const char * message)1304*4882a593Smuzhiyun batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
1305*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry,
1306*4882a593Smuzhiyun u16 flags, const char *message)
1307*4882a593Smuzhiyun {
1308*4882a593Smuzhiyun batadv_tt_local_event(bat_priv, tt_local_entry, flags);
1309*4882a593Smuzhiyun
1310*4882a593Smuzhiyun /* The local client has to be marked as "pending to be removed" but has
1311*4882a593Smuzhiyun * to be kept in the table in order to send it in a full table
1312*4882a593Smuzhiyun * response issued before the net ttvn increment (consistency check)
1313*4882a593Smuzhiyun */
1314*4882a593Smuzhiyun tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
1315*4882a593Smuzhiyun
1316*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
1317*4882a593Smuzhiyun "Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
1318*4882a593Smuzhiyun tt_local_entry->common.addr,
1319*4882a593Smuzhiyun batadv_print_vid(tt_local_entry->common.vid), message);
1320*4882a593Smuzhiyun }
1321*4882a593Smuzhiyun
1322*4882a593Smuzhiyun /**
1323*4882a593Smuzhiyun * batadv_tt_local_remove() - logically remove an entry from the local table
1324*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
1325*4882a593Smuzhiyun * @addr: the MAC address of the client to remove
1326*4882a593Smuzhiyun * @vid: VLAN identifier
1327*4882a593Smuzhiyun * @message: message to append to the log on deletion
1328*4882a593Smuzhiyun * @roaming: true if the deletion is due to a roaming event
1329*4882a593Smuzhiyun *
1330*4882a593Smuzhiyun * Return: the flags assigned to the local entry before being deleted
1331*4882a593Smuzhiyun */
batadv_tt_local_remove(struct batadv_priv * bat_priv,const u8 * addr,unsigned short vid,const char * message,bool roaming)1332*4882a593Smuzhiyun u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
1333*4882a593Smuzhiyun unsigned short vid, const char *message,
1334*4882a593Smuzhiyun bool roaming)
1335*4882a593Smuzhiyun {
1336*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_removed_entry;
1337*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
1338*4882a593Smuzhiyun u16 flags, curr_flags = BATADV_NO_FLAGS;
1339*4882a593Smuzhiyun struct hlist_node *tt_removed_node;
1340*4882a593Smuzhiyun
1341*4882a593Smuzhiyun tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
1342*4882a593Smuzhiyun if (!tt_local_entry)
1343*4882a593Smuzhiyun goto out;
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun curr_flags = tt_local_entry->common.flags;
1346*4882a593Smuzhiyun
1347*4882a593Smuzhiyun flags = BATADV_TT_CLIENT_DEL;
1348*4882a593Smuzhiyun /* if this global entry addition is due to a roaming, the node has to
1349*4882a593Smuzhiyun * mark the local entry as "roamed" in order to correctly reroute
1350*4882a593Smuzhiyun * packets later
1351*4882a593Smuzhiyun */
1352*4882a593Smuzhiyun if (roaming) {
1353*4882a593Smuzhiyun flags |= BATADV_TT_CLIENT_ROAM;
1354*4882a593Smuzhiyun /* mark the local client as ROAMed */
1355*4882a593Smuzhiyun tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
1356*4882a593Smuzhiyun }
1357*4882a593Smuzhiyun
1358*4882a593Smuzhiyun if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) {
1359*4882a593Smuzhiyun batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
1360*4882a593Smuzhiyun message);
1361*4882a593Smuzhiyun goto out;
1362*4882a593Smuzhiyun }
1363*4882a593Smuzhiyun /* if this client has been added right now, it is possible to
1364*4882a593Smuzhiyun * immediately purge it
1365*4882a593Smuzhiyun */
1366*4882a593Smuzhiyun batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
1367*4882a593Smuzhiyun
1368*4882a593Smuzhiyun tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash,
1369*4882a593Smuzhiyun batadv_compare_tt,
1370*4882a593Smuzhiyun batadv_choose_tt,
1371*4882a593Smuzhiyun &tt_local_entry->common);
1372*4882a593Smuzhiyun if (!tt_removed_node)
1373*4882a593Smuzhiyun goto out;
1374*4882a593Smuzhiyun
1375*4882a593Smuzhiyun /* drop reference of remove hash entry */
1376*4882a593Smuzhiyun tt_removed_entry = hlist_entry(tt_removed_node,
1377*4882a593Smuzhiyun struct batadv_tt_local_entry,
1378*4882a593Smuzhiyun common.hash_entry);
1379*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_removed_entry);
1380*4882a593Smuzhiyun
1381*4882a593Smuzhiyun out:
1382*4882a593Smuzhiyun if (tt_local_entry)
1383*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local_entry);
1384*4882a593Smuzhiyun
1385*4882a593Smuzhiyun return curr_flags;
1386*4882a593Smuzhiyun }
1387*4882a593Smuzhiyun
1388*4882a593Smuzhiyun /**
1389*4882a593Smuzhiyun * batadv_tt_local_purge_list() - purge inactive tt local entries
1390*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
1391*4882a593Smuzhiyun * @head: pointer to the list containing the local tt entries
1392*4882a593Smuzhiyun * @timeout: parameter deciding whether a given tt local entry is considered
1393*4882a593Smuzhiyun * inactive or not
1394*4882a593Smuzhiyun */
batadv_tt_local_purge_list(struct batadv_priv * bat_priv,struct hlist_head * head,int timeout)1395*4882a593Smuzhiyun static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
1396*4882a593Smuzhiyun struct hlist_head *head,
1397*4882a593Smuzhiyun int timeout)
1398*4882a593Smuzhiyun {
1399*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
1400*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
1401*4882a593Smuzhiyun struct hlist_node *node_tmp;
1402*4882a593Smuzhiyun
1403*4882a593Smuzhiyun hlist_for_each_entry_safe(tt_common_entry, node_tmp, head,
1404*4882a593Smuzhiyun hash_entry) {
1405*4882a593Smuzhiyun tt_local_entry = container_of(tt_common_entry,
1406*4882a593Smuzhiyun struct batadv_tt_local_entry,
1407*4882a593Smuzhiyun common);
1408*4882a593Smuzhiyun if (tt_local_entry->common.flags & BATADV_TT_CLIENT_NOPURGE)
1409*4882a593Smuzhiyun continue;
1410*4882a593Smuzhiyun
1411*4882a593Smuzhiyun /* entry already marked for deletion */
1412*4882a593Smuzhiyun if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
1413*4882a593Smuzhiyun continue;
1414*4882a593Smuzhiyun
1415*4882a593Smuzhiyun if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
1416*4882a593Smuzhiyun continue;
1417*4882a593Smuzhiyun
1418*4882a593Smuzhiyun batadv_tt_local_set_pending(bat_priv, tt_local_entry,
1419*4882a593Smuzhiyun BATADV_TT_CLIENT_DEL, "timed out");
1420*4882a593Smuzhiyun }
1421*4882a593Smuzhiyun }
1422*4882a593Smuzhiyun
1423*4882a593Smuzhiyun /**
1424*4882a593Smuzhiyun * batadv_tt_local_purge() - purge inactive tt local entries
1425*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
1426*4882a593Smuzhiyun * @timeout: parameter deciding whether a given tt local entry is considered
1427*4882a593Smuzhiyun * inactive or not
1428*4882a593Smuzhiyun */
batadv_tt_local_purge(struct batadv_priv * bat_priv,int timeout)1429*4882a593Smuzhiyun static void batadv_tt_local_purge(struct batadv_priv *bat_priv,
1430*4882a593Smuzhiyun int timeout)
1431*4882a593Smuzhiyun {
1432*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.local_hash;
1433*4882a593Smuzhiyun struct hlist_head *head;
1434*4882a593Smuzhiyun spinlock_t *list_lock; /* protects write access to the hash lists */
1435*4882a593Smuzhiyun u32 i;
1436*4882a593Smuzhiyun
1437*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
1438*4882a593Smuzhiyun head = &hash->table[i];
1439*4882a593Smuzhiyun list_lock = &hash->list_locks[i];
1440*4882a593Smuzhiyun
1441*4882a593Smuzhiyun spin_lock_bh(list_lock);
1442*4882a593Smuzhiyun batadv_tt_local_purge_list(bat_priv, head, timeout);
1443*4882a593Smuzhiyun spin_unlock_bh(list_lock);
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun }
1446*4882a593Smuzhiyun
batadv_tt_local_table_free(struct batadv_priv * bat_priv)1447*4882a593Smuzhiyun static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
1448*4882a593Smuzhiyun {
1449*4882a593Smuzhiyun struct batadv_hashtable *hash;
1450*4882a593Smuzhiyun spinlock_t *list_lock; /* protects write access to the hash lists */
1451*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
1452*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local;
1453*4882a593Smuzhiyun struct hlist_node *node_tmp;
1454*4882a593Smuzhiyun struct hlist_head *head;
1455*4882a593Smuzhiyun u32 i;
1456*4882a593Smuzhiyun
1457*4882a593Smuzhiyun if (!bat_priv->tt.local_hash)
1458*4882a593Smuzhiyun return;
1459*4882a593Smuzhiyun
1460*4882a593Smuzhiyun hash = bat_priv->tt.local_hash;
1461*4882a593Smuzhiyun
1462*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
1463*4882a593Smuzhiyun head = &hash->table[i];
1464*4882a593Smuzhiyun list_lock = &hash->list_locks[i];
1465*4882a593Smuzhiyun
1466*4882a593Smuzhiyun spin_lock_bh(list_lock);
1467*4882a593Smuzhiyun hlist_for_each_entry_safe(tt_common_entry, node_tmp,
1468*4882a593Smuzhiyun head, hash_entry) {
1469*4882a593Smuzhiyun hlist_del_rcu(&tt_common_entry->hash_entry);
1470*4882a593Smuzhiyun tt_local = container_of(tt_common_entry,
1471*4882a593Smuzhiyun struct batadv_tt_local_entry,
1472*4882a593Smuzhiyun common);
1473*4882a593Smuzhiyun
1474*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local);
1475*4882a593Smuzhiyun }
1476*4882a593Smuzhiyun spin_unlock_bh(list_lock);
1477*4882a593Smuzhiyun }
1478*4882a593Smuzhiyun
1479*4882a593Smuzhiyun batadv_hash_destroy(hash);
1480*4882a593Smuzhiyun
1481*4882a593Smuzhiyun bat_priv->tt.local_hash = NULL;
1482*4882a593Smuzhiyun }
1483*4882a593Smuzhiyun
batadv_tt_global_init(struct batadv_priv * bat_priv)1484*4882a593Smuzhiyun static int batadv_tt_global_init(struct batadv_priv *bat_priv)
1485*4882a593Smuzhiyun {
1486*4882a593Smuzhiyun if (bat_priv->tt.global_hash)
1487*4882a593Smuzhiyun return 0;
1488*4882a593Smuzhiyun
1489*4882a593Smuzhiyun bat_priv->tt.global_hash = batadv_hash_new(1024);
1490*4882a593Smuzhiyun
1491*4882a593Smuzhiyun if (!bat_priv->tt.global_hash)
1492*4882a593Smuzhiyun return -ENOMEM;
1493*4882a593Smuzhiyun
1494*4882a593Smuzhiyun batadv_hash_set_lock_class(bat_priv->tt.global_hash,
1495*4882a593Smuzhiyun &batadv_tt_global_hash_lock_class_key);
1496*4882a593Smuzhiyun
1497*4882a593Smuzhiyun return 0;
1498*4882a593Smuzhiyun }
1499*4882a593Smuzhiyun
batadv_tt_changes_list_free(struct batadv_priv * bat_priv)1500*4882a593Smuzhiyun static void batadv_tt_changes_list_free(struct batadv_priv *bat_priv)
1501*4882a593Smuzhiyun {
1502*4882a593Smuzhiyun struct batadv_tt_change_node *entry, *safe;
1503*4882a593Smuzhiyun
1504*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.changes_list_lock);
1505*4882a593Smuzhiyun
1506*4882a593Smuzhiyun list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
1507*4882a593Smuzhiyun list) {
1508*4882a593Smuzhiyun list_del(&entry->list);
1509*4882a593Smuzhiyun kmem_cache_free(batadv_tt_change_cache, entry);
1510*4882a593Smuzhiyun }
1511*4882a593Smuzhiyun
1512*4882a593Smuzhiyun atomic_set(&bat_priv->tt.local_changes, 0);
1513*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.changes_list_lock);
1514*4882a593Smuzhiyun }
1515*4882a593Smuzhiyun
1516*4882a593Smuzhiyun /**
1517*4882a593Smuzhiyun * batadv_tt_global_orig_entry_find() - find a TT orig_list_entry
1518*4882a593Smuzhiyun * @entry: the TT global entry where the orig_list_entry has to be
1519*4882a593Smuzhiyun * extracted from
1520*4882a593Smuzhiyun * @orig_node: the originator for which the orig_list_entry has to be found
1521*4882a593Smuzhiyun *
1522*4882a593Smuzhiyun * retrieve the orig_tt_list_entry belonging to orig_node from the
1523*4882a593Smuzhiyun * batadv_tt_global_entry list
1524*4882a593Smuzhiyun *
1525*4882a593Smuzhiyun * Return: it with an increased refcounter, NULL if not found
1526*4882a593Smuzhiyun */
1527*4882a593Smuzhiyun static struct batadv_tt_orig_list_entry *
batadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry * entry,const struct batadv_orig_node * orig_node)1528*4882a593Smuzhiyun batadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry *entry,
1529*4882a593Smuzhiyun const struct batadv_orig_node *orig_node)
1530*4882a593Smuzhiyun {
1531*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *tmp_orig_entry, *orig_entry = NULL;
1532*4882a593Smuzhiyun const struct hlist_head *head;
1533*4882a593Smuzhiyun
1534*4882a593Smuzhiyun rcu_read_lock();
1535*4882a593Smuzhiyun head = &entry->orig_list;
1536*4882a593Smuzhiyun hlist_for_each_entry_rcu(tmp_orig_entry, head, list) {
1537*4882a593Smuzhiyun if (tmp_orig_entry->orig_node != orig_node)
1538*4882a593Smuzhiyun continue;
1539*4882a593Smuzhiyun if (!kref_get_unless_zero(&tmp_orig_entry->refcount))
1540*4882a593Smuzhiyun continue;
1541*4882a593Smuzhiyun
1542*4882a593Smuzhiyun orig_entry = tmp_orig_entry;
1543*4882a593Smuzhiyun break;
1544*4882a593Smuzhiyun }
1545*4882a593Smuzhiyun rcu_read_unlock();
1546*4882a593Smuzhiyun
1547*4882a593Smuzhiyun return orig_entry;
1548*4882a593Smuzhiyun }
1549*4882a593Smuzhiyun
1550*4882a593Smuzhiyun /**
1551*4882a593Smuzhiyun * batadv_tt_global_entry_has_orig() - check if a TT global entry is also
1552*4882a593Smuzhiyun * handled by a given originator
1553*4882a593Smuzhiyun * @entry: the TT global entry to check
1554*4882a593Smuzhiyun * @orig_node: the originator to search in the list
1555*4882a593Smuzhiyun * @flags: a pointer to store TT flags for the given @entry received
1556*4882a593Smuzhiyun * from @orig_node
1557*4882a593Smuzhiyun *
1558*4882a593Smuzhiyun * find out if an orig_node is already in the list of a tt_global_entry.
1559*4882a593Smuzhiyun *
1560*4882a593Smuzhiyun * Return: true if found, false otherwise
1561*4882a593Smuzhiyun */
1562*4882a593Smuzhiyun static bool
batadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry * entry,const struct batadv_orig_node * orig_node,u8 * flags)1563*4882a593Smuzhiyun batadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry *entry,
1564*4882a593Smuzhiyun const struct batadv_orig_node *orig_node,
1565*4882a593Smuzhiyun u8 *flags)
1566*4882a593Smuzhiyun {
1567*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
1568*4882a593Smuzhiyun bool found = false;
1569*4882a593Smuzhiyun
1570*4882a593Smuzhiyun orig_entry = batadv_tt_global_orig_entry_find(entry, orig_node);
1571*4882a593Smuzhiyun if (orig_entry) {
1572*4882a593Smuzhiyun found = true;
1573*4882a593Smuzhiyun
1574*4882a593Smuzhiyun if (flags)
1575*4882a593Smuzhiyun *flags = orig_entry->flags;
1576*4882a593Smuzhiyun
1577*4882a593Smuzhiyun batadv_tt_orig_list_entry_put(orig_entry);
1578*4882a593Smuzhiyun }
1579*4882a593Smuzhiyun
1580*4882a593Smuzhiyun return found;
1581*4882a593Smuzhiyun }
1582*4882a593Smuzhiyun
1583*4882a593Smuzhiyun /**
1584*4882a593Smuzhiyun * batadv_tt_global_sync_flags() - update TT sync flags
1585*4882a593Smuzhiyun * @tt_global: the TT global entry to update sync flags in
1586*4882a593Smuzhiyun *
1587*4882a593Smuzhiyun * Updates the sync flag bits in the tt_global flag attribute with a logical
1588*4882a593Smuzhiyun * OR of all sync flags from any of its TT orig entries.
1589*4882a593Smuzhiyun */
1590*4882a593Smuzhiyun static void
batadv_tt_global_sync_flags(struct batadv_tt_global_entry * tt_global)1591*4882a593Smuzhiyun batadv_tt_global_sync_flags(struct batadv_tt_global_entry *tt_global)
1592*4882a593Smuzhiyun {
1593*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
1594*4882a593Smuzhiyun const struct hlist_head *head;
1595*4882a593Smuzhiyun u16 flags = BATADV_NO_FLAGS;
1596*4882a593Smuzhiyun
1597*4882a593Smuzhiyun rcu_read_lock();
1598*4882a593Smuzhiyun head = &tt_global->orig_list;
1599*4882a593Smuzhiyun hlist_for_each_entry_rcu(orig_entry, head, list)
1600*4882a593Smuzhiyun flags |= orig_entry->flags;
1601*4882a593Smuzhiyun rcu_read_unlock();
1602*4882a593Smuzhiyun
1603*4882a593Smuzhiyun flags |= tt_global->common.flags & (~BATADV_TT_SYNC_MASK);
1604*4882a593Smuzhiyun tt_global->common.flags = flags;
1605*4882a593Smuzhiyun }
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun /**
1608*4882a593Smuzhiyun * batadv_tt_global_orig_entry_add() - add or update a TT orig entry
1609*4882a593Smuzhiyun * @tt_global: the TT global entry to add an orig entry in
1610*4882a593Smuzhiyun * @orig_node: the originator to add an orig entry for
1611*4882a593Smuzhiyun * @ttvn: translation table version number of this changeset
1612*4882a593Smuzhiyun * @flags: TT sync flags
1613*4882a593Smuzhiyun */
1614*4882a593Smuzhiyun static void
batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry * tt_global,struct batadv_orig_node * orig_node,int ttvn,u8 flags)1615*4882a593Smuzhiyun batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
1616*4882a593Smuzhiyun struct batadv_orig_node *orig_node, int ttvn,
1617*4882a593Smuzhiyun u8 flags)
1618*4882a593Smuzhiyun {
1619*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
1620*4882a593Smuzhiyun
1621*4882a593Smuzhiyun spin_lock_bh(&tt_global->list_lock);
1622*4882a593Smuzhiyun
1623*4882a593Smuzhiyun orig_entry = batadv_tt_global_orig_entry_find(tt_global, orig_node);
1624*4882a593Smuzhiyun if (orig_entry) {
1625*4882a593Smuzhiyun /* refresh the ttvn: the current value could be a bogus one that
1626*4882a593Smuzhiyun * was added during a "temporary client detection"
1627*4882a593Smuzhiyun */
1628*4882a593Smuzhiyun orig_entry->ttvn = ttvn;
1629*4882a593Smuzhiyun orig_entry->flags = flags;
1630*4882a593Smuzhiyun goto sync_flags;
1631*4882a593Smuzhiyun }
1632*4882a593Smuzhiyun
1633*4882a593Smuzhiyun orig_entry = kmem_cache_zalloc(batadv_tt_orig_cache, GFP_ATOMIC);
1634*4882a593Smuzhiyun if (!orig_entry)
1635*4882a593Smuzhiyun goto out;
1636*4882a593Smuzhiyun
1637*4882a593Smuzhiyun INIT_HLIST_NODE(&orig_entry->list);
1638*4882a593Smuzhiyun kref_get(&orig_node->refcount);
1639*4882a593Smuzhiyun batadv_tt_global_size_inc(orig_node, tt_global->common.vid);
1640*4882a593Smuzhiyun orig_entry->orig_node = orig_node;
1641*4882a593Smuzhiyun orig_entry->ttvn = ttvn;
1642*4882a593Smuzhiyun orig_entry->flags = flags;
1643*4882a593Smuzhiyun kref_init(&orig_entry->refcount);
1644*4882a593Smuzhiyun
1645*4882a593Smuzhiyun kref_get(&orig_entry->refcount);
1646*4882a593Smuzhiyun hlist_add_head_rcu(&orig_entry->list,
1647*4882a593Smuzhiyun &tt_global->orig_list);
1648*4882a593Smuzhiyun atomic_inc(&tt_global->orig_list_count);
1649*4882a593Smuzhiyun
1650*4882a593Smuzhiyun sync_flags:
1651*4882a593Smuzhiyun batadv_tt_global_sync_flags(tt_global);
1652*4882a593Smuzhiyun out:
1653*4882a593Smuzhiyun if (orig_entry)
1654*4882a593Smuzhiyun batadv_tt_orig_list_entry_put(orig_entry);
1655*4882a593Smuzhiyun
1656*4882a593Smuzhiyun spin_unlock_bh(&tt_global->list_lock);
1657*4882a593Smuzhiyun }
1658*4882a593Smuzhiyun
1659*4882a593Smuzhiyun /**
1660*4882a593Smuzhiyun * batadv_tt_global_add() - add a new TT global entry or update an existing one
1661*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
1662*4882a593Smuzhiyun * @orig_node: the originator announcing the client
1663*4882a593Smuzhiyun * @tt_addr: the mac address of the non-mesh client
1664*4882a593Smuzhiyun * @vid: VLAN identifier
1665*4882a593Smuzhiyun * @flags: TT flags that have to be set for this non-mesh client
1666*4882a593Smuzhiyun * @ttvn: the tt version number ever announcing this non-mesh client
1667*4882a593Smuzhiyun *
1668*4882a593Smuzhiyun * Add a new TT global entry for the given originator. If the entry already
1669*4882a593Smuzhiyun * exists add a new reference to the given originator (a global entry can have
1670*4882a593Smuzhiyun * references to multiple originators) and adjust the flags attribute to reflect
1671*4882a593Smuzhiyun * the function argument.
1672*4882a593Smuzhiyun * If a TT local entry exists for this non-mesh client remove it.
1673*4882a593Smuzhiyun *
1674*4882a593Smuzhiyun * The caller must hold the orig_node refcount.
1675*4882a593Smuzhiyun *
1676*4882a593Smuzhiyun * Return: true if the new entry has been added, false otherwise
1677*4882a593Smuzhiyun */
batadv_tt_global_add(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,const unsigned char * tt_addr,unsigned short vid,u16 flags,u8 ttvn)1678*4882a593Smuzhiyun static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
1679*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
1680*4882a593Smuzhiyun const unsigned char *tt_addr,
1681*4882a593Smuzhiyun unsigned short vid, u16 flags, u8 ttvn)
1682*4882a593Smuzhiyun {
1683*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
1684*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
1685*4882a593Smuzhiyun bool ret = false;
1686*4882a593Smuzhiyun int hash_added;
1687*4882a593Smuzhiyun struct batadv_tt_common_entry *common;
1688*4882a593Smuzhiyun u16 local_flags;
1689*4882a593Smuzhiyun
1690*4882a593Smuzhiyun /* ignore global entries from backbone nodes */
1691*4882a593Smuzhiyun if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid))
1692*4882a593Smuzhiyun return true;
1693*4882a593Smuzhiyun
1694*4882a593Smuzhiyun tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr, vid);
1695*4882a593Smuzhiyun tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr, vid);
1696*4882a593Smuzhiyun
1697*4882a593Smuzhiyun /* if the node already has a local client for this entry, it has to wait
1698*4882a593Smuzhiyun * for a roaming advertisement instead of manually messing up the global
1699*4882a593Smuzhiyun * table
1700*4882a593Smuzhiyun */
1701*4882a593Smuzhiyun if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry &&
1702*4882a593Smuzhiyun !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW))
1703*4882a593Smuzhiyun goto out;
1704*4882a593Smuzhiyun
1705*4882a593Smuzhiyun if (!tt_global_entry) {
1706*4882a593Smuzhiyun tt_global_entry = kmem_cache_zalloc(batadv_tg_cache,
1707*4882a593Smuzhiyun GFP_ATOMIC);
1708*4882a593Smuzhiyun if (!tt_global_entry)
1709*4882a593Smuzhiyun goto out;
1710*4882a593Smuzhiyun
1711*4882a593Smuzhiyun common = &tt_global_entry->common;
1712*4882a593Smuzhiyun ether_addr_copy(common->addr, tt_addr);
1713*4882a593Smuzhiyun common->vid = vid;
1714*4882a593Smuzhiyun
1715*4882a593Smuzhiyun if (!is_multicast_ether_addr(common->addr))
1716*4882a593Smuzhiyun common->flags = flags & (~BATADV_TT_SYNC_MASK);
1717*4882a593Smuzhiyun
1718*4882a593Smuzhiyun tt_global_entry->roam_at = 0;
1719*4882a593Smuzhiyun /* node must store current time in case of roaming. This is
1720*4882a593Smuzhiyun * needed to purge this entry out on timeout (if nobody claims
1721*4882a593Smuzhiyun * it)
1722*4882a593Smuzhiyun */
1723*4882a593Smuzhiyun if (flags & BATADV_TT_CLIENT_ROAM)
1724*4882a593Smuzhiyun tt_global_entry->roam_at = jiffies;
1725*4882a593Smuzhiyun kref_init(&common->refcount);
1726*4882a593Smuzhiyun common->added_at = jiffies;
1727*4882a593Smuzhiyun
1728*4882a593Smuzhiyun INIT_HLIST_HEAD(&tt_global_entry->orig_list);
1729*4882a593Smuzhiyun atomic_set(&tt_global_entry->orig_list_count, 0);
1730*4882a593Smuzhiyun spin_lock_init(&tt_global_entry->list_lock);
1731*4882a593Smuzhiyun
1732*4882a593Smuzhiyun kref_get(&common->refcount);
1733*4882a593Smuzhiyun hash_added = batadv_hash_add(bat_priv->tt.global_hash,
1734*4882a593Smuzhiyun batadv_compare_tt,
1735*4882a593Smuzhiyun batadv_choose_tt, common,
1736*4882a593Smuzhiyun &common->hash_entry);
1737*4882a593Smuzhiyun
1738*4882a593Smuzhiyun if (unlikely(hash_added != 0)) {
1739*4882a593Smuzhiyun /* remove the reference for the hash */
1740*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
1741*4882a593Smuzhiyun goto out_remove;
1742*4882a593Smuzhiyun }
1743*4882a593Smuzhiyun } else {
1744*4882a593Smuzhiyun common = &tt_global_entry->common;
1745*4882a593Smuzhiyun /* If there is already a global entry, we can use this one for
1746*4882a593Smuzhiyun * our processing.
1747*4882a593Smuzhiyun * But if we are trying to add a temporary client then here are
1748*4882a593Smuzhiyun * two options at this point:
1749*4882a593Smuzhiyun * 1) the global client is not a temporary client: the global
1750*4882a593Smuzhiyun * client has to be left as it is, temporary information
1751*4882a593Smuzhiyun * should never override any already known client state
1752*4882a593Smuzhiyun * 2) the global client is a temporary client: purge the
1753*4882a593Smuzhiyun * originator list and add the new one orig_entry
1754*4882a593Smuzhiyun */
1755*4882a593Smuzhiyun if (flags & BATADV_TT_CLIENT_TEMP) {
1756*4882a593Smuzhiyun if (!(common->flags & BATADV_TT_CLIENT_TEMP))
1757*4882a593Smuzhiyun goto out;
1758*4882a593Smuzhiyun if (batadv_tt_global_entry_has_orig(tt_global_entry,
1759*4882a593Smuzhiyun orig_node, NULL))
1760*4882a593Smuzhiyun goto out_remove;
1761*4882a593Smuzhiyun batadv_tt_global_del_orig_list(tt_global_entry);
1762*4882a593Smuzhiyun goto add_orig_entry;
1763*4882a593Smuzhiyun }
1764*4882a593Smuzhiyun
1765*4882a593Smuzhiyun /* if the client was temporary added before receiving the first
1766*4882a593Smuzhiyun * OGM announcing it, we have to clear the TEMP flag. Also,
1767*4882a593Smuzhiyun * remove the previous temporary orig node and re-add it
1768*4882a593Smuzhiyun * if required. If the orig entry changed, the new one which
1769*4882a593Smuzhiyun * is a non-temporary entry is preferred.
1770*4882a593Smuzhiyun */
1771*4882a593Smuzhiyun if (common->flags & BATADV_TT_CLIENT_TEMP) {
1772*4882a593Smuzhiyun batadv_tt_global_del_orig_list(tt_global_entry);
1773*4882a593Smuzhiyun common->flags &= ~BATADV_TT_CLIENT_TEMP;
1774*4882a593Smuzhiyun }
1775*4882a593Smuzhiyun
1776*4882a593Smuzhiyun /* the change can carry possible "attribute" flags like the
1777*4882a593Smuzhiyun * TT_CLIENT_TEMP, therefore they have to be copied in the
1778*4882a593Smuzhiyun * client entry
1779*4882a593Smuzhiyun */
1780*4882a593Smuzhiyun if (!is_multicast_ether_addr(common->addr))
1781*4882a593Smuzhiyun common->flags |= flags & (~BATADV_TT_SYNC_MASK);
1782*4882a593Smuzhiyun
1783*4882a593Smuzhiyun /* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
1784*4882a593Smuzhiyun * one originator left in the list and we previously received a
1785*4882a593Smuzhiyun * delete + roaming change for this originator.
1786*4882a593Smuzhiyun *
1787*4882a593Smuzhiyun * We should first delete the old originator before adding the
1788*4882a593Smuzhiyun * new one.
1789*4882a593Smuzhiyun */
1790*4882a593Smuzhiyun if (common->flags & BATADV_TT_CLIENT_ROAM) {
1791*4882a593Smuzhiyun batadv_tt_global_del_orig_list(tt_global_entry);
1792*4882a593Smuzhiyun common->flags &= ~BATADV_TT_CLIENT_ROAM;
1793*4882a593Smuzhiyun tt_global_entry->roam_at = 0;
1794*4882a593Smuzhiyun }
1795*4882a593Smuzhiyun }
1796*4882a593Smuzhiyun add_orig_entry:
1797*4882a593Smuzhiyun /* add the new orig_entry (if needed) or update it */
1798*4882a593Smuzhiyun batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn,
1799*4882a593Smuzhiyun flags & BATADV_TT_SYNC_MASK);
1800*4882a593Smuzhiyun
1801*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
1802*4882a593Smuzhiyun "Creating new global tt entry: %pM (vid: %d, via %pM)\n",
1803*4882a593Smuzhiyun common->addr, batadv_print_vid(common->vid),
1804*4882a593Smuzhiyun orig_node->orig);
1805*4882a593Smuzhiyun ret = true;
1806*4882a593Smuzhiyun
1807*4882a593Smuzhiyun out_remove:
1808*4882a593Smuzhiyun /* Do not remove multicast addresses from the local hash on
1809*4882a593Smuzhiyun * global additions
1810*4882a593Smuzhiyun */
1811*4882a593Smuzhiyun if (is_multicast_ether_addr(tt_addr))
1812*4882a593Smuzhiyun goto out;
1813*4882a593Smuzhiyun
1814*4882a593Smuzhiyun /* remove address from local hash if present */
1815*4882a593Smuzhiyun local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid,
1816*4882a593Smuzhiyun "global tt received",
1817*4882a593Smuzhiyun flags & BATADV_TT_CLIENT_ROAM);
1818*4882a593Smuzhiyun tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
1819*4882a593Smuzhiyun
1820*4882a593Smuzhiyun if (!(flags & BATADV_TT_CLIENT_ROAM))
1821*4882a593Smuzhiyun /* this is a normal global add. Therefore the client is not in a
1822*4882a593Smuzhiyun * roaming state anymore.
1823*4882a593Smuzhiyun */
1824*4882a593Smuzhiyun tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
1825*4882a593Smuzhiyun
1826*4882a593Smuzhiyun out:
1827*4882a593Smuzhiyun if (tt_global_entry)
1828*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
1829*4882a593Smuzhiyun if (tt_local_entry)
1830*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local_entry);
1831*4882a593Smuzhiyun return ret;
1832*4882a593Smuzhiyun }
1833*4882a593Smuzhiyun
1834*4882a593Smuzhiyun /**
1835*4882a593Smuzhiyun * batadv_transtable_best_orig() - Get best originator list entry from tt entry
1836*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
1837*4882a593Smuzhiyun * @tt_global_entry: global translation table entry to be analyzed
1838*4882a593Smuzhiyun *
1839*4882a593Smuzhiyun * This function assumes the caller holds rcu_read_lock().
1840*4882a593Smuzhiyun * Return: best originator list entry or NULL on errors.
1841*4882a593Smuzhiyun */
1842*4882a593Smuzhiyun static struct batadv_tt_orig_list_entry *
batadv_transtable_best_orig(struct batadv_priv * bat_priv,struct batadv_tt_global_entry * tt_global_entry)1843*4882a593Smuzhiyun batadv_transtable_best_orig(struct batadv_priv *bat_priv,
1844*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry)
1845*4882a593Smuzhiyun {
1846*4882a593Smuzhiyun struct batadv_neigh_node *router, *best_router = NULL;
1847*4882a593Smuzhiyun struct batadv_algo_ops *bao = bat_priv->algo_ops;
1848*4882a593Smuzhiyun struct hlist_head *head;
1849*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL;
1850*4882a593Smuzhiyun
1851*4882a593Smuzhiyun head = &tt_global_entry->orig_list;
1852*4882a593Smuzhiyun hlist_for_each_entry_rcu(orig_entry, head, list) {
1853*4882a593Smuzhiyun router = batadv_orig_router_get(orig_entry->orig_node,
1854*4882a593Smuzhiyun BATADV_IF_DEFAULT);
1855*4882a593Smuzhiyun if (!router)
1856*4882a593Smuzhiyun continue;
1857*4882a593Smuzhiyun
1858*4882a593Smuzhiyun if (best_router &&
1859*4882a593Smuzhiyun bao->neigh.cmp(router, BATADV_IF_DEFAULT, best_router,
1860*4882a593Smuzhiyun BATADV_IF_DEFAULT) <= 0) {
1861*4882a593Smuzhiyun batadv_neigh_node_put(router);
1862*4882a593Smuzhiyun continue;
1863*4882a593Smuzhiyun }
1864*4882a593Smuzhiyun
1865*4882a593Smuzhiyun /* release the refcount for the "old" best */
1866*4882a593Smuzhiyun if (best_router)
1867*4882a593Smuzhiyun batadv_neigh_node_put(best_router);
1868*4882a593Smuzhiyun
1869*4882a593Smuzhiyun best_entry = orig_entry;
1870*4882a593Smuzhiyun best_router = router;
1871*4882a593Smuzhiyun }
1872*4882a593Smuzhiyun
1873*4882a593Smuzhiyun if (best_router)
1874*4882a593Smuzhiyun batadv_neigh_node_put(best_router);
1875*4882a593Smuzhiyun
1876*4882a593Smuzhiyun return best_entry;
1877*4882a593Smuzhiyun }
1878*4882a593Smuzhiyun
1879*4882a593Smuzhiyun #ifdef CONFIG_BATMAN_ADV_DEBUGFS
1880*4882a593Smuzhiyun /**
1881*4882a593Smuzhiyun * batadv_tt_global_print_entry() - print all orig nodes who announce the
1882*4882a593Smuzhiyun * address for this global entry
1883*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
1884*4882a593Smuzhiyun * @tt_global_entry: global translation table entry to be printed
1885*4882a593Smuzhiyun * @seq: debugfs table seq_file struct
1886*4882a593Smuzhiyun *
1887*4882a593Smuzhiyun * This function assumes the caller holds rcu_read_lock().
1888*4882a593Smuzhiyun */
1889*4882a593Smuzhiyun static void
batadv_tt_global_print_entry(struct batadv_priv * bat_priv,struct batadv_tt_global_entry * tt_global_entry,struct seq_file * seq)1890*4882a593Smuzhiyun batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
1891*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry,
1892*4882a593Smuzhiyun struct seq_file *seq)
1893*4882a593Smuzhiyun {
1894*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
1895*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
1896*4882a593Smuzhiyun struct batadv_orig_node_vlan *vlan;
1897*4882a593Smuzhiyun struct hlist_head *head;
1898*4882a593Smuzhiyun u8 last_ttvn;
1899*4882a593Smuzhiyun u16 flags;
1900*4882a593Smuzhiyun
1901*4882a593Smuzhiyun tt_common_entry = &tt_global_entry->common;
1902*4882a593Smuzhiyun flags = tt_common_entry->flags;
1903*4882a593Smuzhiyun
1904*4882a593Smuzhiyun best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
1905*4882a593Smuzhiyun if (best_entry) {
1906*4882a593Smuzhiyun vlan = batadv_orig_node_vlan_get(best_entry->orig_node,
1907*4882a593Smuzhiyun tt_common_entry->vid);
1908*4882a593Smuzhiyun if (!vlan) {
1909*4882a593Smuzhiyun seq_printf(seq,
1910*4882a593Smuzhiyun " * Cannot retrieve VLAN %d for originator %pM\n",
1911*4882a593Smuzhiyun batadv_print_vid(tt_common_entry->vid),
1912*4882a593Smuzhiyun best_entry->orig_node->orig);
1913*4882a593Smuzhiyun goto print_list;
1914*4882a593Smuzhiyun }
1915*4882a593Smuzhiyun
1916*4882a593Smuzhiyun last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn);
1917*4882a593Smuzhiyun seq_printf(seq,
1918*4882a593Smuzhiyun " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n",
1919*4882a593Smuzhiyun '*', tt_global_entry->common.addr,
1920*4882a593Smuzhiyun batadv_print_vid(tt_global_entry->common.vid),
1921*4882a593Smuzhiyun best_entry->ttvn, best_entry->orig_node->orig,
1922*4882a593Smuzhiyun last_ttvn, vlan->tt.crc,
1923*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'),
1924*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_WIFI) ? 'W' : '.'),
1925*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
1926*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_TEMP) ? 'T' : '.'));
1927*4882a593Smuzhiyun
1928*4882a593Smuzhiyun batadv_orig_node_vlan_put(vlan);
1929*4882a593Smuzhiyun }
1930*4882a593Smuzhiyun
1931*4882a593Smuzhiyun print_list:
1932*4882a593Smuzhiyun head = &tt_global_entry->orig_list;
1933*4882a593Smuzhiyun
1934*4882a593Smuzhiyun hlist_for_each_entry_rcu(orig_entry, head, list) {
1935*4882a593Smuzhiyun if (best_entry == orig_entry)
1936*4882a593Smuzhiyun continue;
1937*4882a593Smuzhiyun
1938*4882a593Smuzhiyun vlan = batadv_orig_node_vlan_get(orig_entry->orig_node,
1939*4882a593Smuzhiyun tt_common_entry->vid);
1940*4882a593Smuzhiyun if (!vlan) {
1941*4882a593Smuzhiyun seq_printf(seq,
1942*4882a593Smuzhiyun " + Cannot retrieve VLAN %d for originator %pM\n",
1943*4882a593Smuzhiyun batadv_print_vid(tt_common_entry->vid),
1944*4882a593Smuzhiyun orig_entry->orig_node->orig);
1945*4882a593Smuzhiyun continue;
1946*4882a593Smuzhiyun }
1947*4882a593Smuzhiyun
1948*4882a593Smuzhiyun last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn);
1949*4882a593Smuzhiyun seq_printf(seq,
1950*4882a593Smuzhiyun " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n",
1951*4882a593Smuzhiyun '+', tt_global_entry->common.addr,
1952*4882a593Smuzhiyun batadv_print_vid(tt_global_entry->common.vid),
1953*4882a593Smuzhiyun orig_entry->ttvn, orig_entry->orig_node->orig,
1954*4882a593Smuzhiyun last_ttvn, vlan->tt.crc,
1955*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'),
1956*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_WIFI) ? 'W' : '.'),
1957*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
1958*4882a593Smuzhiyun ((flags & BATADV_TT_CLIENT_TEMP) ? 'T' : '.'));
1959*4882a593Smuzhiyun
1960*4882a593Smuzhiyun batadv_orig_node_vlan_put(vlan);
1961*4882a593Smuzhiyun }
1962*4882a593Smuzhiyun }
1963*4882a593Smuzhiyun
1964*4882a593Smuzhiyun /**
1965*4882a593Smuzhiyun * batadv_tt_global_seq_print_text() - Print the global tt table in a seq file
1966*4882a593Smuzhiyun * @seq: seq file to print on
1967*4882a593Smuzhiyun * @offset: not used
1968*4882a593Smuzhiyun *
1969*4882a593Smuzhiyun * Return: always 0
1970*4882a593Smuzhiyun */
batadv_tt_global_seq_print_text(struct seq_file * seq,void * offset)1971*4882a593Smuzhiyun int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
1972*4882a593Smuzhiyun {
1973*4882a593Smuzhiyun struct net_device *net_dev = (struct net_device *)seq->private;
1974*4882a593Smuzhiyun struct batadv_priv *bat_priv = netdev_priv(net_dev);
1975*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.global_hash;
1976*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
1977*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global;
1978*4882a593Smuzhiyun struct batadv_hard_iface *primary_if;
1979*4882a593Smuzhiyun struct hlist_head *head;
1980*4882a593Smuzhiyun u32 i;
1981*4882a593Smuzhiyun
1982*4882a593Smuzhiyun primary_if = batadv_seq_print_text_primary_if_get(seq);
1983*4882a593Smuzhiyun if (!primary_if)
1984*4882a593Smuzhiyun goto out;
1985*4882a593Smuzhiyun
1986*4882a593Smuzhiyun seq_printf(seq,
1987*4882a593Smuzhiyun "Globally announced TT entries received via the mesh %s\n",
1988*4882a593Smuzhiyun net_dev->name);
1989*4882a593Smuzhiyun seq_puts(seq,
1990*4882a593Smuzhiyun " Client VID (TTVN) Originator (Curr TTVN) (CRC ) Flags\n");
1991*4882a593Smuzhiyun
1992*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
1993*4882a593Smuzhiyun head = &hash->table[i];
1994*4882a593Smuzhiyun
1995*4882a593Smuzhiyun rcu_read_lock();
1996*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt_common_entry,
1997*4882a593Smuzhiyun head, hash_entry) {
1998*4882a593Smuzhiyun tt_global = container_of(tt_common_entry,
1999*4882a593Smuzhiyun struct batadv_tt_global_entry,
2000*4882a593Smuzhiyun common);
2001*4882a593Smuzhiyun batadv_tt_global_print_entry(bat_priv, tt_global, seq);
2002*4882a593Smuzhiyun }
2003*4882a593Smuzhiyun rcu_read_unlock();
2004*4882a593Smuzhiyun }
2005*4882a593Smuzhiyun out:
2006*4882a593Smuzhiyun if (primary_if)
2007*4882a593Smuzhiyun batadv_hardif_put(primary_if);
2008*4882a593Smuzhiyun return 0;
2009*4882a593Smuzhiyun }
2010*4882a593Smuzhiyun #endif
2011*4882a593Smuzhiyun
2012*4882a593Smuzhiyun /**
2013*4882a593Smuzhiyun * batadv_tt_global_dump_subentry() - Dump all TT local entries into a message
2014*4882a593Smuzhiyun * @msg: Netlink message to dump into
2015*4882a593Smuzhiyun * @portid: Port making netlink request
2016*4882a593Smuzhiyun * @seq: Sequence number of netlink message
2017*4882a593Smuzhiyun * @common: tt local & tt global common data
2018*4882a593Smuzhiyun * @orig: Originator node announcing a non-mesh client
2019*4882a593Smuzhiyun * @best: Is the best originator for the TT entry
2020*4882a593Smuzhiyun *
2021*4882a593Smuzhiyun * Return: Error code, or 0 on success
2022*4882a593Smuzhiyun */
2023*4882a593Smuzhiyun static int
batadv_tt_global_dump_subentry(struct sk_buff * msg,u32 portid,u32 seq,struct batadv_tt_common_entry * common,struct batadv_tt_orig_list_entry * orig,bool best)2024*4882a593Smuzhiyun batadv_tt_global_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
2025*4882a593Smuzhiyun struct batadv_tt_common_entry *common,
2026*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig,
2027*4882a593Smuzhiyun bool best)
2028*4882a593Smuzhiyun {
2029*4882a593Smuzhiyun u16 flags = (common->flags & (~BATADV_TT_SYNC_MASK)) | orig->flags;
2030*4882a593Smuzhiyun void *hdr;
2031*4882a593Smuzhiyun struct batadv_orig_node_vlan *vlan;
2032*4882a593Smuzhiyun u8 last_ttvn;
2033*4882a593Smuzhiyun u32 crc;
2034*4882a593Smuzhiyun
2035*4882a593Smuzhiyun vlan = batadv_orig_node_vlan_get(orig->orig_node,
2036*4882a593Smuzhiyun common->vid);
2037*4882a593Smuzhiyun if (!vlan)
2038*4882a593Smuzhiyun return 0;
2039*4882a593Smuzhiyun
2040*4882a593Smuzhiyun crc = vlan->tt.crc;
2041*4882a593Smuzhiyun
2042*4882a593Smuzhiyun batadv_orig_node_vlan_put(vlan);
2043*4882a593Smuzhiyun
2044*4882a593Smuzhiyun hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family,
2045*4882a593Smuzhiyun NLM_F_MULTI,
2046*4882a593Smuzhiyun BATADV_CMD_GET_TRANSTABLE_GLOBAL);
2047*4882a593Smuzhiyun if (!hdr)
2048*4882a593Smuzhiyun return -ENOBUFS;
2049*4882a593Smuzhiyun
2050*4882a593Smuzhiyun last_ttvn = atomic_read(&orig->orig_node->last_ttvn);
2051*4882a593Smuzhiyun
2052*4882a593Smuzhiyun if (nla_put(msg, BATADV_ATTR_TT_ADDRESS, ETH_ALEN, common->addr) ||
2053*4882a593Smuzhiyun nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
2054*4882a593Smuzhiyun orig->orig_node->orig) ||
2055*4882a593Smuzhiyun nla_put_u8(msg, BATADV_ATTR_TT_TTVN, orig->ttvn) ||
2056*4882a593Smuzhiyun nla_put_u8(msg, BATADV_ATTR_TT_LAST_TTVN, last_ttvn) ||
2057*4882a593Smuzhiyun nla_put_u32(msg, BATADV_ATTR_TT_CRC32, crc) ||
2058*4882a593Smuzhiyun nla_put_u16(msg, BATADV_ATTR_TT_VID, common->vid) ||
2059*4882a593Smuzhiyun nla_put_u32(msg, BATADV_ATTR_TT_FLAGS, flags))
2060*4882a593Smuzhiyun goto nla_put_failure;
2061*4882a593Smuzhiyun
2062*4882a593Smuzhiyun if (best && nla_put_flag(msg, BATADV_ATTR_FLAG_BEST))
2063*4882a593Smuzhiyun goto nla_put_failure;
2064*4882a593Smuzhiyun
2065*4882a593Smuzhiyun genlmsg_end(msg, hdr);
2066*4882a593Smuzhiyun return 0;
2067*4882a593Smuzhiyun
2068*4882a593Smuzhiyun nla_put_failure:
2069*4882a593Smuzhiyun genlmsg_cancel(msg, hdr);
2070*4882a593Smuzhiyun return -EMSGSIZE;
2071*4882a593Smuzhiyun }
2072*4882a593Smuzhiyun
2073*4882a593Smuzhiyun /**
2074*4882a593Smuzhiyun * batadv_tt_global_dump_entry() - Dump one TT global entry into a message
2075*4882a593Smuzhiyun * @msg: Netlink message to dump into
2076*4882a593Smuzhiyun * @portid: Port making netlink request
2077*4882a593Smuzhiyun * @seq: Sequence number of netlink message
2078*4882a593Smuzhiyun * @bat_priv: The bat priv with all the soft interface information
2079*4882a593Smuzhiyun * @common: tt local & tt global common data
2080*4882a593Smuzhiyun * @sub_s: Number of entries to skip
2081*4882a593Smuzhiyun *
2082*4882a593Smuzhiyun * This function assumes the caller holds rcu_read_lock().
2083*4882a593Smuzhiyun *
2084*4882a593Smuzhiyun * Return: Error code, or 0 on success
2085*4882a593Smuzhiyun */
2086*4882a593Smuzhiyun static int
batadv_tt_global_dump_entry(struct sk_buff * msg,u32 portid,u32 seq,struct batadv_priv * bat_priv,struct batadv_tt_common_entry * common,int * sub_s)2087*4882a593Smuzhiyun batadv_tt_global_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
2088*4882a593Smuzhiyun struct batadv_priv *bat_priv,
2089*4882a593Smuzhiyun struct batadv_tt_common_entry *common, int *sub_s)
2090*4882a593Smuzhiyun {
2091*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
2092*4882a593Smuzhiyun struct batadv_tt_global_entry *global;
2093*4882a593Smuzhiyun struct hlist_head *head;
2094*4882a593Smuzhiyun int sub = 0;
2095*4882a593Smuzhiyun bool best;
2096*4882a593Smuzhiyun
2097*4882a593Smuzhiyun global = container_of(common, struct batadv_tt_global_entry, common);
2098*4882a593Smuzhiyun best_entry = batadv_transtable_best_orig(bat_priv, global);
2099*4882a593Smuzhiyun head = &global->orig_list;
2100*4882a593Smuzhiyun
2101*4882a593Smuzhiyun hlist_for_each_entry_rcu(orig_entry, head, list) {
2102*4882a593Smuzhiyun if (sub++ < *sub_s)
2103*4882a593Smuzhiyun continue;
2104*4882a593Smuzhiyun
2105*4882a593Smuzhiyun best = (orig_entry == best_entry);
2106*4882a593Smuzhiyun
2107*4882a593Smuzhiyun if (batadv_tt_global_dump_subentry(msg, portid, seq, common,
2108*4882a593Smuzhiyun orig_entry, best)) {
2109*4882a593Smuzhiyun *sub_s = sub - 1;
2110*4882a593Smuzhiyun return -EMSGSIZE;
2111*4882a593Smuzhiyun }
2112*4882a593Smuzhiyun }
2113*4882a593Smuzhiyun
2114*4882a593Smuzhiyun *sub_s = 0;
2115*4882a593Smuzhiyun return 0;
2116*4882a593Smuzhiyun }
2117*4882a593Smuzhiyun
2118*4882a593Smuzhiyun /**
2119*4882a593Smuzhiyun * batadv_tt_global_dump_bucket() - Dump one TT local bucket into a message
2120*4882a593Smuzhiyun * @msg: Netlink message to dump into
2121*4882a593Smuzhiyun * @portid: Port making netlink request
2122*4882a593Smuzhiyun * @seq: Sequence number of netlink message
2123*4882a593Smuzhiyun * @bat_priv: The bat priv with all the soft interface information
2124*4882a593Smuzhiyun * @head: Pointer to the list containing the global tt entries
2125*4882a593Smuzhiyun * @idx_s: Number of entries to skip
2126*4882a593Smuzhiyun * @sub: Number of entries to skip
2127*4882a593Smuzhiyun *
2128*4882a593Smuzhiyun * Return: Error code, or 0 on success
2129*4882a593Smuzhiyun */
2130*4882a593Smuzhiyun static int
batadv_tt_global_dump_bucket(struct sk_buff * msg,u32 portid,u32 seq,struct batadv_priv * bat_priv,struct hlist_head * head,int * idx_s,int * sub)2131*4882a593Smuzhiyun batadv_tt_global_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
2132*4882a593Smuzhiyun struct batadv_priv *bat_priv,
2133*4882a593Smuzhiyun struct hlist_head *head, int *idx_s, int *sub)
2134*4882a593Smuzhiyun {
2135*4882a593Smuzhiyun struct batadv_tt_common_entry *common;
2136*4882a593Smuzhiyun int idx = 0;
2137*4882a593Smuzhiyun
2138*4882a593Smuzhiyun rcu_read_lock();
2139*4882a593Smuzhiyun hlist_for_each_entry_rcu(common, head, hash_entry) {
2140*4882a593Smuzhiyun if (idx++ < *idx_s)
2141*4882a593Smuzhiyun continue;
2142*4882a593Smuzhiyun
2143*4882a593Smuzhiyun if (batadv_tt_global_dump_entry(msg, portid, seq, bat_priv,
2144*4882a593Smuzhiyun common, sub)) {
2145*4882a593Smuzhiyun rcu_read_unlock();
2146*4882a593Smuzhiyun *idx_s = idx - 1;
2147*4882a593Smuzhiyun return -EMSGSIZE;
2148*4882a593Smuzhiyun }
2149*4882a593Smuzhiyun }
2150*4882a593Smuzhiyun rcu_read_unlock();
2151*4882a593Smuzhiyun
2152*4882a593Smuzhiyun *idx_s = 0;
2153*4882a593Smuzhiyun *sub = 0;
2154*4882a593Smuzhiyun return 0;
2155*4882a593Smuzhiyun }
2156*4882a593Smuzhiyun
2157*4882a593Smuzhiyun /**
2158*4882a593Smuzhiyun * batadv_tt_global_dump() - Dump TT global entries into a message
2159*4882a593Smuzhiyun * @msg: Netlink message to dump into
2160*4882a593Smuzhiyun * @cb: Parameters from query
2161*4882a593Smuzhiyun *
2162*4882a593Smuzhiyun * Return: Error code, or length of message on success
2163*4882a593Smuzhiyun */
batadv_tt_global_dump(struct sk_buff * msg,struct netlink_callback * cb)2164*4882a593Smuzhiyun int batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb)
2165*4882a593Smuzhiyun {
2166*4882a593Smuzhiyun struct net *net = sock_net(cb->skb->sk);
2167*4882a593Smuzhiyun struct net_device *soft_iface;
2168*4882a593Smuzhiyun struct batadv_priv *bat_priv;
2169*4882a593Smuzhiyun struct batadv_hard_iface *primary_if = NULL;
2170*4882a593Smuzhiyun struct batadv_hashtable *hash;
2171*4882a593Smuzhiyun struct hlist_head *head;
2172*4882a593Smuzhiyun int ret;
2173*4882a593Smuzhiyun int ifindex;
2174*4882a593Smuzhiyun int bucket = cb->args[0];
2175*4882a593Smuzhiyun int idx = cb->args[1];
2176*4882a593Smuzhiyun int sub = cb->args[2];
2177*4882a593Smuzhiyun int portid = NETLINK_CB(cb->skb).portid;
2178*4882a593Smuzhiyun
2179*4882a593Smuzhiyun ifindex = batadv_netlink_get_ifindex(cb->nlh, BATADV_ATTR_MESH_IFINDEX);
2180*4882a593Smuzhiyun if (!ifindex)
2181*4882a593Smuzhiyun return -EINVAL;
2182*4882a593Smuzhiyun
2183*4882a593Smuzhiyun soft_iface = dev_get_by_index(net, ifindex);
2184*4882a593Smuzhiyun if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
2185*4882a593Smuzhiyun ret = -ENODEV;
2186*4882a593Smuzhiyun goto out;
2187*4882a593Smuzhiyun }
2188*4882a593Smuzhiyun
2189*4882a593Smuzhiyun bat_priv = netdev_priv(soft_iface);
2190*4882a593Smuzhiyun
2191*4882a593Smuzhiyun primary_if = batadv_primary_if_get_selected(bat_priv);
2192*4882a593Smuzhiyun if (!primary_if || primary_if->if_status != BATADV_IF_ACTIVE) {
2193*4882a593Smuzhiyun ret = -ENOENT;
2194*4882a593Smuzhiyun goto out;
2195*4882a593Smuzhiyun }
2196*4882a593Smuzhiyun
2197*4882a593Smuzhiyun hash = bat_priv->tt.global_hash;
2198*4882a593Smuzhiyun
2199*4882a593Smuzhiyun while (bucket < hash->size) {
2200*4882a593Smuzhiyun head = &hash->table[bucket];
2201*4882a593Smuzhiyun
2202*4882a593Smuzhiyun if (batadv_tt_global_dump_bucket(msg, portid,
2203*4882a593Smuzhiyun cb->nlh->nlmsg_seq, bat_priv,
2204*4882a593Smuzhiyun head, &idx, &sub))
2205*4882a593Smuzhiyun break;
2206*4882a593Smuzhiyun
2207*4882a593Smuzhiyun bucket++;
2208*4882a593Smuzhiyun }
2209*4882a593Smuzhiyun
2210*4882a593Smuzhiyun ret = msg->len;
2211*4882a593Smuzhiyun
2212*4882a593Smuzhiyun out:
2213*4882a593Smuzhiyun if (primary_if)
2214*4882a593Smuzhiyun batadv_hardif_put(primary_if);
2215*4882a593Smuzhiyun if (soft_iface)
2216*4882a593Smuzhiyun dev_put(soft_iface);
2217*4882a593Smuzhiyun
2218*4882a593Smuzhiyun cb->args[0] = bucket;
2219*4882a593Smuzhiyun cb->args[1] = idx;
2220*4882a593Smuzhiyun cb->args[2] = sub;
2221*4882a593Smuzhiyun
2222*4882a593Smuzhiyun return ret;
2223*4882a593Smuzhiyun }
2224*4882a593Smuzhiyun
2225*4882a593Smuzhiyun /**
2226*4882a593Smuzhiyun * _batadv_tt_global_del_orig_entry() - remove and free an orig_entry
2227*4882a593Smuzhiyun * @tt_global_entry: the global entry to remove the orig_entry from
2228*4882a593Smuzhiyun * @orig_entry: the orig entry to remove and free
2229*4882a593Smuzhiyun *
2230*4882a593Smuzhiyun * Remove an orig_entry from its list in the given tt_global_entry and
2231*4882a593Smuzhiyun * free this orig_entry afterwards.
2232*4882a593Smuzhiyun *
2233*4882a593Smuzhiyun * Caller must hold tt_global_entry->list_lock and ensure orig_entry->list is
2234*4882a593Smuzhiyun * part of a list.
2235*4882a593Smuzhiyun */
2236*4882a593Smuzhiyun static void
_batadv_tt_global_del_orig_entry(struct batadv_tt_global_entry * tt_global_entry,struct batadv_tt_orig_list_entry * orig_entry)2237*4882a593Smuzhiyun _batadv_tt_global_del_orig_entry(struct batadv_tt_global_entry *tt_global_entry,
2238*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry)
2239*4882a593Smuzhiyun {
2240*4882a593Smuzhiyun lockdep_assert_held(&tt_global_entry->list_lock);
2241*4882a593Smuzhiyun
2242*4882a593Smuzhiyun batadv_tt_global_size_dec(orig_entry->orig_node,
2243*4882a593Smuzhiyun tt_global_entry->common.vid);
2244*4882a593Smuzhiyun atomic_dec(&tt_global_entry->orig_list_count);
2245*4882a593Smuzhiyun /* requires holding tt_global_entry->list_lock and orig_entry->list
2246*4882a593Smuzhiyun * being part of a list
2247*4882a593Smuzhiyun */
2248*4882a593Smuzhiyun hlist_del_rcu(&orig_entry->list);
2249*4882a593Smuzhiyun batadv_tt_orig_list_entry_put(orig_entry);
2250*4882a593Smuzhiyun }
2251*4882a593Smuzhiyun
2252*4882a593Smuzhiyun /* deletes the orig list of a tt_global_entry */
2253*4882a593Smuzhiyun static void
batadv_tt_global_del_orig_list(struct batadv_tt_global_entry * tt_global_entry)2254*4882a593Smuzhiyun batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
2255*4882a593Smuzhiyun {
2256*4882a593Smuzhiyun struct hlist_head *head;
2257*4882a593Smuzhiyun struct hlist_node *safe;
2258*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
2259*4882a593Smuzhiyun
2260*4882a593Smuzhiyun spin_lock_bh(&tt_global_entry->list_lock);
2261*4882a593Smuzhiyun head = &tt_global_entry->orig_list;
2262*4882a593Smuzhiyun hlist_for_each_entry_safe(orig_entry, safe, head, list)
2263*4882a593Smuzhiyun _batadv_tt_global_del_orig_entry(tt_global_entry, orig_entry);
2264*4882a593Smuzhiyun spin_unlock_bh(&tt_global_entry->list_lock);
2265*4882a593Smuzhiyun }
2266*4882a593Smuzhiyun
2267*4882a593Smuzhiyun /**
2268*4882a593Smuzhiyun * batadv_tt_global_del_orig_node() - remove orig_node from a global tt entry
2269*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2270*4882a593Smuzhiyun * @tt_global_entry: the global entry to remove the orig_node from
2271*4882a593Smuzhiyun * @orig_node: the originator announcing the client
2272*4882a593Smuzhiyun * @message: message to append to the log on deletion
2273*4882a593Smuzhiyun *
2274*4882a593Smuzhiyun * Remove the given orig_node and its according orig_entry from the given
2275*4882a593Smuzhiyun * global tt entry.
2276*4882a593Smuzhiyun */
2277*4882a593Smuzhiyun static void
batadv_tt_global_del_orig_node(struct batadv_priv * bat_priv,struct batadv_tt_global_entry * tt_global_entry,struct batadv_orig_node * orig_node,const char * message)2278*4882a593Smuzhiyun batadv_tt_global_del_orig_node(struct batadv_priv *bat_priv,
2279*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry,
2280*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
2281*4882a593Smuzhiyun const char *message)
2282*4882a593Smuzhiyun {
2283*4882a593Smuzhiyun struct hlist_head *head;
2284*4882a593Smuzhiyun struct hlist_node *safe;
2285*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
2286*4882a593Smuzhiyun unsigned short vid;
2287*4882a593Smuzhiyun
2288*4882a593Smuzhiyun spin_lock_bh(&tt_global_entry->list_lock);
2289*4882a593Smuzhiyun head = &tt_global_entry->orig_list;
2290*4882a593Smuzhiyun hlist_for_each_entry_safe(orig_entry, safe, head, list) {
2291*4882a593Smuzhiyun if (orig_entry->orig_node == orig_node) {
2292*4882a593Smuzhiyun vid = tt_global_entry->common.vid;
2293*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
2294*4882a593Smuzhiyun "Deleting %pM from global tt entry %pM (vid: %d): %s\n",
2295*4882a593Smuzhiyun orig_node->orig,
2296*4882a593Smuzhiyun tt_global_entry->common.addr,
2297*4882a593Smuzhiyun batadv_print_vid(vid), message);
2298*4882a593Smuzhiyun _batadv_tt_global_del_orig_entry(tt_global_entry,
2299*4882a593Smuzhiyun orig_entry);
2300*4882a593Smuzhiyun }
2301*4882a593Smuzhiyun }
2302*4882a593Smuzhiyun spin_unlock_bh(&tt_global_entry->list_lock);
2303*4882a593Smuzhiyun }
2304*4882a593Smuzhiyun
2305*4882a593Smuzhiyun /* If the client is to be deleted, we check if it is the last origantor entry
2306*4882a593Smuzhiyun * within tt_global entry. If yes, we set the BATADV_TT_CLIENT_ROAM flag and the
2307*4882a593Smuzhiyun * timer, otherwise we simply remove the originator scheduled for deletion.
2308*4882a593Smuzhiyun */
2309*4882a593Smuzhiyun static void
batadv_tt_global_del_roaming(struct batadv_priv * bat_priv,struct batadv_tt_global_entry * tt_global_entry,struct batadv_orig_node * orig_node,const char * message)2310*4882a593Smuzhiyun batadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
2311*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry,
2312*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
2313*4882a593Smuzhiyun const char *message)
2314*4882a593Smuzhiyun {
2315*4882a593Smuzhiyun bool last_entry = true;
2316*4882a593Smuzhiyun struct hlist_head *head;
2317*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *orig_entry;
2318*4882a593Smuzhiyun
2319*4882a593Smuzhiyun /* no local entry exists, case 1:
2320*4882a593Smuzhiyun * Check if this is the last one or if other entries exist.
2321*4882a593Smuzhiyun */
2322*4882a593Smuzhiyun
2323*4882a593Smuzhiyun rcu_read_lock();
2324*4882a593Smuzhiyun head = &tt_global_entry->orig_list;
2325*4882a593Smuzhiyun hlist_for_each_entry_rcu(orig_entry, head, list) {
2326*4882a593Smuzhiyun if (orig_entry->orig_node != orig_node) {
2327*4882a593Smuzhiyun last_entry = false;
2328*4882a593Smuzhiyun break;
2329*4882a593Smuzhiyun }
2330*4882a593Smuzhiyun }
2331*4882a593Smuzhiyun rcu_read_unlock();
2332*4882a593Smuzhiyun
2333*4882a593Smuzhiyun if (last_entry) {
2334*4882a593Smuzhiyun /* its the last one, mark for roaming. */
2335*4882a593Smuzhiyun tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
2336*4882a593Smuzhiyun tt_global_entry->roam_at = jiffies;
2337*4882a593Smuzhiyun } else {
2338*4882a593Smuzhiyun /* there is another entry, we can simply delete this
2339*4882a593Smuzhiyun * one and can still use the other one.
2340*4882a593Smuzhiyun */
2341*4882a593Smuzhiyun batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
2342*4882a593Smuzhiyun orig_node, message);
2343*4882a593Smuzhiyun }
2344*4882a593Smuzhiyun }
2345*4882a593Smuzhiyun
2346*4882a593Smuzhiyun /**
2347*4882a593Smuzhiyun * batadv_tt_global_del() - remove a client from the global table
2348*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2349*4882a593Smuzhiyun * @orig_node: an originator serving this client
2350*4882a593Smuzhiyun * @addr: the mac address of the client
2351*4882a593Smuzhiyun * @vid: VLAN identifier
2352*4882a593Smuzhiyun * @message: a message explaining the reason for deleting the client to print
2353*4882a593Smuzhiyun * for debugging purpose
2354*4882a593Smuzhiyun * @roaming: true if the deletion has been triggered by a roaming event
2355*4882a593Smuzhiyun */
batadv_tt_global_del(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,const unsigned char * addr,unsigned short vid,const char * message,bool roaming)2356*4882a593Smuzhiyun static void batadv_tt_global_del(struct batadv_priv *bat_priv,
2357*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
2358*4882a593Smuzhiyun const unsigned char *addr, unsigned short vid,
2359*4882a593Smuzhiyun const char *message, bool roaming)
2360*4882a593Smuzhiyun {
2361*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
2362*4882a593Smuzhiyun struct batadv_tt_local_entry *local_entry = NULL;
2363*4882a593Smuzhiyun
2364*4882a593Smuzhiyun tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
2365*4882a593Smuzhiyun if (!tt_global_entry)
2366*4882a593Smuzhiyun goto out;
2367*4882a593Smuzhiyun
2368*4882a593Smuzhiyun if (!roaming) {
2369*4882a593Smuzhiyun batadv_tt_global_del_orig_node(bat_priv, tt_global_entry,
2370*4882a593Smuzhiyun orig_node, message);
2371*4882a593Smuzhiyun
2372*4882a593Smuzhiyun if (hlist_empty(&tt_global_entry->orig_list))
2373*4882a593Smuzhiyun batadv_tt_global_free(bat_priv, tt_global_entry,
2374*4882a593Smuzhiyun message);
2375*4882a593Smuzhiyun
2376*4882a593Smuzhiyun goto out;
2377*4882a593Smuzhiyun }
2378*4882a593Smuzhiyun
2379*4882a593Smuzhiyun /* if we are deleting a global entry due to a roam
2380*4882a593Smuzhiyun * event, there are two possibilities:
2381*4882a593Smuzhiyun * 1) the client roamed from node A to node B => if there
2382*4882a593Smuzhiyun * is only one originator left for this client, we mark
2383*4882a593Smuzhiyun * it with BATADV_TT_CLIENT_ROAM, we start a timer and we
2384*4882a593Smuzhiyun * wait for node B to claim it. In case of timeout
2385*4882a593Smuzhiyun * the entry is purged.
2386*4882a593Smuzhiyun *
2387*4882a593Smuzhiyun * If there are other originators left, we directly delete
2388*4882a593Smuzhiyun * the originator.
2389*4882a593Smuzhiyun * 2) the client roamed to us => we can directly delete
2390*4882a593Smuzhiyun * the global entry, since it is useless now.
2391*4882a593Smuzhiyun */
2392*4882a593Smuzhiyun local_entry = batadv_tt_local_hash_find(bat_priv,
2393*4882a593Smuzhiyun tt_global_entry->common.addr,
2394*4882a593Smuzhiyun vid);
2395*4882a593Smuzhiyun if (local_entry) {
2396*4882a593Smuzhiyun /* local entry exists, case 2: client roamed to us. */
2397*4882a593Smuzhiyun batadv_tt_global_del_orig_list(tt_global_entry);
2398*4882a593Smuzhiyun batadv_tt_global_free(bat_priv, tt_global_entry, message);
2399*4882a593Smuzhiyun } else {
2400*4882a593Smuzhiyun /* no local entry exists, case 1: check for roaming */
2401*4882a593Smuzhiyun batadv_tt_global_del_roaming(bat_priv, tt_global_entry,
2402*4882a593Smuzhiyun orig_node, message);
2403*4882a593Smuzhiyun }
2404*4882a593Smuzhiyun
2405*4882a593Smuzhiyun out:
2406*4882a593Smuzhiyun if (tt_global_entry)
2407*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
2408*4882a593Smuzhiyun if (local_entry)
2409*4882a593Smuzhiyun batadv_tt_local_entry_put(local_entry);
2410*4882a593Smuzhiyun }
2411*4882a593Smuzhiyun
2412*4882a593Smuzhiyun /**
2413*4882a593Smuzhiyun * batadv_tt_global_del_orig() - remove all the TT global entries belonging to
2414*4882a593Smuzhiyun * the given originator matching the provided vid
2415*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2416*4882a593Smuzhiyun * @orig_node: the originator owning the entries to remove
2417*4882a593Smuzhiyun * @match_vid: the VLAN identifier to match. If negative all the entries will be
2418*4882a593Smuzhiyun * removed
2419*4882a593Smuzhiyun * @message: debug message to print as "reason"
2420*4882a593Smuzhiyun */
batadv_tt_global_del_orig(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,s32 match_vid,const char * message)2421*4882a593Smuzhiyun void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
2422*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
2423*4882a593Smuzhiyun s32 match_vid,
2424*4882a593Smuzhiyun const char *message)
2425*4882a593Smuzhiyun {
2426*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global;
2427*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
2428*4882a593Smuzhiyun u32 i;
2429*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.global_hash;
2430*4882a593Smuzhiyun struct hlist_node *safe;
2431*4882a593Smuzhiyun struct hlist_head *head;
2432*4882a593Smuzhiyun spinlock_t *list_lock; /* protects write access to the hash lists */
2433*4882a593Smuzhiyun unsigned short vid;
2434*4882a593Smuzhiyun
2435*4882a593Smuzhiyun if (!hash)
2436*4882a593Smuzhiyun return;
2437*4882a593Smuzhiyun
2438*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
2439*4882a593Smuzhiyun head = &hash->table[i];
2440*4882a593Smuzhiyun list_lock = &hash->list_locks[i];
2441*4882a593Smuzhiyun
2442*4882a593Smuzhiyun spin_lock_bh(list_lock);
2443*4882a593Smuzhiyun hlist_for_each_entry_safe(tt_common_entry, safe,
2444*4882a593Smuzhiyun head, hash_entry) {
2445*4882a593Smuzhiyun /* remove only matching entries */
2446*4882a593Smuzhiyun if (match_vid >= 0 && tt_common_entry->vid != match_vid)
2447*4882a593Smuzhiyun continue;
2448*4882a593Smuzhiyun
2449*4882a593Smuzhiyun tt_global = container_of(tt_common_entry,
2450*4882a593Smuzhiyun struct batadv_tt_global_entry,
2451*4882a593Smuzhiyun common);
2452*4882a593Smuzhiyun
2453*4882a593Smuzhiyun batadv_tt_global_del_orig_node(bat_priv, tt_global,
2454*4882a593Smuzhiyun orig_node, message);
2455*4882a593Smuzhiyun
2456*4882a593Smuzhiyun if (hlist_empty(&tt_global->orig_list)) {
2457*4882a593Smuzhiyun vid = tt_global->common.vid;
2458*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
2459*4882a593Smuzhiyun "Deleting global tt entry %pM (vid: %d): %s\n",
2460*4882a593Smuzhiyun tt_global->common.addr,
2461*4882a593Smuzhiyun batadv_print_vid(vid), message);
2462*4882a593Smuzhiyun hlist_del_rcu(&tt_common_entry->hash_entry);
2463*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global);
2464*4882a593Smuzhiyun }
2465*4882a593Smuzhiyun }
2466*4882a593Smuzhiyun spin_unlock_bh(list_lock);
2467*4882a593Smuzhiyun }
2468*4882a593Smuzhiyun clear_bit(BATADV_ORIG_CAPA_HAS_TT, &orig_node->capa_initialized);
2469*4882a593Smuzhiyun }
2470*4882a593Smuzhiyun
batadv_tt_global_to_purge(struct batadv_tt_global_entry * tt_global,char ** msg)2471*4882a593Smuzhiyun static bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global,
2472*4882a593Smuzhiyun char **msg)
2473*4882a593Smuzhiyun {
2474*4882a593Smuzhiyun bool purge = false;
2475*4882a593Smuzhiyun unsigned long roam_timeout = BATADV_TT_CLIENT_ROAM_TIMEOUT;
2476*4882a593Smuzhiyun unsigned long temp_timeout = BATADV_TT_CLIENT_TEMP_TIMEOUT;
2477*4882a593Smuzhiyun
2478*4882a593Smuzhiyun if ((tt_global->common.flags & BATADV_TT_CLIENT_ROAM) &&
2479*4882a593Smuzhiyun batadv_has_timed_out(tt_global->roam_at, roam_timeout)) {
2480*4882a593Smuzhiyun purge = true;
2481*4882a593Smuzhiyun *msg = "Roaming timeout\n";
2482*4882a593Smuzhiyun }
2483*4882a593Smuzhiyun
2484*4882a593Smuzhiyun if ((tt_global->common.flags & BATADV_TT_CLIENT_TEMP) &&
2485*4882a593Smuzhiyun batadv_has_timed_out(tt_global->common.added_at, temp_timeout)) {
2486*4882a593Smuzhiyun purge = true;
2487*4882a593Smuzhiyun *msg = "Temporary client timeout\n";
2488*4882a593Smuzhiyun }
2489*4882a593Smuzhiyun
2490*4882a593Smuzhiyun return purge;
2491*4882a593Smuzhiyun }
2492*4882a593Smuzhiyun
batadv_tt_global_purge(struct batadv_priv * bat_priv)2493*4882a593Smuzhiyun static void batadv_tt_global_purge(struct batadv_priv *bat_priv)
2494*4882a593Smuzhiyun {
2495*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.global_hash;
2496*4882a593Smuzhiyun struct hlist_head *head;
2497*4882a593Smuzhiyun struct hlist_node *node_tmp;
2498*4882a593Smuzhiyun spinlock_t *list_lock; /* protects write access to the hash lists */
2499*4882a593Smuzhiyun u32 i;
2500*4882a593Smuzhiyun char *msg = NULL;
2501*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common;
2502*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global;
2503*4882a593Smuzhiyun
2504*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
2505*4882a593Smuzhiyun head = &hash->table[i];
2506*4882a593Smuzhiyun list_lock = &hash->list_locks[i];
2507*4882a593Smuzhiyun
2508*4882a593Smuzhiyun spin_lock_bh(list_lock);
2509*4882a593Smuzhiyun hlist_for_each_entry_safe(tt_common, node_tmp, head,
2510*4882a593Smuzhiyun hash_entry) {
2511*4882a593Smuzhiyun tt_global = container_of(tt_common,
2512*4882a593Smuzhiyun struct batadv_tt_global_entry,
2513*4882a593Smuzhiyun common);
2514*4882a593Smuzhiyun
2515*4882a593Smuzhiyun if (!batadv_tt_global_to_purge(tt_global, &msg))
2516*4882a593Smuzhiyun continue;
2517*4882a593Smuzhiyun
2518*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
2519*4882a593Smuzhiyun "Deleting global tt entry %pM (vid: %d): %s\n",
2520*4882a593Smuzhiyun tt_global->common.addr,
2521*4882a593Smuzhiyun batadv_print_vid(tt_global->common.vid),
2522*4882a593Smuzhiyun msg);
2523*4882a593Smuzhiyun
2524*4882a593Smuzhiyun hlist_del_rcu(&tt_common->hash_entry);
2525*4882a593Smuzhiyun
2526*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global);
2527*4882a593Smuzhiyun }
2528*4882a593Smuzhiyun spin_unlock_bh(list_lock);
2529*4882a593Smuzhiyun }
2530*4882a593Smuzhiyun }
2531*4882a593Smuzhiyun
batadv_tt_global_table_free(struct batadv_priv * bat_priv)2532*4882a593Smuzhiyun static void batadv_tt_global_table_free(struct batadv_priv *bat_priv)
2533*4882a593Smuzhiyun {
2534*4882a593Smuzhiyun struct batadv_hashtable *hash;
2535*4882a593Smuzhiyun spinlock_t *list_lock; /* protects write access to the hash lists */
2536*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
2537*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global;
2538*4882a593Smuzhiyun struct hlist_node *node_tmp;
2539*4882a593Smuzhiyun struct hlist_head *head;
2540*4882a593Smuzhiyun u32 i;
2541*4882a593Smuzhiyun
2542*4882a593Smuzhiyun if (!bat_priv->tt.global_hash)
2543*4882a593Smuzhiyun return;
2544*4882a593Smuzhiyun
2545*4882a593Smuzhiyun hash = bat_priv->tt.global_hash;
2546*4882a593Smuzhiyun
2547*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
2548*4882a593Smuzhiyun head = &hash->table[i];
2549*4882a593Smuzhiyun list_lock = &hash->list_locks[i];
2550*4882a593Smuzhiyun
2551*4882a593Smuzhiyun spin_lock_bh(list_lock);
2552*4882a593Smuzhiyun hlist_for_each_entry_safe(tt_common_entry, node_tmp,
2553*4882a593Smuzhiyun head, hash_entry) {
2554*4882a593Smuzhiyun hlist_del_rcu(&tt_common_entry->hash_entry);
2555*4882a593Smuzhiyun tt_global = container_of(tt_common_entry,
2556*4882a593Smuzhiyun struct batadv_tt_global_entry,
2557*4882a593Smuzhiyun common);
2558*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global);
2559*4882a593Smuzhiyun }
2560*4882a593Smuzhiyun spin_unlock_bh(list_lock);
2561*4882a593Smuzhiyun }
2562*4882a593Smuzhiyun
2563*4882a593Smuzhiyun batadv_hash_destroy(hash);
2564*4882a593Smuzhiyun
2565*4882a593Smuzhiyun bat_priv->tt.global_hash = NULL;
2566*4882a593Smuzhiyun }
2567*4882a593Smuzhiyun
2568*4882a593Smuzhiyun static bool
_batadv_is_ap_isolated(struct batadv_tt_local_entry * tt_local_entry,struct batadv_tt_global_entry * tt_global_entry)2569*4882a593Smuzhiyun _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry,
2570*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry)
2571*4882a593Smuzhiyun {
2572*4882a593Smuzhiyun if (tt_local_entry->common.flags & BATADV_TT_CLIENT_WIFI &&
2573*4882a593Smuzhiyun tt_global_entry->common.flags & BATADV_TT_CLIENT_WIFI)
2574*4882a593Smuzhiyun return true;
2575*4882a593Smuzhiyun
2576*4882a593Smuzhiyun /* check if the two clients are marked as isolated */
2577*4882a593Smuzhiyun if (tt_local_entry->common.flags & BATADV_TT_CLIENT_ISOLA &&
2578*4882a593Smuzhiyun tt_global_entry->common.flags & BATADV_TT_CLIENT_ISOLA)
2579*4882a593Smuzhiyun return true;
2580*4882a593Smuzhiyun
2581*4882a593Smuzhiyun return false;
2582*4882a593Smuzhiyun }
2583*4882a593Smuzhiyun
2584*4882a593Smuzhiyun /**
2585*4882a593Smuzhiyun * batadv_transtable_search() - get the mesh destination for a given client
2586*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2587*4882a593Smuzhiyun * @src: mac address of the source client
2588*4882a593Smuzhiyun * @addr: mac address of the destination client
2589*4882a593Smuzhiyun * @vid: VLAN identifier
2590*4882a593Smuzhiyun *
2591*4882a593Smuzhiyun * Return: a pointer to the originator that was selected as destination in the
2592*4882a593Smuzhiyun * mesh for contacting the client 'addr', NULL otherwise.
2593*4882a593Smuzhiyun * In case of multiple originators serving the same client, the function returns
2594*4882a593Smuzhiyun * the best one (best in terms of metric towards the destination node).
2595*4882a593Smuzhiyun *
2596*4882a593Smuzhiyun * If the two clients are AP isolated the function returns NULL.
2597*4882a593Smuzhiyun */
batadv_transtable_search(struct batadv_priv * bat_priv,const u8 * src,const u8 * addr,unsigned short vid)2598*4882a593Smuzhiyun struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
2599*4882a593Smuzhiyun const u8 *src,
2600*4882a593Smuzhiyun const u8 *addr,
2601*4882a593Smuzhiyun unsigned short vid)
2602*4882a593Smuzhiyun {
2603*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry = NULL;
2604*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry = NULL;
2605*4882a593Smuzhiyun struct batadv_orig_node *orig_node = NULL;
2606*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *best_entry;
2607*4882a593Smuzhiyun
2608*4882a593Smuzhiyun if (src && batadv_vlan_ap_isola_get(bat_priv, vid)) {
2609*4882a593Smuzhiyun tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid);
2610*4882a593Smuzhiyun if (!tt_local_entry ||
2611*4882a593Smuzhiyun (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
2612*4882a593Smuzhiyun goto out;
2613*4882a593Smuzhiyun }
2614*4882a593Smuzhiyun
2615*4882a593Smuzhiyun tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
2616*4882a593Smuzhiyun if (!tt_global_entry)
2617*4882a593Smuzhiyun goto out;
2618*4882a593Smuzhiyun
2619*4882a593Smuzhiyun /* check whether the clients should not communicate due to AP
2620*4882a593Smuzhiyun * isolation
2621*4882a593Smuzhiyun */
2622*4882a593Smuzhiyun if (tt_local_entry &&
2623*4882a593Smuzhiyun _batadv_is_ap_isolated(tt_local_entry, tt_global_entry))
2624*4882a593Smuzhiyun goto out;
2625*4882a593Smuzhiyun
2626*4882a593Smuzhiyun rcu_read_lock();
2627*4882a593Smuzhiyun best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
2628*4882a593Smuzhiyun /* found anything? */
2629*4882a593Smuzhiyun if (best_entry)
2630*4882a593Smuzhiyun orig_node = best_entry->orig_node;
2631*4882a593Smuzhiyun if (orig_node && !kref_get_unless_zero(&orig_node->refcount))
2632*4882a593Smuzhiyun orig_node = NULL;
2633*4882a593Smuzhiyun rcu_read_unlock();
2634*4882a593Smuzhiyun
2635*4882a593Smuzhiyun out:
2636*4882a593Smuzhiyun if (tt_global_entry)
2637*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
2638*4882a593Smuzhiyun if (tt_local_entry)
2639*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local_entry);
2640*4882a593Smuzhiyun
2641*4882a593Smuzhiyun return orig_node;
2642*4882a593Smuzhiyun }
2643*4882a593Smuzhiyun
2644*4882a593Smuzhiyun /**
2645*4882a593Smuzhiyun * batadv_tt_global_crc() - calculates the checksum of the local table belonging
2646*4882a593Smuzhiyun * to the given orig_node
2647*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2648*4882a593Smuzhiyun * @orig_node: originator for which the CRC should be computed
2649*4882a593Smuzhiyun * @vid: VLAN identifier for which the CRC32 has to be computed
2650*4882a593Smuzhiyun *
2651*4882a593Smuzhiyun * This function computes the checksum for the global table corresponding to a
2652*4882a593Smuzhiyun * specific originator. In particular, the checksum is computed as follows: For
2653*4882a593Smuzhiyun * each client connected to the originator the CRC32C of the MAC address and the
2654*4882a593Smuzhiyun * VID is computed and then all the CRC32Cs of the various clients are xor'ed
2655*4882a593Smuzhiyun * together.
2656*4882a593Smuzhiyun *
2657*4882a593Smuzhiyun * The idea behind is that CRC32C should be used as much as possible in order to
2658*4882a593Smuzhiyun * produce a unique hash of the table, but since the order which is used to feed
2659*4882a593Smuzhiyun * the CRC32C function affects the result and since every node in the network
2660*4882a593Smuzhiyun * probably sorts the clients differently, the hash function cannot be directly
2661*4882a593Smuzhiyun * computed over the entire table. Hence the CRC32C is used only on
2662*4882a593Smuzhiyun * the single client entry, while all the results are then xor'ed together
2663*4882a593Smuzhiyun * because the XOR operation can combine them all while trying to reduce the
2664*4882a593Smuzhiyun * noise as much as possible.
2665*4882a593Smuzhiyun *
2666*4882a593Smuzhiyun * Return: the checksum of the global table of a given originator.
2667*4882a593Smuzhiyun */
batadv_tt_global_crc(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,unsigned short vid)2668*4882a593Smuzhiyun static u32 batadv_tt_global_crc(struct batadv_priv *bat_priv,
2669*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
2670*4882a593Smuzhiyun unsigned short vid)
2671*4882a593Smuzhiyun {
2672*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.global_hash;
2673*4882a593Smuzhiyun struct batadv_tt_orig_list_entry *tt_orig;
2674*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common;
2675*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global;
2676*4882a593Smuzhiyun struct hlist_head *head;
2677*4882a593Smuzhiyun u32 i, crc_tmp, crc = 0;
2678*4882a593Smuzhiyun u8 flags;
2679*4882a593Smuzhiyun __be16 tmp_vid;
2680*4882a593Smuzhiyun
2681*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
2682*4882a593Smuzhiyun head = &hash->table[i];
2683*4882a593Smuzhiyun
2684*4882a593Smuzhiyun rcu_read_lock();
2685*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt_common, head, hash_entry) {
2686*4882a593Smuzhiyun tt_global = container_of(tt_common,
2687*4882a593Smuzhiyun struct batadv_tt_global_entry,
2688*4882a593Smuzhiyun common);
2689*4882a593Smuzhiyun /* compute the CRC only for entries belonging to the
2690*4882a593Smuzhiyun * VLAN identified by the vid passed as parameter
2691*4882a593Smuzhiyun */
2692*4882a593Smuzhiyun if (tt_common->vid != vid)
2693*4882a593Smuzhiyun continue;
2694*4882a593Smuzhiyun
2695*4882a593Smuzhiyun /* Roaming clients are in the global table for
2696*4882a593Smuzhiyun * consistency only. They don't have to be
2697*4882a593Smuzhiyun * taken into account while computing the
2698*4882a593Smuzhiyun * global crc
2699*4882a593Smuzhiyun */
2700*4882a593Smuzhiyun if (tt_common->flags & BATADV_TT_CLIENT_ROAM)
2701*4882a593Smuzhiyun continue;
2702*4882a593Smuzhiyun /* Temporary clients have not been announced yet, so
2703*4882a593Smuzhiyun * they have to be skipped while computing the global
2704*4882a593Smuzhiyun * crc
2705*4882a593Smuzhiyun */
2706*4882a593Smuzhiyun if (tt_common->flags & BATADV_TT_CLIENT_TEMP)
2707*4882a593Smuzhiyun continue;
2708*4882a593Smuzhiyun
2709*4882a593Smuzhiyun /* find out if this global entry is announced by this
2710*4882a593Smuzhiyun * originator
2711*4882a593Smuzhiyun */
2712*4882a593Smuzhiyun tt_orig = batadv_tt_global_orig_entry_find(tt_global,
2713*4882a593Smuzhiyun orig_node);
2714*4882a593Smuzhiyun if (!tt_orig)
2715*4882a593Smuzhiyun continue;
2716*4882a593Smuzhiyun
2717*4882a593Smuzhiyun /* use network order to read the VID: this ensures that
2718*4882a593Smuzhiyun * every node reads the bytes in the same order.
2719*4882a593Smuzhiyun */
2720*4882a593Smuzhiyun tmp_vid = htons(tt_common->vid);
2721*4882a593Smuzhiyun crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid));
2722*4882a593Smuzhiyun
2723*4882a593Smuzhiyun /* compute the CRC on flags that have to be kept in sync
2724*4882a593Smuzhiyun * among nodes
2725*4882a593Smuzhiyun */
2726*4882a593Smuzhiyun flags = tt_orig->flags;
2727*4882a593Smuzhiyun crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
2728*4882a593Smuzhiyun
2729*4882a593Smuzhiyun crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
2730*4882a593Smuzhiyun
2731*4882a593Smuzhiyun batadv_tt_orig_list_entry_put(tt_orig);
2732*4882a593Smuzhiyun }
2733*4882a593Smuzhiyun rcu_read_unlock();
2734*4882a593Smuzhiyun }
2735*4882a593Smuzhiyun
2736*4882a593Smuzhiyun return crc;
2737*4882a593Smuzhiyun }
2738*4882a593Smuzhiyun
2739*4882a593Smuzhiyun /**
2740*4882a593Smuzhiyun * batadv_tt_local_crc() - calculates the checksum of the local table
2741*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2742*4882a593Smuzhiyun * @vid: VLAN identifier for which the CRC32 has to be computed
2743*4882a593Smuzhiyun *
2744*4882a593Smuzhiyun * For details about the computation, please refer to the documentation for
2745*4882a593Smuzhiyun * batadv_tt_global_crc().
2746*4882a593Smuzhiyun *
2747*4882a593Smuzhiyun * Return: the checksum of the local table
2748*4882a593Smuzhiyun */
batadv_tt_local_crc(struct batadv_priv * bat_priv,unsigned short vid)2749*4882a593Smuzhiyun static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
2750*4882a593Smuzhiyun unsigned short vid)
2751*4882a593Smuzhiyun {
2752*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.local_hash;
2753*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common;
2754*4882a593Smuzhiyun struct hlist_head *head;
2755*4882a593Smuzhiyun u32 i, crc_tmp, crc = 0;
2756*4882a593Smuzhiyun u8 flags;
2757*4882a593Smuzhiyun __be16 tmp_vid;
2758*4882a593Smuzhiyun
2759*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
2760*4882a593Smuzhiyun head = &hash->table[i];
2761*4882a593Smuzhiyun
2762*4882a593Smuzhiyun rcu_read_lock();
2763*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt_common, head, hash_entry) {
2764*4882a593Smuzhiyun /* compute the CRC only for entries belonging to the
2765*4882a593Smuzhiyun * VLAN identified by vid
2766*4882a593Smuzhiyun */
2767*4882a593Smuzhiyun if (tt_common->vid != vid)
2768*4882a593Smuzhiyun continue;
2769*4882a593Smuzhiyun
2770*4882a593Smuzhiyun /* not yet committed clients have not to be taken into
2771*4882a593Smuzhiyun * account while computing the CRC
2772*4882a593Smuzhiyun */
2773*4882a593Smuzhiyun if (tt_common->flags & BATADV_TT_CLIENT_NEW)
2774*4882a593Smuzhiyun continue;
2775*4882a593Smuzhiyun
2776*4882a593Smuzhiyun /* use network order to read the VID: this ensures that
2777*4882a593Smuzhiyun * every node reads the bytes in the same order.
2778*4882a593Smuzhiyun */
2779*4882a593Smuzhiyun tmp_vid = htons(tt_common->vid);
2780*4882a593Smuzhiyun crc_tmp = crc32c(0, &tmp_vid, sizeof(tmp_vid));
2781*4882a593Smuzhiyun
2782*4882a593Smuzhiyun /* compute the CRC on flags that have to be kept in sync
2783*4882a593Smuzhiyun * among nodes
2784*4882a593Smuzhiyun */
2785*4882a593Smuzhiyun flags = tt_common->flags & BATADV_TT_SYNC_MASK;
2786*4882a593Smuzhiyun crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
2787*4882a593Smuzhiyun
2788*4882a593Smuzhiyun crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
2789*4882a593Smuzhiyun }
2790*4882a593Smuzhiyun rcu_read_unlock();
2791*4882a593Smuzhiyun }
2792*4882a593Smuzhiyun
2793*4882a593Smuzhiyun return crc;
2794*4882a593Smuzhiyun }
2795*4882a593Smuzhiyun
2796*4882a593Smuzhiyun /**
2797*4882a593Smuzhiyun * batadv_tt_req_node_release() - free tt_req node entry
2798*4882a593Smuzhiyun * @ref: kref pointer of the tt req_node entry
2799*4882a593Smuzhiyun */
batadv_tt_req_node_release(struct kref * ref)2800*4882a593Smuzhiyun static void batadv_tt_req_node_release(struct kref *ref)
2801*4882a593Smuzhiyun {
2802*4882a593Smuzhiyun struct batadv_tt_req_node *tt_req_node;
2803*4882a593Smuzhiyun
2804*4882a593Smuzhiyun tt_req_node = container_of(ref, struct batadv_tt_req_node, refcount);
2805*4882a593Smuzhiyun
2806*4882a593Smuzhiyun kmem_cache_free(batadv_tt_req_cache, tt_req_node);
2807*4882a593Smuzhiyun }
2808*4882a593Smuzhiyun
2809*4882a593Smuzhiyun /**
2810*4882a593Smuzhiyun * batadv_tt_req_node_put() - decrement the tt_req_node refcounter and
2811*4882a593Smuzhiyun * possibly release it
2812*4882a593Smuzhiyun * @tt_req_node: tt_req_node to be free'd
2813*4882a593Smuzhiyun */
batadv_tt_req_node_put(struct batadv_tt_req_node * tt_req_node)2814*4882a593Smuzhiyun static void batadv_tt_req_node_put(struct batadv_tt_req_node *tt_req_node)
2815*4882a593Smuzhiyun {
2816*4882a593Smuzhiyun if (!tt_req_node)
2817*4882a593Smuzhiyun return;
2818*4882a593Smuzhiyun
2819*4882a593Smuzhiyun kref_put(&tt_req_node->refcount, batadv_tt_req_node_release);
2820*4882a593Smuzhiyun }
2821*4882a593Smuzhiyun
batadv_tt_req_list_free(struct batadv_priv * bat_priv)2822*4882a593Smuzhiyun static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
2823*4882a593Smuzhiyun {
2824*4882a593Smuzhiyun struct batadv_tt_req_node *node;
2825*4882a593Smuzhiyun struct hlist_node *safe;
2826*4882a593Smuzhiyun
2827*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.req_list_lock);
2828*4882a593Smuzhiyun
2829*4882a593Smuzhiyun hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
2830*4882a593Smuzhiyun hlist_del_init(&node->list);
2831*4882a593Smuzhiyun batadv_tt_req_node_put(node);
2832*4882a593Smuzhiyun }
2833*4882a593Smuzhiyun
2834*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.req_list_lock);
2835*4882a593Smuzhiyun }
2836*4882a593Smuzhiyun
batadv_tt_save_orig_buffer(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,const void * tt_buff,u16 tt_buff_len)2837*4882a593Smuzhiyun static void batadv_tt_save_orig_buffer(struct batadv_priv *bat_priv,
2838*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
2839*4882a593Smuzhiyun const void *tt_buff,
2840*4882a593Smuzhiyun u16 tt_buff_len)
2841*4882a593Smuzhiyun {
2842*4882a593Smuzhiyun /* Replace the old buffer only if I received something in the
2843*4882a593Smuzhiyun * last OGM (the OGM could carry no changes)
2844*4882a593Smuzhiyun */
2845*4882a593Smuzhiyun spin_lock_bh(&orig_node->tt_buff_lock);
2846*4882a593Smuzhiyun if (tt_buff_len > 0) {
2847*4882a593Smuzhiyun kfree(orig_node->tt_buff);
2848*4882a593Smuzhiyun orig_node->tt_buff_len = 0;
2849*4882a593Smuzhiyun orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC);
2850*4882a593Smuzhiyun if (orig_node->tt_buff) {
2851*4882a593Smuzhiyun memcpy(orig_node->tt_buff, tt_buff, tt_buff_len);
2852*4882a593Smuzhiyun orig_node->tt_buff_len = tt_buff_len;
2853*4882a593Smuzhiyun }
2854*4882a593Smuzhiyun }
2855*4882a593Smuzhiyun spin_unlock_bh(&orig_node->tt_buff_lock);
2856*4882a593Smuzhiyun }
2857*4882a593Smuzhiyun
batadv_tt_req_purge(struct batadv_priv * bat_priv)2858*4882a593Smuzhiyun static void batadv_tt_req_purge(struct batadv_priv *bat_priv)
2859*4882a593Smuzhiyun {
2860*4882a593Smuzhiyun struct batadv_tt_req_node *node;
2861*4882a593Smuzhiyun struct hlist_node *safe;
2862*4882a593Smuzhiyun
2863*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.req_list_lock);
2864*4882a593Smuzhiyun hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
2865*4882a593Smuzhiyun if (batadv_has_timed_out(node->issued_at,
2866*4882a593Smuzhiyun BATADV_TT_REQUEST_TIMEOUT)) {
2867*4882a593Smuzhiyun hlist_del_init(&node->list);
2868*4882a593Smuzhiyun batadv_tt_req_node_put(node);
2869*4882a593Smuzhiyun }
2870*4882a593Smuzhiyun }
2871*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.req_list_lock);
2872*4882a593Smuzhiyun }
2873*4882a593Smuzhiyun
2874*4882a593Smuzhiyun /**
2875*4882a593Smuzhiyun * batadv_tt_req_node_new() - search and possibly create a tt_req_node object
2876*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2877*4882a593Smuzhiyun * @orig_node: orig node this request is being issued for
2878*4882a593Smuzhiyun *
2879*4882a593Smuzhiyun * Return: the pointer to the new tt_req_node struct if no request
2880*4882a593Smuzhiyun * has already been issued for this orig_node, NULL otherwise.
2881*4882a593Smuzhiyun */
2882*4882a593Smuzhiyun static struct batadv_tt_req_node *
batadv_tt_req_node_new(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node)2883*4882a593Smuzhiyun batadv_tt_req_node_new(struct batadv_priv *bat_priv,
2884*4882a593Smuzhiyun struct batadv_orig_node *orig_node)
2885*4882a593Smuzhiyun {
2886*4882a593Smuzhiyun struct batadv_tt_req_node *tt_req_node_tmp, *tt_req_node = NULL;
2887*4882a593Smuzhiyun
2888*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.req_list_lock);
2889*4882a593Smuzhiyun hlist_for_each_entry(tt_req_node_tmp, &bat_priv->tt.req_list, list) {
2890*4882a593Smuzhiyun if (batadv_compare_eth(tt_req_node_tmp, orig_node) &&
2891*4882a593Smuzhiyun !batadv_has_timed_out(tt_req_node_tmp->issued_at,
2892*4882a593Smuzhiyun BATADV_TT_REQUEST_TIMEOUT))
2893*4882a593Smuzhiyun goto unlock;
2894*4882a593Smuzhiyun }
2895*4882a593Smuzhiyun
2896*4882a593Smuzhiyun tt_req_node = kmem_cache_alloc(batadv_tt_req_cache, GFP_ATOMIC);
2897*4882a593Smuzhiyun if (!tt_req_node)
2898*4882a593Smuzhiyun goto unlock;
2899*4882a593Smuzhiyun
2900*4882a593Smuzhiyun kref_init(&tt_req_node->refcount);
2901*4882a593Smuzhiyun ether_addr_copy(tt_req_node->addr, orig_node->orig);
2902*4882a593Smuzhiyun tt_req_node->issued_at = jiffies;
2903*4882a593Smuzhiyun
2904*4882a593Smuzhiyun kref_get(&tt_req_node->refcount);
2905*4882a593Smuzhiyun hlist_add_head(&tt_req_node->list, &bat_priv->tt.req_list);
2906*4882a593Smuzhiyun unlock:
2907*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.req_list_lock);
2908*4882a593Smuzhiyun return tt_req_node;
2909*4882a593Smuzhiyun }
2910*4882a593Smuzhiyun
2911*4882a593Smuzhiyun /**
2912*4882a593Smuzhiyun * batadv_tt_local_valid() - verify local tt entry and get flags
2913*4882a593Smuzhiyun * @entry_ptr: to be checked local tt entry
2914*4882a593Smuzhiyun * @data_ptr: not used but definition required to satisfy the callback prototype
2915*4882a593Smuzhiyun * @flags: a pointer to store TT flags for this client to
2916*4882a593Smuzhiyun *
2917*4882a593Smuzhiyun * Checks the validity of the given local TT entry. If it is, then the provided
2918*4882a593Smuzhiyun * flags pointer is updated.
2919*4882a593Smuzhiyun *
2920*4882a593Smuzhiyun * Return: true if the entry is a valid, false otherwise.
2921*4882a593Smuzhiyun */
batadv_tt_local_valid(const void * entry_ptr,const void * data_ptr,u8 * flags)2922*4882a593Smuzhiyun static bool batadv_tt_local_valid(const void *entry_ptr,
2923*4882a593Smuzhiyun const void *data_ptr,
2924*4882a593Smuzhiyun u8 *flags)
2925*4882a593Smuzhiyun {
2926*4882a593Smuzhiyun const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
2927*4882a593Smuzhiyun
2928*4882a593Smuzhiyun if (tt_common_entry->flags & BATADV_TT_CLIENT_NEW)
2929*4882a593Smuzhiyun return false;
2930*4882a593Smuzhiyun
2931*4882a593Smuzhiyun if (flags)
2932*4882a593Smuzhiyun *flags = tt_common_entry->flags;
2933*4882a593Smuzhiyun
2934*4882a593Smuzhiyun return true;
2935*4882a593Smuzhiyun }
2936*4882a593Smuzhiyun
2937*4882a593Smuzhiyun /**
2938*4882a593Smuzhiyun * batadv_tt_global_valid() - verify global tt entry and get flags
2939*4882a593Smuzhiyun * @entry_ptr: to be checked global tt entry
2940*4882a593Smuzhiyun * @data_ptr: an orig_node object (may be NULL)
2941*4882a593Smuzhiyun * @flags: a pointer to store TT flags for this client to
2942*4882a593Smuzhiyun *
2943*4882a593Smuzhiyun * Checks the validity of the given global TT entry. If it is, then the provided
2944*4882a593Smuzhiyun * flags pointer is updated either with the common (summed) TT flags if data_ptr
2945*4882a593Smuzhiyun * is NULL or the specific, per originator TT flags otherwise.
2946*4882a593Smuzhiyun *
2947*4882a593Smuzhiyun * Return: true if the entry is a valid, false otherwise.
2948*4882a593Smuzhiyun */
batadv_tt_global_valid(const void * entry_ptr,const void * data_ptr,u8 * flags)2949*4882a593Smuzhiyun static bool batadv_tt_global_valid(const void *entry_ptr,
2950*4882a593Smuzhiyun const void *data_ptr,
2951*4882a593Smuzhiyun u8 *flags)
2952*4882a593Smuzhiyun {
2953*4882a593Smuzhiyun const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
2954*4882a593Smuzhiyun const struct batadv_tt_global_entry *tt_global_entry;
2955*4882a593Smuzhiyun const struct batadv_orig_node *orig_node = data_ptr;
2956*4882a593Smuzhiyun
2957*4882a593Smuzhiyun if (tt_common_entry->flags & BATADV_TT_CLIENT_ROAM ||
2958*4882a593Smuzhiyun tt_common_entry->flags & BATADV_TT_CLIENT_TEMP)
2959*4882a593Smuzhiyun return false;
2960*4882a593Smuzhiyun
2961*4882a593Smuzhiyun tt_global_entry = container_of(tt_common_entry,
2962*4882a593Smuzhiyun struct batadv_tt_global_entry,
2963*4882a593Smuzhiyun common);
2964*4882a593Smuzhiyun
2965*4882a593Smuzhiyun return batadv_tt_global_entry_has_orig(tt_global_entry, orig_node,
2966*4882a593Smuzhiyun flags);
2967*4882a593Smuzhiyun }
2968*4882a593Smuzhiyun
2969*4882a593Smuzhiyun /**
2970*4882a593Smuzhiyun * batadv_tt_tvlv_generate() - fill the tvlv buff with the tt entries from the
2971*4882a593Smuzhiyun * specified tt hash
2972*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
2973*4882a593Smuzhiyun * @hash: hash table containing the tt entries
2974*4882a593Smuzhiyun * @tt_len: expected tvlv tt data buffer length in number of bytes
2975*4882a593Smuzhiyun * @tvlv_buff: pointer to the buffer to fill with the TT data
2976*4882a593Smuzhiyun * @valid_cb: function to filter tt change entries and to return TT flags
2977*4882a593Smuzhiyun * @cb_data: data passed to the filter function as argument
2978*4882a593Smuzhiyun *
2979*4882a593Smuzhiyun * Fills the tvlv buff with the tt entries from the specified hash. If valid_cb
2980*4882a593Smuzhiyun * is not provided then this becomes a no-op.
2981*4882a593Smuzhiyun */
batadv_tt_tvlv_generate(struct batadv_priv * bat_priv,struct batadv_hashtable * hash,void * tvlv_buff,u16 tt_len,bool (* valid_cb)(const void *,const void *,u8 * flags),void * cb_data)2982*4882a593Smuzhiyun static void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv,
2983*4882a593Smuzhiyun struct batadv_hashtable *hash,
2984*4882a593Smuzhiyun void *tvlv_buff, u16 tt_len,
2985*4882a593Smuzhiyun bool (*valid_cb)(const void *,
2986*4882a593Smuzhiyun const void *,
2987*4882a593Smuzhiyun u8 *flags),
2988*4882a593Smuzhiyun void *cb_data)
2989*4882a593Smuzhiyun {
2990*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
2991*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change;
2992*4882a593Smuzhiyun struct hlist_head *head;
2993*4882a593Smuzhiyun u16 tt_tot, tt_num_entries = 0;
2994*4882a593Smuzhiyun u8 flags;
2995*4882a593Smuzhiyun bool ret;
2996*4882a593Smuzhiyun u32 i;
2997*4882a593Smuzhiyun
2998*4882a593Smuzhiyun tt_tot = batadv_tt_entries(tt_len);
2999*4882a593Smuzhiyun tt_change = (struct batadv_tvlv_tt_change *)tvlv_buff;
3000*4882a593Smuzhiyun
3001*4882a593Smuzhiyun if (!valid_cb)
3002*4882a593Smuzhiyun return;
3003*4882a593Smuzhiyun
3004*4882a593Smuzhiyun rcu_read_lock();
3005*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
3006*4882a593Smuzhiyun head = &hash->table[i];
3007*4882a593Smuzhiyun
3008*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt_common_entry,
3009*4882a593Smuzhiyun head, hash_entry) {
3010*4882a593Smuzhiyun if (tt_tot == tt_num_entries)
3011*4882a593Smuzhiyun break;
3012*4882a593Smuzhiyun
3013*4882a593Smuzhiyun ret = valid_cb(tt_common_entry, cb_data, &flags);
3014*4882a593Smuzhiyun if (!ret)
3015*4882a593Smuzhiyun continue;
3016*4882a593Smuzhiyun
3017*4882a593Smuzhiyun ether_addr_copy(tt_change->addr, tt_common_entry->addr);
3018*4882a593Smuzhiyun tt_change->flags = flags;
3019*4882a593Smuzhiyun tt_change->vid = htons(tt_common_entry->vid);
3020*4882a593Smuzhiyun memset(tt_change->reserved, 0,
3021*4882a593Smuzhiyun sizeof(tt_change->reserved));
3022*4882a593Smuzhiyun
3023*4882a593Smuzhiyun tt_num_entries++;
3024*4882a593Smuzhiyun tt_change++;
3025*4882a593Smuzhiyun }
3026*4882a593Smuzhiyun }
3027*4882a593Smuzhiyun rcu_read_unlock();
3028*4882a593Smuzhiyun }
3029*4882a593Smuzhiyun
3030*4882a593Smuzhiyun /**
3031*4882a593Smuzhiyun * batadv_tt_global_check_crc() - check if all the CRCs are correct
3032*4882a593Smuzhiyun * @orig_node: originator for which the CRCs have to be checked
3033*4882a593Smuzhiyun * @tt_vlan: pointer to the first tvlv VLAN entry
3034*4882a593Smuzhiyun * @num_vlan: number of tvlv VLAN entries
3035*4882a593Smuzhiyun *
3036*4882a593Smuzhiyun * Return: true if all the received CRCs match the locally stored ones, false
3037*4882a593Smuzhiyun * otherwise
3038*4882a593Smuzhiyun */
batadv_tt_global_check_crc(struct batadv_orig_node * orig_node,struct batadv_tvlv_tt_vlan_data * tt_vlan,u16 num_vlan)3039*4882a593Smuzhiyun static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
3040*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan,
3041*4882a593Smuzhiyun u16 num_vlan)
3042*4882a593Smuzhiyun {
3043*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
3044*4882a593Smuzhiyun struct batadv_orig_node_vlan *vlan;
3045*4882a593Smuzhiyun int i, orig_num_vlan;
3046*4882a593Smuzhiyun u32 crc;
3047*4882a593Smuzhiyun
3048*4882a593Smuzhiyun /* check if each received CRC matches the locally stored one */
3049*4882a593Smuzhiyun for (i = 0; i < num_vlan; i++) {
3050*4882a593Smuzhiyun tt_vlan_tmp = tt_vlan + i;
3051*4882a593Smuzhiyun
3052*4882a593Smuzhiyun /* if orig_node is a backbone node for this VLAN, don't check
3053*4882a593Smuzhiyun * the CRC as we ignore all the global entries over it
3054*4882a593Smuzhiyun */
3055*4882a593Smuzhiyun if (batadv_bla_is_backbone_gw_orig(orig_node->bat_priv,
3056*4882a593Smuzhiyun orig_node->orig,
3057*4882a593Smuzhiyun ntohs(tt_vlan_tmp->vid)))
3058*4882a593Smuzhiyun continue;
3059*4882a593Smuzhiyun
3060*4882a593Smuzhiyun vlan = batadv_orig_node_vlan_get(orig_node,
3061*4882a593Smuzhiyun ntohs(tt_vlan_tmp->vid));
3062*4882a593Smuzhiyun if (!vlan)
3063*4882a593Smuzhiyun return false;
3064*4882a593Smuzhiyun
3065*4882a593Smuzhiyun crc = vlan->tt.crc;
3066*4882a593Smuzhiyun batadv_orig_node_vlan_put(vlan);
3067*4882a593Smuzhiyun
3068*4882a593Smuzhiyun if (crc != ntohl(tt_vlan_tmp->crc))
3069*4882a593Smuzhiyun return false;
3070*4882a593Smuzhiyun }
3071*4882a593Smuzhiyun
3072*4882a593Smuzhiyun /* check if any excess VLANs exist locally for the originator
3073*4882a593Smuzhiyun * which are not mentioned in the TVLV from the originator.
3074*4882a593Smuzhiyun */
3075*4882a593Smuzhiyun rcu_read_lock();
3076*4882a593Smuzhiyun orig_num_vlan = 0;
3077*4882a593Smuzhiyun hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list)
3078*4882a593Smuzhiyun orig_num_vlan++;
3079*4882a593Smuzhiyun rcu_read_unlock();
3080*4882a593Smuzhiyun
3081*4882a593Smuzhiyun if (orig_num_vlan > num_vlan)
3082*4882a593Smuzhiyun return false;
3083*4882a593Smuzhiyun
3084*4882a593Smuzhiyun return true;
3085*4882a593Smuzhiyun }
3086*4882a593Smuzhiyun
3087*4882a593Smuzhiyun /**
3088*4882a593Smuzhiyun * batadv_tt_local_update_crc() - update all the local CRCs
3089*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3090*4882a593Smuzhiyun */
batadv_tt_local_update_crc(struct batadv_priv * bat_priv)3091*4882a593Smuzhiyun static void batadv_tt_local_update_crc(struct batadv_priv *bat_priv)
3092*4882a593Smuzhiyun {
3093*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
3094*4882a593Smuzhiyun
3095*4882a593Smuzhiyun /* recompute the global CRC for each VLAN */
3096*4882a593Smuzhiyun rcu_read_lock();
3097*4882a593Smuzhiyun hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
3098*4882a593Smuzhiyun vlan->tt.crc = batadv_tt_local_crc(bat_priv, vlan->vid);
3099*4882a593Smuzhiyun }
3100*4882a593Smuzhiyun rcu_read_unlock();
3101*4882a593Smuzhiyun }
3102*4882a593Smuzhiyun
3103*4882a593Smuzhiyun /**
3104*4882a593Smuzhiyun * batadv_tt_global_update_crc() - update all the global CRCs for this orig_node
3105*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3106*4882a593Smuzhiyun * @orig_node: the orig_node for which the CRCs have to be updated
3107*4882a593Smuzhiyun */
batadv_tt_global_update_crc(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node)3108*4882a593Smuzhiyun static void batadv_tt_global_update_crc(struct batadv_priv *bat_priv,
3109*4882a593Smuzhiyun struct batadv_orig_node *orig_node)
3110*4882a593Smuzhiyun {
3111*4882a593Smuzhiyun struct batadv_orig_node_vlan *vlan;
3112*4882a593Smuzhiyun u32 crc;
3113*4882a593Smuzhiyun
3114*4882a593Smuzhiyun /* recompute the global CRC for each VLAN */
3115*4882a593Smuzhiyun rcu_read_lock();
3116*4882a593Smuzhiyun hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
3117*4882a593Smuzhiyun /* if orig_node is a backbone node for this VLAN, don't compute
3118*4882a593Smuzhiyun * the CRC as we ignore all the global entries over it
3119*4882a593Smuzhiyun */
3120*4882a593Smuzhiyun if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig,
3121*4882a593Smuzhiyun vlan->vid))
3122*4882a593Smuzhiyun continue;
3123*4882a593Smuzhiyun
3124*4882a593Smuzhiyun crc = batadv_tt_global_crc(bat_priv, orig_node, vlan->vid);
3125*4882a593Smuzhiyun vlan->tt.crc = crc;
3126*4882a593Smuzhiyun }
3127*4882a593Smuzhiyun rcu_read_unlock();
3128*4882a593Smuzhiyun }
3129*4882a593Smuzhiyun
3130*4882a593Smuzhiyun /**
3131*4882a593Smuzhiyun * batadv_send_tt_request() - send a TT Request message to a given node
3132*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3133*4882a593Smuzhiyun * @dst_orig_node: the destination of the message
3134*4882a593Smuzhiyun * @ttvn: the version number that the source of the message is looking for
3135*4882a593Smuzhiyun * @tt_vlan: pointer to the first tvlv VLAN object to request
3136*4882a593Smuzhiyun * @num_vlan: number of tvlv VLAN entries
3137*4882a593Smuzhiyun * @full_table: ask for the entire translation table if true, while only for the
3138*4882a593Smuzhiyun * last TT diff otherwise
3139*4882a593Smuzhiyun *
3140*4882a593Smuzhiyun * Return: true if the TT Request was sent, false otherwise
3141*4882a593Smuzhiyun */
batadv_send_tt_request(struct batadv_priv * bat_priv,struct batadv_orig_node * dst_orig_node,u8 ttvn,struct batadv_tvlv_tt_vlan_data * tt_vlan,u16 num_vlan,bool full_table)3142*4882a593Smuzhiyun static bool batadv_send_tt_request(struct batadv_priv *bat_priv,
3143*4882a593Smuzhiyun struct batadv_orig_node *dst_orig_node,
3144*4882a593Smuzhiyun u8 ttvn,
3145*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan,
3146*4882a593Smuzhiyun u16 num_vlan, bool full_table)
3147*4882a593Smuzhiyun {
3148*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
3149*4882a593Smuzhiyun struct batadv_tt_req_node *tt_req_node = NULL;
3150*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan_req;
3151*4882a593Smuzhiyun struct batadv_hard_iface *primary_if;
3152*4882a593Smuzhiyun bool ret = false;
3153*4882a593Smuzhiyun int i, size;
3154*4882a593Smuzhiyun
3155*4882a593Smuzhiyun primary_if = batadv_primary_if_get_selected(bat_priv);
3156*4882a593Smuzhiyun if (!primary_if)
3157*4882a593Smuzhiyun goto out;
3158*4882a593Smuzhiyun
3159*4882a593Smuzhiyun /* The new tt_req will be issued only if I'm not waiting for a
3160*4882a593Smuzhiyun * reply from the same orig_node yet
3161*4882a593Smuzhiyun */
3162*4882a593Smuzhiyun tt_req_node = batadv_tt_req_node_new(bat_priv, dst_orig_node);
3163*4882a593Smuzhiyun if (!tt_req_node)
3164*4882a593Smuzhiyun goto out;
3165*4882a593Smuzhiyun
3166*4882a593Smuzhiyun size = sizeof(*tvlv_tt_data) + sizeof(*tt_vlan_req) * num_vlan;
3167*4882a593Smuzhiyun tvlv_tt_data = kzalloc(size, GFP_ATOMIC);
3168*4882a593Smuzhiyun if (!tvlv_tt_data)
3169*4882a593Smuzhiyun goto out;
3170*4882a593Smuzhiyun
3171*4882a593Smuzhiyun tvlv_tt_data->flags = BATADV_TT_REQUEST;
3172*4882a593Smuzhiyun tvlv_tt_data->ttvn = ttvn;
3173*4882a593Smuzhiyun tvlv_tt_data->num_vlan = htons(num_vlan);
3174*4882a593Smuzhiyun
3175*4882a593Smuzhiyun /* send all the CRCs within the request. This is needed by intermediate
3176*4882a593Smuzhiyun * nodes to ensure they have the correct table before replying
3177*4882a593Smuzhiyun */
3178*4882a593Smuzhiyun tt_vlan_req = (struct batadv_tvlv_tt_vlan_data *)(tvlv_tt_data + 1);
3179*4882a593Smuzhiyun for (i = 0; i < num_vlan; i++) {
3180*4882a593Smuzhiyun tt_vlan_req->vid = tt_vlan->vid;
3181*4882a593Smuzhiyun tt_vlan_req->crc = tt_vlan->crc;
3182*4882a593Smuzhiyun
3183*4882a593Smuzhiyun tt_vlan_req++;
3184*4882a593Smuzhiyun tt_vlan++;
3185*4882a593Smuzhiyun }
3186*4882a593Smuzhiyun
3187*4882a593Smuzhiyun if (full_table)
3188*4882a593Smuzhiyun tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
3189*4882a593Smuzhiyun
3190*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n",
3191*4882a593Smuzhiyun dst_orig_node->orig, full_table ? 'F' : '.');
3192*4882a593Smuzhiyun
3193*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
3194*4882a593Smuzhiyun batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
3195*4882a593Smuzhiyun dst_orig_node->orig, BATADV_TVLV_TT, 1,
3196*4882a593Smuzhiyun tvlv_tt_data, size);
3197*4882a593Smuzhiyun ret = true;
3198*4882a593Smuzhiyun
3199*4882a593Smuzhiyun out:
3200*4882a593Smuzhiyun if (primary_if)
3201*4882a593Smuzhiyun batadv_hardif_put(primary_if);
3202*4882a593Smuzhiyun
3203*4882a593Smuzhiyun if (ret && tt_req_node) {
3204*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.req_list_lock);
3205*4882a593Smuzhiyun if (!hlist_unhashed(&tt_req_node->list)) {
3206*4882a593Smuzhiyun hlist_del_init(&tt_req_node->list);
3207*4882a593Smuzhiyun batadv_tt_req_node_put(tt_req_node);
3208*4882a593Smuzhiyun }
3209*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.req_list_lock);
3210*4882a593Smuzhiyun }
3211*4882a593Smuzhiyun
3212*4882a593Smuzhiyun if (tt_req_node)
3213*4882a593Smuzhiyun batadv_tt_req_node_put(tt_req_node);
3214*4882a593Smuzhiyun
3215*4882a593Smuzhiyun kfree(tvlv_tt_data);
3216*4882a593Smuzhiyun return ret;
3217*4882a593Smuzhiyun }
3218*4882a593Smuzhiyun
3219*4882a593Smuzhiyun /**
3220*4882a593Smuzhiyun * batadv_send_other_tt_response() - send reply to tt request concerning another
3221*4882a593Smuzhiyun * node's translation table
3222*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3223*4882a593Smuzhiyun * @tt_data: tt data containing the tt request information
3224*4882a593Smuzhiyun * @req_src: mac address of tt request sender
3225*4882a593Smuzhiyun * @req_dst: mac address of tt request recipient
3226*4882a593Smuzhiyun *
3227*4882a593Smuzhiyun * Return: true if tt request reply was sent, false otherwise.
3228*4882a593Smuzhiyun */
batadv_send_other_tt_response(struct batadv_priv * bat_priv,struct batadv_tvlv_tt_data * tt_data,u8 * req_src,u8 * req_dst)3229*4882a593Smuzhiyun static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv,
3230*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data,
3231*4882a593Smuzhiyun u8 *req_src, u8 *req_dst)
3232*4882a593Smuzhiyun {
3233*4882a593Smuzhiyun struct batadv_orig_node *req_dst_orig_node;
3234*4882a593Smuzhiyun struct batadv_orig_node *res_dst_orig_node = NULL;
3235*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change;
3236*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
3237*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan;
3238*4882a593Smuzhiyun bool ret = false, full_table;
3239*4882a593Smuzhiyun u8 orig_ttvn, req_ttvn;
3240*4882a593Smuzhiyun u16 tvlv_len;
3241*4882a593Smuzhiyun s32 tt_len;
3242*4882a593Smuzhiyun
3243*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3244*4882a593Smuzhiyun "Received TT_REQUEST from %pM for ttvn: %u (%pM) [%c]\n",
3245*4882a593Smuzhiyun req_src, tt_data->ttvn, req_dst,
3246*4882a593Smuzhiyun ((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
3247*4882a593Smuzhiyun
3248*4882a593Smuzhiyun /* Let's get the orig node of the REAL destination */
3249*4882a593Smuzhiyun req_dst_orig_node = batadv_orig_hash_find(bat_priv, req_dst);
3250*4882a593Smuzhiyun if (!req_dst_orig_node)
3251*4882a593Smuzhiyun goto out;
3252*4882a593Smuzhiyun
3253*4882a593Smuzhiyun res_dst_orig_node = batadv_orig_hash_find(bat_priv, req_src);
3254*4882a593Smuzhiyun if (!res_dst_orig_node)
3255*4882a593Smuzhiyun goto out;
3256*4882a593Smuzhiyun
3257*4882a593Smuzhiyun orig_ttvn = (u8)atomic_read(&req_dst_orig_node->last_ttvn);
3258*4882a593Smuzhiyun req_ttvn = tt_data->ttvn;
3259*4882a593Smuzhiyun
3260*4882a593Smuzhiyun tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
3261*4882a593Smuzhiyun /* this node doesn't have the requested data */
3262*4882a593Smuzhiyun if (orig_ttvn != req_ttvn ||
3263*4882a593Smuzhiyun !batadv_tt_global_check_crc(req_dst_orig_node, tt_vlan,
3264*4882a593Smuzhiyun ntohs(tt_data->num_vlan)))
3265*4882a593Smuzhiyun goto out;
3266*4882a593Smuzhiyun
3267*4882a593Smuzhiyun /* If the full table has been explicitly requested */
3268*4882a593Smuzhiyun if (tt_data->flags & BATADV_TT_FULL_TABLE ||
3269*4882a593Smuzhiyun !req_dst_orig_node->tt_buff)
3270*4882a593Smuzhiyun full_table = true;
3271*4882a593Smuzhiyun else
3272*4882a593Smuzhiyun full_table = false;
3273*4882a593Smuzhiyun
3274*4882a593Smuzhiyun /* TT fragmentation hasn't been implemented yet, so send as many
3275*4882a593Smuzhiyun * TT entries fit a single packet as possible only
3276*4882a593Smuzhiyun */
3277*4882a593Smuzhiyun if (!full_table) {
3278*4882a593Smuzhiyun spin_lock_bh(&req_dst_orig_node->tt_buff_lock);
3279*4882a593Smuzhiyun tt_len = req_dst_orig_node->tt_buff_len;
3280*4882a593Smuzhiyun
3281*4882a593Smuzhiyun tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
3282*4882a593Smuzhiyun &tvlv_tt_data,
3283*4882a593Smuzhiyun &tt_change,
3284*4882a593Smuzhiyun &tt_len);
3285*4882a593Smuzhiyun if (!tt_len)
3286*4882a593Smuzhiyun goto unlock;
3287*4882a593Smuzhiyun
3288*4882a593Smuzhiyun /* Copy the last orig_node's OGM buffer */
3289*4882a593Smuzhiyun memcpy(tt_change, req_dst_orig_node->tt_buff,
3290*4882a593Smuzhiyun req_dst_orig_node->tt_buff_len);
3291*4882a593Smuzhiyun spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
3292*4882a593Smuzhiyun } else {
3293*4882a593Smuzhiyun /* allocate the tvlv, put the tt_data and all the tt_vlan_data
3294*4882a593Smuzhiyun * in the initial part
3295*4882a593Smuzhiyun */
3296*4882a593Smuzhiyun tt_len = -1;
3297*4882a593Smuzhiyun tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
3298*4882a593Smuzhiyun &tvlv_tt_data,
3299*4882a593Smuzhiyun &tt_change,
3300*4882a593Smuzhiyun &tt_len);
3301*4882a593Smuzhiyun if (!tt_len)
3302*4882a593Smuzhiyun goto out;
3303*4882a593Smuzhiyun
3304*4882a593Smuzhiyun /* fill the rest of the tvlv with the real TT entries */
3305*4882a593Smuzhiyun batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.global_hash,
3306*4882a593Smuzhiyun tt_change, tt_len,
3307*4882a593Smuzhiyun batadv_tt_global_valid,
3308*4882a593Smuzhiyun req_dst_orig_node);
3309*4882a593Smuzhiyun }
3310*4882a593Smuzhiyun
3311*4882a593Smuzhiyun /* Don't send the response, if larger than fragmented packet. */
3312*4882a593Smuzhiyun tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len;
3313*4882a593Smuzhiyun if (tt_len > atomic_read(&bat_priv->packet_size_max)) {
3314*4882a593Smuzhiyun net_ratelimited_function(batadv_info, bat_priv->soft_iface,
3315*4882a593Smuzhiyun "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n",
3316*4882a593Smuzhiyun res_dst_orig_node->orig);
3317*4882a593Smuzhiyun goto out;
3318*4882a593Smuzhiyun }
3319*4882a593Smuzhiyun
3320*4882a593Smuzhiyun tvlv_tt_data->flags = BATADV_TT_RESPONSE;
3321*4882a593Smuzhiyun tvlv_tt_data->ttvn = req_ttvn;
3322*4882a593Smuzhiyun
3323*4882a593Smuzhiyun if (full_table)
3324*4882a593Smuzhiyun tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
3325*4882a593Smuzhiyun
3326*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3327*4882a593Smuzhiyun "Sending TT_RESPONSE %pM for %pM [%c] (ttvn: %u)\n",
3328*4882a593Smuzhiyun res_dst_orig_node->orig, req_dst_orig_node->orig,
3329*4882a593Smuzhiyun full_table ? 'F' : '.', req_ttvn);
3330*4882a593Smuzhiyun
3331*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
3332*4882a593Smuzhiyun
3333*4882a593Smuzhiyun batadv_tvlv_unicast_send(bat_priv, req_dst_orig_node->orig,
3334*4882a593Smuzhiyun req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
3335*4882a593Smuzhiyun tvlv_len);
3336*4882a593Smuzhiyun
3337*4882a593Smuzhiyun ret = true;
3338*4882a593Smuzhiyun goto out;
3339*4882a593Smuzhiyun
3340*4882a593Smuzhiyun unlock:
3341*4882a593Smuzhiyun spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
3342*4882a593Smuzhiyun
3343*4882a593Smuzhiyun out:
3344*4882a593Smuzhiyun if (res_dst_orig_node)
3345*4882a593Smuzhiyun batadv_orig_node_put(res_dst_orig_node);
3346*4882a593Smuzhiyun if (req_dst_orig_node)
3347*4882a593Smuzhiyun batadv_orig_node_put(req_dst_orig_node);
3348*4882a593Smuzhiyun kfree(tvlv_tt_data);
3349*4882a593Smuzhiyun return ret;
3350*4882a593Smuzhiyun }
3351*4882a593Smuzhiyun
3352*4882a593Smuzhiyun /**
3353*4882a593Smuzhiyun * batadv_send_my_tt_response() - send reply to tt request concerning this
3354*4882a593Smuzhiyun * node's translation table
3355*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3356*4882a593Smuzhiyun * @tt_data: tt data containing the tt request information
3357*4882a593Smuzhiyun * @req_src: mac address of tt request sender
3358*4882a593Smuzhiyun *
3359*4882a593Smuzhiyun * Return: true if tt request reply was sent, false otherwise.
3360*4882a593Smuzhiyun */
batadv_send_my_tt_response(struct batadv_priv * bat_priv,struct batadv_tvlv_tt_data * tt_data,u8 * req_src)3361*4882a593Smuzhiyun static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
3362*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data,
3363*4882a593Smuzhiyun u8 *req_src)
3364*4882a593Smuzhiyun {
3365*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
3366*4882a593Smuzhiyun struct batadv_hard_iface *primary_if = NULL;
3367*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change;
3368*4882a593Smuzhiyun struct batadv_orig_node *orig_node;
3369*4882a593Smuzhiyun u8 my_ttvn, req_ttvn;
3370*4882a593Smuzhiyun u16 tvlv_len;
3371*4882a593Smuzhiyun bool full_table;
3372*4882a593Smuzhiyun s32 tt_len;
3373*4882a593Smuzhiyun
3374*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3375*4882a593Smuzhiyun "Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n",
3376*4882a593Smuzhiyun req_src, tt_data->ttvn,
3377*4882a593Smuzhiyun ((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
3378*4882a593Smuzhiyun
3379*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.commit_lock);
3380*4882a593Smuzhiyun
3381*4882a593Smuzhiyun my_ttvn = (u8)atomic_read(&bat_priv->tt.vn);
3382*4882a593Smuzhiyun req_ttvn = tt_data->ttvn;
3383*4882a593Smuzhiyun
3384*4882a593Smuzhiyun orig_node = batadv_orig_hash_find(bat_priv, req_src);
3385*4882a593Smuzhiyun if (!orig_node)
3386*4882a593Smuzhiyun goto out;
3387*4882a593Smuzhiyun
3388*4882a593Smuzhiyun primary_if = batadv_primary_if_get_selected(bat_priv);
3389*4882a593Smuzhiyun if (!primary_if)
3390*4882a593Smuzhiyun goto out;
3391*4882a593Smuzhiyun
3392*4882a593Smuzhiyun /* If the full table has been explicitly requested or the gap
3393*4882a593Smuzhiyun * is too big send the whole local translation table
3394*4882a593Smuzhiyun */
3395*4882a593Smuzhiyun if (tt_data->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn ||
3396*4882a593Smuzhiyun !bat_priv->tt.last_changeset)
3397*4882a593Smuzhiyun full_table = true;
3398*4882a593Smuzhiyun else
3399*4882a593Smuzhiyun full_table = false;
3400*4882a593Smuzhiyun
3401*4882a593Smuzhiyun /* TT fragmentation hasn't been implemented yet, so send as many
3402*4882a593Smuzhiyun * TT entries fit a single packet as possible only
3403*4882a593Smuzhiyun */
3404*4882a593Smuzhiyun if (!full_table) {
3405*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.last_changeset_lock);
3406*4882a593Smuzhiyun
3407*4882a593Smuzhiyun tt_len = bat_priv->tt.last_changeset_len;
3408*4882a593Smuzhiyun tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
3409*4882a593Smuzhiyun &tvlv_tt_data,
3410*4882a593Smuzhiyun &tt_change,
3411*4882a593Smuzhiyun &tt_len);
3412*4882a593Smuzhiyun if (!tt_len || !tvlv_len)
3413*4882a593Smuzhiyun goto unlock;
3414*4882a593Smuzhiyun
3415*4882a593Smuzhiyun /* Copy the last orig_node's OGM buffer */
3416*4882a593Smuzhiyun memcpy(tt_change, bat_priv->tt.last_changeset,
3417*4882a593Smuzhiyun bat_priv->tt.last_changeset_len);
3418*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
3419*4882a593Smuzhiyun } else {
3420*4882a593Smuzhiyun req_ttvn = (u8)atomic_read(&bat_priv->tt.vn);
3421*4882a593Smuzhiyun
3422*4882a593Smuzhiyun /* allocate the tvlv, put the tt_data and all the tt_vlan_data
3423*4882a593Smuzhiyun * in the initial part
3424*4882a593Smuzhiyun */
3425*4882a593Smuzhiyun tt_len = -1;
3426*4882a593Smuzhiyun tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
3427*4882a593Smuzhiyun &tvlv_tt_data,
3428*4882a593Smuzhiyun &tt_change,
3429*4882a593Smuzhiyun &tt_len);
3430*4882a593Smuzhiyun if (!tt_len || !tvlv_len)
3431*4882a593Smuzhiyun goto out;
3432*4882a593Smuzhiyun
3433*4882a593Smuzhiyun /* fill the rest of the tvlv with the real TT entries */
3434*4882a593Smuzhiyun batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.local_hash,
3435*4882a593Smuzhiyun tt_change, tt_len,
3436*4882a593Smuzhiyun batadv_tt_local_valid, NULL);
3437*4882a593Smuzhiyun }
3438*4882a593Smuzhiyun
3439*4882a593Smuzhiyun tvlv_tt_data->flags = BATADV_TT_RESPONSE;
3440*4882a593Smuzhiyun tvlv_tt_data->ttvn = req_ttvn;
3441*4882a593Smuzhiyun
3442*4882a593Smuzhiyun if (full_table)
3443*4882a593Smuzhiyun tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
3444*4882a593Smuzhiyun
3445*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3446*4882a593Smuzhiyun "Sending TT_RESPONSE to %pM [%c] (ttvn: %u)\n",
3447*4882a593Smuzhiyun orig_node->orig, full_table ? 'F' : '.', req_ttvn);
3448*4882a593Smuzhiyun
3449*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
3450*4882a593Smuzhiyun
3451*4882a593Smuzhiyun batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
3452*4882a593Smuzhiyun req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
3453*4882a593Smuzhiyun tvlv_len);
3454*4882a593Smuzhiyun
3455*4882a593Smuzhiyun goto out;
3456*4882a593Smuzhiyun
3457*4882a593Smuzhiyun unlock:
3458*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
3459*4882a593Smuzhiyun out:
3460*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.commit_lock);
3461*4882a593Smuzhiyun if (orig_node)
3462*4882a593Smuzhiyun batadv_orig_node_put(orig_node);
3463*4882a593Smuzhiyun if (primary_if)
3464*4882a593Smuzhiyun batadv_hardif_put(primary_if);
3465*4882a593Smuzhiyun kfree(tvlv_tt_data);
3466*4882a593Smuzhiyun /* The packet was for this host, so it doesn't need to be re-routed */
3467*4882a593Smuzhiyun return true;
3468*4882a593Smuzhiyun }
3469*4882a593Smuzhiyun
3470*4882a593Smuzhiyun /**
3471*4882a593Smuzhiyun * batadv_send_tt_response() - send reply to tt request
3472*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3473*4882a593Smuzhiyun * @tt_data: tt data containing the tt request information
3474*4882a593Smuzhiyun * @req_src: mac address of tt request sender
3475*4882a593Smuzhiyun * @req_dst: mac address of tt request recipient
3476*4882a593Smuzhiyun *
3477*4882a593Smuzhiyun * Return: true if tt request reply was sent, false otherwise.
3478*4882a593Smuzhiyun */
batadv_send_tt_response(struct batadv_priv * bat_priv,struct batadv_tvlv_tt_data * tt_data,u8 * req_src,u8 * req_dst)3479*4882a593Smuzhiyun static bool batadv_send_tt_response(struct batadv_priv *bat_priv,
3480*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data,
3481*4882a593Smuzhiyun u8 *req_src, u8 *req_dst)
3482*4882a593Smuzhiyun {
3483*4882a593Smuzhiyun if (batadv_is_my_mac(bat_priv, req_dst))
3484*4882a593Smuzhiyun return batadv_send_my_tt_response(bat_priv, tt_data, req_src);
3485*4882a593Smuzhiyun return batadv_send_other_tt_response(bat_priv, tt_data, req_src,
3486*4882a593Smuzhiyun req_dst);
3487*4882a593Smuzhiyun }
3488*4882a593Smuzhiyun
_batadv_tt_update_changes(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,struct batadv_tvlv_tt_change * tt_change,u16 tt_num_changes,u8 ttvn)3489*4882a593Smuzhiyun static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
3490*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
3491*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change,
3492*4882a593Smuzhiyun u16 tt_num_changes, u8 ttvn)
3493*4882a593Smuzhiyun {
3494*4882a593Smuzhiyun int i;
3495*4882a593Smuzhiyun int roams;
3496*4882a593Smuzhiyun
3497*4882a593Smuzhiyun for (i = 0; i < tt_num_changes; i++) {
3498*4882a593Smuzhiyun if ((tt_change + i)->flags & BATADV_TT_CLIENT_DEL) {
3499*4882a593Smuzhiyun roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM;
3500*4882a593Smuzhiyun batadv_tt_global_del(bat_priv, orig_node,
3501*4882a593Smuzhiyun (tt_change + i)->addr,
3502*4882a593Smuzhiyun ntohs((tt_change + i)->vid),
3503*4882a593Smuzhiyun "tt removed by changes",
3504*4882a593Smuzhiyun roams);
3505*4882a593Smuzhiyun } else {
3506*4882a593Smuzhiyun if (!batadv_tt_global_add(bat_priv, orig_node,
3507*4882a593Smuzhiyun (tt_change + i)->addr,
3508*4882a593Smuzhiyun ntohs((tt_change + i)->vid),
3509*4882a593Smuzhiyun (tt_change + i)->flags, ttvn))
3510*4882a593Smuzhiyun /* In case of problem while storing a
3511*4882a593Smuzhiyun * global_entry, we stop the updating
3512*4882a593Smuzhiyun * procedure without committing the
3513*4882a593Smuzhiyun * ttvn change. This will avoid to send
3514*4882a593Smuzhiyun * corrupted data on tt_request
3515*4882a593Smuzhiyun */
3516*4882a593Smuzhiyun return;
3517*4882a593Smuzhiyun }
3518*4882a593Smuzhiyun }
3519*4882a593Smuzhiyun set_bit(BATADV_ORIG_CAPA_HAS_TT, &orig_node->capa_initialized);
3520*4882a593Smuzhiyun }
3521*4882a593Smuzhiyun
batadv_tt_fill_gtable(struct batadv_priv * bat_priv,struct batadv_tvlv_tt_change * tt_change,u8 ttvn,u8 * resp_src,u16 num_entries)3522*4882a593Smuzhiyun static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
3523*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change,
3524*4882a593Smuzhiyun u8 ttvn, u8 *resp_src,
3525*4882a593Smuzhiyun u16 num_entries)
3526*4882a593Smuzhiyun {
3527*4882a593Smuzhiyun struct batadv_orig_node *orig_node;
3528*4882a593Smuzhiyun
3529*4882a593Smuzhiyun orig_node = batadv_orig_hash_find(bat_priv, resp_src);
3530*4882a593Smuzhiyun if (!orig_node)
3531*4882a593Smuzhiyun goto out;
3532*4882a593Smuzhiyun
3533*4882a593Smuzhiyun /* Purge the old table first.. */
3534*4882a593Smuzhiyun batadv_tt_global_del_orig(bat_priv, orig_node, -1,
3535*4882a593Smuzhiyun "Received full table");
3536*4882a593Smuzhiyun
3537*4882a593Smuzhiyun _batadv_tt_update_changes(bat_priv, orig_node, tt_change, num_entries,
3538*4882a593Smuzhiyun ttvn);
3539*4882a593Smuzhiyun
3540*4882a593Smuzhiyun spin_lock_bh(&orig_node->tt_buff_lock);
3541*4882a593Smuzhiyun kfree(orig_node->tt_buff);
3542*4882a593Smuzhiyun orig_node->tt_buff_len = 0;
3543*4882a593Smuzhiyun orig_node->tt_buff = NULL;
3544*4882a593Smuzhiyun spin_unlock_bh(&orig_node->tt_buff_lock);
3545*4882a593Smuzhiyun
3546*4882a593Smuzhiyun atomic_set(&orig_node->last_ttvn, ttvn);
3547*4882a593Smuzhiyun
3548*4882a593Smuzhiyun out:
3549*4882a593Smuzhiyun if (orig_node)
3550*4882a593Smuzhiyun batadv_orig_node_put(orig_node);
3551*4882a593Smuzhiyun }
3552*4882a593Smuzhiyun
batadv_tt_update_changes(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,u16 tt_num_changes,u8 ttvn,struct batadv_tvlv_tt_change * tt_change)3553*4882a593Smuzhiyun static void batadv_tt_update_changes(struct batadv_priv *bat_priv,
3554*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
3555*4882a593Smuzhiyun u16 tt_num_changes, u8 ttvn,
3556*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change)
3557*4882a593Smuzhiyun {
3558*4882a593Smuzhiyun _batadv_tt_update_changes(bat_priv, orig_node, tt_change,
3559*4882a593Smuzhiyun tt_num_changes, ttvn);
3560*4882a593Smuzhiyun
3561*4882a593Smuzhiyun batadv_tt_save_orig_buffer(bat_priv, orig_node, tt_change,
3562*4882a593Smuzhiyun batadv_tt_len(tt_num_changes));
3563*4882a593Smuzhiyun atomic_set(&orig_node->last_ttvn, ttvn);
3564*4882a593Smuzhiyun }
3565*4882a593Smuzhiyun
3566*4882a593Smuzhiyun /**
3567*4882a593Smuzhiyun * batadv_is_my_client() - check if a client is served by the local node
3568*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3569*4882a593Smuzhiyun * @addr: the mac address of the client to check
3570*4882a593Smuzhiyun * @vid: VLAN identifier
3571*4882a593Smuzhiyun *
3572*4882a593Smuzhiyun * Return: true if the client is served by this node, false otherwise.
3573*4882a593Smuzhiyun */
batadv_is_my_client(struct batadv_priv * bat_priv,const u8 * addr,unsigned short vid)3574*4882a593Smuzhiyun bool batadv_is_my_client(struct batadv_priv *bat_priv, const u8 *addr,
3575*4882a593Smuzhiyun unsigned short vid)
3576*4882a593Smuzhiyun {
3577*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
3578*4882a593Smuzhiyun bool ret = false;
3579*4882a593Smuzhiyun
3580*4882a593Smuzhiyun tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
3581*4882a593Smuzhiyun if (!tt_local_entry)
3582*4882a593Smuzhiyun goto out;
3583*4882a593Smuzhiyun /* Check if the client has been logically deleted (but is kept for
3584*4882a593Smuzhiyun * consistency purpose)
3585*4882a593Smuzhiyun */
3586*4882a593Smuzhiyun if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) ||
3587*4882a593Smuzhiyun (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM))
3588*4882a593Smuzhiyun goto out;
3589*4882a593Smuzhiyun ret = true;
3590*4882a593Smuzhiyun out:
3591*4882a593Smuzhiyun if (tt_local_entry)
3592*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local_entry);
3593*4882a593Smuzhiyun return ret;
3594*4882a593Smuzhiyun }
3595*4882a593Smuzhiyun
3596*4882a593Smuzhiyun /**
3597*4882a593Smuzhiyun * batadv_handle_tt_response() - process incoming tt reply
3598*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3599*4882a593Smuzhiyun * @tt_data: tt data containing the tt request information
3600*4882a593Smuzhiyun * @resp_src: mac address of tt reply sender
3601*4882a593Smuzhiyun * @num_entries: number of tt change entries appended to the tt data
3602*4882a593Smuzhiyun */
batadv_handle_tt_response(struct batadv_priv * bat_priv,struct batadv_tvlv_tt_data * tt_data,u8 * resp_src,u16 num_entries)3603*4882a593Smuzhiyun static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
3604*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data,
3605*4882a593Smuzhiyun u8 *resp_src, u16 num_entries)
3606*4882a593Smuzhiyun {
3607*4882a593Smuzhiyun struct batadv_tt_req_node *node;
3608*4882a593Smuzhiyun struct hlist_node *safe;
3609*4882a593Smuzhiyun struct batadv_orig_node *orig_node = NULL;
3610*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change;
3611*4882a593Smuzhiyun u8 *tvlv_ptr = (u8 *)tt_data;
3612*4882a593Smuzhiyun u16 change_offset;
3613*4882a593Smuzhiyun
3614*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3615*4882a593Smuzhiyun "Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n",
3616*4882a593Smuzhiyun resp_src, tt_data->ttvn, num_entries,
3617*4882a593Smuzhiyun ((tt_data->flags & BATADV_TT_FULL_TABLE) ? 'F' : '.'));
3618*4882a593Smuzhiyun
3619*4882a593Smuzhiyun orig_node = batadv_orig_hash_find(bat_priv, resp_src);
3620*4882a593Smuzhiyun if (!orig_node)
3621*4882a593Smuzhiyun goto out;
3622*4882a593Smuzhiyun
3623*4882a593Smuzhiyun spin_lock_bh(&orig_node->tt_lock);
3624*4882a593Smuzhiyun
3625*4882a593Smuzhiyun change_offset = sizeof(struct batadv_tvlv_tt_vlan_data);
3626*4882a593Smuzhiyun change_offset *= ntohs(tt_data->num_vlan);
3627*4882a593Smuzhiyun change_offset += sizeof(*tt_data);
3628*4882a593Smuzhiyun tvlv_ptr += change_offset;
3629*4882a593Smuzhiyun
3630*4882a593Smuzhiyun tt_change = (struct batadv_tvlv_tt_change *)tvlv_ptr;
3631*4882a593Smuzhiyun if (tt_data->flags & BATADV_TT_FULL_TABLE) {
3632*4882a593Smuzhiyun batadv_tt_fill_gtable(bat_priv, tt_change, tt_data->ttvn,
3633*4882a593Smuzhiyun resp_src, num_entries);
3634*4882a593Smuzhiyun } else {
3635*4882a593Smuzhiyun batadv_tt_update_changes(bat_priv, orig_node, num_entries,
3636*4882a593Smuzhiyun tt_data->ttvn, tt_change);
3637*4882a593Smuzhiyun }
3638*4882a593Smuzhiyun
3639*4882a593Smuzhiyun /* Recalculate the CRC for this orig_node and store it */
3640*4882a593Smuzhiyun batadv_tt_global_update_crc(bat_priv, orig_node);
3641*4882a593Smuzhiyun
3642*4882a593Smuzhiyun spin_unlock_bh(&orig_node->tt_lock);
3643*4882a593Smuzhiyun
3644*4882a593Smuzhiyun /* Delete the tt_req_node from pending tt_requests list */
3645*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.req_list_lock);
3646*4882a593Smuzhiyun hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
3647*4882a593Smuzhiyun if (!batadv_compare_eth(node->addr, resp_src))
3648*4882a593Smuzhiyun continue;
3649*4882a593Smuzhiyun hlist_del_init(&node->list);
3650*4882a593Smuzhiyun batadv_tt_req_node_put(node);
3651*4882a593Smuzhiyun }
3652*4882a593Smuzhiyun
3653*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.req_list_lock);
3654*4882a593Smuzhiyun out:
3655*4882a593Smuzhiyun if (orig_node)
3656*4882a593Smuzhiyun batadv_orig_node_put(orig_node);
3657*4882a593Smuzhiyun }
3658*4882a593Smuzhiyun
batadv_tt_roam_list_free(struct batadv_priv * bat_priv)3659*4882a593Smuzhiyun static void batadv_tt_roam_list_free(struct batadv_priv *bat_priv)
3660*4882a593Smuzhiyun {
3661*4882a593Smuzhiyun struct batadv_tt_roam_node *node, *safe;
3662*4882a593Smuzhiyun
3663*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.roam_list_lock);
3664*4882a593Smuzhiyun
3665*4882a593Smuzhiyun list_for_each_entry_safe(node, safe, &bat_priv->tt.roam_list, list) {
3666*4882a593Smuzhiyun list_del(&node->list);
3667*4882a593Smuzhiyun kmem_cache_free(batadv_tt_roam_cache, node);
3668*4882a593Smuzhiyun }
3669*4882a593Smuzhiyun
3670*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.roam_list_lock);
3671*4882a593Smuzhiyun }
3672*4882a593Smuzhiyun
batadv_tt_roam_purge(struct batadv_priv * bat_priv)3673*4882a593Smuzhiyun static void batadv_tt_roam_purge(struct batadv_priv *bat_priv)
3674*4882a593Smuzhiyun {
3675*4882a593Smuzhiyun struct batadv_tt_roam_node *node, *safe;
3676*4882a593Smuzhiyun
3677*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.roam_list_lock);
3678*4882a593Smuzhiyun list_for_each_entry_safe(node, safe, &bat_priv->tt.roam_list, list) {
3679*4882a593Smuzhiyun if (!batadv_has_timed_out(node->first_time,
3680*4882a593Smuzhiyun BATADV_ROAMING_MAX_TIME))
3681*4882a593Smuzhiyun continue;
3682*4882a593Smuzhiyun
3683*4882a593Smuzhiyun list_del(&node->list);
3684*4882a593Smuzhiyun kmem_cache_free(batadv_tt_roam_cache, node);
3685*4882a593Smuzhiyun }
3686*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.roam_list_lock);
3687*4882a593Smuzhiyun }
3688*4882a593Smuzhiyun
3689*4882a593Smuzhiyun /**
3690*4882a593Smuzhiyun * batadv_tt_check_roam_count() - check if a client has roamed too frequently
3691*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3692*4882a593Smuzhiyun * @client: mac address of the roaming client
3693*4882a593Smuzhiyun *
3694*4882a593Smuzhiyun * This function checks whether the client already reached the
3695*4882a593Smuzhiyun * maximum number of possible roaming phases. In this case the ROAMING_ADV
3696*4882a593Smuzhiyun * will not be sent.
3697*4882a593Smuzhiyun *
3698*4882a593Smuzhiyun * Return: true if the ROAMING_ADV can be sent, false otherwise
3699*4882a593Smuzhiyun */
batadv_tt_check_roam_count(struct batadv_priv * bat_priv,u8 * client)3700*4882a593Smuzhiyun static bool batadv_tt_check_roam_count(struct batadv_priv *bat_priv, u8 *client)
3701*4882a593Smuzhiyun {
3702*4882a593Smuzhiyun struct batadv_tt_roam_node *tt_roam_node;
3703*4882a593Smuzhiyun bool ret = false;
3704*4882a593Smuzhiyun
3705*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.roam_list_lock);
3706*4882a593Smuzhiyun /* The new tt_req will be issued only if I'm not waiting for a
3707*4882a593Smuzhiyun * reply from the same orig_node yet
3708*4882a593Smuzhiyun */
3709*4882a593Smuzhiyun list_for_each_entry(tt_roam_node, &bat_priv->tt.roam_list, list) {
3710*4882a593Smuzhiyun if (!batadv_compare_eth(tt_roam_node->addr, client))
3711*4882a593Smuzhiyun continue;
3712*4882a593Smuzhiyun
3713*4882a593Smuzhiyun if (batadv_has_timed_out(tt_roam_node->first_time,
3714*4882a593Smuzhiyun BATADV_ROAMING_MAX_TIME))
3715*4882a593Smuzhiyun continue;
3716*4882a593Smuzhiyun
3717*4882a593Smuzhiyun if (!batadv_atomic_dec_not_zero(&tt_roam_node->counter))
3718*4882a593Smuzhiyun /* Sorry, you roamed too many times! */
3719*4882a593Smuzhiyun goto unlock;
3720*4882a593Smuzhiyun ret = true;
3721*4882a593Smuzhiyun break;
3722*4882a593Smuzhiyun }
3723*4882a593Smuzhiyun
3724*4882a593Smuzhiyun if (!ret) {
3725*4882a593Smuzhiyun tt_roam_node = kmem_cache_alloc(batadv_tt_roam_cache,
3726*4882a593Smuzhiyun GFP_ATOMIC);
3727*4882a593Smuzhiyun if (!tt_roam_node)
3728*4882a593Smuzhiyun goto unlock;
3729*4882a593Smuzhiyun
3730*4882a593Smuzhiyun tt_roam_node->first_time = jiffies;
3731*4882a593Smuzhiyun atomic_set(&tt_roam_node->counter,
3732*4882a593Smuzhiyun BATADV_ROAMING_MAX_COUNT - 1);
3733*4882a593Smuzhiyun ether_addr_copy(tt_roam_node->addr, client);
3734*4882a593Smuzhiyun
3735*4882a593Smuzhiyun list_add(&tt_roam_node->list, &bat_priv->tt.roam_list);
3736*4882a593Smuzhiyun ret = true;
3737*4882a593Smuzhiyun }
3738*4882a593Smuzhiyun
3739*4882a593Smuzhiyun unlock:
3740*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.roam_list_lock);
3741*4882a593Smuzhiyun return ret;
3742*4882a593Smuzhiyun }
3743*4882a593Smuzhiyun
3744*4882a593Smuzhiyun /**
3745*4882a593Smuzhiyun * batadv_send_roam_adv() - send a roaming advertisement message
3746*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3747*4882a593Smuzhiyun * @client: mac address of the roaming client
3748*4882a593Smuzhiyun * @vid: VLAN identifier
3749*4882a593Smuzhiyun * @orig_node: message destination
3750*4882a593Smuzhiyun *
3751*4882a593Smuzhiyun * Send a ROAMING_ADV message to the node which was previously serving this
3752*4882a593Smuzhiyun * client. This is done to inform the node that from now on all traffic destined
3753*4882a593Smuzhiyun * for this particular roamed client has to be forwarded to the sender of the
3754*4882a593Smuzhiyun * roaming message.
3755*4882a593Smuzhiyun */
batadv_send_roam_adv(struct batadv_priv * bat_priv,u8 * client,unsigned short vid,struct batadv_orig_node * orig_node)3756*4882a593Smuzhiyun static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client,
3757*4882a593Smuzhiyun unsigned short vid,
3758*4882a593Smuzhiyun struct batadv_orig_node *orig_node)
3759*4882a593Smuzhiyun {
3760*4882a593Smuzhiyun struct batadv_hard_iface *primary_if;
3761*4882a593Smuzhiyun struct batadv_tvlv_roam_adv tvlv_roam;
3762*4882a593Smuzhiyun
3763*4882a593Smuzhiyun primary_if = batadv_primary_if_get_selected(bat_priv);
3764*4882a593Smuzhiyun if (!primary_if)
3765*4882a593Smuzhiyun goto out;
3766*4882a593Smuzhiyun
3767*4882a593Smuzhiyun /* before going on we have to check whether the client has
3768*4882a593Smuzhiyun * already roamed to us too many times
3769*4882a593Smuzhiyun */
3770*4882a593Smuzhiyun if (!batadv_tt_check_roam_count(bat_priv, client))
3771*4882a593Smuzhiyun goto out;
3772*4882a593Smuzhiyun
3773*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3774*4882a593Smuzhiyun "Sending ROAMING_ADV to %pM (client %pM, vid: %d)\n",
3775*4882a593Smuzhiyun orig_node->orig, client, batadv_print_vid(vid));
3776*4882a593Smuzhiyun
3777*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
3778*4882a593Smuzhiyun
3779*4882a593Smuzhiyun memcpy(tvlv_roam.client, client, sizeof(tvlv_roam.client));
3780*4882a593Smuzhiyun tvlv_roam.vid = htons(vid);
3781*4882a593Smuzhiyun
3782*4882a593Smuzhiyun batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
3783*4882a593Smuzhiyun orig_node->orig, BATADV_TVLV_ROAM, 1,
3784*4882a593Smuzhiyun &tvlv_roam, sizeof(tvlv_roam));
3785*4882a593Smuzhiyun
3786*4882a593Smuzhiyun out:
3787*4882a593Smuzhiyun if (primary_if)
3788*4882a593Smuzhiyun batadv_hardif_put(primary_if);
3789*4882a593Smuzhiyun }
3790*4882a593Smuzhiyun
batadv_tt_purge(struct work_struct * work)3791*4882a593Smuzhiyun static void batadv_tt_purge(struct work_struct *work)
3792*4882a593Smuzhiyun {
3793*4882a593Smuzhiyun struct delayed_work *delayed_work;
3794*4882a593Smuzhiyun struct batadv_priv_tt *priv_tt;
3795*4882a593Smuzhiyun struct batadv_priv *bat_priv;
3796*4882a593Smuzhiyun
3797*4882a593Smuzhiyun delayed_work = to_delayed_work(work);
3798*4882a593Smuzhiyun priv_tt = container_of(delayed_work, struct batadv_priv_tt, work);
3799*4882a593Smuzhiyun bat_priv = container_of(priv_tt, struct batadv_priv, tt);
3800*4882a593Smuzhiyun
3801*4882a593Smuzhiyun batadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT);
3802*4882a593Smuzhiyun batadv_tt_global_purge(bat_priv);
3803*4882a593Smuzhiyun batadv_tt_req_purge(bat_priv);
3804*4882a593Smuzhiyun batadv_tt_roam_purge(bat_priv);
3805*4882a593Smuzhiyun
3806*4882a593Smuzhiyun queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
3807*4882a593Smuzhiyun msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
3808*4882a593Smuzhiyun }
3809*4882a593Smuzhiyun
3810*4882a593Smuzhiyun /**
3811*4882a593Smuzhiyun * batadv_tt_free() - Free translation table of soft interface
3812*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3813*4882a593Smuzhiyun */
batadv_tt_free(struct batadv_priv * bat_priv)3814*4882a593Smuzhiyun void batadv_tt_free(struct batadv_priv *bat_priv)
3815*4882a593Smuzhiyun {
3816*4882a593Smuzhiyun batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_ROAM, 1);
3817*4882a593Smuzhiyun
3818*4882a593Smuzhiyun batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_TT, 1);
3819*4882a593Smuzhiyun batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_TT, 1);
3820*4882a593Smuzhiyun
3821*4882a593Smuzhiyun cancel_delayed_work_sync(&bat_priv->tt.work);
3822*4882a593Smuzhiyun
3823*4882a593Smuzhiyun batadv_tt_local_table_free(bat_priv);
3824*4882a593Smuzhiyun batadv_tt_global_table_free(bat_priv);
3825*4882a593Smuzhiyun batadv_tt_req_list_free(bat_priv);
3826*4882a593Smuzhiyun batadv_tt_changes_list_free(bat_priv);
3827*4882a593Smuzhiyun batadv_tt_roam_list_free(bat_priv);
3828*4882a593Smuzhiyun
3829*4882a593Smuzhiyun kfree(bat_priv->tt.last_changeset);
3830*4882a593Smuzhiyun }
3831*4882a593Smuzhiyun
3832*4882a593Smuzhiyun /**
3833*4882a593Smuzhiyun * batadv_tt_local_set_flags() - set or unset the specified flags on the local
3834*4882a593Smuzhiyun * table and possibly count them in the TT size
3835*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3836*4882a593Smuzhiyun * @flags: the flag to switch
3837*4882a593Smuzhiyun * @enable: whether to set or unset the flag
3838*4882a593Smuzhiyun * @count: whether to increase the TT size by the number of changed entries
3839*4882a593Smuzhiyun */
batadv_tt_local_set_flags(struct batadv_priv * bat_priv,u16 flags,bool enable,bool count)3840*4882a593Smuzhiyun static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, u16 flags,
3841*4882a593Smuzhiyun bool enable, bool count)
3842*4882a593Smuzhiyun {
3843*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.local_hash;
3844*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common_entry;
3845*4882a593Smuzhiyun struct hlist_head *head;
3846*4882a593Smuzhiyun u32 i;
3847*4882a593Smuzhiyun
3848*4882a593Smuzhiyun if (!hash)
3849*4882a593Smuzhiyun return;
3850*4882a593Smuzhiyun
3851*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
3852*4882a593Smuzhiyun head = &hash->table[i];
3853*4882a593Smuzhiyun
3854*4882a593Smuzhiyun rcu_read_lock();
3855*4882a593Smuzhiyun hlist_for_each_entry_rcu(tt_common_entry,
3856*4882a593Smuzhiyun head, hash_entry) {
3857*4882a593Smuzhiyun if (enable) {
3858*4882a593Smuzhiyun if ((tt_common_entry->flags & flags) == flags)
3859*4882a593Smuzhiyun continue;
3860*4882a593Smuzhiyun tt_common_entry->flags |= flags;
3861*4882a593Smuzhiyun } else {
3862*4882a593Smuzhiyun if (!(tt_common_entry->flags & flags))
3863*4882a593Smuzhiyun continue;
3864*4882a593Smuzhiyun tt_common_entry->flags &= ~flags;
3865*4882a593Smuzhiyun }
3866*4882a593Smuzhiyun
3867*4882a593Smuzhiyun if (!count)
3868*4882a593Smuzhiyun continue;
3869*4882a593Smuzhiyun
3870*4882a593Smuzhiyun batadv_tt_local_size_inc(bat_priv,
3871*4882a593Smuzhiyun tt_common_entry->vid);
3872*4882a593Smuzhiyun }
3873*4882a593Smuzhiyun rcu_read_unlock();
3874*4882a593Smuzhiyun }
3875*4882a593Smuzhiyun }
3876*4882a593Smuzhiyun
3877*4882a593Smuzhiyun /* Purge out all the tt local entries marked with BATADV_TT_CLIENT_PENDING */
batadv_tt_local_purge_pending_clients(struct batadv_priv * bat_priv)3878*4882a593Smuzhiyun static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
3879*4882a593Smuzhiyun {
3880*4882a593Smuzhiyun struct batadv_hashtable *hash = bat_priv->tt.local_hash;
3881*4882a593Smuzhiyun struct batadv_tt_common_entry *tt_common;
3882*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local;
3883*4882a593Smuzhiyun struct hlist_node *node_tmp;
3884*4882a593Smuzhiyun struct hlist_head *head;
3885*4882a593Smuzhiyun spinlock_t *list_lock; /* protects write access to the hash lists */
3886*4882a593Smuzhiyun u32 i;
3887*4882a593Smuzhiyun
3888*4882a593Smuzhiyun if (!hash)
3889*4882a593Smuzhiyun return;
3890*4882a593Smuzhiyun
3891*4882a593Smuzhiyun for (i = 0; i < hash->size; i++) {
3892*4882a593Smuzhiyun head = &hash->table[i];
3893*4882a593Smuzhiyun list_lock = &hash->list_locks[i];
3894*4882a593Smuzhiyun
3895*4882a593Smuzhiyun spin_lock_bh(list_lock);
3896*4882a593Smuzhiyun hlist_for_each_entry_safe(tt_common, node_tmp, head,
3897*4882a593Smuzhiyun hash_entry) {
3898*4882a593Smuzhiyun if (!(tt_common->flags & BATADV_TT_CLIENT_PENDING))
3899*4882a593Smuzhiyun continue;
3900*4882a593Smuzhiyun
3901*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3902*4882a593Smuzhiyun "Deleting local tt entry (%pM, vid: %d): pending\n",
3903*4882a593Smuzhiyun tt_common->addr,
3904*4882a593Smuzhiyun batadv_print_vid(tt_common->vid));
3905*4882a593Smuzhiyun
3906*4882a593Smuzhiyun batadv_tt_local_size_dec(bat_priv, tt_common->vid);
3907*4882a593Smuzhiyun hlist_del_rcu(&tt_common->hash_entry);
3908*4882a593Smuzhiyun tt_local = container_of(tt_common,
3909*4882a593Smuzhiyun struct batadv_tt_local_entry,
3910*4882a593Smuzhiyun common);
3911*4882a593Smuzhiyun
3912*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local);
3913*4882a593Smuzhiyun }
3914*4882a593Smuzhiyun spin_unlock_bh(list_lock);
3915*4882a593Smuzhiyun }
3916*4882a593Smuzhiyun }
3917*4882a593Smuzhiyun
3918*4882a593Smuzhiyun /**
3919*4882a593Smuzhiyun * batadv_tt_local_commit_changes_nolock() - commit all pending local tt changes
3920*4882a593Smuzhiyun * which have been queued in the time since the last commit
3921*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3922*4882a593Smuzhiyun *
3923*4882a593Smuzhiyun * Caller must hold tt->commit_lock.
3924*4882a593Smuzhiyun */
batadv_tt_local_commit_changes_nolock(struct batadv_priv * bat_priv)3925*4882a593Smuzhiyun static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
3926*4882a593Smuzhiyun {
3927*4882a593Smuzhiyun lockdep_assert_held(&bat_priv->tt.commit_lock);
3928*4882a593Smuzhiyun
3929*4882a593Smuzhiyun if (atomic_read(&bat_priv->tt.local_changes) < 1) {
3930*4882a593Smuzhiyun if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
3931*4882a593Smuzhiyun batadv_tt_tvlv_container_update(bat_priv);
3932*4882a593Smuzhiyun return;
3933*4882a593Smuzhiyun }
3934*4882a593Smuzhiyun
3935*4882a593Smuzhiyun batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true);
3936*4882a593Smuzhiyun
3937*4882a593Smuzhiyun batadv_tt_local_purge_pending_clients(bat_priv);
3938*4882a593Smuzhiyun batadv_tt_local_update_crc(bat_priv);
3939*4882a593Smuzhiyun
3940*4882a593Smuzhiyun /* Increment the TTVN only once per OGM interval */
3941*4882a593Smuzhiyun atomic_inc(&bat_priv->tt.vn);
3942*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
3943*4882a593Smuzhiyun "Local changes committed, updating to ttvn %u\n",
3944*4882a593Smuzhiyun (u8)atomic_read(&bat_priv->tt.vn));
3945*4882a593Smuzhiyun
3946*4882a593Smuzhiyun /* reset the sending counter */
3947*4882a593Smuzhiyun atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
3948*4882a593Smuzhiyun batadv_tt_tvlv_container_update(bat_priv);
3949*4882a593Smuzhiyun }
3950*4882a593Smuzhiyun
3951*4882a593Smuzhiyun /**
3952*4882a593Smuzhiyun * batadv_tt_local_commit_changes() - commit all pending local tt changes which
3953*4882a593Smuzhiyun * have been queued in the time since the last commit
3954*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3955*4882a593Smuzhiyun */
batadv_tt_local_commit_changes(struct batadv_priv * bat_priv)3956*4882a593Smuzhiyun void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
3957*4882a593Smuzhiyun {
3958*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.commit_lock);
3959*4882a593Smuzhiyun batadv_tt_local_commit_changes_nolock(bat_priv);
3960*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.commit_lock);
3961*4882a593Smuzhiyun }
3962*4882a593Smuzhiyun
3963*4882a593Smuzhiyun /**
3964*4882a593Smuzhiyun * batadv_is_ap_isolated() - Check if packet from upper layer should be dropped
3965*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
3966*4882a593Smuzhiyun * @src: source mac address of packet
3967*4882a593Smuzhiyun * @dst: destination mac address of packet
3968*4882a593Smuzhiyun * @vid: vlan id of packet
3969*4882a593Smuzhiyun *
3970*4882a593Smuzhiyun * Return: true when src+dst(+vid) pair should be isolated, false otherwise
3971*4882a593Smuzhiyun */
batadv_is_ap_isolated(struct batadv_priv * bat_priv,u8 * src,u8 * dst,unsigned short vid)3972*4882a593Smuzhiyun bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
3973*4882a593Smuzhiyun unsigned short vid)
3974*4882a593Smuzhiyun {
3975*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
3976*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
3977*4882a593Smuzhiyun struct batadv_softif_vlan *vlan;
3978*4882a593Smuzhiyun bool ret = false;
3979*4882a593Smuzhiyun
3980*4882a593Smuzhiyun vlan = batadv_softif_vlan_get(bat_priv, vid);
3981*4882a593Smuzhiyun if (!vlan)
3982*4882a593Smuzhiyun return false;
3983*4882a593Smuzhiyun
3984*4882a593Smuzhiyun if (!atomic_read(&vlan->ap_isolation))
3985*4882a593Smuzhiyun goto vlan_put;
3986*4882a593Smuzhiyun
3987*4882a593Smuzhiyun tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid);
3988*4882a593Smuzhiyun if (!tt_local_entry)
3989*4882a593Smuzhiyun goto vlan_put;
3990*4882a593Smuzhiyun
3991*4882a593Smuzhiyun tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid);
3992*4882a593Smuzhiyun if (!tt_global_entry)
3993*4882a593Smuzhiyun goto local_entry_put;
3994*4882a593Smuzhiyun
3995*4882a593Smuzhiyun if (_batadv_is_ap_isolated(tt_local_entry, tt_global_entry))
3996*4882a593Smuzhiyun ret = true;
3997*4882a593Smuzhiyun
3998*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
3999*4882a593Smuzhiyun local_entry_put:
4000*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local_entry);
4001*4882a593Smuzhiyun vlan_put:
4002*4882a593Smuzhiyun batadv_softif_vlan_put(vlan);
4003*4882a593Smuzhiyun return ret;
4004*4882a593Smuzhiyun }
4005*4882a593Smuzhiyun
4006*4882a593Smuzhiyun /**
4007*4882a593Smuzhiyun * batadv_tt_update_orig() - update global translation table with new tt
4008*4882a593Smuzhiyun * information received via ogms
4009*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4010*4882a593Smuzhiyun * @orig_node: the orig_node of the ogm
4011*4882a593Smuzhiyun * @tt_buff: pointer to the first tvlv VLAN entry
4012*4882a593Smuzhiyun * @tt_num_vlan: number of tvlv VLAN entries
4013*4882a593Smuzhiyun * @tt_change: pointer to the first entry in the TT buffer
4014*4882a593Smuzhiyun * @tt_num_changes: number of tt changes inside the tt buffer
4015*4882a593Smuzhiyun * @ttvn: translation table version number of this changeset
4016*4882a593Smuzhiyun */
batadv_tt_update_orig(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,const void * tt_buff,u16 tt_num_vlan,struct batadv_tvlv_tt_change * tt_change,u16 tt_num_changes,u8 ttvn)4017*4882a593Smuzhiyun static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
4018*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
4019*4882a593Smuzhiyun const void *tt_buff, u16 tt_num_vlan,
4020*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change,
4021*4882a593Smuzhiyun u16 tt_num_changes, u8 ttvn)
4022*4882a593Smuzhiyun {
4023*4882a593Smuzhiyun u8 orig_ttvn = (u8)atomic_read(&orig_node->last_ttvn);
4024*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan;
4025*4882a593Smuzhiyun bool full_table = true;
4026*4882a593Smuzhiyun bool has_tt_init;
4027*4882a593Smuzhiyun
4028*4882a593Smuzhiyun tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff;
4029*4882a593Smuzhiyun has_tt_init = test_bit(BATADV_ORIG_CAPA_HAS_TT,
4030*4882a593Smuzhiyun &orig_node->capa_initialized);
4031*4882a593Smuzhiyun
4032*4882a593Smuzhiyun /* orig table not initialised AND first diff is in the OGM OR the ttvn
4033*4882a593Smuzhiyun * increased by one -> we can apply the attached changes
4034*4882a593Smuzhiyun */
4035*4882a593Smuzhiyun if ((!has_tt_init && ttvn == 1) || ttvn - orig_ttvn == 1) {
4036*4882a593Smuzhiyun /* the OGM could not contain the changes due to their size or
4037*4882a593Smuzhiyun * because they have already been sent BATADV_TT_OGM_APPEND_MAX
4038*4882a593Smuzhiyun * times.
4039*4882a593Smuzhiyun * In this case send a tt request
4040*4882a593Smuzhiyun */
4041*4882a593Smuzhiyun if (!tt_num_changes) {
4042*4882a593Smuzhiyun full_table = false;
4043*4882a593Smuzhiyun goto request_table;
4044*4882a593Smuzhiyun }
4045*4882a593Smuzhiyun
4046*4882a593Smuzhiyun spin_lock_bh(&orig_node->tt_lock);
4047*4882a593Smuzhiyun
4048*4882a593Smuzhiyun batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes,
4049*4882a593Smuzhiyun ttvn, tt_change);
4050*4882a593Smuzhiyun
4051*4882a593Smuzhiyun /* Even if we received the precomputed crc with the OGM, we
4052*4882a593Smuzhiyun * prefer to recompute it to spot any possible inconsistency
4053*4882a593Smuzhiyun * in the global table
4054*4882a593Smuzhiyun */
4055*4882a593Smuzhiyun batadv_tt_global_update_crc(bat_priv, orig_node);
4056*4882a593Smuzhiyun
4057*4882a593Smuzhiyun spin_unlock_bh(&orig_node->tt_lock);
4058*4882a593Smuzhiyun
4059*4882a593Smuzhiyun /* The ttvn alone is not enough to guarantee consistency
4060*4882a593Smuzhiyun * because a single value could represent different states
4061*4882a593Smuzhiyun * (due to the wrap around). Thus a node has to check whether
4062*4882a593Smuzhiyun * the resulting table (after applying the changes) is still
4063*4882a593Smuzhiyun * consistent or not. E.g. a node could disconnect while its
4064*4882a593Smuzhiyun * ttvn is X and reconnect on ttvn = X + TTVN_MAX: in this case
4065*4882a593Smuzhiyun * checking the CRC value is mandatory to detect the
4066*4882a593Smuzhiyun * inconsistency
4067*4882a593Smuzhiyun */
4068*4882a593Smuzhiyun if (!batadv_tt_global_check_crc(orig_node, tt_vlan,
4069*4882a593Smuzhiyun tt_num_vlan))
4070*4882a593Smuzhiyun goto request_table;
4071*4882a593Smuzhiyun } else {
4072*4882a593Smuzhiyun /* if we missed more than one change or our tables are not
4073*4882a593Smuzhiyun * in sync anymore -> request fresh tt data
4074*4882a593Smuzhiyun */
4075*4882a593Smuzhiyun if (!has_tt_init || ttvn != orig_ttvn ||
4076*4882a593Smuzhiyun !batadv_tt_global_check_crc(orig_node, tt_vlan,
4077*4882a593Smuzhiyun tt_num_vlan)) {
4078*4882a593Smuzhiyun request_table:
4079*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
4080*4882a593Smuzhiyun "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u num_changes: %u)\n",
4081*4882a593Smuzhiyun orig_node->orig, ttvn, orig_ttvn,
4082*4882a593Smuzhiyun tt_num_changes);
4083*4882a593Smuzhiyun batadv_send_tt_request(bat_priv, orig_node, ttvn,
4084*4882a593Smuzhiyun tt_vlan, tt_num_vlan,
4085*4882a593Smuzhiyun full_table);
4086*4882a593Smuzhiyun return;
4087*4882a593Smuzhiyun }
4088*4882a593Smuzhiyun }
4089*4882a593Smuzhiyun }
4090*4882a593Smuzhiyun
4091*4882a593Smuzhiyun /**
4092*4882a593Smuzhiyun * batadv_tt_global_client_is_roaming() - check if a client is marked as roaming
4093*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4094*4882a593Smuzhiyun * @addr: the mac address of the client to check
4095*4882a593Smuzhiyun * @vid: VLAN identifier
4096*4882a593Smuzhiyun *
4097*4882a593Smuzhiyun * Return: true if we know that the client has moved from its old originator
4098*4882a593Smuzhiyun * to another one. This entry is still kept for consistency purposes and will be
4099*4882a593Smuzhiyun * deleted later by a DEL or because of timeout
4100*4882a593Smuzhiyun */
batadv_tt_global_client_is_roaming(struct batadv_priv * bat_priv,u8 * addr,unsigned short vid)4101*4882a593Smuzhiyun bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
4102*4882a593Smuzhiyun u8 *addr, unsigned short vid)
4103*4882a593Smuzhiyun {
4104*4882a593Smuzhiyun struct batadv_tt_global_entry *tt_global_entry;
4105*4882a593Smuzhiyun bool ret = false;
4106*4882a593Smuzhiyun
4107*4882a593Smuzhiyun tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
4108*4882a593Smuzhiyun if (!tt_global_entry)
4109*4882a593Smuzhiyun goto out;
4110*4882a593Smuzhiyun
4111*4882a593Smuzhiyun ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
4112*4882a593Smuzhiyun batadv_tt_global_entry_put(tt_global_entry);
4113*4882a593Smuzhiyun out:
4114*4882a593Smuzhiyun return ret;
4115*4882a593Smuzhiyun }
4116*4882a593Smuzhiyun
4117*4882a593Smuzhiyun /**
4118*4882a593Smuzhiyun * batadv_tt_local_client_is_roaming() - tells whether the client is roaming
4119*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4120*4882a593Smuzhiyun * @addr: the mac address of the local client to query
4121*4882a593Smuzhiyun * @vid: VLAN identifier
4122*4882a593Smuzhiyun *
4123*4882a593Smuzhiyun * Return: true if the local client is known to be roaming (it is not served by
4124*4882a593Smuzhiyun * this node anymore) or not. If yes, the client is still present in the table
4125*4882a593Smuzhiyun * to keep the latter consistent with the node TTVN
4126*4882a593Smuzhiyun */
batadv_tt_local_client_is_roaming(struct batadv_priv * bat_priv,u8 * addr,unsigned short vid)4127*4882a593Smuzhiyun bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
4128*4882a593Smuzhiyun u8 *addr, unsigned short vid)
4129*4882a593Smuzhiyun {
4130*4882a593Smuzhiyun struct batadv_tt_local_entry *tt_local_entry;
4131*4882a593Smuzhiyun bool ret = false;
4132*4882a593Smuzhiyun
4133*4882a593Smuzhiyun tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
4134*4882a593Smuzhiyun if (!tt_local_entry)
4135*4882a593Smuzhiyun goto out;
4136*4882a593Smuzhiyun
4137*4882a593Smuzhiyun ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM;
4138*4882a593Smuzhiyun batadv_tt_local_entry_put(tt_local_entry);
4139*4882a593Smuzhiyun out:
4140*4882a593Smuzhiyun return ret;
4141*4882a593Smuzhiyun }
4142*4882a593Smuzhiyun
4143*4882a593Smuzhiyun /**
4144*4882a593Smuzhiyun * batadv_tt_add_temporary_global_entry() - Add temporary entry to global TT
4145*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4146*4882a593Smuzhiyun * @orig_node: orig node which the temporary entry should be associated with
4147*4882a593Smuzhiyun * @addr: mac address of the client
4148*4882a593Smuzhiyun * @vid: VLAN id of the new temporary global translation table
4149*4882a593Smuzhiyun *
4150*4882a593Smuzhiyun * Return: true when temporary tt entry could be added, false otherwise
4151*4882a593Smuzhiyun */
batadv_tt_add_temporary_global_entry(struct batadv_priv * bat_priv,struct batadv_orig_node * orig_node,const unsigned char * addr,unsigned short vid)4152*4882a593Smuzhiyun bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
4153*4882a593Smuzhiyun struct batadv_orig_node *orig_node,
4154*4882a593Smuzhiyun const unsigned char *addr,
4155*4882a593Smuzhiyun unsigned short vid)
4156*4882a593Smuzhiyun {
4157*4882a593Smuzhiyun /* ignore loop detect macs, they are not supposed to be in the tt local
4158*4882a593Smuzhiyun * data as well.
4159*4882a593Smuzhiyun */
4160*4882a593Smuzhiyun if (batadv_bla_is_loopdetect_mac(addr))
4161*4882a593Smuzhiyun return false;
4162*4882a593Smuzhiyun
4163*4882a593Smuzhiyun if (!batadv_tt_global_add(bat_priv, orig_node, addr, vid,
4164*4882a593Smuzhiyun BATADV_TT_CLIENT_TEMP,
4165*4882a593Smuzhiyun atomic_read(&orig_node->last_ttvn)))
4166*4882a593Smuzhiyun return false;
4167*4882a593Smuzhiyun
4168*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
4169*4882a593Smuzhiyun "Added temporary global client (addr: %pM, vid: %d, orig: %pM)\n",
4170*4882a593Smuzhiyun addr, batadv_print_vid(vid), orig_node->orig);
4171*4882a593Smuzhiyun
4172*4882a593Smuzhiyun return true;
4173*4882a593Smuzhiyun }
4174*4882a593Smuzhiyun
4175*4882a593Smuzhiyun /**
4176*4882a593Smuzhiyun * batadv_tt_local_resize_to_mtu() - resize the local translation table fit the
4177*4882a593Smuzhiyun * maximum packet size that can be transported through the mesh
4178*4882a593Smuzhiyun * @soft_iface: netdev struct of the mesh interface
4179*4882a593Smuzhiyun *
4180*4882a593Smuzhiyun * Remove entries older than 'timeout' and half timeout if more entries need
4181*4882a593Smuzhiyun * to be removed.
4182*4882a593Smuzhiyun */
batadv_tt_local_resize_to_mtu(struct net_device * soft_iface)4183*4882a593Smuzhiyun void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface)
4184*4882a593Smuzhiyun {
4185*4882a593Smuzhiyun struct batadv_priv *bat_priv = netdev_priv(soft_iface);
4186*4882a593Smuzhiyun int packet_size_max = atomic_read(&bat_priv->packet_size_max);
4187*4882a593Smuzhiyun int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2;
4188*4882a593Smuzhiyun bool reduced = false;
4189*4882a593Smuzhiyun
4190*4882a593Smuzhiyun spin_lock_bh(&bat_priv->tt.commit_lock);
4191*4882a593Smuzhiyun
4192*4882a593Smuzhiyun while (true) {
4193*4882a593Smuzhiyun table_size = batadv_tt_local_table_transmit_size(bat_priv);
4194*4882a593Smuzhiyun if (packet_size_max >= table_size)
4195*4882a593Smuzhiyun break;
4196*4882a593Smuzhiyun
4197*4882a593Smuzhiyun batadv_tt_local_purge(bat_priv, timeout);
4198*4882a593Smuzhiyun batadv_tt_local_purge_pending_clients(bat_priv);
4199*4882a593Smuzhiyun
4200*4882a593Smuzhiyun timeout /= 2;
4201*4882a593Smuzhiyun reduced = true;
4202*4882a593Smuzhiyun net_ratelimited_function(batadv_info, soft_iface,
4203*4882a593Smuzhiyun "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n",
4204*4882a593Smuzhiyun packet_size_max);
4205*4882a593Smuzhiyun }
4206*4882a593Smuzhiyun
4207*4882a593Smuzhiyun /* commit these changes immediately, to avoid synchronization problem
4208*4882a593Smuzhiyun * with the TTVN
4209*4882a593Smuzhiyun */
4210*4882a593Smuzhiyun if (reduced)
4211*4882a593Smuzhiyun batadv_tt_local_commit_changes_nolock(bat_priv);
4212*4882a593Smuzhiyun
4213*4882a593Smuzhiyun spin_unlock_bh(&bat_priv->tt.commit_lock);
4214*4882a593Smuzhiyun }
4215*4882a593Smuzhiyun
4216*4882a593Smuzhiyun /**
4217*4882a593Smuzhiyun * batadv_tt_tvlv_ogm_handler_v1() - process incoming tt tvlv container
4218*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4219*4882a593Smuzhiyun * @orig: the orig_node of the ogm
4220*4882a593Smuzhiyun * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
4221*4882a593Smuzhiyun * @tvlv_value: tvlv buffer containing the gateway data
4222*4882a593Smuzhiyun * @tvlv_value_len: tvlv buffer length
4223*4882a593Smuzhiyun */
batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv * bat_priv,struct batadv_orig_node * orig,u8 flags,void * tvlv_value,u16 tvlv_value_len)4224*4882a593Smuzhiyun static void batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
4225*4882a593Smuzhiyun struct batadv_orig_node *orig,
4226*4882a593Smuzhiyun u8 flags, void *tvlv_value,
4227*4882a593Smuzhiyun u16 tvlv_value_len)
4228*4882a593Smuzhiyun {
4229*4882a593Smuzhiyun struct batadv_tvlv_tt_vlan_data *tt_vlan;
4230*4882a593Smuzhiyun struct batadv_tvlv_tt_change *tt_change;
4231*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data;
4232*4882a593Smuzhiyun u16 num_entries, num_vlan;
4233*4882a593Smuzhiyun
4234*4882a593Smuzhiyun if (tvlv_value_len < sizeof(*tt_data))
4235*4882a593Smuzhiyun return;
4236*4882a593Smuzhiyun
4237*4882a593Smuzhiyun tt_data = (struct batadv_tvlv_tt_data *)tvlv_value;
4238*4882a593Smuzhiyun tvlv_value_len -= sizeof(*tt_data);
4239*4882a593Smuzhiyun
4240*4882a593Smuzhiyun num_vlan = ntohs(tt_data->num_vlan);
4241*4882a593Smuzhiyun
4242*4882a593Smuzhiyun if (tvlv_value_len < sizeof(*tt_vlan) * num_vlan)
4243*4882a593Smuzhiyun return;
4244*4882a593Smuzhiyun
4245*4882a593Smuzhiyun tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
4246*4882a593Smuzhiyun tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan);
4247*4882a593Smuzhiyun tvlv_value_len -= sizeof(*tt_vlan) * num_vlan;
4248*4882a593Smuzhiyun
4249*4882a593Smuzhiyun num_entries = batadv_tt_entries(tvlv_value_len);
4250*4882a593Smuzhiyun
4251*4882a593Smuzhiyun batadv_tt_update_orig(bat_priv, orig, tt_vlan, num_vlan, tt_change,
4252*4882a593Smuzhiyun num_entries, tt_data->ttvn);
4253*4882a593Smuzhiyun }
4254*4882a593Smuzhiyun
4255*4882a593Smuzhiyun /**
4256*4882a593Smuzhiyun * batadv_tt_tvlv_unicast_handler_v1() - process incoming (unicast) tt tvlv
4257*4882a593Smuzhiyun * container
4258*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4259*4882a593Smuzhiyun * @src: mac address of tt tvlv sender
4260*4882a593Smuzhiyun * @dst: mac address of tt tvlv recipient
4261*4882a593Smuzhiyun * @tvlv_value: tvlv buffer containing the tt data
4262*4882a593Smuzhiyun * @tvlv_value_len: tvlv buffer length
4263*4882a593Smuzhiyun *
4264*4882a593Smuzhiyun * Return: NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS
4265*4882a593Smuzhiyun * otherwise.
4266*4882a593Smuzhiyun */
batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv * bat_priv,u8 * src,u8 * dst,void * tvlv_value,u16 tvlv_value_len)4267*4882a593Smuzhiyun static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
4268*4882a593Smuzhiyun u8 *src, u8 *dst,
4269*4882a593Smuzhiyun void *tvlv_value,
4270*4882a593Smuzhiyun u16 tvlv_value_len)
4271*4882a593Smuzhiyun {
4272*4882a593Smuzhiyun struct batadv_tvlv_tt_data *tt_data;
4273*4882a593Smuzhiyun u16 tt_vlan_len, tt_num_entries;
4274*4882a593Smuzhiyun char tt_flag;
4275*4882a593Smuzhiyun bool ret;
4276*4882a593Smuzhiyun
4277*4882a593Smuzhiyun if (tvlv_value_len < sizeof(*tt_data))
4278*4882a593Smuzhiyun return NET_RX_SUCCESS;
4279*4882a593Smuzhiyun
4280*4882a593Smuzhiyun tt_data = (struct batadv_tvlv_tt_data *)tvlv_value;
4281*4882a593Smuzhiyun tvlv_value_len -= sizeof(*tt_data);
4282*4882a593Smuzhiyun
4283*4882a593Smuzhiyun tt_vlan_len = sizeof(struct batadv_tvlv_tt_vlan_data);
4284*4882a593Smuzhiyun tt_vlan_len *= ntohs(tt_data->num_vlan);
4285*4882a593Smuzhiyun
4286*4882a593Smuzhiyun if (tvlv_value_len < tt_vlan_len)
4287*4882a593Smuzhiyun return NET_RX_SUCCESS;
4288*4882a593Smuzhiyun
4289*4882a593Smuzhiyun tvlv_value_len -= tt_vlan_len;
4290*4882a593Smuzhiyun tt_num_entries = batadv_tt_entries(tvlv_value_len);
4291*4882a593Smuzhiyun
4292*4882a593Smuzhiyun switch (tt_data->flags & BATADV_TT_DATA_TYPE_MASK) {
4293*4882a593Smuzhiyun case BATADV_TT_REQUEST:
4294*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
4295*4882a593Smuzhiyun
4296*4882a593Smuzhiyun /* If this node cannot provide a TT response the tt_request is
4297*4882a593Smuzhiyun * forwarded
4298*4882a593Smuzhiyun */
4299*4882a593Smuzhiyun ret = batadv_send_tt_response(bat_priv, tt_data, src, dst);
4300*4882a593Smuzhiyun if (!ret) {
4301*4882a593Smuzhiyun if (tt_data->flags & BATADV_TT_FULL_TABLE)
4302*4882a593Smuzhiyun tt_flag = 'F';
4303*4882a593Smuzhiyun else
4304*4882a593Smuzhiyun tt_flag = '.';
4305*4882a593Smuzhiyun
4306*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
4307*4882a593Smuzhiyun "Routing TT_REQUEST to %pM [%c]\n",
4308*4882a593Smuzhiyun dst, tt_flag);
4309*4882a593Smuzhiyun /* tvlv API will re-route the packet */
4310*4882a593Smuzhiyun return NET_RX_DROP;
4311*4882a593Smuzhiyun }
4312*4882a593Smuzhiyun break;
4313*4882a593Smuzhiyun case BATADV_TT_RESPONSE:
4314*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
4315*4882a593Smuzhiyun
4316*4882a593Smuzhiyun if (batadv_is_my_mac(bat_priv, dst)) {
4317*4882a593Smuzhiyun batadv_handle_tt_response(bat_priv, tt_data,
4318*4882a593Smuzhiyun src, tt_num_entries);
4319*4882a593Smuzhiyun return NET_RX_SUCCESS;
4320*4882a593Smuzhiyun }
4321*4882a593Smuzhiyun
4322*4882a593Smuzhiyun if (tt_data->flags & BATADV_TT_FULL_TABLE)
4323*4882a593Smuzhiyun tt_flag = 'F';
4324*4882a593Smuzhiyun else
4325*4882a593Smuzhiyun tt_flag = '.';
4326*4882a593Smuzhiyun
4327*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
4328*4882a593Smuzhiyun "Routing TT_RESPONSE to %pM [%c]\n", dst, tt_flag);
4329*4882a593Smuzhiyun
4330*4882a593Smuzhiyun /* tvlv API will re-route the packet */
4331*4882a593Smuzhiyun return NET_RX_DROP;
4332*4882a593Smuzhiyun }
4333*4882a593Smuzhiyun
4334*4882a593Smuzhiyun return NET_RX_SUCCESS;
4335*4882a593Smuzhiyun }
4336*4882a593Smuzhiyun
4337*4882a593Smuzhiyun /**
4338*4882a593Smuzhiyun * batadv_roam_tvlv_unicast_handler_v1() - process incoming tt roam tvlv
4339*4882a593Smuzhiyun * container
4340*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4341*4882a593Smuzhiyun * @src: mac address of tt tvlv sender
4342*4882a593Smuzhiyun * @dst: mac address of tt tvlv recipient
4343*4882a593Smuzhiyun * @tvlv_value: tvlv buffer containing the tt data
4344*4882a593Smuzhiyun * @tvlv_value_len: tvlv buffer length
4345*4882a593Smuzhiyun *
4346*4882a593Smuzhiyun * Return: NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS
4347*4882a593Smuzhiyun * otherwise.
4348*4882a593Smuzhiyun */
batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv * bat_priv,u8 * src,u8 * dst,void * tvlv_value,u16 tvlv_value_len)4349*4882a593Smuzhiyun static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
4350*4882a593Smuzhiyun u8 *src, u8 *dst,
4351*4882a593Smuzhiyun void *tvlv_value,
4352*4882a593Smuzhiyun u16 tvlv_value_len)
4353*4882a593Smuzhiyun {
4354*4882a593Smuzhiyun struct batadv_tvlv_roam_adv *roaming_adv;
4355*4882a593Smuzhiyun struct batadv_orig_node *orig_node = NULL;
4356*4882a593Smuzhiyun
4357*4882a593Smuzhiyun /* If this node is not the intended recipient of the
4358*4882a593Smuzhiyun * roaming advertisement the packet is forwarded
4359*4882a593Smuzhiyun * (the tvlv API will re-route the packet).
4360*4882a593Smuzhiyun */
4361*4882a593Smuzhiyun if (!batadv_is_my_mac(bat_priv, dst))
4362*4882a593Smuzhiyun return NET_RX_DROP;
4363*4882a593Smuzhiyun
4364*4882a593Smuzhiyun if (tvlv_value_len < sizeof(*roaming_adv))
4365*4882a593Smuzhiyun goto out;
4366*4882a593Smuzhiyun
4367*4882a593Smuzhiyun orig_node = batadv_orig_hash_find(bat_priv, src);
4368*4882a593Smuzhiyun if (!orig_node)
4369*4882a593Smuzhiyun goto out;
4370*4882a593Smuzhiyun
4371*4882a593Smuzhiyun batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
4372*4882a593Smuzhiyun roaming_adv = (struct batadv_tvlv_roam_adv *)tvlv_value;
4373*4882a593Smuzhiyun
4374*4882a593Smuzhiyun batadv_dbg(BATADV_DBG_TT, bat_priv,
4375*4882a593Smuzhiyun "Received ROAMING_ADV from %pM (client %pM)\n",
4376*4882a593Smuzhiyun src, roaming_adv->client);
4377*4882a593Smuzhiyun
4378*4882a593Smuzhiyun batadv_tt_global_add(bat_priv, orig_node, roaming_adv->client,
4379*4882a593Smuzhiyun ntohs(roaming_adv->vid), BATADV_TT_CLIENT_ROAM,
4380*4882a593Smuzhiyun atomic_read(&orig_node->last_ttvn) + 1);
4381*4882a593Smuzhiyun
4382*4882a593Smuzhiyun out:
4383*4882a593Smuzhiyun if (orig_node)
4384*4882a593Smuzhiyun batadv_orig_node_put(orig_node);
4385*4882a593Smuzhiyun return NET_RX_SUCCESS;
4386*4882a593Smuzhiyun }
4387*4882a593Smuzhiyun
4388*4882a593Smuzhiyun /**
4389*4882a593Smuzhiyun * batadv_tt_init() - initialise the translation table internals
4390*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4391*4882a593Smuzhiyun *
4392*4882a593Smuzhiyun * Return: 0 on success or negative error number in case of failure.
4393*4882a593Smuzhiyun */
batadv_tt_init(struct batadv_priv * bat_priv)4394*4882a593Smuzhiyun int batadv_tt_init(struct batadv_priv *bat_priv)
4395*4882a593Smuzhiyun {
4396*4882a593Smuzhiyun int ret;
4397*4882a593Smuzhiyun
4398*4882a593Smuzhiyun /* synchronized flags must be remote */
4399*4882a593Smuzhiyun BUILD_BUG_ON(!(BATADV_TT_SYNC_MASK & BATADV_TT_REMOTE_MASK));
4400*4882a593Smuzhiyun
4401*4882a593Smuzhiyun ret = batadv_tt_local_init(bat_priv);
4402*4882a593Smuzhiyun if (ret < 0)
4403*4882a593Smuzhiyun return ret;
4404*4882a593Smuzhiyun
4405*4882a593Smuzhiyun ret = batadv_tt_global_init(bat_priv);
4406*4882a593Smuzhiyun if (ret < 0) {
4407*4882a593Smuzhiyun batadv_tt_local_table_free(bat_priv);
4408*4882a593Smuzhiyun return ret;
4409*4882a593Smuzhiyun }
4410*4882a593Smuzhiyun
4411*4882a593Smuzhiyun batadv_tvlv_handler_register(bat_priv, batadv_tt_tvlv_ogm_handler_v1,
4412*4882a593Smuzhiyun batadv_tt_tvlv_unicast_handler_v1,
4413*4882a593Smuzhiyun BATADV_TVLV_TT, 1, BATADV_NO_FLAGS);
4414*4882a593Smuzhiyun
4415*4882a593Smuzhiyun batadv_tvlv_handler_register(bat_priv, NULL,
4416*4882a593Smuzhiyun batadv_roam_tvlv_unicast_handler_v1,
4417*4882a593Smuzhiyun BATADV_TVLV_ROAM, 1, BATADV_NO_FLAGS);
4418*4882a593Smuzhiyun
4419*4882a593Smuzhiyun INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge);
4420*4882a593Smuzhiyun queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
4421*4882a593Smuzhiyun msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
4422*4882a593Smuzhiyun
4423*4882a593Smuzhiyun return 1;
4424*4882a593Smuzhiyun }
4425*4882a593Smuzhiyun
4426*4882a593Smuzhiyun /**
4427*4882a593Smuzhiyun * batadv_tt_global_is_isolated() - check if a client is marked as isolated
4428*4882a593Smuzhiyun * @bat_priv: the bat priv with all the soft interface information
4429*4882a593Smuzhiyun * @addr: the mac address of the client
4430*4882a593Smuzhiyun * @vid: the identifier of the VLAN where this client is connected
4431*4882a593Smuzhiyun *
4432*4882a593Smuzhiyun * Return: true if the client is marked with the TT_CLIENT_ISOLA flag, false
4433*4882a593Smuzhiyun * otherwise
4434*4882a593Smuzhiyun */
batadv_tt_global_is_isolated(struct batadv_priv * bat_priv,const u8 * addr,unsigned short vid)4435*4882a593Smuzhiyun bool batadv_tt_global_is_isolated(struct batadv_priv *bat_priv,
4436*4882a593Smuzhiyun const u8 *addr, unsigned short vid)
4437*4882a593Smuzhiyun {
4438*4882a593Smuzhiyun struct batadv_tt_global_entry *tt;
4439*4882a593Smuzhiyun bool ret;
4440*4882a593Smuzhiyun
4441*4882a593Smuzhiyun tt = batadv_tt_global_hash_find(bat_priv, addr, vid);
4442*4882a593Smuzhiyun if (!tt)
4443*4882a593Smuzhiyun return false;
4444*4882a593Smuzhiyun
4445*4882a593Smuzhiyun ret = tt->common.flags & BATADV_TT_CLIENT_ISOLA;
4446*4882a593Smuzhiyun
4447*4882a593Smuzhiyun batadv_tt_global_entry_put(tt);
4448*4882a593Smuzhiyun
4449*4882a593Smuzhiyun return ret;
4450*4882a593Smuzhiyun }
4451*4882a593Smuzhiyun
4452*4882a593Smuzhiyun /**
4453*4882a593Smuzhiyun * batadv_tt_cache_init() - Initialize tt memory object cache
4454*4882a593Smuzhiyun *
4455*4882a593Smuzhiyun * Return: 0 on success or negative error number in case of failure.
4456*4882a593Smuzhiyun */
batadv_tt_cache_init(void)4457*4882a593Smuzhiyun int __init batadv_tt_cache_init(void)
4458*4882a593Smuzhiyun {
4459*4882a593Smuzhiyun size_t tl_size = sizeof(struct batadv_tt_local_entry);
4460*4882a593Smuzhiyun size_t tg_size = sizeof(struct batadv_tt_global_entry);
4461*4882a593Smuzhiyun size_t tt_orig_size = sizeof(struct batadv_tt_orig_list_entry);
4462*4882a593Smuzhiyun size_t tt_change_size = sizeof(struct batadv_tt_change_node);
4463*4882a593Smuzhiyun size_t tt_req_size = sizeof(struct batadv_tt_req_node);
4464*4882a593Smuzhiyun size_t tt_roam_size = sizeof(struct batadv_tt_roam_node);
4465*4882a593Smuzhiyun
4466*4882a593Smuzhiyun batadv_tl_cache = kmem_cache_create("batadv_tl_cache", tl_size, 0,
4467*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
4468*4882a593Smuzhiyun if (!batadv_tl_cache)
4469*4882a593Smuzhiyun return -ENOMEM;
4470*4882a593Smuzhiyun
4471*4882a593Smuzhiyun batadv_tg_cache = kmem_cache_create("batadv_tg_cache", tg_size, 0,
4472*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
4473*4882a593Smuzhiyun if (!batadv_tg_cache)
4474*4882a593Smuzhiyun goto err_tt_tl_destroy;
4475*4882a593Smuzhiyun
4476*4882a593Smuzhiyun batadv_tt_orig_cache = kmem_cache_create("batadv_tt_orig_cache",
4477*4882a593Smuzhiyun tt_orig_size, 0,
4478*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
4479*4882a593Smuzhiyun if (!batadv_tt_orig_cache)
4480*4882a593Smuzhiyun goto err_tt_tg_destroy;
4481*4882a593Smuzhiyun
4482*4882a593Smuzhiyun batadv_tt_change_cache = kmem_cache_create("batadv_tt_change_cache",
4483*4882a593Smuzhiyun tt_change_size, 0,
4484*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
4485*4882a593Smuzhiyun if (!batadv_tt_change_cache)
4486*4882a593Smuzhiyun goto err_tt_orig_destroy;
4487*4882a593Smuzhiyun
4488*4882a593Smuzhiyun batadv_tt_req_cache = kmem_cache_create("batadv_tt_req_cache",
4489*4882a593Smuzhiyun tt_req_size, 0,
4490*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
4491*4882a593Smuzhiyun if (!batadv_tt_req_cache)
4492*4882a593Smuzhiyun goto err_tt_change_destroy;
4493*4882a593Smuzhiyun
4494*4882a593Smuzhiyun batadv_tt_roam_cache = kmem_cache_create("batadv_tt_roam_cache",
4495*4882a593Smuzhiyun tt_roam_size, 0,
4496*4882a593Smuzhiyun SLAB_HWCACHE_ALIGN, NULL);
4497*4882a593Smuzhiyun if (!batadv_tt_roam_cache)
4498*4882a593Smuzhiyun goto err_tt_req_destroy;
4499*4882a593Smuzhiyun
4500*4882a593Smuzhiyun return 0;
4501*4882a593Smuzhiyun
4502*4882a593Smuzhiyun err_tt_req_destroy:
4503*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_req_cache);
4504*4882a593Smuzhiyun batadv_tt_req_cache = NULL;
4505*4882a593Smuzhiyun err_tt_change_destroy:
4506*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_change_cache);
4507*4882a593Smuzhiyun batadv_tt_change_cache = NULL;
4508*4882a593Smuzhiyun err_tt_orig_destroy:
4509*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_orig_cache);
4510*4882a593Smuzhiyun batadv_tt_orig_cache = NULL;
4511*4882a593Smuzhiyun err_tt_tg_destroy:
4512*4882a593Smuzhiyun kmem_cache_destroy(batadv_tg_cache);
4513*4882a593Smuzhiyun batadv_tg_cache = NULL;
4514*4882a593Smuzhiyun err_tt_tl_destroy:
4515*4882a593Smuzhiyun kmem_cache_destroy(batadv_tl_cache);
4516*4882a593Smuzhiyun batadv_tl_cache = NULL;
4517*4882a593Smuzhiyun
4518*4882a593Smuzhiyun return -ENOMEM;
4519*4882a593Smuzhiyun }
4520*4882a593Smuzhiyun
4521*4882a593Smuzhiyun /**
4522*4882a593Smuzhiyun * batadv_tt_cache_destroy() - Destroy tt memory object cache
4523*4882a593Smuzhiyun */
batadv_tt_cache_destroy(void)4524*4882a593Smuzhiyun void batadv_tt_cache_destroy(void)
4525*4882a593Smuzhiyun {
4526*4882a593Smuzhiyun kmem_cache_destroy(batadv_tl_cache);
4527*4882a593Smuzhiyun kmem_cache_destroy(batadv_tg_cache);
4528*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_orig_cache);
4529*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_change_cache);
4530*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_req_cache);
4531*4882a593Smuzhiyun kmem_cache_destroy(batadv_tt_roam_cache);
4532*4882a593Smuzhiyun }
4533