diff options
Diffstat (limited to 'net/batman-adv/translation-table.c')
-rw-r--r-- | net/batman-adv/translation-table.c | 1566 |
1 files changed, 1325 insertions, 241 deletions
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 7b729660cbfd..fb6931d00cd7 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c | |||
@@ -23,38 +23,45 @@ | |||
23 | #include "translation-table.h" | 23 | #include "translation-table.h" |
24 | #include "soft-interface.h" | 24 | #include "soft-interface.h" |
25 | #include "hard-interface.h" | 25 | #include "hard-interface.h" |
26 | #include "send.h" | ||
26 | #include "hash.h" | 27 | #include "hash.h" |
27 | #include "originator.h" | 28 | #include "originator.h" |
29 | #include "routing.h" | ||
28 | 30 | ||
29 | static void tt_local_purge(struct work_struct *work); | 31 | #include <linux/crc16.h> |
30 | static void _tt_global_del_orig(struct bat_priv *bat_priv, | 32 | |
31 | struct tt_global_entry *tt_global_entry, | 33 | static void _tt_global_del(struct bat_priv *bat_priv, |
32 | char *message); | 34 | struct tt_global_entry *tt_global_entry, |
35 | const char *message); | ||
36 | static void tt_purge(struct work_struct *work); | ||
33 | 37 | ||
34 | /* returns 1 if they are the same mac addr */ | 38 | /* returns 1 if they are the same mac addr */ |
35 | static int compare_ltt(struct hlist_node *node, void *data2) | 39 | static int compare_ltt(const struct hlist_node *node, const void *data2) |
36 | { | 40 | { |
37 | void *data1 = container_of(node, struct tt_local_entry, hash_entry); | 41 | const void *data1 = container_of(node, struct tt_local_entry, |
42 | hash_entry); | ||
38 | 43 | ||
39 | return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); | 44 | return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); |
40 | } | 45 | } |
41 | 46 | ||
42 | /* returns 1 if they are the same mac addr */ | 47 | /* returns 1 if they are the same mac addr */ |
43 | static int compare_gtt(struct hlist_node *node, void *data2) | 48 | static int compare_gtt(const struct hlist_node *node, const void *data2) |
44 | { | 49 | { |
45 | void *data1 = container_of(node, struct tt_global_entry, hash_entry); | 50 | const void *data1 = container_of(node, struct tt_global_entry, |
51 | hash_entry); | ||
46 | 52 | ||
47 | return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); | 53 | return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); |
48 | } | 54 | } |
49 | 55 | ||
50 | static void tt_local_start_timer(struct bat_priv *bat_priv) | 56 | static void tt_start_timer(struct bat_priv *bat_priv) |
51 | { | 57 | { |
52 | INIT_DELAYED_WORK(&bat_priv->tt_work, tt_local_purge); | 58 | INIT_DELAYED_WORK(&bat_priv->tt_work, tt_purge); |
53 | queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, 10 * HZ); | 59 | queue_delayed_work(bat_event_workqueue, &bat_priv->tt_work, |
60 | msecs_to_jiffies(5000)); | ||
54 | } | 61 | } |
55 | 62 | ||
56 | static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, | 63 | static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, |
57 | void *data) | 64 | const void *data) |
58 | { | 65 | { |
59 | struct hashtable_t *hash = bat_priv->tt_local_hash; | 66 | struct hashtable_t *hash = bat_priv->tt_local_hash; |
60 | struct hlist_head *head; | 67 | struct hlist_head *head; |
@@ -73,6 +80,9 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, | |||
73 | if (!compare_eth(tt_local_entry, data)) | 80 | if (!compare_eth(tt_local_entry, data)) |
74 | continue; | 81 | continue; |
75 | 82 | ||
83 | if (!atomic_inc_not_zero(&tt_local_entry->refcount)) | ||
84 | continue; | ||
85 | |||
76 | tt_local_entry_tmp = tt_local_entry; | 86 | tt_local_entry_tmp = tt_local_entry; |
77 | break; | 87 | break; |
78 | } | 88 | } |
@@ -82,7 +92,7 @@ static struct tt_local_entry *tt_local_hash_find(struct bat_priv *bat_priv, | |||
82 | } | 92 | } |
83 | 93 | ||
84 | static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, | 94 | static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, |
85 | void *data) | 95 | const void *data) |
86 | { | 96 | { |
87 | struct hashtable_t *hash = bat_priv->tt_global_hash; | 97 | struct hashtable_t *hash = bat_priv->tt_global_hash; |
88 | struct hlist_head *head; | 98 | struct hlist_head *head; |
@@ -102,6 +112,9 @@ static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, | |||
102 | if (!compare_eth(tt_global_entry, data)) | 112 | if (!compare_eth(tt_global_entry, data)) |
103 | continue; | 113 | continue; |
104 | 114 | ||
115 | if (!atomic_inc_not_zero(&tt_global_entry->refcount)) | ||
116 | continue; | ||
117 | |||
105 | tt_global_entry_tmp = tt_global_entry; | 118 | tt_global_entry_tmp = tt_global_entry; |
106 | break; | 119 | break; |
107 | } | 120 | } |
@@ -110,7 +123,54 @@ static struct tt_global_entry *tt_global_hash_find(struct bat_priv *bat_priv, | |||
110 | return tt_global_entry_tmp; | 123 | return tt_global_entry_tmp; |
111 | } | 124 | } |
112 | 125 | ||
113 | int tt_local_init(struct bat_priv *bat_priv) | 126 | static bool is_out_of_time(unsigned long starting_time, unsigned long timeout) |
127 | { | ||
128 | unsigned long deadline; | ||
129 | deadline = starting_time + msecs_to_jiffies(timeout); | ||
130 | |||
131 | return time_after(jiffies, deadline); | ||
132 | } | ||
133 | |||
134 | static void tt_local_entry_free_ref(struct tt_local_entry *tt_local_entry) | ||
135 | { | ||
136 | if (atomic_dec_and_test(&tt_local_entry->refcount)) | ||
137 | kfree_rcu(tt_local_entry, rcu); | ||
138 | } | ||
139 | |||
140 | static void tt_global_entry_free_ref(struct tt_global_entry *tt_global_entry) | ||
141 | { | ||
142 | if (atomic_dec_and_test(&tt_global_entry->refcount)) | ||
143 | kfree_rcu(tt_global_entry, rcu); | ||
144 | } | ||
145 | |||
146 | static void tt_local_event(struct bat_priv *bat_priv, const uint8_t *addr, | ||
147 | uint8_t flags) | ||
148 | { | ||
149 | struct tt_change_node *tt_change_node; | ||
150 | |||
151 | tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC); | ||
152 | |||
153 | if (!tt_change_node) | ||
154 | return; | ||
155 | |||
156 | tt_change_node->change.flags = flags; | ||
157 | memcpy(tt_change_node->change.addr, addr, ETH_ALEN); | ||
158 | |||
159 | spin_lock_bh(&bat_priv->tt_changes_list_lock); | ||
160 | /* track the change in the OGMinterval list */ | ||
161 | list_add_tail(&tt_change_node->list, &bat_priv->tt_changes_list); | ||
162 | atomic_inc(&bat_priv->tt_local_changes); | ||
163 | spin_unlock_bh(&bat_priv->tt_changes_list_lock); | ||
164 | |||
165 | atomic_set(&bat_priv->tt_ogm_append_cnt, 0); | ||
166 | } | ||
167 | |||
168 | int tt_len(int changes_num) | ||
169 | { | ||
170 | return changes_num * sizeof(struct tt_change); | ||
171 | } | ||
172 | |||
173 | static int tt_local_init(struct bat_priv *bat_priv) | ||
114 | { | 174 | { |
115 | if (bat_priv->tt_local_hash) | 175 | if (bat_priv->tt_local_hash) |
116 | return 1; | 176 | return 1; |
@@ -120,116 +180,114 @@ int tt_local_init(struct bat_priv *bat_priv) | |||
120 | if (!bat_priv->tt_local_hash) | 180 | if (!bat_priv->tt_local_hash) |
121 | return 0; | 181 | return 0; |
122 | 182 | ||
123 | atomic_set(&bat_priv->tt_local_changed, 0); | ||
124 | tt_local_start_timer(bat_priv); | ||
125 | |||
126 | return 1; | 183 | return 1; |
127 | } | 184 | } |
128 | 185 | ||
129 | void tt_local_add(struct net_device *soft_iface, uint8_t *addr) | 186 | void tt_local_add(struct net_device *soft_iface, const uint8_t *addr) |
130 | { | 187 | { |
131 | struct bat_priv *bat_priv = netdev_priv(soft_iface); | 188 | struct bat_priv *bat_priv = netdev_priv(soft_iface); |
132 | struct tt_local_entry *tt_local_entry; | 189 | struct tt_local_entry *tt_local_entry = NULL; |
133 | struct tt_global_entry *tt_global_entry; | 190 | struct tt_global_entry *tt_global_entry = NULL; |
134 | int required_bytes; | ||
135 | 191 | ||
136 | spin_lock_bh(&bat_priv->tt_lhash_lock); | ||
137 | tt_local_entry = tt_local_hash_find(bat_priv, addr); | 192 | tt_local_entry = tt_local_hash_find(bat_priv, addr); |
138 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | ||
139 | 193 | ||
140 | if (tt_local_entry) { | 194 | if (tt_local_entry) { |
141 | tt_local_entry->last_seen = jiffies; | 195 | tt_local_entry->last_seen = jiffies; |
142 | return; | 196 | goto out; |
143 | } | ||
144 | |||
145 | /* only announce as many hosts as possible in the batman-packet and | ||
146 | space in batman_packet->num_tt That also should give a limit to | ||
147 | MAC-flooding. */ | ||
148 | required_bytes = (bat_priv->num_local_tt + 1) * ETH_ALEN; | ||
149 | required_bytes += BAT_PACKET_LEN; | ||
150 | |||
151 | if ((required_bytes > ETH_DATA_LEN) || | ||
152 | (atomic_read(&bat_priv->aggregated_ogms) && | ||
153 | required_bytes > MAX_AGGREGATION_BYTES) || | ||
154 | (bat_priv->num_local_tt + 1 > 255)) { | ||
155 | bat_dbg(DBG_ROUTES, bat_priv, | ||
156 | "Can't add new local tt entry (%pM): " | ||
157 | "number of local tt entries exceeds packet size\n", | ||
158 | addr); | ||
159 | return; | ||
160 | } | 197 | } |
161 | 198 | ||
162 | bat_dbg(DBG_ROUTES, bat_priv, | 199 | tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC); |
163 | "Creating new local tt entry: %pM\n", addr); | ||
164 | |||
165 | tt_local_entry = kmalloc(sizeof(struct tt_local_entry), GFP_ATOMIC); | ||
166 | if (!tt_local_entry) | 200 | if (!tt_local_entry) |
167 | return; | 201 | goto out; |
202 | |||
203 | bat_dbg(DBG_TT, bat_priv, | ||
204 | "Creating new local tt entry: %pM (ttvn: %d)\n", addr, | ||
205 | (uint8_t)atomic_read(&bat_priv->ttvn)); | ||
168 | 206 | ||
169 | memcpy(tt_local_entry->addr, addr, ETH_ALEN); | 207 | memcpy(tt_local_entry->addr, addr, ETH_ALEN); |
170 | tt_local_entry->last_seen = jiffies; | 208 | tt_local_entry->last_seen = jiffies; |
209 | tt_local_entry->flags = NO_FLAGS; | ||
210 | atomic_set(&tt_local_entry->refcount, 2); | ||
171 | 211 | ||
172 | /* the batman interface mac address should never be purged */ | 212 | /* the batman interface mac address should never be purged */ |
173 | if (compare_eth(addr, soft_iface->dev_addr)) | 213 | if (compare_eth(addr, soft_iface->dev_addr)) |
174 | tt_local_entry->never_purge = 1; | 214 | tt_local_entry->flags |= TT_CLIENT_NOPURGE; |
175 | else | ||
176 | tt_local_entry->never_purge = 0; | ||
177 | 215 | ||
178 | spin_lock_bh(&bat_priv->tt_lhash_lock); | 216 | tt_local_event(bat_priv, addr, tt_local_entry->flags); |
217 | |||
218 | /* The local entry has to be marked as NEW to avoid to send it in | ||
219 | * a full table response going out before the next ttvn increment | ||
220 | * (consistency check) */ | ||
221 | tt_local_entry->flags |= TT_CLIENT_NEW; | ||
179 | 222 | ||
180 | hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig, | 223 | hash_add(bat_priv->tt_local_hash, compare_ltt, choose_orig, |
181 | tt_local_entry, &tt_local_entry->hash_entry); | 224 | tt_local_entry, &tt_local_entry->hash_entry); |
182 | bat_priv->num_local_tt++; | ||
183 | atomic_set(&bat_priv->tt_local_changed, 1); | ||
184 | |||
185 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | ||
186 | 225 | ||
187 | /* remove address from global hash if present */ | 226 | /* remove address from global hash if present */ |
188 | spin_lock_bh(&bat_priv->tt_ghash_lock); | ||
189 | |||
190 | tt_global_entry = tt_global_hash_find(bat_priv, addr); | 227 | tt_global_entry = tt_global_hash_find(bat_priv, addr); |
191 | 228 | ||
229 | /* Check whether it is a roaming! */ | ||
230 | if (tt_global_entry) { | ||
231 | /* This node is probably going to update its tt table */ | ||
232 | tt_global_entry->orig_node->tt_poss_change = true; | ||
233 | /* The global entry has to be marked as PENDING and has to be | ||
234 | * kept for consistency purpose */ | ||
235 | tt_global_entry->flags |= TT_CLIENT_PENDING; | ||
236 | send_roam_adv(bat_priv, tt_global_entry->addr, | ||
237 | tt_global_entry->orig_node); | ||
238 | } | ||
239 | out: | ||
240 | if (tt_local_entry) | ||
241 | tt_local_entry_free_ref(tt_local_entry); | ||
192 | if (tt_global_entry) | 242 | if (tt_global_entry) |
193 | _tt_global_del_orig(bat_priv, tt_global_entry, | 243 | tt_global_entry_free_ref(tt_global_entry); |
194 | "local tt received"); | ||
195 | |||
196 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | ||
197 | } | 244 | } |
198 | 245 | ||
199 | int tt_local_fill_buffer(struct bat_priv *bat_priv, | 246 | int tt_changes_fill_buffer(struct bat_priv *bat_priv, |
200 | unsigned char *buff, int buff_len) | 247 | unsigned char *buff, int buff_len) |
201 | { | 248 | { |
202 | struct hashtable_t *hash = bat_priv->tt_local_hash; | 249 | int count = 0, tot_changes = 0; |
203 | struct tt_local_entry *tt_local_entry; | 250 | struct tt_change_node *entry, *safe; |
204 | struct hlist_node *node; | ||
205 | struct hlist_head *head; | ||
206 | int i, count = 0; | ||
207 | 251 | ||
208 | spin_lock_bh(&bat_priv->tt_lhash_lock); | 252 | if (buff_len > 0) |
253 | tot_changes = buff_len / tt_len(1); | ||
209 | 254 | ||
210 | for (i = 0; i < hash->size; i++) { | 255 | spin_lock_bh(&bat_priv->tt_changes_list_lock); |
211 | head = &hash->table[i]; | 256 | atomic_set(&bat_priv->tt_local_changes, 0); |
212 | |||
213 | rcu_read_lock(); | ||
214 | hlist_for_each_entry_rcu(tt_local_entry, node, | ||
215 | head, hash_entry) { | ||
216 | if (buff_len < (count + 1) * ETH_ALEN) | ||
217 | break; | ||
218 | |||
219 | memcpy(buff + (count * ETH_ALEN), tt_local_entry->addr, | ||
220 | ETH_ALEN); | ||
221 | 257 | ||
258 | list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list, | ||
259 | list) { | ||
260 | if (count < tot_changes) { | ||
261 | memcpy(buff + tt_len(count), | ||
262 | &entry->change, sizeof(struct tt_change)); | ||
222 | count++; | 263 | count++; |
223 | } | 264 | } |
224 | rcu_read_unlock(); | 265 | list_del(&entry->list); |
266 | kfree(entry); | ||
225 | } | 267 | } |
268 | spin_unlock_bh(&bat_priv->tt_changes_list_lock); | ||
269 | |||
270 | /* Keep the buffer for possible tt_request */ | ||
271 | spin_lock_bh(&bat_priv->tt_buff_lock); | ||
272 | kfree(bat_priv->tt_buff); | ||
273 | bat_priv->tt_buff_len = 0; | ||
274 | bat_priv->tt_buff = NULL; | ||
275 | /* We check whether this new OGM has no changes due to size | ||
276 | * problems */ | ||
277 | if (buff_len > 0) { | ||
278 | /** | ||
279 | * if kmalloc() fails we will reply with the full table | ||
280 | * instead of providing the diff | ||
281 | */ | ||
282 | bat_priv->tt_buff = kmalloc(buff_len, GFP_ATOMIC); | ||
283 | if (bat_priv->tt_buff) { | ||
284 | memcpy(bat_priv->tt_buff, buff, buff_len); | ||
285 | bat_priv->tt_buff_len = buff_len; | ||
286 | } | ||
287 | } | ||
288 | spin_unlock_bh(&bat_priv->tt_buff_lock); | ||
226 | 289 | ||
227 | /* if we did not get all new local tts see you next time ;-) */ | 290 | return tot_changes; |
228 | if (count == bat_priv->num_local_tt) | ||
229 | atomic_set(&bat_priv->tt_local_changed, 0); | ||
230 | |||
231 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | ||
232 | return count; | ||
233 | } | 291 | } |
234 | 292 | ||
235 | int tt_local_seq_print_text(struct seq_file *seq, void *offset) | 293 | int tt_local_seq_print_text(struct seq_file *seq, void *offset) |
@@ -261,10 +319,8 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) | |||
261 | } | 319 | } |
262 | 320 | ||
263 | seq_printf(seq, "Locally retrieved addresses (from %s) " | 321 | seq_printf(seq, "Locally retrieved addresses (from %s) " |
264 | "announced via TT:\n", | 322 | "announced via TT (TTVN: %u):\n", |
265 | net_dev->name); | 323 | net_dev->name, (uint8_t)atomic_read(&bat_priv->ttvn)); |
266 | |||
267 | spin_lock_bh(&bat_priv->tt_lhash_lock); | ||
268 | 324 | ||
269 | buf_size = 1; | 325 | buf_size = 1; |
270 | /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ | 326 | /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ |
@@ -279,7 +335,6 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) | |||
279 | 335 | ||
280 | buff = kmalloc(buf_size, GFP_ATOMIC); | 336 | buff = kmalloc(buf_size, GFP_ATOMIC); |
281 | if (!buff) { | 337 | if (!buff) { |
282 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | ||
283 | ret = -ENOMEM; | 338 | ret = -ENOMEM; |
284 | goto out; | 339 | goto out; |
285 | } | 340 | } |
@@ -299,8 +354,6 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) | |||
299 | rcu_read_unlock(); | 354 | rcu_read_unlock(); |
300 | } | 355 | } |
301 | 356 | ||
302 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | ||
303 | |||
304 | seq_printf(seq, "%s", buff); | 357 | seq_printf(seq, "%s", buff); |
305 | kfree(buff); | 358 | kfree(buff); |
306 | out: | 359 | out: |
@@ -309,92 +362,109 @@ out: | |||
309 | return ret; | 362 | return ret; |
310 | } | 363 | } |
311 | 364 | ||
312 | static void _tt_local_del(struct hlist_node *node, void *arg) | 365 | static void tt_local_set_pending(struct bat_priv *bat_priv, |
366 | struct tt_local_entry *tt_local_entry, | ||
367 | uint16_t flags) | ||
313 | { | 368 | { |
314 | struct bat_priv *bat_priv = (struct bat_priv *)arg; | 369 | tt_local_event(bat_priv, tt_local_entry->addr, |
315 | void *data = container_of(node, struct tt_local_entry, hash_entry); | 370 | tt_local_entry->flags | flags); |
316 | 371 | ||
317 | kfree(data); | 372 | /* The local client has to be merked as "pending to be removed" but has |
318 | bat_priv->num_local_tt--; | 373 | * to be kept in the table in order to send it in an full tables |
319 | atomic_set(&bat_priv->tt_local_changed, 1); | 374 | * response issued before the net ttvn increment (consistency check) */ |
375 | tt_local_entry->flags |= TT_CLIENT_PENDING; | ||
320 | } | 376 | } |
321 | 377 | ||
322 | static void tt_local_del(struct bat_priv *bat_priv, | 378 | void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr, |
323 | struct tt_local_entry *tt_local_entry, | 379 | const char *message, bool roaming) |
324 | char *message) | ||
325 | { | 380 | { |
326 | bat_dbg(DBG_ROUTES, bat_priv, "Deleting local tt entry (%pM): %s\n", | 381 | struct tt_local_entry *tt_local_entry = NULL; |
327 | tt_local_entry->addr, message); | ||
328 | |||
329 | hash_remove(bat_priv->tt_local_hash, compare_ltt, choose_orig, | ||
330 | tt_local_entry->addr); | ||
331 | _tt_local_del(&tt_local_entry->hash_entry, bat_priv); | ||
332 | } | ||
333 | |||
334 | void tt_local_remove(struct bat_priv *bat_priv, | ||
335 | uint8_t *addr, char *message) | ||
336 | { | ||
337 | struct tt_local_entry *tt_local_entry; | ||
338 | |||
339 | spin_lock_bh(&bat_priv->tt_lhash_lock); | ||
340 | 382 | ||
341 | tt_local_entry = tt_local_hash_find(bat_priv, addr); | 383 | tt_local_entry = tt_local_hash_find(bat_priv, addr); |
384 | if (!tt_local_entry) | ||
385 | goto out; | ||
342 | 386 | ||
343 | if (tt_local_entry) | 387 | tt_local_set_pending(bat_priv, tt_local_entry, TT_CLIENT_DEL | |
344 | tt_local_del(bat_priv, tt_local_entry, message); | 388 | (roaming ? TT_CLIENT_ROAM : NO_FLAGS)); |
345 | 389 | ||
346 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | 390 | bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) pending to be removed: " |
391 | "%s\n", tt_local_entry->addr, message); | ||
392 | out: | ||
393 | if (tt_local_entry) | ||
394 | tt_local_entry_free_ref(tt_local_entry); | ||
347 | } | 395 | } |
348 | 396 | ||
349 | static void tt_local_purge(struct work_struct *work) | 397 | static void tt_local_purge(struct bat_priv *bat_priv) |
350 | { | 398 | { |
351 | struct delayed_work *delayed_work = | ||
352 | container_of(work, struct delayed_work, work); | ||
353 | struct bat_priv *bat_priv = | ||
354 | container_of(delayed_work, struct bat_priv, tt_work); | ||
355 | struct hashtable_t *hash = bat_priv->tt_local_hash; | 399 | struct hashtable_t *hash = bat_priv->tt_local_hash; |
356 | struct tt_local_entry *tt_local_entry; | 400 | struct tt_local_entry *tt_local_entry; |
357 | struct hlist_node *node, *node_tmp; | 401 | struct hlist_node *node, *node_tmp; |
358 | struct hlist_head *head; | 402 | struct hlist_head *head; |
359 | unsigned long timeout; | 403 | spinlock_t *list_lock; /* protects write access to the hash lists */ |
360 | int i; | 404 | int i; |
361 | 405 | ||
362 | spin_lock_bh(&bat_priv->tt_lhash_lock); | ||
363 | |||
364 | for (i = 0; i < hash->size; i++) { | 406 | for (i = 0; i < hash->size; i++) { |
365 | head = &hash->table[i]; | 407 | head = &hash->table[i]; |
408 | list_lock = &hash->list_locks[i]; | ||
366 | 409 | ||
410 | spin_lock_bh(list_lock); | ||
367 | hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, | 411 | hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, |
368 | head, hash_entry) { | 412 | head, hash_entry) { |
369 | if (tt_local_entry->never_purge) | 413 | if (tt_local_entry->flags & TT_CLIENT_NOPURGE) |
370 | continue; | 414 | continue; |
371 | 415 | ||
372 | timeout = tt_local_entry->last_seen; | 416 | /* entry already marked for deletion */ |
373 | timeout += TT_LOCAL_TIMEOUT * HZ; | 417 | if (tt_local_entry->flags & TT_CLIENT_PENDING) |
418 | continue; | ||
374 | 419 | ||
375 | if (time_before(jiffies, timeout)) | 420 | if (!is_out_of_time(tt_local_entry->last_seen, |
421 | TT_LOCAL_TIMEOUT * 1000)) | ||
376 | continue; | 422 | continue; |
377 | 423 | ||
378 | tt_local_del(bat_priv, tt_local_entry, | 424 | tt_local_set_pending(bat_priv, tt_local_entry, |
379 | "address timed out"); | 425 | TT_CLIENT_DEL); |
426 | bat_dbg(DBG_TT, bat_priv, "Local tt entry (%pM) " | ||
427 | "pending to be removed: timed out\n", | ||
428 | tt_local_entry->addr); | ||
380 | } | 429 | } |
430 | spin_unlock_bh(list_lock); | ||
381 | } | 431 | } |
382 | 432 | ||
383 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | ||
384 | tt_local_start_timer(bat_priv); | ||
385 | } | 433 | } |
386 | 434 | ||
387 | void tt_local_free(struct bat_priv *bat_priv) | 435 | static void tt_local_table_free(struct bat_priv *bat_priv) |
388 | { | 436 | { |
437 | struct hashtable_t *hash; | ||
438 | spinlock_t *list_lock; /* protects write access to the hash lists */ | ||
439 | struct tt_local_entry *tt_local_entry; | ||
440 | struct hlist_node *node, *node_tmp; | ||
441 | struct hlist_head *head; | ||
442 | int i; | ||
443 | |||
389 | if (!bat_priv->tt_local_hash) | 444 | if (!bat_priv->tt_local_hash) |
390 | return; | 445 | return; |
391 | 446 | ||
392 | cancel_delayed_work_sync(&bat_priv->tt_work); | 447 | hash = bat_priv->tt_local_hash; |
393 | hash_delete(bat_priv->tt_local_hash, _tt_local_del, bat_priv); | 448 | |
449 | for (i = 0; i < hash->size; i++) { | ||
450 | head = &hash->table[i]; | ||
451 | list_lock = &hash->list_locks[i]; | ||
452 | |||
453 | spin_lock_bh(list_lock); | ||
454 | hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, | ||
455 | head, hash_entry) { | ||
456 | hlist_del_rcu(node); | ||
457 | tt_local_entry_free_ref(tt_local_entry); | ||
458 | } | ||
459 | spin_unlock_bh(list_lock); | ||
460 | } | ||
461 | |||
462 | hash_destroy(hash); | ||
463 | |||
394 | bat_priv->tt_local_hash = NULL; | 464 | bat_priv->tt_local_hash = NULL; |
395 | } | 465 | } |
396 | 466 | ||
397 | int tt_global_init(struct bat_priv *bat_priv) | 467 | static int tt_global_init(struct bat_priv *bat_priv) |
398 | { | 468 | { |
399 | if (bat_priv->tt_global_hash) | 469 | if (bat_priv->tt_global_hash) |
400 | return 1; | 470 | return 1; |
@@ -407,74 +477,78 @@ int tt_global_init(struct bat_priv *bat_priv) | |||
407 | return 1; | 477 | return 1; |
408 | } | 478 | } |
409 | 479 | ||
410 | void tt_global_add_orig(struct bat_priv *bat_priv, | 480 | static void tt_changes_list_free(struct bat_priv *bat_priv) |
411 | struct orig_node *orig_node, | ||
412 | unsigned char *tt_buff, int tt_buff_len) | ||
413 | { | 481 | { |
414 | struct tt_global_entry *tt_global_entry; | 482 | struct tt_change_node *entry, *safe; |
415 | struct tt_local_entry *tt_local_entry; | ||
416 | int tt_buff_count = 0; | ||
417 | unsigned char *tt_ptr; | ||
418 | 483 | ||
419 | while ((tt_buff_count + 1) * ETH_ALEN <= tt_buff_len) { | 484 | spin_lock_bh(&bat_priv->tt_changes_list_lock); |
420 | spin_lock_bh(&bat_priv->tt_ghash_lock); | ||
421 | 485 | ||
422 | tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN); | 486 | list_for_each_entry_safe(entry, safe, &bat_priv->tt_changes_list, |
423 | tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr); | 487 | list) { |
424 | 488 | list_del(&entry->list); | |
425 | if (!tt_global_entry) { | 489 | kfree(entry); |
426 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | 490 | } |
427 | |||
428 | tt_global_entry = | ||
429 | kmalloc(sizeof(struct tt_global_entry), | ||
430 | GFP_ATOMIC); | ||
431 | |||
432 | if (!tt_global_entry) | ||
433 | break; | ||
434 | 491 | ||
435 | memcpy(tt_global_entry->addr, tt_ptr, ETH_ALEN); | 492 | atomic_set(&bat_priv->tt_local_changes, 0); |
493 | spin_unlock_bh(&bat_priv->tt_changes_list_lock); | ||
494 | } | ||
436 | 495 | ||
437 | bat_dbg(DBG_ROUTES, bat_priv, | 496 | /* caller must hold orig_node refcount */ |
438 | "Creating new global tt entry: " | 497 | int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node, |
439 | "%pM (via %pM)\n", | 498 | const unsigned char *tt_addr, uint8_t ttvn, bool roaming) |
440 | tt_global_entry->addr, orig_node->orig); | 499 | { |
500 | struct tt_global_entry *tt_global_entry; | ||
501 | struct orig_node *orig_node_tmp; | ||
502 | int ret = 0; | ||
441 | 503 | ||
442 | spin_lock_bh(&bat_priv->tt_ghash_lock); | 504 | tt_global_entry = tt_global_hash_find(bat_priv, tt_addr); |
443 | hash_add(bat_priv->tt_global_hash, compare_gtt, | ||
444 | choose_orig, tt_global_entry, | ||
445 | &tt_global_entry->hash_entry); | ||
446 | 505 | ||
447 | } | 506 | if (!tt_global_entry) { |
507 | tt_global_entry = | ||
508 | kmalloc(sizeof(*tt_global_entry), | ||
509 | GFP_ATOMIC); | ||
510 | if (!tt_global_entry) | ||
511 | goto out; | ||
448 | 512 | ||
513 | memcpy(tt_global_entry->addr, tt_addr, ETH_ALEN); | ||
514 | /* Assign the new orig_node */ | ||
515 | atomic_inc(&orig_node->refcount); | ||
449 | tt_global_entry->orig_node = orig_node; | 516 | tt_global_entry->orig_node = orig_node; |
450 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | 517 | tt_global_entry->ttvn = ttvn; |
451 | 518 | tt_global_entry->flags = NO_FLAGS; | |
452 | /* remove address from local hash if present */ | 519 | tt_global_entry->roam_at = 0; |
453 | spin_lock_bh(&bat_priv->tt_lhash_lock); | 520 | atomic_set(&tt_global_entry->refcount, 2); |
454 | 521 | ||
455 | tt_ptr = tt_buff + (tt_buff_count * ETH_ALEN); | 522 | hash_add(bat_priv->tt_global_hash, compare_gtt, |
456 | tt_local_entry = tt_local_hash_find(bat_priv, tt_ptr); | 523 | choose_orig, tt_global_entry, |
457 | 524 | &tt_global_entry->hash_entry); | |
458 | if (tt_local_entry) | 525 | atomic_inc(&orig_node->tt_size); |
459 | tt_local_del(bat_priv, tt_local_entry, | 526 | } else { |
460 | "global tt received"); | 527 | if (tt_global_entry->orig_node != orig_node) { |
461 | 528 | atomic_dec(&tt_global_entry->orig_node->tt_size); | |
462 | spin_unlock_bh(&bat_priv->tt_lhash_lock); | 529 | orig_node_tmp = tt_global_entry->orig_node; |
463 | 530 | atomic_inc(&orig_node->refcount); | |
464 | tt_buff_count++; | 531 | tt_global_entry->orig_node = orig_node; |
532 | orig_node_free_ref(orig_node_tmp); | ||
533 | atomic_inc(&orig_node->tt_size); | ||
534 | } | ||
535 | tt_global_entry->ttvn = ttvn; | ||
536 | tt_global_entry->flags = NO_FLAGS; | ||
537 | tt_global_entry->roam_at = 0; | ||
465 | } | 538 | } |
466 | 539 | ||
467 | /* initialize, and overwrite if malloc succeeds */ | 540 | bat_dbg(DBG_TT, bat_priv, |
468 | orig_node->tt_buff = NULL; | 541 | "Creating new global tt entry: %pM (via %pM)\n", |
469 | orig_node->tt_buff_len = 0; | 542 | tt_global_entry->addr, orig_node->orig); |
470 | 543 | ||
471 | if (tt_buff_len > 0) { | 544 | /* remove address from local hash if present */ |
472 | orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC); | 545 | tt_local_remove(bat_priv, tt_global_entry->addr, |
473 | if (orig_node->tt_buff) { | 546 | "global tt received", roaming); |
474 | memcpy(orig_node->tt_buff, tt_buff, tt_buff_len); | 547 | ret = 1; |
475 | orig_node->tt_buff_len = tt_buff_len; | 548 | out: |
476 | } | 549 | if (tt_global_entry) |
477 | } | 550 | tt_global_entry_free_ref(tt_global_entry); |
551 | return ret; | ||
478 | } | 552 | } |
479 | 553 | ||
480 | int tt_global_seq_print_text(struct seq_file *seq, void *offset) | 554 | int tt_global_seq_print_text(struct seq_file *seq, void *offset) |
@@ -508,26 +582,27 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) | |||
508 | seq_printf(seq, | 582 | seq_printf(seq, |
509 | "Globally announced TT entries received via the mesh %s\n", | 583 | "Globally announced TT entries received via the mesh %s\n", |
510 | net_dev->name); | 584 | net_dev->name); |
511 | 585 | seq_printf(seq, " %-13s %s %-15s %s\n", | |
512 | spin_lock_bh(&bat_priv->tt_ghash_lock); | 586 | "Client", "(TTVN)", "Originator", "(Curr TTVN)"); |
513 | 587 | ||
514 | buf_size = 1; | 588 | buf_size = 1; |
515 | /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/ | 589 | /* Estimate length for: " * xx:xx:xx:xx:xx:xx (ttvn) via |
590 | * xx:xx:xx:xx:xx:xx (cur_ttvn)\n"*/ | ||
516 | for (i = 0; i < hash->size; i++) { | 591 | for (i = 0; i < hash->size; i++) { |
517 | head = &hash->table[i]; | 592 | head = &hash->table[i]; |
518 | 593 | ||
519 | rcu_read_lock(); | 594 | rcu_read_lock(); |
520 | __hlist_for_each_rcu(node, head) | 595 | __hlist_for_each_rcu(node, head) |
521 | buf_size += 43; | 596 | buf_size += 59; |
522 | rcu_read_unlock(); | 597 | rcu_read_unlock(); |
523 | } | 598 | } |
524 | 599 | ||
525 | buff = kmalloc(buf_size, GFP_ATOMIC); | 600 | buff = kmalloc(buf_size, GFP_ATOMIC); |
526 | if (!buff) { | 601 | if (!buff) { |
527 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | ||
528 | ret = -ENOMEM; | 602 | ret = -ENOMEM; |
529 | goto out; | 603 | goto out; |
530 | } | 604 | } |
605 | |||
531 | buff[0] = '\0'; | 606 | buff[0] = '\0'; |
532 | pos = 0; | 607 | pos = 0; |
533 | 608 | ||
@@ -537,16 +612,18 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) | |||
537 | rcu_read_lock(); | 612 | rcu_read_lock(); |
538 | hlist_for_each_entry_rcu(tt_global_entry, node, | 613 | hlist_for_each_entry_rcu(tt_global_entry, node, |
539 | head, hash_entry) { | 614 | head, hash_entry) { |
540 | pos += snprintf(buff + pos, 44, | 615 | pos += snprintf(buff + pos, 61, |
541 | " * %pM via %pM\n", | 616 | " * %pM (%3u) via %pM (%3u)\n", |
542 | tt_global_entry->addr, | 617 | tt_global_entry->addr, |
543 | tt_global_entry->orig_node->orig); | 618 | tt_global_entry->ttvn, |
619 | tt_global_entry->orig_node->orig, | ||
620 | (uint8_t) atomic_read( | ||
621 | &tt_global_entry->orig_node-> | ||
622 | last_ttvn)); | ||
544 | } | 623 | } |
545 | rcu_read_unlock(); | 624 | rcu_read_unlock(); |
546 | } | 625 | } |
547 | 626 | ||
548 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | ||
549 | |||
550 | seq_printf(seq, "%s", buff); | 627 | seq_printf(seq, "%s", buff); |
551 | kfree(buff); | 628 | kfree(buff); |
552 | out: | 629 | out: |
@@ -555,84 +632,1091 @@ out: | |||
555 | return ret; | 632 | return ret; |
556 | } | 633 | } |
557 | 634 | ||
558 | static void _tt_global_del_orig(struct bat_priv *bat_priv, | 635 | static void _tt_global_del(struct bat_priv *bat_priv, |
559 | struct tt_global_entry *tt_global_entry, | 636 | struct tt_global_entry *tt_global_entry, |
560 | char *message) | 637 | const char *message) |
561 | { | 638 | { |
562 | bat_dbg(DBG_ROUTES, bat_priv, | 639 | if (!tt_global_entry) |
640 | goto out; | ||
641 | |||
642 | bat_dbg(DBG_TT, bat_priv, | ||
563 | "Deleting global tt entry %pM (via %pM): %s\n", | 643 | "Deleting global tt entry %pM (via %pM): %s\n", |
564 | tt_global_entry->addr, tt_global_entry->orig_node->orig, | 644 | tt_global_entry->addr, tt_global_entry->orig_node->orig, |
565 | message); | 645 | message); |
566 | 646 | ||
647 | atomic_dec(&tt_global_entry->orig_node->tt_size); | ||
648 | |||
567 | hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig, | 649 | hash_remove(bat_priv->tt_global_hash, compare_gtt, choose_orig, |
568 | tt_global_entry->addr); | 650 | tt_global_entry->addr); |
569 | kfree(tt_global_entry); | 651 | out: |
652 | if (tt_global_entry) | ||
653 | tt_global_entry_free_ref(tt_global_entry); | ||
570 | } | 654 | } |
571 | 655 | ||
572 | void tt_global_del_orig(struct bat_priv *bat_priv, | 656 | void tt_global_del(struct bat_priv *bat_priv, |
573 | struct orig_node *orig_node, char *message) | 657 | struct orig_node *orig_node, const unsigned char *addr, |
658 | const char *message, bool roaming) | ||
574 | { | 659 | { |
575 | struct tt_global_entry *tt_global_entry; | 660 | struct tt_global_entry *tt_global_entry = NULL; |
576 | int tt_buff_count = 0; | ||
577 | unsigned char *tt_ptr; | ||
578 | 661 | ||
579 | if (orig_node->tt_buff_len == 0) | 662 | tt_global_entry = tt_global_hash_find(bat_priv, addr); |
580 | return; | 663 | if (!tt_global_entry) |
664 | goto out; | ||
581 | 665 | ||
582 | spin_lock_bh(&bat_priv->tt_ghash_lock); | 666 | if (tt_global_entry->orig_node == orig_node) { |
667 | if (roaming) { | ||
668 | tt_global_entry->flags |= TT_CLIENT_ROAM; | ||
669 | tt_global_entry->roam_at = jiffies; | ||
670 | goto out; | ||
671 | } | ||
672 | _tt_global_del(bat_priv, tt_global_entry, message); | ||
673 | } | ||
674 | out: | ||
675 | if (tt_global_entry) | ||
676 | tt_global_entry_free_ref(tt_global_entry); | ||
677 | } | ||
583 | 678 | ||
584 | while ((tt_buff_count + 1) * ETH_ALEN <= orig_node->tt_buff_len) { | 679 | void tt_global_del_orig(struct bat_priv *bat_priv, |
585 | tt_ptr = orig_node->tt_buff + (tt_buff_count * ETH_ALEN); | 680 | struct orig_node *orig_node, const char *message) |
586 | tt_global_entry = tt_global_hash_find(bat_priv, tt_ptr); | 681 | { |
682 | struct tt_global_entry *tt_global_entry; | ||
683 | int i; | ||
684 | struct hashtable_t *hash = bat_priv->tt_global_hash; | ||
685 | struct hlist_node *node, *safe; | ||
686 | struct hlist_head *head; | ||
687 | spinlock_t *list_lock; /* protects write access to the hash lists */ | ||
587 | 688 | ||
588 | if ((tt_global_entry) && | 689 | for (i = 0; i < hash->size; i++) { |
589 | (tt_global_entry->orig_node == orig_node)) | 690 | head = &hash->table[i]; |
590 | _tt_global_del_orig(bat_priv, tt_global_entry, | 691 | list_lock = &hash->list_locks[i]; |
591 | message); | ||
592 | 692 | ||
593 | tt_buff_count++; | 693 | spin_lock_bh(list_lock); |
694 | hlist_for_each_entry_safe(tt_global_entry, node, safe, | ||
695 | head, hash_entry) { | ||
696 | if (tt_global_entry->orig_node == orig_node) { | ||
697 | bat_dbg(DBG_TT, bat_priv, | ||
698 | "Deleting global tt entry %pM " | ||
699 | "(via %pM): originator time out\n", | ||
700 | tt_global_entry->addr, | ||
701 | tt_global_entry->orig_node->orig); | ||
702 | hlist_del_rcu(node); | ||
703 | tt_global_entry_free_ref(tt_global_entry); | ||
704 | } | ||
705 | } | ||
706 | spin_unlock_bh(list_lock); | ||
594 | } | 707 | } |
595 | 708 | atomic_set(&orig_node->tt_size, 0); | |
596 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | ||
597 | |||
598 | orig_node->tt_buff_len = 0; | ||
599 | kfree(orig_node->tt_buff); | ||
600 | orig_node->tt_buff = NULL; | ||
601 | } | 709 | } |
602 | 710 | ||
603 | static void tt_global_del(struct hlist_node *node, void *arg) | 711 | static void tt_global_roam_purge(struct bat_priv *bat_priv) |
604 | { | 712 | { |
605 | void *data = container_of(node, struct tt_global_entry, hash_entry); | 713 | struct hashtable_t *hash = bat_priv->tt_global_hash; |
714 | struct tt_global_entry *tt_global_entry; | ||
715 | struct hlist_node *node, *node_tmp; | ||
716 | struct hlist_head *head; | ||
717 | spinlock_t *list_lock; /* protects write access to the hash lists */ | ||
718 | int i; | ||
719 | |||
720 | for (i = 0; i < hash->size; i++) { | ||
721 | head = &hash->table[i]; | ||
722 | list_lock = &hash->list_locks[i]; | ||
723 | |||
724 | spin_lock_bh(list_lock); | ||
725 | hlist_for_each_entry_safe(tt_global_entry, node, node_tmp, | ||
726 | head, hash_entry) { | ||
727 | if (!(tt_global_entry->flags & TT_CLIENT_ROAM)) | ||
728 | continue; | ||
729 | if (!is_out_of_time(tt_global_entry->roam_at, | ||
730 | TT_CLIENT_ROAM_TIMEOUT * 1000)) | ||
731 | continue; | ||
732 | |||
733 | bat_dbg(DBG_TT, bat_priv, "Deleting global " | ||
734 | "tt entry (%pM): Roaming timeout\n", | ||
735 | tt_global_entry->addr); | ||
736 | atomic_dec(&tt_global_entry->orig_node->tt_size); | ||
737 | hlist_del_rcu(node); | ||
738 | tt_global_entry_free_ref(tt_global_entry); | ||
739 | } | ||
740 | spin_unlock_bh(list_lock); | ||
741 | } | ||
606 | 742 | ||
607 | kfree(data); | ||
608 | } | 743 | } |
609 | 744 | ||
610 | void tt_global_free(struct bat_priv *bat_priv) | 745 | static void tt_global_table_free(struct bat_priv *bat_priv) |
611 | { | 746 | { |
747 | struct hashtable_t *hash; | ||
748 | spinlock_t *list_lock; /* protects write access to the hash lists */ | ||
749 | struct tt_global_entry *tt_global_entry; | ||
750 | struct hlist_node *node, *node_tmp; | ||
751 | struct hlist_head *head; | ||
752 | int i; | ||
753 | |||
612 | if (!bat_priv->tt_global_hash) | 754 | if (!bat_priv->tt_global_hash) |
613 | return; | 755 | return; |
614 | 756 | ||
615 | hash_delete(bat_priv->tt_global_hash, tt_global_del, NULL); | 757 | hash = bat_priv->tt_global_hash; |
758 | |||
759 | for (i = 0; i < hash->size; i++) { | ||
760 | head = &hash->table[i]; | ||
761 | list_lock = &hash->list_locks[i]; | ||
762 | |||
763 | spin_lock_bh(list_lock); | ||
764 | hlist_for_each_entry_safe(tt_global_entry, node, node_tmp, | ||
765 | head, hash_entry) { | ||
766 | hlist_del_rcu(node); | ||
767 | tt_global_entry_free_ref(tt_global_entry); | ||
768 | } | ||
769 | spin_unlock_bh(list_lock); | ||
770 | } | ||
771 | |||
772 | hash_destroy(hash); | ||
773 | |||
616 | bat_priv->tt_global_hash = NULL; | 774 | bat_priv->tt_global_hash = NULL; |
617 | } | 775 | } |
618 | 776 | ||
619 | struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) | 777 | struct orig_node *transtable_search(struct bat_priv *bat_priv, |
778 | const uint8_t *addr) | ||
620 | { | 779 | { |
621 | struct tt_global_entry *tt_global_entry; | 780 | struct tt_global_entry *tt_global_entry; |
622 | struct orig_node *orig_node = NULL; | 781 | struct orig_node *orig_node = NULL; |
623 | 782 | ||
624 | spin_lock_bh(&bat_priv->tt_ghash_lock); | ||
625 | tt_global_entry = tt_global_hash_find(bat_priv, addr); | 783 | tt_global_entry = tt_global_hash_find(bat_priv, addr); |
626 | 784 | ||
627 | if (!tt_global_entry) | 785 | if (!tt_global_entry) |
628 | goto out; | 786 | goto out; |
629 | 787 | ||
630 | if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount)) | 788 | if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount)) |
631 | goto out; | 789 | goto free_tt; |
790 | |||
791 | /* A global client marked as PENDING has already moved from that | ||
792 | * originator */ | ||
793 | if (tt_global_entry->flags & TT_CLIENT_PENDING) | ||
794 | goto free_tt; | ||
632 | 795 | ||
633 | orig_node = tt_global_entry->orig_node; | 796 | orig_node = tt_global_entry->orig_node; |
634 | 797 | ||
798 | free_tt: | ||
799 | tt_global_entry_free_ref(tt_global_entry); | ||
635 | out: | 800 | out: |
636 | spin_unlock_bh(&bat_priv->tt_ghash_lock); | ||
637 | return orig_node; | 801 | return orig_node; |
638 | } | 802 | } |
803 | |||
804 | /* Calculates the checksum of the local table of a given orig_node */ | ||
805 | uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node) | ||
806 | { | ||
807 | uint16_t total = 0, total_one; | ||
808 | struct hashtable_t *hash = bat_priv->tt_global_hash; | ||
809 | struct tt_global_entry *tt_global_entry; | ||
810 | struct hlist_node *node; | ||
811 | struct hlist_head *head; | ||
812 | int i, j; | ||
813 | |||
814 | for (i = 0; i < hash->size; i++) { | ||
815 | head = &hash->table[i]; | ||
816 | |||
817 | rcu_read_lock(); | ||
818 | hlist_for_each_entry_rcu(tt_global_entry, node, | ||
819 | head, hash_entry) { | ||
820 | if (compare_eth(tt_global_entry->orig_node, | ||
821 | orig_node)) { | ||
822 | /* Roaming clients are in the global table for | ||
823 | * consistency only. They don't have to be | ||
824 | * taken into account while computing the | ||
825 | * global crc */ | ||
826 | if (tt_global_entry->flags & TT_CLIENT_ROAM) | ||
827 | continue; | ||
828 | total_one = 0; | ||
829 | for (j = 0; j < ETH_ALEN; j++) | ||
830 | total_one = crc16_byte(total_one, | ||
831 | tt_global_entry->addr[j]); | ||
832 | total ^= total_one; | ||
833 | } | ||
834 | } | ||
835 | rcu_read_unlock(); | ||
836 | } | ||
837 | |||
838 | return total; | ||
839 | } | ||
840 | |||
841 | /* Calculates the checksum of the local table */ | ||
842 | uint16_t tt_local_crc(struct bat_priv *bat_priv) | ||
843 | { | ||
844 | uint16_t total = 0, total_one; | ||
845 | struct hashtable_t *hash = bat_priv->tt_local_hash; | ||
846 | struct tt_local_entry *tt_local_entry; | ||
847 | struct hlist_node *node; | ||
848 | struct hlist_head *head; | ||
849 | int i, j; | ||
850 | |||
851 | for (i = 0; i < hash->size; i++) { | ||
852 | head = &hash->table[i]; | ||
853 | |||
854 | rcu_read_lock(); | ||
855 | hlist_for_each_entry_rcu(tt_local_entry, node, | ||
856 | head, hash_entry) { | ||
857 | /* not yet committed clients have not to be taken into | ||
858 | * account while computing the CRC */ | ||
859 | if (tt_local_entry->flags & TT_CLIENT_NEW) | ||
860 | continue; | ||
861 | total_one = 0; | ||
862 | for (j = 0; j < ETH_ALEN; j++) | ||
863 | total_one = crc16_byte(total_one, | ||
864 | tt_local_entry->addr[j]); | ||
865 | total ^= total_one; | ||
866 | } | ||
867 | rcu_read_unlock(); | ||
868 | } | ||
869 | |||
870 | return total; | ||
871 | } | ||
872 | |||
873 | static void tt_req_list_free(struct bat_priv *bat_priv) | ||
874 | { | ||
875 | struct tt_req_node *node, *safe; | ||
876 | |||
877 | spin_lock_bh(&bat_priv->tt_req_list_lock); | ||
878 | |||
879 | list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) { | ||
880 | list_del(&node->list); | ||
881 | kfree(node); | ||
882 | } | ||
883 | |||
884 | spin_unlock_bh(&bat_priv->tt_req_list_lock); | ||
885 | } | ||
886 | |||
887 | void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node, | ||
888 | const unsigned char *tt_buff, uint8_t tt_num_changes) | ||
889 | { | ||
890 | uint16_t tt_buff_len = tt_len(tt_num_changes); | ||
891 | |||
892 | /* Replace the old buffer only if I received something in the | ||
893 | * last OGM (the OGM could carry no changes) */ | ||
894 | spin_lock_bh(&orig_node->tt_buff_lock); | ||
895 | if (tt_buff_len > 0) { | ||
896 | kfree(orig_node->tt_buff); | ||
897 | orig_node->tt_buff_len = 0; | ||
898 | orig_node->tt_buff = kmalloc(tt_buff_len, GFP_ATOMIC); | ||
899 | if (orig_node->tt_buff) { | ||
900 | memcpy(orig_node->tt_buff, tt_buff, tt_buff_len); | ||
901 | orig_node->tt_buff_len = tt_buff_len; | ||
902 | } | ||
903 | } | ||
904 | spin_unlock_bh(&orig_node->tt_buff_lock); | ||
905 | } | ||
906 | |||
907 | static void tt_req_purge(struct bat_priv *bat_priv) | ||
908 | { | ||
909 | struct tt_req_node *node, *safe; | ||
910 | |||
911 | spin_lock_bh(&bat_priv->tt_req_list_lock); | ||
912 | list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) { | ||
913 | if (is_out_of_time(node->issued_at, | ||
914 | TT_REQUEST_TIMEOUT * 1000)) { | ||
915 | list_del(&node->list); | ||
916 | kfree(node); | ||
917 | } | ||
918 | } | ||
919 | spin_unlock_bh(&bat_priv->tt_req_list_lock); | ||
920 | } | ||
921 | |||
922 | /* returns the pointer to the new tt_req_node struct if no request | ||
923 | * has already been issued for this orig_node, NULL otherwise */ | ||
924 | static struct tt_req_node *new_tt_req_node(struct bat_priv *bat_priv, | ||
925 | struct orig_node *orig_node) | ||
926 | { | ||
927 | struct tt_req_node *tt_req_node_tmp, *tt_req_node = NULL; | ||
928 | |||
929 | spin_lock_bh(&bat_priv->tt_req_list_lock); | ||
930 | list_for_each_entry(tt_req_node_tmp, &bat_priv->tt_req_list, list) { | ||
931 | if (compare_eth(tt_req_node_tmp, orig_node) && | ||
932 | !is_out_of_time(tt_req_node_tmp->issued_at, | ||
933 | TT_REQUEST_TIMEOUT * 1000)) | ||
934 | goto unlock; | ||
935 | } | ||
936 | |||
937 | tt_req_node = kmalloc(sizeof(*tt_req_node), GFP_ATOMIC); | ||
938 | if (!tt_req_node) | ||
939 | goto unlock; | ||
940 | |||
941 | memcpy(tt_req_node->addr, orig_node->orig, ETH_ALEN); | ||
942 | tt_req_node->issued_at = jiffies; | ||
943 | |||
944 | list_add(&tt_req_node->list, &bat_priv->tt_req_list); | ||
945 | unlock: | ||
946 | spin_unlock_bh(&bat_priv->tt_req_list_lock); | ||
947 | return tt_req_node; | ||
948 | } | ||
949 | |||
950 | /* data_ptr is useless here, but has to be kept to respect the prototype */ | ||
951 | static int tt_local_valid_entry(const void *entry_ptr, const void *data_ptr) | ||
952 | { | ||
953 | const struct tt_local_entry *tt_local_entry = entry_ptr; | ||
954 | |||
955 | if (tt_local_entry->flags & TT_CLIENT_NEW) | ||
956 | return 0; | ||
957 | return 1; | ||
958 | } | ||
959 | |||
960 | static int tt_global_valid_entry(const void *entry_ptr, const void *data_ptr) | ||
961 | { | ||
962 | const struct tt_global_entry *tt_global_entry = entry_ptr; | ||
963 | const struct orig_node *orig_node = data_ptr; | ||
964 | |||
965 | if (tt_global_entry->flags & TT_CLIENT_ROAM) | ||
966 | return 0; | ||
967 | |||
968 | return (tt_global_entry->orig_node == orig_node); | ||
969 | } | ||
970 | |||
971 | static struct sk_buff *tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, | ||
972 | struct hashtable_t *hash, | ||
973 | struct hard_iface *primary_if, | ||
974 | int (*valid_cb)(const void *, | ||
975 | const void *), | ||
976 | void *cb_data) | ||
977 | { | ||
978 | struct tt_local_entry *tt_local_entry; | ||
979 | struct tt_query_packet *tt_response; | ||
980 | struct tt_change *tt_change; | ||
981 | struct hlist_node *node; | ||
982 | struct hlist_head *head; | ||
983 | struct sk_buff *skb = NULL; | ||
984 | uint16_t tt_tot, tt_count; | ||
985 | ssize_t tt_query_size = sizeof(struct tt_query_packet); | ||
986 | int i; | ||
987 | |||
988 | if (tt_query_size + tt_len > primary_if->soft_iface->mtu) { | ||
989 | tt_len = primary_if->soft_iface->mtu - tt_query_size; | ||
990 | tt_len -= tt_len % sizeof(struct tt_change); | ||
991 | } | ||
992 | tt_tot = tt_len / sizeof(struct tt_change); | ||
993 | |||
994 | skb = dev_alloc_skb(tt_query_size + tt_len + ETH_HLEN); | ||
995 | if (!skb) | ||
996 | goto out; | ||
997 | |||
998 | skb_reserve(skb, ETH_HLEN); | ||
999 | tt_response = (struct tt_query_packet *)skb_put(skb, | ||
1000 | tt_query_size + tt_len); | ||
1001 | tt_response->ttvn = ttvn; | ||
1002 | tt_response->tt_data = htons(tt_tot); | ||
1003 | |||
1004 | tt_change = (struct tt_change *)(skb->data + tt_query_size); | ||
1005 | tt_count = 0; | ||
1006 | |||
1007 | rcu_read_lock(); | ||
1008 | for (i = 0; i < hash->size; i++) { | ||
1009 | head = &hash->table[i]; | ||
1010 | |||
1011 | hlist_for_each_entry_rcu(tt_local_entry, node, | ||
1012 | head, hash_entry) { | ||
1013 | if (tt_count == tt_tot) | ||
1014 | break; | ||
1015 | |||
1016 | if ((valid_cb) && (!valid_cb(tt_local_entry, cb_data))) | ||
1017 | continue; | ||
1018 | |||
1019 | memcpy(tt_change->addr, tt_local_entry->addr, ETH_ALEN); | ||
1020 | tt_change->flags = NO_FLAGS; | ||
1021 | |||
1022 | tt_count++; | ||
1023 | tt_change++; | ||
1024 | } | ||
1025 | } | ||
1026 | rcu_read_unlock(); | ||
1027 | |||
1028 | out: | ||
1029 | return skb; | ||
1030 | } | ||
1031 | |||
1032 | int send_tt_request(struct bat_priv *bat_priv, struct orig_node *dst_orig_node, | ||
1033 | uint8_t ttvn, uint16_t tt_crc, bool full_table) | ||
1034 | { | ||
1035 | struct sk_buff *skb = NULL; | ||
1036 | struct tt_query_packet *tt_request; | ||
1037 | struct neigh_node *neigh_node = NULL; | ||
1038 | struct hard_iface *primary_if; | ||
1039 | struct tt_req_node *tt_req_node = NULL; | ||
1040 | int ret = 1; | ||
1041 | |||
1042 | primary_if = primary_if_get_selected(bat_priv); | ||
1043 | if (!primary_if) | ||
1044 | goto out; | ||
1045 | |||
1046 | /* The new tt_req will be issued only if I'm not waiting for a | ||
1047 | * reply from the same orig_node yet */ | ||
1048 | tt_req_node = new_tt_req_node(bat_priv, dst_orig_node); | ||
1049 | if (!tt_req_node) | ||
1050 | goto out; | ||
1051 | |||
1052 | skb = dev_alloc_skb(sizeof(struct tt_query_packet) + ETH_HLEN); | ||
1053 | if (!skb) | ||
1054 | goto out; | ||
1055 | |||
1056 | skb_reserve(skb, ETH_HLEN); | ||
1057 | |||
1058 | tt_request = (struct tt_query_packet *)skb_put(skb, | ||
1059 | sizeof(struct tt_query_packet)); | ||
1060 | |||
1061 | tt_request->packet_type = BAT_TT_QUERY; | ||
1062 | tt_request->version = COMPAT_VERSION; | ||
1063 | memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN); | ||
1064 | memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN); | ||
1065 | tt_request->ttl = TTL; | ||
1066 | tt_request->ttvn = ttvn; | ||
1067 | tt_request->tt_data = tt_crc; | ||
1068 | tt_request->flags = TT_REQUEST; | ||
1069 | |||
1070 | if (full_table) | ||
1071 | tt_request->flags |= TT_FULL_TABLE; | ||
1072 | |||
1073 | neigh_node = orig_node_get_router(dst_orig_node); | ||
1074 | if (!neigh_node) | ||
1075 | goto out; | ||
1076 | |||
1077 | bat_dbg(DBG_TT, bat_priv, "Sending TT_REQUEST to %pM via %pM " | ||
1078 | "[%c]\n", dst_orig_node->orig, neigh_node->addr, | ||
1079 | (full_table ? 'F' : '.')); | ||
1080 | |||
1081 | send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); | ||
1082 | ret = 0; | ||
1083 | |||
1084 | out: | ||
1085 | if (neigh_node) | ||
1086 | neigh_node_free_ref(neigh_node); | ||
1087 | if (primary_if) | ||
1088 | hardif_free_ref(primary_if); | ||
1089 | if (ret) | ||
1090 | kfree_skb(skb); | ||
1091 | if (ret && tt_req_node) { | ||
1092 | spin_lock_bh(&bat_priv->tt_req_list_lock); | ||
1093 | list_del(&tt_req_node->list); | ||
1094 | spin_unlock_bh(&bat_priv->tt_req_list_lock); | ||
1095 | kfree(tt_req_node); | ||
1096 | } | ||
1097 | return ret; | ||
1098 | } | ||
1099 | |||
1100 | static bool send_other_tt_response(struct bat_priv *bat_priv, | ||
1101 | struct tt_query_packet *tt_request) | ||
1102 | { | ||
1103 | struct orig_node *req_dst_orig_node = NULL, *res_dst_orig_node = NULL; | ||
1104 | struct neigh_node *neigh_node = NULL; | ||
1105 | struct hard_iface *primary_if = NULL; | ||
1106 | uint8_t orig_ttvn, req_ttvn, ttvn; | ||
1107 | int ret = false; | ||
1108 | unsigned char *tt_buff; | ||
1109 | bool full_table; | ||
1110 | uint16_t tt_len, tt_tot; | ||
1111 | struct sk_buff *skb = NULL; | ||
1112 | struct tt_query_packet *tt_response; | ||
1113 | |||
1114 | bat_dbg(DBG_TT, bat_priv, | ||
1115 | "Received TT_REQUEST from %pM for " | ||
1116 | "ttvn: %u (%pM) [%c]\n", tt_request->src, | ||
1117 | tt_request->ttvn, tt_request->dst, | ||
1118 | (tt_request->flags & TT_FULL_TABLE ? 'F' : '.')); | ||
1119 | |||
1120 | /* Let's get the orig node of the REAL destination */ | ||
1121 | req_dst_orig_node = get_orig_node(bat_priv, tt_request->dst); | ||
1122 | if (!req_dst_orig_node) | ||
1123 | goto out; | ||
1124 | |||
1125 | res_dst_orig_node = get_orig_node(bat_priv, tt_request->src); | ||
1126 | if (!res_dst_orig_node) | ||
1127 | goto out; | ||
1128 | |||
1129 | neigh_node = orig_node_get_router(res_dst_orig_node); | ||
1130 | if (!neigh_node) | ||
1131 | goto out; | ||
1132 | |||
1133 | primary_if = primary_if_get_selected(bat_priv); | ||
1134 | if (!primary_if) | ||
1135 | goto out; | ||
1136 | |||
1137 | orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); | ||
1138 | req_ttvn = tt_request->ttvn; | ||
1139 | |||
1140 | /* I have not the requested data */ | ||
1141 | if (orig_ttvn != req_ttvn || | ||
1142 | tt_request->tt_data != req_dst_orig_node->tt_crc) | ||
1143 | goto out; | ||
1144 | |||
1145 | /* If it has explicitly been requested the full table */ | ||
1146 | if (tt_request->flags & TT_FULL_TABLE || | ||
1147 | !req_dst_orig_node->tt_buff) | ||
1148 | full_table = true; | ||
1149 | else | ||
1150 | full_table = false; | ||
1151 | |||
1152 | /* In this version, fragmentation is not implemented, then | ||
1153 | * I'll send only one packet with as much TT entries as I can */ | ||
1154 | if (!full_table) { | ||
1155 | spin_lock_bh(&req_dst_orig_node->tt_buff_lock); | ||
1156 | tt_len = req_dst_orig_node->tt_buff_len; | ||
1157 | tt_tot = tt_len / sizeof(struct tt_change); | ||
1158 | |||
1159 | skb = dev_alloc_skb(sizeof(struct tt_query_packet) + | ||
1160 | tt_len + ETH_HLEN); | ||
1161 | if (!skb) | ||
1162 | goto unlock; | ||
1163 | |||
1164 | skb_reserve(skb, ETH_HLEN); | ||
1165 | tt_response = (struct tt_query_packet *)skb_put(skb, | ||
1166 | sizeof(struct tt_query_packet) + tt_len); | ||
1167 | tt_response->ttvn = req_ttvn; | ||
1168 | tt_response->tt_data = htons(tt_tot); | ||
1169 | |||
1170 | tt_buff = skb->data + sizeof(struct tt_query_packet); | ||
1171 | /* Copy the last orig_node's OGM buffer */ | ||
1172 | memcpy(tt_buff, req_dst_orig_node->tt_buff, | ||
1173 | req_dst_orig_node->tt_buff_len); | ||
1174 | |||
1175 | spin_unlock_bh(&req_dst_orig_node->tt_buff_lock); | ||
1176 | } else { | ||
1177 | tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size) * | ||
1178 | sizeof(struct tt_change); | ||
1179 | ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); | ||
1180 | |||
1181 | skb = tt_response_fill_table(tt_len, ttvn, | ||
1182 | bat_priv->tt_global_hash, | ||
1183 | primary_if, tt_global_valid_entry, | ||
1184 | req_dst_orig_node); | ||
1185 | if (!skb) | ||
1186 | goto out; | ||
1187 | |||
1188 | tt_response = (struct tt_query_packet *)skb->data; | ||
1189 | } | ||
1190 | |||
1191 | tt_response->packet_type = BAT_TT_QUERY; | ||
1192 | tt_response->version = COMPAT_VERSION; | ||
1193 | tt_response->ttl = TTL; | ||
1194 | memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN); | ||
1195 | memcpy(tt_response->dst, tt_request->src, ETH_ALEN); | ||
1196 | tt_response->flags = TT_RESPONSE; | ||
1197 | |||
1198 | if (full_table) | ||
1199 | tt_response->flags |= TT_FULL_TABLE; | ||
1200 | |||
1201 | bat_dbg(DBG_TT, bat_priv, | ||
1202 | "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n", | ||
1203 | res_dst_orig_node->orig, neigh_node->addr, | ||
1204 | req_dst_orig_node->orig, req_ttvn); | ||
1205 | |||
1206 | send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); | ||
1207 | ret = true; | ||
1208 | goto out; | ||
1209 | |||
1210 | unlock: | ||
1211 | spin_unlock_bh(&req_dst_orig_node->tt_buff_lock); | ||
1212 | |||
1213 | out: | ||
1214 | if (res_dst_orig_node) | ||
1215 | orig_node_free_ref(res_dst_orig_node); | ||
1216 | if (req_dst_orig_node) | ||
1217 | orig_node_free_ref(req_dst_orig_node); | ||
1218 | if (neigh_node) | ||
1219 | neigh_node_free_ref(neigh_node); | ||
1220 | if (primary_if) | ||
1221 | hardif_free_ref(primary_if); | ||
1222 | if (!ret) | ||
1223 | kfree_skb(skb); | ||
1224 | return ret; | ||
1225 | |||
1226 | } | ||
1227 | static bool send_my_tt_response(struct bat_priv *bat_priv, | ||
1228 | struct tt_query_packet *tt_request) | ||
1229 | { | ||
1230 | struct orig_node *orig_node = NULL; | ||
1231 | struct neigh_node *neigh_node = NULL; | ||
1232 | struct hard_iface *primary_if = NULL; | ||
1233 | uint8_t my_ttvn, req_ttvn, ttvn; | ||
1234 | int ret = false; | ||
1235 | unsigned char *tt_buff; | ||
1236 | bool full_table; | ||
1237 | uint16_t tt_len, tt_tot; | ||
1238 | struct sk_buff *skb = NULL; | ||
1239 | struct tt_query_packet *tt_response; | ||
1240 | |||
1241 | bat_dbg(DBG_TT, bat_priv, | ||
1242 | "Received TT_REQUEST from %pM for " | ||
1243 | "ttvn: %u (me) [%c]\n", tt_request->src, | ||
1244 | tt_request->ttvn, | ||
1245 | (tt_request->flags & TT_FULL_TABLE ? 'F' : '.')); | ||
1246 | |||
1247 | |||
1248 | my_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn); | ||
1249 | req_ttvn = tt_request->ttvn; | ||
1250 | |||
1251 | orig_node = get_orig_node(bat_priv, tt_request->src); | ||
1252 | if (!orig_node) | ||
1253 | goto out; | ||
1254 | |||
1255 | neigh_node = orig_node_get_router(orig_node); | ||
1256 | if (!neigh_node) | ||
1257 | goto out; | ||
1258 | |||
1259 | primary_if = primary_if_get_selected(bat_priv); | ||
1260 | if (!primary_if) | ||
1261 | goto out; | ||
1262 | |||
1263 | /* If the full table has been explicitly requested or the gap | ||
1264 | * is too big send the whole local translation table */ | ||
1265 | if (tt_request->flags & TT_FULL_TABLE || my_ttvn != req_ttvn || | ||
1266 | !bat_priv->tt_buff) | ||
1267 | full_table = true; | ||
1268 | else | ||
1269 | full_table = false; | ||
1270 | |||
1271 | /* In this version, fragmentation is not implemented, then | ||
1272 | * I'll send only one packet with as much TT entries as I can */ | ||
1273 | if (!full_table) { | ||
1274 | spin_lock_bh(&bat_priv->tt_buff_lock); | ||
1275 | tt_len = bat_priv->tt_buff_len; | ||
1276 | tt_tot = tt_len / sizeof(struct tt_change); | ||
1277 | |||
1278 | skb = dev_alloc_skb(sizeof(struct tt_query_packet) + | ||
1279 | tt_len + ETH_HLEN); | ||
1280 | if (!skb) | ||
1281 | goto unlock; | ||
1282 | |||
1283 | skb_reserve(skb, ETH_HLEN); | ||
1284 | tt_response = (struct tt_query_packet *)skb_put(skb, | ||
1285 | sizeof(struct tt_query_packet) + tt_len); | ||
1286 | tt_response->ttvn = req_ttvn; | ||
1287 | tt_response->tt_data = htons(tt_tot); | ||
1288 | |||
1289 | tt_buff = skb->data + sizeof(struct tt_query_packet); | ||
1290 | memcpy(tt_buff, bat_priv->tt_buff, | ||
1291 | bat_priv->tt_buff_len); | ||
1292 | spin_unlock_bh(&bat_priv->tt_buff_lock); | ||
1293 | } else { | ||
1294 | tt_len = (uint16_t)atomic_read(&bat_priv->num_local_tt) * | ||
1295 | sizeof(struct tt_change); | ||
1296 | ttvn = (uint8_t)atomic_read(&bat_priv->ttvn); | ||
1297 | |||
1298 | skb = tt_response_fill_table(tt_len, ttvn, | ||
1299 | bat_priv->tt_local_hash, | ||
1300 | primary_if, tt_local_valid_entry, | ||
1301 | NULL); | ||
1302 | if (!skb) | ||
1303 | goto out; | ||
1304 | |||
1305 | tt_response = (struct tt_query_packet *)skb->data; | ||
1306 | } | ||
1307 | |||
1308 | tt_response->packet_type = BAT_TT_QUERY; | ||
1309 | tt_response->version = COMPAT_VERSION; | ||
1310 | tt_response->ttl = TTL; | ||
1311 | memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN); | ||
1312 | memcpy(tt_response->dst, tt_request->src, ETH_ALEN); | ||
1313 | tt_response->flags = TT_RESPONSE; | ||
1314 | |||
1315 | if (full_table) | ||
1316 | tt_response->flags |= TT_FULL_TABLE; | ||
1317 | |||
1318 | bat_dbg(DBG_TT, bat_priv, | ||
1319 | "Sending TT_RESPONSE to %pM via %pM [%c]\n", | ||
1320 | orig_node->orig, neigh_node->addr, | ||
1321 | (tt_response->flags & TT_FULL_TABLE ? 'F' : '.')); | ||
1322 | |||
1323 | send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); | ||
1324 | ret = true; | ||
1325 | goto out; | ||
1326 | |||
1327 | unlock: | ||
1328 | spin_unlock_bh(&bat_priv->tt_buff_lock); | ||
1329 | out: | ||
1330 | if (orig_node) | ||
1331 | orig_node_free_ref(orig_node); | ||
1332 | if (neigh_node) | ||
1333 | neigh_node_free_ref(neigh_node); | ||
1334 | if (primary_if) | ||
1335 | hardif_free_ref(primary_if); | ||
1336 | if (!ret) | ||
1337 | kfree_skb(skb); | ||
1338 | /* This packet was for me, so it doesn't need to be re-routed */ | ||
1339 | return true; | ||
1340 | } | ||
1341 | |||
1342 | bool send_tt_response(struct bat_priv *bat_priv, | ||
1343 | struct tt_query_packet *tt_request) | ||
1344 | { | ||
1345 | if (is_my_mac(tt_request->dst)) | ||
1346 | return send_my_tt_response(bat_priv, tt_request); | ||
1347 | else | ||
1348 | return send_other_tt_response(bat_priv, tt_request); | ||
1349 | } | ||
1350 | |||
1351 | static void _tt_update_changes(struct bat_priv *bat_priv, | ||
1352 | struct orig_node *orig_node, | ||
1353 | struct tt_change *tt_change, | ||
1354 | uint16_t tt_num_changes, uint8_t ttvn) | ||
1355 | { | ||
1356 | int i; | ||
1357 | |||
1358 | for (i = 0; i < tt_num_changes; i++) { | ||
1359 | if ((tt_change + i)->flags & TT_CLIENT_DEL) | ||
1360 | tt_global_del(bat_priv, orig_node, | ||
1361 | (tt_change + i)->addr, | ||
1362 | "tt removed by changes", | ||
1363 | (tt_change + i)->flags & TT_CLIENT_ROAM); | ||
1364 | else | ||
1365 | if (!tt_global_add(bat_priv, orig_node, | ||
1366 | (tt_change + i)->addr, ttvn, false)) | ||
1367 | /* In case of problem while storing a | ||
1368 | * global_entry, we stop the updating | ||
1369 | * procedure without committing the | ||
1370 | * ttvn change. This will avoid to send | ||
1371 | * corrupted data on tt_request | ||
1372 | */ | ||
1373 | return; | ||
1374 | } | ||
1375 | } | ||
1376 | |||
1377 | static void tt_fill_gtable(struct bat_priv *bat_priv, | ||
1378 | struct tt_query_packet *tt_response) | ||
1379 | { | ||
1380 | struct orig_node *orig_node = NULL; | ||
1381 | |||
1382 | orig_node = orig_hash_find(bat_priv, tt_response->src); | ||
1383 | if (!orig_node) | ||
1384 | goto out; | ||
1385 | |||
1386 | /* Purge the old table first.. */ | ||
1387 | tt_global_del_orig(bat_priv, orig_node, "Received full table"); | ||
1388 | |||
1389 | _tt_update_changes(bat_priv, orig_node, | ||
1390 | (struct tt_change *)(tt_response + 1), | ||
1391 | tt_response->tt_data, tt_response->ttvn); | ||
1392 | |||
1393 | spin_lock_bh(&orig_node->tt_buff_lock); | ||
1394 | kfree(orig_node->tt_buff); | ||
1395 | orig_node->tt_buff_len = 0; | ||
1396 | orig_node->tt_buff = NULL; | ||
1397 | spin_unlock_bh(&orig_node->tt_buff_lock); | ||
1398 | |||
1399 | atomic_set(&orig_node->last_ttvn, tt_response->ttvn); | ||
1400 | |||
1401 | out: | ||
1402 | if (orig_node) | ||
1403 | orig_node_free_ref(orig_node); | ||
1404 | } | ||
1405 | |||
1406 | void tt_update_changes(struct bat_priv *bat_priv, struct orig_node *orig_node, | ||
1407 | uint16_t tt_num_changes, uint8_t ttvn, | ||
1408 | struct tt_change *tt_change) | ||
1409 | { | ||
1410 | _tt_update_changes(bat_priv, orig_node, tt_change, tt_num_changes, | ||
1411 | ttvn); | ||
1412 | |||
1413 | tt_save_orig_buffer(bat_priv, orig_node, (unsigned char *)tt_change, | ||
1414 | tt_num_changes); | ||
1415 | atomic_set(&orig_node->last_ttvn, ttvn); | ||
1416 | } | ||
1417 | |||
1418 | bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr) | ||
1419 | { | ||
1420 | struct tt_local_entry *tt_local_entry = NULL; | ||
1421 | bool ret = false; | ||
1422 | |||
1423 | tt_local_entry = tt_local_hash_find(bat_priv, addr); | ||
1424 | if (!tt_local_entry) | ||
1425 | goto out; | ||
1426 | /* Check if the client has been logically deleted (but is kept for | ||
1427 | * consistency purpose) */ | ||
1428 | if (tt_local_entry->flags & TT_CLIENT_PENDING) | ||
1429 | goto out; | ||
1430 | ret = true; | ||
1431 | out: | ||
1432 | if (tt_local_entry) | ||
1433 | tt_local_entry_free_ref(tt_local_entry); | ||
1434 | return ret; | ||
1435 | } | ||
1436 | |||
1437 | void handle_tt_response(struct bat_priv *bat_priv, | ||
1438 | struct tt_query_packet *tt_response) | ||
1439 | { | ||
1440 | struct tt_req_node *node, *safe; | ||
1441 | struct orig_node *orig_node = NULL; | ||
1442 | |||
1443 | bat_dbg(DBG_TT, bat_priv, "Received TT_RESPONSE from %pM for " | ||
1444 | "ttvn %d t_size: %d [%c]\n", | ||
1445 | tt_response->src, tt_response->ttvn, | ||
1446 | tt_response->tt_data, | ||
1447 | (tt_response->flags & TT_FULL_TABLE ? 'F' : '.')); | ||
1448 | |||
1449 | orig_node = orig_hash_find(bat_priv, tt_response->src); | ||
1450 | if (!orig_node) | ||
1451 | goto out; | ||
1452 | |||
1453 | if (tt_response->flags & TT_FULL_TABLE) | ||
1454 | tt_fill_gtable(bat_priv, tt_response); | ||
1455 | else | ||
1456 | tt_update_changes(bat_priv, orig_node, tt_response->tt_data, | ||
1457 | tt_response->ttvn, | ||
1458 | (struct tt_change *)(tt_response + 1)); | ||
1459 | |||
1460 | /* Delete the tt_req_node from pending tt_requests list */ | ||
1461 | spin_lock_bh(&bat_priv->tt_req_list_lock); | ||
1462 | list_for_each_entry_safe(node, safe, &bat_priv->tt_req_list, list) { | ||
1463 | if (!compare_eth(node->addr, tt_response->src)) | ||
1464 | continue; | ||
1465 | list_del(&node->list); | ||
1466 | kfree(node); | ||
1467 | } | ||
1468 | spin_unlock_bh(&bat_priv->tt_req_list_lock); | ||
1469 | |||
1470 | /* Recalculate the CRC for this orig_node and store it */ | ||
1471 | orig_node->tt_crc = tt_global_crc(bat_priv, orig_node); | ||
1472 | /* Roaming phase is over: tables are in sync again. I can | ||
1473 | * unset the flag */ | ||
1474 | orig_node->tt_poss_change = false; | ||
1475 | out: | ||
1476 | if (orig_node) | ||
1477 | orig_node_free_ref(orig_node); | ||
1478 | } | ||
1479 | |||
1480 | int tt_init(struct bat_priv *bat_priv) | ||
1481 | { | ||
1482 | if (!tt_local_init(bat_priv)) | ||
1483 | return 0; | ||
1484 | |||
1485 | if (!tt_global_init(bat_priv)) | ||
1486 | return 0; | ||
1487 | |||
1488 | tt_start_timer(bat_priv); | ||
1489 | |||
1490 | return 1; | ||
1491 | } | ||
1492 | |||
1493 | static void tt_roam_list_free(struct bat_priv *bat_priv) | ||
1494 | { | ||
1495 | struct tt_roam_node *node, *safe; | ||
1496 | |||
1497 | spin_lock_bh(&bat_priv->tt_roam_list_lock); | ||
1498 | |||
1499 | list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) { | ||
1500 | list_del(&node->list); | ||
1501 | kfree(node); | ||
1502 | } | ||
1503 | |||
1504 | spin_unlock_bh(&bat_priv->tt_roam_list_lock); | ||
1505 | } | ||
1506 | |||
1507 | static void tt_roam_purge(struct bat_priv *bat_priv) | ||
1508 | { | ||
1509 | struct tt_roam_node *node, *safe; | ||
1510 | |||
1511 | spin_lock_bh(&bat_priv->tt_roam_list_lock); | ||
1512 | list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) { | ||
1513 | if (!is_out_of_time(node->first_time, | ||
1514 | ROAMING_MAX_TIME * 1000)) | ||
1515 | continue; | ||
1516 | |||
1517 | list_del(&node->list); | ||
1518 | kfree(node); | ||
1519 | } | ||
1520 | spin_unlock_bh(&bat_priv->tt_roam_list_lock); | ||
1521 | } | ||
1522 | |||
1523 | /* This function checks whether the client already reached the | ||
1524 | * maximum number of possible roaming phases. In this case the ROAMING_ADV | ||
1525 | * will not be sent. | ||
1526 | * | ||
1527 | * returns true if the ROAMING_ADV can be sent, false otherwise */ | ||
1528 | static bool tt_check_roam_count(struct bat_priv *bat_priv, | ||
1529 | uint8_t *client) | ||
1530 | { | ||
1531 | struct tt_roam_node *tt_roam_node; | ||
1532 | bool ret = false; | ||
1533 | |||
1534 | spin_lock_bh(&bat_priv->tt_roam_list_lock); | ||
1535 | /* The new tt_req will be issued only if I'm not waiting for a | ||
1536 | * reply from the same orig_node yet */ | ||
1537 | list_for_each_entry(tt_roam_node, &bat_priv->tt_roam_list, list) { | ||
1538 | if (!compare_eth(tt_roam_node->addr, client)) | ||
1539 | continue; | ||
1540 | |||
1541 | if (is_out_of_time(tt_roam_node->first_time, | ||
1542 | ROAMING_MAX_TIME * 1000)) | ||
1543 | continue; | ||
1544 | |||
1545 | if (!atomic_dec_not_zero(&tt_roam_node->counter)) | ||
1546 | /* Sorry, you roamed too many times! */ | ||
1547 | goto unlock; | ||
1548 | ret = true; | ||
1549 | break; | ||
1550 | } | ||
1551 | |||
1552 | if (!ret) { | ||
1553 | tt_roam_node = kmalloc(sizeof(*tt_roam_node), GFP_ATOMIC); | ||
1554 | if (!tt_roam_node) | ||
1555 | goto unlock; | ||
1556 | |||
1557 | tt_roam_node->first_time = jiffies; | ||
1558 | atomic_set(&tt_roam_node->counter, ROAMING_MAX_COUNT - 1); | ||
1559 | memcpy(tt_roam_node->addr, client, ETH_ALEN); | ||
1560 | |||
1561 | list_add(&tt_roam_node->list, &bat_priv->tt_roam_list); | ||
1562 | ret = true; | ||
1563 | } | ||
1564 | |||
1565 | unlock: | ||
1566 | spin_unlock_bh(&bat_priv->tt_roam_list_lock); | ||
1567 | return ret; | ||
1568 | } | ||
1569 | |||
1570 | void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client, | ||
1571 | struct orig_node *orig_node) | ||
1572 | { | ||
1573 | struct neigh_node *neigh_node = NULL; | ||
1574 | struct sk_buff *skb = NULL; | ||
1575 | struct roam_adv_packet *roam_adv_packet; | ||
1576 | int ret = 1; | ||
1577 | struct hard_iface *primary_if; | ||
1578 | |||
1579 | /* before going on we have to check whether the client has | ||
1580 | * already roamed to us too many times */ | ||
1581 | if (!tt_check_roam_count(bat_priv, client)) | ||
1582 | goto out; | ||
1583 | |||
1584 | skb = dev_alloc_skb(sizeof(struct roam_adv_packet) + ETH_HLEN); | ||
1585 | if (!skb) | ||
1586 | goto out; | ||
1587 | |||
1588 | skb_reserve(skb, ETH_HLEN); | ||
1589 | |||
1590 | roam_adv_packet = (struct roam_adv_packet *)skb_put(skb, | ||
1591 | sizeof(struct roam_adv_packet)); | ||
1592 | |||
1593 | roam_adv_packet->packet_type = BAT_ROAM_ADV; | ||
1594 | roam_adv_packet->version = COMPAT_VERSION; | ||
1595 | roam_adv_packet->ttl = TTL; | ||
1596 | primary_if = primary_if_get_selected(bat_priv); | ||
1597 | if (!primary_if) | ||
1598 | goto out; | ||
1599 | memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN); | ||
1600 | hardif_free_ref(primary_if); | ||
1601 | memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN); | ||
1602 | memcpy(roam_adv_packet->client, client, ETH_ALEN); | ||
1603 | |||
1604 | neigh_node = orig_node_get_router(orig_node); | ||
1605 | if (!neigh_node) | ||
1606 | goto out; | ||
1607 | |||
1608 | bat_dbg(DBG_TT, bat_priv, | ||
1609 | "Sending ROAMING_ADV to %pM (client %pM) via %pM\n", | ||
1610 | orig_node->orig, client, neigh_node->addr); | ||
1611 | |||
1612 | send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); | ||
1613 | ret = 0; | ||
1614 | |||
1615 | out: | ||
1616 | if (neigh_node) | ||
1617 | neigh_node_free_ref(neigh_node); | ||
1618 | if (ret) | ||
1619 | kfree_skb(skb); | ||
1620 | return; | ||
1621 | } | ||
1622 | |||
1623 | static void tt_purge(struct work_struct *work) | ||
1624 | { | ||
1625 | struct delayed_work *delayed_work = | ||
1626 | container_of(work, struct delayed_work, work); | ||
1627 | struct bat_priv *bat_priv = | ||
1628 | container_of(delayed_work, struct bat_priv, tt_work); | ||
1629 | |||
1630 | tt_local_purge(bat_priv); | ||
1631 | tt_global_roam_purge(bat_priv); | ||
1632 | tt_req_purge(bat_priv); | ||
1633 | tt_roam_purge(bat_priv); | ||
1634 | |||
1635 | tt_start_timer(bat_priv); | ||
1636 | } | ||
1637 | |||
1638 | void tt_free(struct bat_priv *bat_priv) | ||
1639 | { | ||
1640 | cancel_delayed_work_sync(&bat_priv->tt_work); | ||
1641 | |||
1642 | tt_local_table_free(bat_priv); | ||
1643 | tt_global_table_free(bat_priv); | ||
1644 | tt_req_list_free(bat_priv); | ||
1645 | tt_changes_list_free(bat_priv); | ||
1646 | tt_roam_list_free(bat_priv); | ||
1647 | |||
1648 | kfree(bat_priv->tt_buff); | ||
1649 | } | ||
1650 | |||
1651 | /* This function will reset the specified flags from all the entries in | ||
1652 | * the given hash table and will increment num_local_tt for each involved | ||
1653 | * entry */ | ||
1654 | static void tt_local_reset_flags(struct bat_priv *bat_priv, uint16_t flags) | ||
1655 | { | ||
1656 | int i; | ||
1657 | struct hashtable_t *hash = bat_priv->tt_local_hash; | ||
1658 | struct hlist_head *head; | ||
1659 | struct hlist_node *node; | ||
1660 | struct tt_local_entry *tt_local_entry; | ||
1661 | |||
1662 | if (!hash) | ||
1663 | return; | ||
1664 | |||
1665 | for (i = 0; i < hash->size; i++) { | ||
1666 | head = &hash->table[i]; | ||
1667 | |||
1668 | rcu_read_lock(); | ||
1669 | hlist_for_each_entry_rcu(tt_local_entry, node, | ||
1670 | head, hash_entry) { | ||
1671 | tt_local_entry->flags &= ~flags; | ||
1672 | atomic_inc(&bat_priv->num_local_tt); | ||
1673 | } | ||
1674 | rcu_read_unlock(); | ||
1675 | } | ||
1676 | |||
1677 | } | ||
1678 | |||
1679 | /* Purge out all the tt local entries marked with TT_CLIENT_PENDING */ | ||
1680 | static void tt_local_purge_pending_clients(struct bat_priv *bat_priv) | ||
1681 | { | ||
1682 | struct hashtable_t *hash = bat_priv->tt_local_hash; | ||
1683 | struct tt_local_entry *tt_local_entry; | ||
1684 | struct hlist_node *node, *node_tmp; | ||
1685 | struct hlist_head *head; | ||
1686 | spinlock_t *list_lock; /* protects write access to the hash lists */ | ||
1687 | int i; | ||
1688 | |||
1689 | if (!hash) | ||
1690 | return; | ||
1691 | |||
1692 | for (i = 0; i < hash->size; i++) { | ||
1693 | head = &hash->table[i]; | ||
1694 | list_lock = &hash->list_locks[i]; | ||
1695 | |||
1696 | spin_lock_bh(list_lock); | ||
1697 | hlist_for_each_entry_safe(tt_local_entry, node, node_tmp, | ||
1698 | head, hash_entry) { | ||
1699 | if (!(tt_local_entry->flags & TT_CLIENT_PENDING)) | ||
1700 | continue; | ||
1701 | |||
1702 | bat_dbg(DBG_TT, bat_priv, "Deleting local tt entry " | ||
1703 | "(%pM): pending\n", tt_local_entry->addr); | ||
1704 | |||
1705 | atomic_dec(&bat_priv->num_local_tt); | ||
1706 | hlist_del_rcu(node); | ||
1707 | tt_local_entry_free_ref(tt_local_entry); | ||
1708 | } | ||
1709 | spin_unlock_bh(list_lock); | ||
1710 | } | ||
1711 | |||
1712 | } | ||
1713 | |||
1714 | void tt_commit_changes(struct bat_priv *bat_priv) | ||
1715 | { | ||
1716 | tt_local_reset_flags(bat_priv, TT_CLIENT_NEW); | ||
1717 | tt_local_purge_pending_clients(bat_priv); | ||
1718 | |||
1719 | /* Increment the TTVN only once per OGM interval */ | ||
1720 | atomic_inc(&bat_priv->ttvn); | ||
1721 | bat_priv->tt_poss_change = false; | ||
1722 | } | ||