aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv
diff options
context:
space:
mode:
authorAntonio Quartulli <ordex@autistici.org>2011-04-27 08:27:57 -0400
committerSven Eckelmann <sven@narfation.org>2011-06-20 05:37:27 -0400
commitcc47f66e6b9ec7e7d465f74739a6fc9844593894 (patch)
tree8fbda7f59267bca45f9e887d09ba1de2f3c62f8d /net/batman-adv
parenta73105b8d4c765d9ebfb664d0a66802127d8e4c7 (diff)
batman-adv: improved roaming mechanism
With the current client announcement implementation, in case of roaming, an update is triggered on the new AP serving the client. At that point the new information is spread around by means of the OGM broadcasting mechanism. Until this operations is not executed, no node is able to correctly route traffic towards the client. This obviously causes packet drops and introduces a delay in the time needed by the client to recover its connections. A new packet type called ROAMING_ADVERTISEMENT is added to account this issue. This message is sent in case of roaming from the new AP serving the client to the old one and will contain the client MAC address. In this way an out-of-OGM update is immediately committed, so that the old node can update its global translation table. Traffic reaching this node will then be redirected to the correct destination utilising the fresher information. Thus reducing the packet drops and the connection recovery delay. Signed-off-by: Antonio Quartulli <ordex@autistici.org> Signed-off-by: Sven Eckelmann <sven@narfation.org>
Diffstat (limited to 'net/batman-adv')
-rw-r--r--net/batman-adv/hard-interface.c4
-rw-r--r--net/batman-adv/main.c2
-rw-r--r--net/batman-adv/main.h6
-rw-r--r--net/batman-adv/originator.c1
-rw-r--r--net/batman-adv/packet.h13
-rw-r--r--net/batman-adv/routing.c61
-rw-r--r--net/batman-adv/routing.h1
-rw-r--r--net/batman-adv/send.c1
-rw-r--r--net/batman-adv/soft-interface.c3
-rw-r--r--net/batman-adv/translation-table.c241
-rw-r--r--net/batman-adv/translation-table.h8
-rw-r--r--net/batman-adv/types.h26
12 files changed, 334 insertions, 33 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index d40426cc5e29..55b5def08d5a 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -658,6 +658,10 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
658 case BAT_TT_QUERY: 658 case BAT_TT_QUERY:
659 ret = recv_tt_query(skb, hard_iface); 659 ret = recv_tt_query(skb, hard_iface);
660 break; 660 break;
661 /* Roaming advertisement */
662 case BAT_ROAM_ADV:
663 ret = recv_roam_adv(skb, hard_iface);
664 break;
661 default: 665 default:
662 ret = NET_RX_DROP; 666 ret = NET_RX_DROP;
663 } 667 }
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 49a5e64b2d4f..3318ee27fe23 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -88,6 +88,7 @@ int mesh_init(struct net_device *soft_iface)
88 spin_lock_init(&bat_priv->tt_ghash_lock); 88 spin_lock_init(&bat_priv->tt_ghash_lock);
89 spin_lock_init(&bat_priv->tt_changes_list_lock); 89 spin_lock_init(&bat_priv->tt_changes_list_lock);
90 spin_lock_init(&bat_priv->tt_req_list_lock); 90 spin_lock_init(&bat_priv->tt_req_list_lock);
91 spin_lock_init(&bat_priv->tt_roam_list_lock);
91 spin_lock_init(&bat_priv->tt_buff_lock); 92 spin_lock_init(&bat_priv->tt_buff_lock);
92 spin_lock_init(&bat_priv->gw_list_lock); 93 spin_lock_init(&bat_priv->gw_list_lock);
93 spin_lock_init(&bat_priv->vis_hash_lock); 94 spin_lock_init(&bat_priv->vis_hash_lock);
@@ -101,6 +102,7 @@ int mesh_init(struct net_device *soft_iface)
101 INIT_HLIST_HEAD(&bat_priv->softif_neigh_vids); 102 INIT_HLIST_HEAD(&bat_priv->softif_neigh_vids);
102 INIT_LIST_HEAD(&bat_priv->tt_changes_list); 103 INIT_LIST_HEAD(&bat_priv->tt_changes_list);
103 INIT_LIST_HEAD(&bat_priv->tt_req_list); 104 INIT_LIST_HEAD(&bat_priv->tt_req_list);
105 INIT_LIST_HEAD(&bat_priv->tt_roam_list);
104 106
105 if (originator_init(bat_priv) < 1) 107 if (originator_init(bat_priv) < 1)
106 goto err; 108 goto err;
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 6f53a1de778c..8eae05e4dc1b 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -42,7 +42,7 @@
42 * -> TODO: check influence on TQ_LOCAL_WINDOW_SIZE */ 42 * -> TODO: check influence on TQ_LOCAL_WINDOW_SIZE */
43#define PURGE_TIMEOUT 200 43#define PURGE_TIMEOUT 200
44#define TT_LOCAL_TIMEOUT 3600 /* in seconds */ 44#define TT_LOCAL_TIMEOUT 3600 /* in seconds */
45 45#define TT_CLIENT_ROAM_TIMEOUT 600
46/* sliding packet range of received originator messages in squence numbers 46/* sliding packet range of received originator messages in squence numbers
47 * (should be a multiple of our word size) */ 47 * (should be a multiple of our word size) */
48#define TQ_LOCAL_WINDOW_SIZE 64 48#define TQ_LOCAL_WINDOW_SIZE 64
@@ -55,6 +55,10 @@
55 55
56#define TT_OGM_APPEND_MAX 3 /* number of OGMs sent with the last tt diff */ 56#define TT_OGM_APPEND_MAX 3 /* number of OGMs sent with the last tt diff */
57 57
58#define ROAMING_MAX_TIME 20 /* Time in which a client can roam at most
59 * ROAMING_MAX_COUNT times */
60#define ROAMING_MAX_COUNT 5
61
58#define NO_FLAGS 0 62#define NO_FLAGS 0
59 63
60#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE) 64#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 25e7e50eef25..338b3c597e4a 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -219,6 +219,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, const uint8_t *addr)
219 /* extra reference for return */ 219 /* extra reference for return */
220 atomic_set(&orig_node->refcount, 2); 220 atomic_set(&orig_node->refcount, 2);
221 221
222 orig_node->tt_poss_change = false;
222 orig_node->bat_priv = bat_priv; 223 orig_node->bat_priv = bat_priv;
223 memcpy(orig_node->orig, addr, ETH_ALEN); 224 memcpy(orig_node->orig, addr, ETH_ALEN);
224 orig_node->router = NULL; 225 orig_node->router = NULL;
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 407dd2e84aff..c5f081dfc6d1 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -31,7 +31,8 @@ enum bat_packettype {
31 BAT_BCAST = 0x04, 31 BAT_BCAST = 0x04,
32 BAT_VIS = 0x05, 32 BAT_VIS = 0x05,
33 BAT_UNICAST_FRAG = 0x06, 33 BAT_UNICAST_FRAG = 0x06,
34 BAT_TT_QUERY = 0x07 34 BAT_TT_QUERY = 0x07,
35 BAT_ROAM_ADV = 0x08
35}; 36};
36 37
37/* this file is included by batctl which needs these defines */ 38/* this file is included by batctl which needs these defines */
@@ -194,6 +195,16 @@ struct tt_query_packet {
194 uint16_t tt_data; 195 uint16_t tt_data;
195} __packed; 196} __packed;
196 197
198struct roam_adv_packet {
199 uint8_t packet_type;
200 uint8_t version;
201 uint8_t ttl;
202 uint8_t reserved;
203 uint8_t dst[ETH_ALEN];
204 uint8_t src[ETH_ALEN];
205 uint8_t client[ETH_ALEN];
206} __packed;
207
197struct tt_change { 208struct tt_change {
198 uint8_t flags; 209 uint8_t flags;
199 uint8_t addr[ETH_ALEN]; 210 uint8_t addr[ETH_ALEN];
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 8b0f8330b06d..05d50ca3c4db 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -93,6 +93,9 @@ static void update_transtable(struct bat_priv *bat_priv,
93 spin_lock_bh(&bat_priv->tt_ghash_lock); 93 spin_lock_bh(&bat_priv->tt_ghash_lock);
94 orig_node->tt_crc = tt_global_crc(bat_priv, orig_node); 94 orig_node->tt_crc = tt_global_crc(bat_priv, orig_node);
95 spin_unlock_bh(&bat_priv->tt_ghash_lock); 95 spin_unlock_bh(&bat_priv->tt_ghash_lock);
96 /* Roaming phase is over: tables are in sync again. I can
97 * unset the flag */
98 orig_node->tt_poss_change = false;
96 } else { 99 } else {
97 /* if we missed more than one change or our tables are not 100 /* if we missed more than one change or our tables are not
98 * in sync anymore -> request fresh tt data */ 101 * in sync anymore -> request fresh tt data */
@@ -1252,6 +1255,54 @@ out:
1252 return NET_RX_DROP; 1255 return NET_RX_DROP;
1253} 1256}
1254 1257
1258int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if)
1259{
1260 struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface);
1261 struct roam_adv_packet *roam_adv_packet;
1262 struct orig_node *orig_node;
1263 struct ethhdr *ethhdr;
1264
1265 /* drop packet if it has not necessary minimum size */
1266 if (unlikely(!pskb_may_pull(skb, sizeof(struct roam_adv_packet))))
1267 goto out;
1268
1269 ethhdr = (struct ethhdr *)skb_mac_header(skb);
1270
1271 /* packet with unicast indication but broadcast recipient */
1272 if (is_broadcast_ether_addr(ethhdr->h_dest))
1273 goto out;
1274
1275 /* packet with broadcast sender address */
1276 if (is_broadcast_ether_addr(ethhdr->h_source))
1277 goto out;
1278
1279 roam_adv_packet = (struct roam_adv_packet *)skb->data;
1280
1281 if (!is_my_mac(roam_adv_packet->dst))
1282 return route_unicast_packet(skb, recv_if);
1283
1284 orig_node = orig_hash_find(bat_priv, roam_adv_packet->src);
1285 if (!orig_node)
1286 goto out;
1287
1288 bat_dbg(DBG_TT, bat_priv, "Received ROAMING_ADV from %pM "
1289 "(client %pM)\n", roam_adv_packet->src,
1290 roam_adv_packet->client);
1291
1292 tt_global_add(bat_priv, orig_node, roam_adv_packet->client,
1293 atomic_read(&orig_node->last_ttvn) + 1, true);
1294
1295 /* Roaming phase starts: I have new information but the ttvn has not
1296 * been incremented yet. This flag will make me check all the incoming
1297 * packets for the correct destination. */
1298 bat_priv->tt_poss_change = true;
1299
1300 orig_node_free_ref(orig_node);
1301out:
1302 /* returning NET_RX_DROP will make the caller function kfree the skb */
1303 return NET_RX_DROP;
1304}
1305
1255/* find a suitable router for this originator, and use 1306/* find a suitable router for this originator, and use
1256 * bonding if possible. increases the found neighbors 1307 * bonding if possible. increases the found neighbors
1257 * refcount.*/ 1308 * refcount.*/
@@ -1445,6 +1496,7 @@ static int check_unicast_ttvn(struct bat_priv *bat_priv,
1445 struct ethhdr *ethhdr; 1496 struct ethhdr *ethhdr;
1446 struct hard_iface *primary_if; 1497 struct hard_iface *primary_if;
1447 struct unicast_packet *unicast_packet; 1498 struct unicast_packet *unicast_packet;
1499 bool tt_poss_change;
1448 1500
1449 /* I could need to modify it */ 1501 /* I could need to modify it */
1450 if (skb_cow(skb, sizeof(struct unicast_packet)) < 0) 1502 if (skb_cow(skb, sizeof(struct unicast_packet)) < 0)
@@ -1452,27 +1504,28 @@ static int check_unicast_ttvn(struct bat_priv *bat_priv,
1452 1504
1453 unicast_packet = (struct unicast_packet *)skb->data; 1505 unicast_packet = (struct unicast_packet *)skb->data;
1454 1506
1455 if (is_my_mac(unicast_packet->dest)) 1507 if (is_my_mac(unicast_packet->dest)) {
1508 tt_poss_change = bat_priv->tt_poss_change;
1456 curr_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn); 1509 curr_ttvn = (uint8_t)atomic_read(&bat_priv->ttvn);
1457 else { 1510 } else {
1458 orig_node = orig_hash_find(bat_priv, unicast_packet->dest); 1511 orig_node = orig_hash_find(bat_priv, unicast_packet->dest);
1459 1512
1460 if (!orig_node) 1513 if (!orig_node)
1461 return 0; 1514 return 0;
1462 1515
1463 curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); 1516 curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
1517 tt_poss_change = orig_node->tt_poss_change;
1464 orig_node_free_ref(orig_node); 1518 orig_node_free_ref(orig_node);
1465 } 1519 }
1466 1520
1467 /* Check whether I have to reroute the packet */ 1521 /* Check whether I have to reroute the packet */
1468 if (seq_before(unicast_packet->ttvn, curr_ttvn)) { 1522 if (seq_before(unicast_packet->ttvn, curr_ttvn) || tt_poss_change) {
1469 /* Linearize the skb before accessing it */ 1523 /* Linearize the skb before accessing it */
1470 if (skb_linearize(skb) < 0) 1524 if (skb_linearize(skb) < 0)
1471 return 0; 1525 return 0;
1472 1526
1473 ethhdr = (struct ethhdr *)(skb->data + 1527 ethhdr = (struct ethhdr *)(skb->data +
1474 sizeof(struct unicast_packet)); 1528 sizeof(struct unicast_packet));
1475
1476 orig_node = transtable_search(bat_priv, ethhdr->h_dest); 1529 orig_node = transtable_search(bat_priv, ethhdr->h_dest);
1477 1530
1478 if (!orig_node) { 1531 if (!orig_node) {
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h
index e77d46440d2d..fb14e9579b19 100644
--- a/net/batman-adv/routing.h
+++ b/net/batman-adv/routing.h
@@ -37,6 +37,7 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
37int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if); 37int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if);
38int recv_bat_packet(struct sk_buff *skb, struct hard_iface *recv_if); 38int recv_bat_packet(struct sk_buff *skb, struct hard_iface *recv_if);
39int recv_tt_query(struct sk_buff *skb, struct hard_iface *recv_if); 39int recv_tt_query(struct sk_buff *skb, struct hard_iface *recv_if);
40int recv_roam_adv(struct sk_buff *skb, struct hard_iface *recv_if);
40struct neigh_node *find_router(struct bat_priv *bat_priv, 41struct neigh_node *find_router(struct bat_priv *bat_priv,
41 struct orig_node *orig_node, 42 struct orig_node *orig_node,
42 const struct hard_iface *recv_if); 43 const struct hard_iface *recv_if);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 6b1407570e44..7a2f0823f1c2 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -303,6 +303,7 @@ void schedule_own_packet(struct hard_iface *hard_iface)
303 prepare_packet_buffer(bat_priv, hard_iface); 303 prepare_packet_buffer(bat_priv, hard_iface);
304 /* Increment the TTVN only once per OGM interval */ 304 /* Increment the TTVN only once per OGM interval */
305 atomic_inc(&bat_priv->ttvn); 305 atomic_inc(&bat_priv->ttvn);
306 bat_priv->tt_poss_change = false;
306 } 307 }
307 308
308 /* if the changes have been sent enough times */ 309 /* if the changes have been sent enough times */
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index c288d937a154..3371ece680a2 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -534,7 +534,7 @@ static int interface_set_mac_addr(struct net_device *dev, void *p)
534 /* only modify transtable if it has been initialised before */ 534 /* only modify transtable if it has been initialised before */
535 if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) { 535 if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) {
536 tt_local_remove(bat_priv, dev->dev_addr, 536 tt_local_remove(bat_priv, dev->dev_addr,
537 "mac address changed"); 537 "mac address changed", false);
538 tt_local_add(dev, addr->sa_data); 538 tt_local_add(dev, addr->sa_data);
539 } 539 }
540 540
@@ -836,6 +836,7 @@ struct net_device *softif_create(const char *name)
836 836
837 bat_priv->tt_buff = NULL; 837 bat_priv->tt_buff = NULL;
838 bat_priv->tt_buff_len = 0; 838 bat_priv->tt_buff_len = 0;
839 bat_priv->tt_poss_change = false;
839 840
840 bat_priv->primary_if = NULL; 841 bat_priv->primary_if = NULL;
841 bat_priv->num_ifaces = 0; 842 bat_priv->num_ifaces = 0;
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 597cd1a43058..d516d8591cfc 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -126,7 +126,7 @@ static bool is_out_of_time(unsigned long starting_time, unsigned long timeout)
126} 126}
127 127
128static void tt_local_event(struct bat_priv *bat_priv, uint8_t op, 128static void tt_local_event(struct bat_priv *bat_priv, uint8_t op,
129 const uint8_t *addr) 129 const uint8_t *addr, uint8_t roaming)
130{ 130{
131 struct tt_change_node *tt_change_node; 131 struct tt_change_node *tt_change_node;
132 132
@@ -136,6 +136,9 @@ static void tt_local_event(struct bat_priv *bat_priv, uint8_t op,
136 return; 136 return;
137 137
138 tt_change_node->change.flags = op; 138 tt_change_node->change.flags = op;
139 if (roaming)
140 tt_change_node->change.flags |= TT_CLIENT_ROAM;
141
139 memcpy(tt_change_node->change.addr, addr, ETH_ALEN); 142 memcpy(tt_change_node->change.addr, addr, ETH_ALEN);
140 143
141 spin_lock_bh(&bat_priv->tt_changes_list_lock); 144 spin_lock_bh(&bat_priv->tt_changes_list_lock);
@@ -170,6 +173,7 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
170 struct bat_priv *bat_priv = netdev_priv(soft_iface); 173 struct bat_priv *bat_priv = netdev_priv(soft_iface);
171 struct tt_local_entry *tt_local_entry; 174 struct tt_local_entry *tt_local_entry;
172 struct tt_global_entry *tt_global_entry; 175 struct tt_global_entry *tt_global_entry;
176 uint8_t roam_addr[ETH_ALEN];
173 177
174 spin_lock_bh(&bat_priv->tt_lhash_lock); 178 spin_lock_bh(&bat_priv->tt_lhash_lock);
175 tt_local_entry = tt_local_hash_find(bat_priv, addr); 179 tt_local_entry = tt_local_hash_find(bat_priv, addr);
@@ -183,7 +187,7 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
183 if (!tt_local_entry) 187 if (!tt_local_entry)
184 goto unlock; 188 goto unlock;
185 189
186 tt_local_event(bat_priv, NO_FLAGS, addr); 190 tt_local_event(bat_priv, NO_FLAGS, addr, false);
187 191
188 bat_dbg(DBG_TT, bat_priv, 192 bat_dbg(DBG_TT, bat_priv,
189 "Creating new local tt entry: %pM (ttvn: %d)\n", addr, 193 "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
@@ -208,11 +212,19 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr)
208 212
209 tt_global_entry = tt_global_hash_find(bat_priv, addr); 213 tt_global_entry = tt_global_hash_find(bat_priv, addr);
210 214
211 if (tt_global_entry) 215 /* Check whether it is a roaming! */
216 if (tt_global_entry) {
217 memcpy(roam_addr, tt_global_entry->addr, ETH_ALEN);
218 /* This node is probably going to update its tt table */
219 tt_global_entry->orig_node->tt_poss_change = true;
212 _tt_global_del(bat_priv, tt_global_entry, 220 _tt_global_del(bat_priv, tt_global_entry,
213 "local tt received"); 221 "local tt received");
222 spin_unlock_bh(&bat_priv->tt_ghash_lock);
223 send_roam_adv(bat_priv, tt_global_entry->addr,
224 tt_global_entry->orig_node);
225 } else
226 spin_unlock_bh(&bat_priv->tt_ghash_lock);
214 227
215 spin_unlock_bh(&bat_priv->tt_ghash_lock);
216 return; 228 return;
217unlock: 229unlock:
218 spin_unlock_bh(&bat_priv->tt_lhash_lock); 230 spin_unlock_bh(&bat_priv->tt_lhash_lock);
@@ -367,7 +379,7 @@ static void tt_local_del(struct bat_priv *bat_priv,
367} 379}
368 380
369void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr, 381void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
370 const char *message) 382 const char *message, bool roaming)
371{ 383{
372 struct tt_local_entry *tt_local_entry; 384 struct tt_local_entry *tt_local_entry;
373 385
@@ -375,7 +387,8 @@ void tt_local_remove(struct bat_priv *bat_priv, const uint8_t *addr,
375 tt_local_entry = tt_local_hash_find(bat_priv, addr); 387 tt_local_entry = tt_local_hash_find(bat_priv, addr);
376 388
377 if (tt_local_entry) { 389 if (tt_local_entry) {
378 tt_local_event(bat_priv, TT_CHANGE_DEL, tt_local_entry->addr); 390 tt_local_event(bat_priv, TT_CHANGE_DEL, tt_local_entry->addr,
391 roaming);
379 tt_local_del(bat_priv, tt_local_entry, message); 392 tt_local_del(bat_priv, tt_local_entry, message);
380 } 393 }
381 spin_unlock_bh(&bat_priv->tt_lhash_lock); 394 spin_unlock_bh(&bat_priv->tt_lhash_lock);
@@ -404,7 +417,7 @@ static void tt_local_purge(struct bat_priv *bat_priv)
404 continue; 417 continue;
405 418
406 tt_local_event(bat_priv, TT_CHANGE_DEL, 419 tt_local_event(bat_priv, TT_CHANGE_DEL,
407 tt_local_entry->addr); 420 tt_local_entry->addr, false);
408 tt_local_del(bat_priv, tt_local_entry, 421 tt_local_del(bat_priv, tt_local_entry,
409 "address timed out"); 422 "address timed out");
410 } 423 }
@@ -476,7 +489,7 @@ static void tt_changes_list_free(struct bat_priv *bat_priv)
476 489
477/* caller must hold orig_node refcount */ 490/* caller must hold orig_node refcount */
478int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node, 491int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
479 const unsigned char *tt_addr, uint8_t ttvn) 492 const unsigned char *tt_addr, uint8_t ttvn, bool roaming)
480{ 493{
481 struct tt_global_entry *tt_global_entry; 494 struct tt_global_entry *tt_global_entry;
482 struct tt_local_entry *tt_local_entry; 495 struct tt_local_entry *tt_local_entry;
@@ -496,6 +509,8 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
496 atomic_inc(&orig_node->refcount); 509 atomic_inc(&orig_node->refcount);
497 tt_global_entry->orig_node = orig_node; 510 tt_global_entry->orig_node = orig_node;
498 tt_global_entry->ttvn = ttvn; 511 tt_global_entry->ttvn = ttvn;
512 tt_global_entry->flags = NO_FLAGS;
513 tt_global_entry->roam_at = 0;
499 atomic_inc(&orig_node->tt_size); 514 atomic_inc(&orig_node->tt_size);
500 hash_add(bat_priv->tt_global_hash, compare_gtt, 515 hash_add(bat_priv->tt_global_hash, compare_gtt,
501 choose_orig, tt_global_entry, 516 choose_orig, tt_global_entry,
@@ -506,10 +521,12 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
506 orig_node_tmp = tt_global_entry->orig_node; 521 orig_node_tmp = tt_global_entry->orig_node;
507 atomic_inc(&orig_node->refcount); 522 atomic_inc(&orig_node->refcount);
508 tt_global_entry->orig_node = orig_node; 523 tt_global_entry->orig_node = orig_node;
509 tt_global_entry->ttvn = ttvn;
510 orig_node_free_ref(orig_node_tmp); 524 orig_node_free_ref(orig_node_tmp);
511 atomic_inc(&orig_node->tt_size); 525 atomic_inc(&orig_node->tt_size);
512 } 526 }
527 tt_global_entry->ttvn = ttvn;
528 tt_global_entry->flags = NO_FLAGS;
529 tt_global_entry->roam_at = 0;
513 } 530 }
514 531
515 spin_unlock_bh(&bat_priv->tt_ghash_lock); 532 spin_unlock_bh(&bat_priv->tt_ghash_lock);
@@ -523,8 +540,9 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node,
523 tt_local_entry = tt_local_hash_find(bat_priv, tt_addr); 540 tt_local_entry = tt_local_hash_find(bat_priv, tt_addr);
524 541
525 if (tt_local_entry) 542 if (tt_local_entry)
526 tt_local_del(bat_priv, tt_local_entry, 543 tt_local_remove(bat_priv, tt_global_entry->addr,
527 "global tt received"); 544 "global tt received", roaming);
545
528 spin_unlock_bh(&bat_priv->tt_lhash_lock); 546 spin_unlock_bh(&bat_priv->tt_lhash_lock);
529 return 1; 547 return 1;
530unlock: 548unlock:
@@ -637,7 +655,7 @@ static void _tt_global_del(struct bat_priv *bat_priv,
637 655
638void tt_global_del(struct bat_priv *bat_priv, 656void tt_global_del(struct bat_priv *bat_priv,
639 struct orig_node *orig_node, const unsigned char *addr, 657 struct orig_node *orig_node, const unsigned char *addr,
640 const char *message) 658 const char *message, bool roaming)
641{ 659{
642 struct tt_global_entry *tt_global_entry; 660 struct tt_global_entry *tt_global_entry;
643 661
@@ -645,9 +663,15 @@ void tt_global_del(struct bat_priv *bat_priv,
645 tt_global_entry = tt_global_hash_find(bat_priv, addr); 663 tt_global_entry = tt_global_hash_find(bat_priv, addr);
646 664
647 if (tt_global_entry && tt_global_entry->orig_node == orig_node) { 665 if (tt_global_entry && tt_global_entry->orig_node == orig_node) {
666 if (roaming) {
667 tt_global_entry->flags |= TT_CLIENT_ROAM;
668 tt_global_entry->roam_at = jiffies;
669 goto out;
670 }
648 atomic_dec(&orig_node->tt_size); 671 atomic_dec(&orig_node->tt_size);
649 _tt_global_del(bat_priv, tt_global_entry, message); 672 _tt_global_del(bat_priv, tt_global_entry, message);
650 } 673 }
674out:
651 spin_unlock_bh(&bat_priv->tt_ghash_lock); 675 spin_unlock_bh(&bat_priv->tt_ghash_lock);
652} 676}
653 677
@@ -685,6 +709,35 @@ static void tt_global_entry_free(struct hlist_node *node, void *arg)
685 kfree(data); 709 kfree(data);
686} 710}
687 711
712static void tt_global_roam_purge(struct bat_priv *bat_priv)
713{
714 struct hashtable_t *hash = bat_priv->tt_global_hash;
715 struct tt_global_entry *tt_global_entry;
716 struct hlist_node *node, *node_tmp;
717 struct hlist_head *head;
718 int i;
719
720 spin_lock_bh(&bat_priv->tt_ghash_lock);
721
722 for (i = 0; i < hash->size; i++) {
723 head = &hash->table[i];
724
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 _tt_global_del(bat_priv, tt_global_entry,
734 "Roaming timeout");
735 }
736 }
737
738 spin_unlock_bh(&bat_priv->tt_ghash_lock);
739}
740
688static void tt_global_table_free(struct bat_priv *bat_priv) 741static void tt_global_table_free(struct bat_priv *bat_priv)
689{ 742{
690 if (!bat_priv->tt_global_hash) 743 if (!bat_priv->tt_global_hash)
@@ -734,6 +787,12 @@ uint16_t tt_global_crc(struct bat_priv *bat_priv, struct orig_node *orig_node)
734 head, hash_entry) { 787 head, hash_entry) {
735 if (compare_eth(tt_global_entry->orig_node, 788 if (compare_eth(tt_global_entry->orig_node,
736 orig_node)) { 789 orig_node)) {
790 /* Roaming clients are in the global table for
791 * consistency only. They don't have to be
792 * taken into account while computing the
793 * global crc */
794 if (tt_global_entry->flags & TT_CLIENT_ROAM)
795 continue;
737 total_one = 0; 796 total_one = 0;
738 for (j = 0; j < ETH_ALEN; j++) 797 for (j = 0; j < ETH_ALEN; j++)
739 total_one = crc16_byte(total_one, 798 total_one = crc16_byte(total_one,
@@ -858,6 +917,9 @@ static int tt_global_valid_entry(const void *entry_ptr, const void *data_ptr)
858 const struct tt_global_entry *tt_global_entry = entry_ptr; 917 const struct tt_global_entry *tt_global_entry = entry_ptr;
859 const struct orig_node *orig_node = data_ptr; 918 const struct orig_node *orig_node = data_ptr;
860 919
920 if (tt_global_entry->flags & TT_CLIENT_ROAM)
921 return 0;
922
861 return (tt_global_entry->orig_node == orig_node); 923 return (tt_global_entry->orig_node == orig_node);
862} 924}
863 925
@@ -1251,10 +1313,11 @@ static void _tt_update_changes(struct bat_priv *bat_priv,
1251 if ((tt_change + i)->flags & TT_CHANGE_DEL) 1313 if ((tt_change + i)->flags & TT_CHANGE_DEL)
1252 tt_global_del(bat_priv, orig_node, 1314 tt_global_del(bat_priv, orig_node,
1253 (tt_change + i)->addr, 1315 (tt_change + i)->addr,
1254 "tt removed by changes"); 1316 "tt removed by changes",
1317 (tt_change + i)->flags & TT_CLIENT_ROAM);
1255 else 1318 else
1256 if (!tt_global_add(bat_priv, orig_node, 1319 if (!tt_global_add(bat_priv, orig_node,
1257 (tt_change + i)->addr, ttvn)) 1320 (tt_change + i)->addr, ttvn, false))
1258 /* In case of problem while storing a 1321 /* In case of problem while storing a
1259 * global_entry, we stop the updating 1322 * global_entry, we stop the updating
1260 * procedure without committing the 1323 * procedure without committing the
@@ -1356,6 +1419,9 @@ void handle_tt_response(struct bat_priv *bat_priv,
1356 spin_lock_bh(&bat_priv->tt_ghash_lock); 1419 spin_lock_bh(&bat_priv->tt_ghash_lock);
1357 orig_node->tt_crc = tt_global_crc(bat_priv, orig_node); 1420 orig_node->tt_crc = tt_global_crc(bat_priv, orig_node);
1358 spin_unlock_bh(&bat_priv->tt_ghash_lock); 1421 spin_unlock_bh(&bat_priv->tt_ghash_lock);
1422 /* Roaming phase is over: tables are in sync again. I can
1423 * unset the flag */
1424 orig_node->tt_poss_change = false;
1359out: 1425out:
1360 if (orig_node) 1426 if (orig_node)
1361 orig_node_free_ref(orig_node); 1427 orig_node_free_ref(orig_node);
@@ -1374,16 +1440,134 @@ int tt_init(struct bat_priv *bat_priv)
1374 return 1; 1440 return 1;
1375} 1441}
1376 1442
1377void tt_free(struct bat_priv *bat_priv) 1443static void tt_roam_list_free(struct bat_priv *bat_priv)
1378{ 1444{
1379 cancel_delayed_work_sync(&bat_priv->tt_work); 1445 struct tt_roam_node *node, *safe;
1380 1446
1381 tt_local_table_free(bat_priv); 1447 spin_lock_bh(&bat_priv->tt_roam_list_lock);
1382 tt_global_table_free(bat_priv);
1383 tt_req_list_free(bat_priv);
1384 tt_changes_list_free(bat_priv);
1385 1448
1386 kfree(bat_priv->tt_buff); 1449 list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) {
1450 list_del(&node->list);
1451 kfree(node);
1452 }
1453
1454 spin_unlock_bh(&bat_priv->tt_roam_list_lock);
1455}
1456
1457static void tt_roam_purge(struct bat_priv *bat_priv)
1458{
1459 struct tt_roam_node *node, *safe;
1460
1461 spin_lock_bh(&bat_priv->tt_roam_list_lock);
1462 list_for_each_entry_safe(node, safe, &bat_priv->tt_roam_list, list) {
1463 if (!is_out_of_time(node->first_time,
1464 ROAMING_MAX_TIME * 1000))
1465 continue;
1466
1467 list_del(&node->list);
1468 kfree(node);
1469 }
1470 spin_unlock_bh(&bat_priv->tt_roam_list_lock);
1471}
1472
1473/* This function checks whether the client already reached the
1474 * maximum number of possible roaming phases. In this case the ROAMING_ADV
1475 * will not be sent.
1476 *
1477 * returns true if the ROAMING_ADV can be sent, false otherwise */
1478static bool tt_check_roam_count(struct bat_priv *bat_priv,
1479 uint8_t *client)
1480{
1481 struct tt_roam_node *tt_roam_node;
1482 bool ret = false;
1483
1484 spin_lock_bh(&bat_priv->tt_roam_list_lock);
1485 /* The new tt_req will be issued only if I'm not waiting for a
1486 * reply from the same orig_node yet */
1487 list_for_each_entry(tt_roam_node, &bat_priv->tt_roam_list, list) {
1488 if (!compare_eth(tt_roam_node->addr, client))
1489 continue;
1490
1491 if (is_out_of_time(tt_roam_node->first_time,
1492 ROAMING_MAX_TIME * 1000))
1493 continue;
1494
1495 if (!atomic_dec_not_zero(&tt_roam_node->counter))
1496 /* Sorry, you roamed too many times! */
1497 goto unlock;
1498 ret = true;
1499 break;
1500 }
1501
1502 if (!ret) {
1503 tt_roam_node = kmalloc(sizeof(*tt_roam_node), GFP_ATOMIC);
1504 if (!tt_roam_node)
1505 goto unlock;
1506
1507 tt_roam_node->first_time = jiffies;
1508 atomic_set(&tt_roam_node->counter, ROAMING_MAX_COUNT - 1);
1509 memcpy(tt_roam_node->addr, client, ETH_ALEN);
1510
1511 list_add(&tt_roam_node->list, &bat_priv->tt_roam_list);
1512 ret = true;
1513 }
1514
1515unlock:
1516 spin_unlock_bh(&bat_priv->tt_roam_list_lock);
1517 return ret;
1518}
1519
1520void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
1521 struct orig_node *orig_node)
1522{
1523 struct neigh_node *neigh_node = NULL;
1524 struct sk_buff *skb = NULL;
1525 struct roam_adv_packet *roam_adv_packet;
1526 int ret = 1;
1527 struct hard_iface *primary_if;
1528
1529 /* before going on we have to check whether the client has
1530 * already roamed to us too many times */
1531 if (!tt_check_roam_count(bat_priv, client))
1532 goto out;
1533
1534 skb = dev_alloc_skb(sizeof(struct roam_adv_packet) + ETH_HLEN);
1535 if (!skb)
1536 goto out;
1537
1538 skb_reserve(skb, ETH_HLEN);
1539
1540 roam_adv_packet = (struct roam_adv_packet *)skb_put(skb,
1541 sizeof(struct roam_adv_packet));
1542
1543 roam_adv_packet->packet_type = BAT_ROAM_ADV;
1544 roam_adv_packet->version = COMPAT_VERSION;
1545 roam_adv_packet->ttl = TTL;
1546 primary_if = primary_if_get_selected(bat_priv);
1547 if (!primary_if)
1548 goto out;
1549 memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
1550 hardif_free_ref(primary_if);
1551 memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN);
1552 memcpy(roam_adv_packet->client, client, ETH_ALEN);
1553
1554 neigh_node = orig_node_get_router(orig_node);
1555 if (!neigh_node)
1556 goto out;
1557
1558 bat_dbg(DBG_TT, bat_priv,
1559 "Sending ROAMING_ADV to %pM (client %pM) via %pM\n",
1560 orig_node->orig, client, neigh_node->addr);
1561
1562 send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
1563 ret = 0;
1564
1565out:
1566 if (neigh_node)
1567 neigh_node_free_ref(neigh_node);
1568 if (ret)
1569 kfree_skb(skb);
1570 return;
1387} 1571}
1388 1572
1389static void tt_purge(struct work_struct *work) 1573static void tt_purge(struct work_struct *work)
@@ -1394,7 +1578,22 @@ static void tt_purge(struct work_struct *work)
1394 container_of(delayed_work, struct bat_priv, tt_work); 1578 container_of(delayed_work, struct bat_priv, tt_work);
1395 1579
1396 tt_local_purge(bat_priv); 1580 tt_local_purge(bat_priv);
1581 tt_global_roam_purge(bat_priv);
1397 tt_req_purge(bat_priv); 1582 tt_req_purge(bat_priv);
1583 tt_roam_purge(bat_priv);
1398 1584
1399 tt_start_timer(bat_priv); 1585 tt_start_timer(bat_priv);
1400} 1586}
1587
1588void tt_free(struct bat_priv *bat_priv)
1589{
1590 cancel_delayed_work_sync(&bat_priv->tt_work);
1591
1592 tt_local_table_free(bat_priv);
1593 tt_global_table_free(bat_priv);
1594 tt_req_list_free(bat_priv);
1595 tt_changes_list_free(bat_priv);
1596 tt_roam_list_free(bat_priv);
1597
1598 kfree(bat_priv->tt_buff);
1599}
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index 51f7e3060f6f..1cd2d39529fe 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -28,20 +28,20 @@ int tt_changes_fill_buffer(struct bat_priv *bat_priv,
28int tt_init(struct bat_priv *bat_priv); 28int tt_init(struct bat_priv *bat_priv);
29void tt_local_add(struct net_device *soft_iface, const uint8_t *addr); 29void tt_local_add(struct net_device *soft_iface, const uint8_t *addr);
30void tt_local_remove(struct bat_priv *bat_priv, 30void tt_local_remove(struct bat_priv *bat_priv,
31 const uint8_t *addr, const char *message); 31 const uint8_t *addr, const char *message, bool roaming);
32int tt_local_seq_print_text(struct seq_file *seq, void *offset); 32int tt_local_seq_print_text(struct seq_file *seq, void *offset);
33void tt_global_add_orig(struct bat_priv *bat_priv, 33void tt_global_add_orig(struct bat_priv *bat_priv,
34 struct orig_node *orig_node, 34 struct orig_node *orig_node,
35 const unsigned char *tt_buff, int tt_buff_len); 35 const unsigned char *tt_buff, int tt_buff_len);
36int tt_global_add(struct bat_priv *bat_priv, 36int tt_global_add(struct bat_priv *bat_priv,
37 struct orig_node *orig_node, const unsigned char *addr, 37 struct orig_node *orig_node, const unsigned char *addr,
38 uint8_t ttvn); 38 uint8_t ttvn, bool roaming);
39int tt_global_seq_print_text(struct seq_file *seq, void *offset); 39int tt_global_seq_print_text(struct seq_file *seq, void *offset);
40void tt_global_del_orig(struct bat_priv *bat_priv, 40void tt_global_del_orig(struct bat_priv *bat_priv,
41 struct orig_node *orig_node, const char *message); 41 struct orig_node *orig_node, const char *message);
42void tt_global_del(struct bat_priv *bat_priv, 42void tt_global_del(struct bat_priv *bat_priv,
43 struct orig_node *orig_node, const unsigned char *addr, 43 struct orig_node *orig_node, const unsigned char *addr,
44 const char *message); 44 const char *message, bool roaming);
45struct orig_node *transtable_search(struct bat_priv *bat_priv, 45struct orig_node *transtable_search(struct bat_priv *bat_priv,
46 const uint8_t *addr); 46 const uint8_t *addr);
47void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node, 47void tt_save_orig_buffer(struct bat_priv *bat_priv, struct orig_node *orig_node,
@@ -60,5 +60,7 @@ void tt_update_changes(struct bat_priv *bat_priv, struct orig_node *orig_node,
60bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr); 60bool is_my_client(struct bat_priv *bat_priv, const uint8_t *addr);
61void handle_tt_response(struct bat_priv *bat_priv, 61void handle_tt_response(struct bat_priv *bat_priv,
62 struct tt_query_packet *tt_response); 62 struct tt_query_packet *tt_response);
63void send_roam_adv(struct bat_priv *bat_priv, uint8_t *client,
64 struct orig_node *orig_node);
63 65
64#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ 66#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 3b642a9e086e..9c84fa9f0968 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -81,6 +81,12 @@ struct orig_node {
81 int16_t tt_buff_len; 81 int16_t tt_buff_len;
82 spinlock_t tt_buff_lock; /* protects tt_buff */ 82 spinlock_t tt_buff_lock; /* protects tt_buff */
83 atomic_t tt_size; 83 atomic_t tt_size;
84 /* The tt_poss_change flag is used to detect an ongoing roaming phase.
85 * If true, then I sent a Roaming_adv to this orig_node and I have to
86 * inspect every packet directed to it to check whether it is still
87 * the true destination or not. This flag will be reset to false as
88 * soon as I receive a new TTVN from this orig_node */
89 bool tt_poss_change;
84 uint32_t last_real_seqno; 90 uint32_t last_real_seqno;
85 uint8_t last_ttl; 91 uint8_t last_ttl;
86 unsigned long bcast_bits[NUM_WORDS]; 92 unsigned long bcast_bits[NUM_WORDS];
@@ -153,6 +159,12 @@ struct bat_priv {
153 atomic_t ttvn; /* tranlation table version number */ 159 atomic_t ttvn; /* tranlation table version number */
154 atomic_t tt_ogm_append_cnt; 160 atomic_t tt_ogm_append_cnt;
155 atomic_t tt_local_changes; /* changes registered in a OGM interval */ 161 atomic_t tt_local_changes; /* changes registered in a OGM interval */
162 /* The tt_poss_change flag is used to detect an ongoing roaming phase.
163 * If true, then I received a Roaming_adv and I have to inspect every
164 * packet directed to me to check whether I am still the true
165 * destination or not. This flag will be reset to false as soon as I
166 * increase my TTVN */
167 bool tt_poss_change;
156 char num_ifaces; 168 char num_ifaces;
157 struct debug_log *debug_log; 169 struct debug_log *debug_log;
158 struct kobject *mesh_obj; 170 struct kobject *mesh_obj;
@@ -167,6 +179,7 @@ struct bat_priv {
167 struct hashtable_t *tt_local_hash; 179 struct hashtable_t *tt_local_hash;
168 struct hashtable_t *tt_global_hash; 180 struct hashtable_t *tt_global_hash;
169 struct list_head tt_req_list; /* list of pending tt_requests */ 181 struct list_head tt_req_list; /* list of pending tt_requests */
182 struct list_head tt_roam_list;
170 struct hashtable_t *vis_hash; 183 struct hashtable_t *vis_hash;
171 spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ 184 spinlock_t forw_bat_list_lock; /* protects forw_bat_list */
172 spinlock_t forw_bcast_list_lock; /* protects */ 185 spinlock_t forw_bcast_list_lock; /* protects */
@@ -174,6 +187,7 @@ struct bat_priv {
174 spinlock_t tt_lhash_lock; /* protects tt_local_hash */ 187 spinlock_t tt_lhash_lock; /* protects tt_local_hash */
175 spinlock_t tt_ghash_lock; /* protects tt_global_hash */ 188 spinlock_t tt_ghash_lock; /* protects tt_global_hash */
176 spinlock_t tt_req_list_lock; /* protects tt_req_list */ 189 spinlock_t tt_req_list_lock; /* protects tt_req_list */
190 spinlock_t tt_roam_list_lock; /* protects tt_roam_list */
177 spinlock_t gw_list_lock; /* protects gw_list and curr_gw */ 191 spinlock_t gw_list_lock; /* protects gw_list and curr_gw */
178 spinlock_t vis_hash_lock; /* protects vis_hash */ 192 spinlock_t vis_hash_lock; /* protects vis_hash */
179 spinlock_t vis_list_lock; /* protects vis_info::recv_list */ 193 spinlock_t vis_list_lock; /* protects vis_info::recv_list */
@@ -219,8 +233,9 @@ struct tt_global_entry {
219 uint8_t addr[ETH_ALEN]; 233 uint8_t addr[ETH_ALEN];
220 struct orig_node *orig_node; 234 struct orig_node *orig_node;
221 uint8_t ttvn; 235 uint8_t ttvn;
222 /* entry in the global table */ 236 uint8_t flags; /* only TT_GLOBAL_ROAM is used */
223 struct hlist_node hash_entry; 237 unsigned long roam_at; /* time at which TT_GLOBAL_ROAM was set */
238 struct hlist_node hash_entry; /* entry in the global table */
224}; 239};
225 240
226struct tt_change_node { 241struct tt_change_node {
@@ -234,6 +249,13 @@ struct tt_req_node {
234 struct list_head list; 249 struct list_head list;
235}; 250};
236 251
252struct tt_roam_node {
253 uint8_t addr[ETH_ALEN];
254 atomic_t counter;
255 unsigned long first_time;
256 struct list_head list;
257};
258
237/** 259/**
238 * forw_packet - structure for forw_list maintaining packets to be 260 * forw_packet - structure for forw_list maintaining packets to be
239 * send/forwarded 261 * send/forwarded