|
|
|
@ -183,7 +183,8 @@ static int tt_local_init(struct bat_priv *bat_priv) |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void tt_local_add(struct net_device *soft_iface, const uint8_t *addr) |
|
|
|
|
void tt_local_add(struct net_device *soft_iface, const uint8_t *addr, |
|
|
|
|
int ifindex) |
|
|
|
|
{ |
|
|
|
|
struct bat_priv *bat_priv = netdev_priv(soft_iface); |
|
|
|
|
struct tt_local_entry *tt_local_entry = NULL; |
|
|
|
@ -207,6 +208,8 @@ void tt_local_add(struct net_device *soft_iface, const uint8_t *addr) |
|
|
|
|
memcpy(tt_local_entry->addr, addr, ETH_ALEN); |
|
|
|
|
tt_local_entry->last_seen = jiffies; |
|
|
|
|
tt_local_entry->flags = NO_FLAGS; |
|
|
|
|
if (is_wifi_iface(ifindex)) |
|
|
|
|
tt_local_entry->flags |= TT_CLIENT_WIFI; |
|
|
|
|
atomic_set(&tt_local_entry->refcount, 2); |
|
|
|
|
|
|
|
|
|
/* the batman interface mac address should never be purged */ |
|
|
|
@ -329,7 +332,7 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) |
|
|
|
|
|
|
|
|
|
rcu_read_lock(); |
|
|
|
|
__hlist_for_each_rcu(node, head) |
|
|
|
|
buf_size += 21; |
|
|
|
|
buf_size += 29; |
|
|
|
|
rcu_read_unlock(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -348,8 +351,19 @@ int tt_local_seq_print_text(struct seq_file *seq, void *offset) |
|
|
|
|
rcu_read_lock(); |
|
|
|
|
hlist_for_each_entry_rcu(tt_local_entry, node, |
|
|
|
|
head, hash_entry) { |
|
|
|
|
pos += snprintf(buff + pos, 22, " * %pM\n", |
|
|
|
|
tt_local_entry->addr); |
|
|
|
|
pos += snprintf(buff + pos, 30, " * %pM " |
|
|
|
|
"[%c%c%c%c%c]\n", |
|
|
|
|
tt_local_entry->addr, |
|
|
|
|
(tt_local_entry->flags & |
|
|
|
|
TT_CLIENT_ROAM ? 'R' : '.'), |
|
|
|
|
(tt_local_entry->flags & |
|
|
|
|
TT_CLIENT_NOPURGE ? 'P' : '.'), |
|
|
|
|
(tt_local_entry->flags & |
|
|
|
|
TT_CLIENT_NEW ? 'N' : '.'), |
|
|
|
|
(tt_local_entry->flags & |
|
|
|
|
TT_CLIENT_PENDING ? 'X' : '.'), |
|
|
|
|
(tt_local_entry->flags & |
|
|
|
|
TT_CLIENT_WIFI ? 'W' : '.')); |
|
|
|
|
} |
|
|
|
|
rcu_read_unlock(); |
|
|
|
|
} |
|
|
|
@ -369,8 +383,8 @@ static void tt_local_set_pending(struct bat_priv *bat_priv, |
|
|
|
|
tt_local_event(bat_priv, tt_local_entry->addr, |
|
|
|
|
tt_local_entry->flags | flags); |
|
|
|
|
|
|
|
|
|
/* The local client has to be merked as "pending to be removed" but has
|
|
|
|
|
* to be kept in the table in order to send it in an full tables |
|
|
|
|
/* The local client has to be marked as "pending to be removed" but has
|
|
|
|
|
* to be kept in the table in order to send it in a full table |
|
|
|
|
* response issued before the net ttvn increment (consistency check) */ |
|
|
|
|
tt_local_entry->flags |= TT_CLIENT_PENDING; |
|
|
|
|
} |
|
|
|
@ -495,7 +509,8 @@ static void tt_changes_list_free(struct bat_priv *bat_priv) |
|
|
|
|
|
|
|
|
|
/* caller must hold orig_node refcount */ |
|
|
|
|
int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node, |
|
|
|
|
const unsigned char *tt_addr, uint8_t ttvn, bool roaming) |
|
|
|
|
const unsigned char *tt_addr, uint8_t ttvn, bool roaming, |
|
|
|
|
bool wifi) |
|
|
|
|
{ |
|
|
|
|
struct tt_global_entry *tt_global_entry; |
|
|
|
|
struct orig_node *orig_node_tmp; |
|
|
|
@ -537,6 +552,9 @@ int tt_global_add(struct bat_priv *bat_priv, struct orig_node *orig_node, |
|
|
|
|
tt_global_entry->roam_at = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (wifi) |
|
|
|
|
tt_global_entry->flags |= TT_CLIENT_WIFI; |
|
|
|
|
|
|
|
|
|
bat_dbg(DBG_TT, bat_priv, |
|
|
|
|
"Creating new global tt entry: %pM (via %pM)\n", |
|
|
|
|
tt_global_entry->addr, orig_node->orig); |
|
|
|
@ -582,8 +600,8 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) |
|
|
|
|
seq_printf(seq, |
|
|
|
|
"Globally announced TT entries received via the mesh %s\n", |
|
|
|
|
net_dev->name); |
|
|
|
|
seq_printf(seq, " %-13s %s %-15s %s\n", |
|
|
|
|
"Client", "(TTVN)", "Originator", "(Curr TTVN)"); |
|
|
|
|
seq_printf(seq, " %-13s %s %-15s %s %s\n", |
|
|
|
|
"Client", "(TTVN)", "Originator", "(Curr TTVN)", "Flags"); |
|
|
|
|
|
|
|
|
|
buf_size = 1; |
|
|
|
|
/* Estimate length for: " * xx:xx:xx:xx:xx:xx (ttvn) via
|
|
|
|
@ -593,7 +611,7 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) |
|
|
|
|
|
|
|
|
|
rcu_read_lock(); |
|
|
|
|
__hlist_for_each_rcu(node, head) |
|
|
|
|
buf_size += 59; |
|
|
|
|
buf_size += 67; |
|
|
|
|
rcu_read_unlock(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -612,14 +630,20 @@ int tt_global_seq_print_text(struct seq_file *seq, void *offset) |
|
|
|
|
rcu_read_lock(); |
|
|
|
|
hlist_for_each_entry_rcu(tt_global_entry, node, |
|
|
|
|
head, hash_entry) { |
|
|
|
|
pos += snprintf(buff + pos, 61, |
|
|
|
|
" * %pM (%3u) via %pM (%3u)\n", |
|
|
|
|
tt_global_entry->addr, |
|
|
|
|
pos += snprintf(buff + pos, 69, |
|
|
|
|
" * %pM (%3u) via %pM (%3u) " |
|
|
|
|
"[%c%c%c]\n", tt_global_entry->addr, |
|
|
|
|
tt_global_entry->ttvn, |
|
|
|
|
tt_global_entry->orig_node->orig, |
|
|
|
|
(uint8_t) atomic_read( |
|
|
|
|
&tt_global_entry->orig_node-> |
|
|
|
|
last_ttvn)); |
|
|
|
|
last_ttvn), |
|
|
|
|
(tt_global_entry->flags & |
|
|
|
|
TT_CLIENT_ROAM ? 'R' : '.'), |
|
|
|
|
(tt_global_entry->flags & |
|
|
|
|
TT_CLIENT_PENDING ? 'X' : '.'), |
|
|
|
|
(tt_global_entry->flags & |
|
|
|
|
TT_CLIENT_WIFI ? 'W' : '.')); |
|
|
|
|
} |
|
|
|
|
rcu_read_unlock(); |
|
|
|
|
} |
|
|
|
@ -774,30 +798,56 @@ static void tt_global_table_free(struct bat_priv *bat_priv) |
|
|
|
|
bat_priv->tt_global_hash = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool _is_ap_isolated(struct tt_local_entry *tt_local_entry, |
|
|
|
|
struct tt_global_entry *tt_global_entry) |
|
|
|
|
{ |
|
|
|
|
bool ret = false; |
|
|
|
|
|
|
|
|
|
if (tt_local_entry->flags & TT_CLIENT_WIFI && |
|
|
|
|
tt_global_entry->flags & TT_CLIENT_WIFI) |
|
|
|
|
ret = true; |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct orig_node *transtable_search(struct bat_priv *bat_priv, |
|
|
|
|
const uint8_t *addr) |
|
|
|
|
const uint8_t *src, const uint8_t *addr) |
|
|
|
|
{ |
|
|
|
|
struct tt_global_entry *tt_global_entry; |
|
|
|
|
struct tt_local_entry *tt_local_entry = NULL; |
|
|
|
|
struct tt_global_entry *tt_global_entry = NULL; |
|
|
|
|
struct orig_node *orig_node = NULL; |
|
|
|
|
|
|
|
|
|
tt_global_entry = tt_global_hash_find(bat_priv, addr); |
|
|
|
|
if (src && atomic_read(&bat_priv->ap_isolation)) { |
|
|
|
|
tt_local_entry = tt_local_hash_find(bat_priv, src); |
|
|
|
|
if (!tt_local_entry) |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tt_global_entry = tt_global_hash_find(bat_priv, addr); |
|
|
|
|
if (!tt_global_entry) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
/* check whether the clients should not communicate due to AP
|
|
|
|
|
* isolation */ |
|
|
|
|
if (tt_local_entry && _is_ap_isolated(tt_local_entry, tt_global_entry)) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
if (!atomic_inc_not_zero(&tt_global_entry->orig_node->refcount)) |
|
|
|
|
goto free_tt; |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
/* A global client marked as PENDING has already moved from that
|
|
|
|
|
* originator */ |
|
|
|
|
if (tt_global_entry->flags & TT_CLIENT_PENDING) |
|
|
|
|
goto free_tt; |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
orig_node = tt_global_entry->orig_node; |
|
|
|
|
|
|
|
|
|
free_tt: |
|
|
|
|
tt_global_entry_free_ref(tt_global_entry); |
|
|
|
|
out: |
|
|
|
|
if (tt_global_entry) |
|
|
|
|
tt_global_entry_free_ref(tt_global_entry); |
|
|
|
|
if (tt_local_entry) |
|
|
|
|
tt_local_entry_free_ref(tt_local_entry); |
|
|
|
|
|
|
|
|
|
return orig_node; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1029,8 +1079,9 @@ out: |
|
|
|
|
return skb; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int send_tt_request(struct bat_priv *bat_priv, struct orig_node *dst_orig_node, |
|
|
|
|
uint8_t ttvn, uint16_t tt_crc, bool full_table) |
|
|
|
|
static int send_tt_request(struct bat_priv *bat_priv, |
|
|
|
|
struct orig_node *dst_orig_node, |
|
|
|
|
uint8_t ttvn, uint16_t tt_crc, bool full_table) |
|
|
|
|
{ |
|
|
|
|
struct sk_buff *skb = NULL; |
|
|
|
|
struct tt_query_packet *tt_request; |
|
|
|
@ -1137,12 +1188,12 @@ static bool send_other_tt_response(struct bat_priv *bat_priv, |
|
|
|
|
orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); |
|
|
|
|
req_ttvn = tt_request->ttvn; |
|
|
|
|
|
|
|
|
|
/* I have not the requested data */ |
|
|
|
|
/* I don't have the requested data */ |
|
|
|
|
if (orig_ttvn != req_ttvn || |
|
|
|
|
tt_request->tt_data != req_dst_orig_node->tt_crc) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
/* If it has explicitly been requested the full table */ |
|
|
|
|
/* If the full table has been explicitly requested */ |
|
|
|
|
if (tt_request->flags & TT_FULL_TABLE || |
|
|
|
|
!req_dst_orig_node->tt_buff) |
|
|
|
|
full_table = true; |
|
|
|
@ -1363,7 +1414,9 @@ static void _tt_update_changes(struct bat_priv *bat_priv, |
|
|
|
|
(tt_change + i)->flags & TT_CLIENT_ROAM); |
|
|
|
|
else |
|
|
|
|
if (!tt_global_add(bat_priv, orig_node, |
|
|
|
|
(tt_change + i)->addr, ttvn, false)) |
|
|
|
|
(tt_change + i)->addr, ttvn, false, |
|
|
|
|
(tt_change + i)->flags & |
|
|
|
|
TT_CLIENT_WIFI)) |
|
|
|
|
/* In case of problem while storing a
|
|
|
|
|
* global_entry, we stop the updating |
|
|
|
|
* procedure without committing the |
|
|
|
@ -1403,9 +1456,10 @@ out: |
|
|
|
|
orig_node_free_ref(orig_node); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void tt_update_changes(struct bat_priv *bat_priv, struct orig_node *orig_node, |
|
|
|
|
uint16_t tt_num_changes, uint8_t ttvn, |
|
|
|
|
struct tt_change *tt_change) |
|
|
|
|
static void tt_update_changes(struct bat_priv *bat_priv, |
|
|
|
|
struct orig_node *orig_node, |
|
|
|
|
uint16_t tt_num_changes, uint8_t ttvn, |
|
|
|
|
struct tt_change *tt_change) |
|
|
|
|
{ |
|
|
|
|
_tt_update_changes(bat_priv, orig_node, tt_change, tt_num_changes, |
|
|
|
|
ttvn); |
|
|
|
@ -1720,3 +1774,90 @@ void tt_commit_changes(struct bat_priv *bat_priv) |
|
|
|
|
atomic_inc(&bat_priv->ttvn); |
|
|
|
|
bat_priv->tt_poss_change = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool is_ap_isolated(struct bat_priv *bat_priv, uint8_t *src, uint8_t *dst) |
|
|
|
|
{ |
|
|
|
|
struct tt_local_entry *tt_local_entry = NULL; |
|
|
|
|
struct tt_global_entry *tt_global_entry = NULL; |
|
|
|
|
bool ret = true; |
|
|
|
|
|
|
|
|
|
if (!atomic_read(&bat_priv->ap_isolation)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
tt_local_entry = tt_local_hash_find(bat_priv, dst); |
|
|
|
|
if (!tt_local_entry) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
tt_global_entry = tt_global_hash_find(bat_priv, src); |
|
|
|
|
if (!tt_global_entry) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
if (_is_ap_isolated(tt_local_entry, tt_global_entry)) |
|
|
|
|
goto out; |
|
|
|
|
|
|
|
|
|
ret = false; |
|
|
|
|
|
|
|
|
|
out: |
|
|
|
|
if (tt_global_entry) |
|
|
|
|
tt_global_entry_free_ref(tt_global_entry); |
|
|
|
|
if (tt_local_entry) |
|
|
|
|
tt_local_entry_free_ref(tt_local_entry); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void tt_update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, |
|
|
|
|
const unsigned char *tt_buff, uint8_t tt_num_changes, |
|
|
|
|
uint8_t ttvn, uint16_t tt_crc) |
|
|
|
|
{ |
|
|
|
|
uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); |
|
|
|
|
bool full_table = true; |
|
|
|
|
|
|
|
|
|
/* the ttvn increased by one -> we can apply the attached changes */ |
|
|
|
|
if (ttvn - orig_ttvn == 1) { |
|
|
|
|
/* the OGM could not contain the changes due to their size or
|
|
|
|
|
* because they have already been sent TT_OGM_APPEND_MAX times. |
|
|
|
|
* In this case send a tt request */ |
|
|
|
|
if (!tt_num_changes) { |
|
|
|
|
full_table = false; |
|
|
|
|
goto request_table; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tt_update_changes(bat_priv, orig_node, tt_num_changes, ttvn, |
|
|
|
|
(struct tt_change *)tt_buff); |
|
|
|
|
|
|
|
|
|
/* Even if we received the precomputed crc with the OGM, we
|
|
|
|
|
* prefer to recompute it to spot any possible inconsistency |
|
|
|
|
* in the global table */ |
|
|
|
|
orig_node->tt_crc = tt_global_crc(bat_priv, orig_node); |
|
|
|
|
|
|
|
|
|
/* The ttvn alone is not enough to guarantee consistency
|
|
|
|
|
* because a single value could represent different states |
|
|
|
|
* (due to the wrap around). Thus a node has to check whether |
|
|
|
|
* the resulting table (after applying the changes) is still |
|
|
|
|
* consistent or not. E.g. a node could disconnect while its |
|
|
|
|
* ttvn is X and reconnect on ttvn = X + TTVN_MAX: in this case |
|
|
|
|
* checking the CRC value is mandatory to detect the |
|
|
|
|
* inconsistency */ |
|
|
|
|
if (orig_node->tt_crc != tt_crc) |
|
|
|
|
goto request_table; |
|
|
|
|
|
|
|
|
|
/* Roaming phase is over: tables are in sync again. I can
|
|
|
|
|
* unset the flag */ |
|
|
|
|
orig_node->tt_poss_change = false; |
|
|
|
|
} else { |
|
|
|
|
/* if we missed more than one change or our tables are not
|
|
|
|
|
* in sync anymore -> request fresh tt data */ |
|
|
|
|
if (ttvn != orig_ttvn || orig_node->tt_crc != tt_crc) { |
|
|
|
|
request_table: |
|
|
|
|
bat_dbg(DBG_TT, bat_priv, "TT inconsistency for %pM. " |
|
|
|
|
"Need to retrieve the correct information " |
|
|
|
|
"(ttvn: %u last_ttvn: %u crc: %u last_crc: " |
|
|
|
|
"%u num_changes: %u)\n", orig_node->orig, ttvn, |
|
|
|
|
orig_ttvn, tt_crc, orig_node->tt_crc, |
|
|
|
|
tt_num_changes); |
|
|
|
|
send_tt_request(bat_priv, orig_node, ttvn, tt_crc, |
|
|
|
|
full_table); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|