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