summaryrefslogtreecommitdiffstats
path: root/net/batman-adv
diff options
context:
space:
mode:
Diffstat (limited to 'net/batman-adv')
-rw-r--r--net/batman-adv/Makefile3
-rw-r--r--net/batman-adv/bat_iv_ogm.c510
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c64
-rw-r--r--net/batman-adv/bridge_loop_avoidance.h10
-rw-r--r--net/batman-adv/debugfs.c9
-rw-r--r--net/batman-adv/distributed-arp-table.c231
-rw-r--r--net/batman-adv/distributed-arp-table.h5
-rw-r--r--net/batman-adv/fragmentation.c491
-rw-r--r--net/batman-adv/fragmentation.h50
-rw-r--r--net/batman-adv/gateway_client.c247
-rw-r--r--net/batman-adv/gateway_client.h2
-rw-r--r--net/batman-adv/gateway_common.c230
-rw-r--r--net/batman-adv/gateway_common.h14
-rw-r--r--net/batman-adv/hard-interface.c110
-rw-r--r--net/batman-adv/hard-interface.h2
-rw-r--r--net/batman-adv/icmp_socket.c128
-rw-r--r--net/batman-adv/icmp_socket.h2
-rw-r--r--net/batman-adv/main.c689
-rw-r--r--net/batman-adv/main.h68
-rw-r--r--net/batman-adv/network-coding.c71
-rw-r--r--net/batman-adv/network-coding.h5
-rw-r--r--net/batman-adv/originator.c364
-rw-r--r--net/batman-adv/originator.h13
-rw-r--r--net/batman-adv/packet.h356
-rw-r--r--net/batman-adv/routing.c557
-rw-r--r--net/batman-adv/routing.h13
-rw-r--r--net/batman-adv/send.c239
-rw-r--r--net/batman-adv/send.h53
-rw-r--r--net/batman-adv/soft-interface.c269
-rw-r--r--net/batman-adv/soft-interface.h4
-rw-r--r--net/batman-adv/sysfs.c274
-rw-r--r--net/batman-adv/sysfs.h10
-rw-r--r--net/batman-adv/translation-table.c2086
-rw-r--r--net/batman-adv/translation-table.h46
-rw-r--r--net/batman-adv/types.h436
-rw-r--r--net/batman-adv/unicast.c491
-rw-r--r--net/batman-adv/unicast.h92
-rw-r--r--net/batman-adv/vis.c938
-rw-r--r--net/batman-adv/vis.h36
39 files changed, 5605 insertions, 3613 deletions
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile
index 489bb36f1b94..4f4aabbd8eab 100644
--- a/net/batman-adv/Makefile
+++ b/net/batman-adv/Makefile
@@ -24,6 +24,7 @@ batman-adv-y += bitarray.o
batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
batman-adv-y += debugfs.o
batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
+batman-adv-y += fragmentation.o
batman-adv-y += gateway_client.o
batman-adv-y += gateway_common.o
batman-adv-y += hard-interface.o
@@ -37,5 +38,3 @@ batman-adv-y += send.o
batman-adv-y += soft-interface.o
batman-adv-y += sysfs.o
batman-adv-y += translation-table.o
-batman-adv-y += unicast.o
-batman-adv-y += vis.o
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 0a8a80cd4bf1..a2b480a90872 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -87,22 +87,198 @@ static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[])
return (uint8_t)(sum / count);
}
+/**
+ * batadv_iv_ogm_orig_free - free the private resources allocated for this
+ * orig_node
+ * @orig_node: the orig_node for which the resources have to be free'd
+ */
+static void batadv_iv_ogm_orig_free(struct batadv_orig_node *orig_node)
+{
+ kfree(orig_node->bat_iv.bcast_own);
+ kfree(orig_node->bat_iv.bcast_own_sum);
+}
+
+/**
+ * batadv_iv_ogm_orig_add_if - change the private structures of the orig_node to
+ * include the new hard-interface
+ * @orig_node: the orig_node that has to be changed
+ * @max_if_num: the current amount of interfaces
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node,
+ int max_if_num)
+{
+ void *data_ptr;
+ size_t data_size, old_size;
+ int ret = -ENOMEM;
+
+ spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+ data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS;
+ old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS;
+ data_ptr = kmalloc(data_size, GFP_ATOMIC);
+ if (!data_ptr)
+ goto unlock;
+
+ memcpy(data_ptr, orig_node->bat_iv.bcast_own, old_size);
+ kfree(orig_node->bat_iv.bcast_own);
+ orig_node->bat_iv.bcast_own = data_ptr;
+
+ data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
+ if (!data_ptr) {
+ kfree(orig_node->bat_iv.bcast_own);
+ goto unlock;
+ }
+
+ memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum,
+ (max_if_num - 1) * sizeof(uint8_t));
+ kfree(orig_node->bat_iv.bcast_own_sum);
+ orig_node->bat_iv.bcast_own_sum = data_ptr;
+
+ ret = 0;
+
+unlock:
+ spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+ return ret;
+}
+
+/**
+ * batadv_iv_ogm_orig_del_if - change the private structures of the orig_node to
+ * exclude the removed interface
+ * @orig_node: the orig_node that has to be changed
+ * @max_if_num: the current amount of interfaces
+ * @del_if_num: the index of the interface being removed
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
+ int max_if_num, int del_if_num)
+{
+ int chunk_size, ret = -ENOMEM, if_offset;
+ void *data_ptr = NULL;
+
+ spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+ /* last interface was removed */
+ if (max_if_num == 0)
+ goto free_bcast_own;
+
+ chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS;
+ data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
+ if (!data_ptr)
+ goto unlock;
+
+ /* copy first part */
+ memcpy(data_ptr, orig_node->bat_iv.bcast_own, del_if_num * chunk_size);
+
+ /* copy second part */
+ memcpy((char *)data_ptr + del_if_num * chunk_size,
+ orig_node->bat_iv.bcast_own + ((del_if_num + 1) * chunk_size),
+ (max_if_num - del_if_num) * chunk_size);
+
+free_bcast_own:
+ kfree(orig_node->bat_iv.bcast_own);
+ orig_node->bat_iv.bcast_own = data_ptr;
+
+ if (max_if_num == 0)
+ goto free_own_sum;
+
+ data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
+ if (!data_ptr) {
+ kfree(orig_node->bat_iv.bcast_own);
+ goto unlock;
+ }
+
+ memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum,
+ del_if_num * sizeof(uint8_t));
+
+ if_offset = (del_if_num + 1) * sizeof(uint8_t);
+ memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t),
+ orig_node->bat_iv.bcast_own_sum + if_offset,
+ (max_if_num - del_if_num) * sizeof(uint8_t));
+
+free_own_sum:
+ kfree(orig_node->bat_iv.bcast_own_sum);
+ orig_node->bat_iv.bcast_own_sum = data_ptr;
+
+ ret = 0;
+unlock:
+ spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+
+ return ret;
+}
+
+/**
+ * batadv_iv_ogm_orig_get - retrieve or create (if does not exist) an originator
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: mac address of the originator
+ *
+ * Returns the originator object corresponding to the passed mac address or NULL
+ * on failure.
+ * If the object does not exists it is created an initialised.
+ */
+static struct batadv_orig_node *
+batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr)
+{
+ struct batadv_orig_node *orig_node;
+ int size, hash_added;
+
+ orig_node = batadv_orig_hash_find(bat_priv, addr);
+ if (orig_node)
+ return orig_node;
+
+ orig_node = batadv_orig_node_new(bat_priv, addr);
+ if (!orig_node)
+ return NULL;
+
+ spin_lock_init(&orig_node->bat_iv.ogm_cnt_lock);
+
+ size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS;
+ orig_node->bat_iv.bcast_own = kzalloc(size, GFP_ATOMIC);
+ if (!orig_node->bat_iv.bcast_own)
+ goto free_orig_node;
+
+ size = bat_priv->num_ifaces * sizeof(uint8_t);
+ orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC);
+ if (!orig_node->bat_iv.bcast_own_sum)
+ goto free_bcast_own;
+
+ hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
+ batadv_choose_orig, orig_node,
+ &orig_node->hash_entry);
+ if (hash_added != 0)
+ goto free_bcast_own;
+
+ return orig_node;
+
+free_bcast_own:
+ kfree(orig_node->bat_iv.bcast_own);
+free_orig_node:
+ batadv_orig_node_free_ref(orig_node);
+
+ return NULL;
+}
+
static struct batadv_neigh_node *
batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
const uint8_t *neigh_addr,
struct batadv_orig_node *orig_node,
struct batadv_orig_node *orig_neigh)
{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct batadv_neigh_node *neigh_node;
- neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr);
+ neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node);
if (!neigh_node)
goto out;
- INIT_LIST_HEAD(&neigh_node->bonding_list);
+ spin_lock_init(&neigh_node->bat_iv.lq_update_lock);
- neigh_node->orig_node = orig_neigh;
- neigh_node->if_incoming = hard_iface;
+ batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+ "Creating new neighbor %pM for orig_node %pM on interface %s\n",
+ neigh_addr, orig_node->orig, hard_iface->net_dev->name);
spin_lock_bh(&orig_node->neigh_list_lock);
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
@@ -135,9 +311,8 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION;
batadv_ogm_packet->header.ttl = 2;
batadv_ogm_packet->flags = BATADV_NO_FLAGS;
+ batadv_ogm_packet->reserved = 0;
batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
- batadv_ogm_packet->tt_num_changes = 0;
- batadv_ogm_packet->ttvn = 0;
res = 0;
@@ -207,12 +382,12 @@ static uint8_t batadv_hop_penalty(uint8_t tq,
/* is there another aggregated packet here? */
static int batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len,
- int tt_num_changes)
+ __be16 tvlv_len)
{
int next_buff_pos = 0;
next_buff_pos += buff_pos + BATADV_OGM_HLEN;
- next_buff_pos += batadv_tt_len(tt_num_changes);
+ next_buff_pos += ntohs(tvlv_len);
return (next_buff_pos <= packet_len) &&
(next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES);
@@ -240,7 +415,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
/* adjust all flags and log packets */
while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len,
- batadv_ogm_packet->tt_num_changes)) {
+ batadv_ogm_packet->tvlv_len)) {
/* we might have aggregated direct link packets with an
* ordinary base packet
*/
@@ -256,18 +431,18 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
fwd_str = "Sending own";
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n",
+ "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s) on interface %s [%pM]\n",
fwd_str, (packet_num > 0 ? "aggregated " : ""),
batadv_ogm_packet->orig,
ntohl(batadv_ogm_packet->seqno),
batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl,
(batadv_ogm_packet->flags & BATADV_DIRECTLINK ?
"on" : "off"),
- batadv_ogm_packet->ttvn, hard_iface->net_dev->name,
+ hard_iface->net_dev->name,
hard_iface->net_dev->dev_addr);
buff_pos += BATADV_OGM_HLEN;
- buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
+ buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
packet_num++;
packet_pos = forw_packet->skb->data + buff_pos;
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
@@ -601,7 +776,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_incoming)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
- uint8_t tt_num_changes;
+ uint16_t tvlv_len;
if (batadv_ogm_packet->header.ttl <= 1) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
@@ -621,7 +796,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
return;
}
- tt_num_changes = batadv_ogm_packet->tt_num_changes;
+ tvlv_len = ntohs(batadv_ogm_packet->tvlv_len);
batadv_ogm_packet->header.ttl--;
memcpy(batadv_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
@@ -642,7 +817,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK;
batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet,
- BATADV_OGM_HLEN + batadv_tt_len(tt_num_changes),
+ BATADV_OGM_HLEN + tvlv_len,
if_incoming, 0, batadv_iv_ogm_fwd_send_time());
}
@@ -662,20 +837,22 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
uint32_t i;
size_t word_index;
uint8_t *w;
+ int if_num;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- spin_lock_bh(&orig_node->ogm_cnt_lock);
+ spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
word_index = hard_iface->if_num * BATADV_NUM_WORDS;
- word = &(orig_node->bcast_own[word_index]);
+ word = &(orig_node->bat_iv.bcast_own[word_index]);
batadv_bit_get_packet(bat_priv, word, 1, 0);
- w = &orig_node->bcast_own_sum[hard_iface->if_num];
+ if_num = hard_iface->if_num;
+ w = &orig_node->bat_iv.bcast_own_sum[if_num];
*w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE);
- spin_unlock_bh(&orig_node->ogm_cnt_lock);
+ spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
}
rcu_read_unlock();
}
@@ -688,43 +865,29 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
struct batadv_ogm_packet *batadv_ogm_packet;
struct batadv_hard_iface *primary_if;
int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
- int vis_server, tt_num_changes = 0;
uint32_t seqno;
- uint8_t bandwidth;
+ uint16_t tvlv_len = 0;
- vis_server = atomic_read(&bat_priv->vis_mode);
primary_if = batadv_primary_if_get_selected(bat_priv);
- if (hard_iface == primary_if)
- tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff,
- ogm_buff_len,
- BATADV_OGM_HLEN);
+ if (hard_iface == primary_if) {
+ /* tt changes have to be committed before the tvlv data is
+ * appended as it may alter the tt tvlv container
+ */
+ batadv_tt_local_commit_changes(bat_priv);
+ tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff,
+ ogm_buff_len,
+ BATADV_OGM_HLEN);
+ }
batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
+ batadv_ogm_packet->tvlv_len = htons(tvlv_len);
/* change sequence number to network order */
seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->seqno = htonl(seqno);
atomic_inc(&hard_iface->bat_iv.ogm_seqno);
- batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn);
- batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc);
- if (tt_num_changes >= 0)
- batadv_ogm_packet->tt_num_changes = tt_num_changes;
-
- if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC)
- batadv_ogm_packet->flags |= BATADV_VIS_SERVER;
- else
- batadv_ogm_packet->flags &= ~BATADV_VIS_SERVER;
-
- if (hard_iface == primary_if &&
- atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_SERVER) {
- bandwidth = (uint8_t)atomic_read(&bat_priv->gw_bandwidth);
- batadv_ogm_packet->gw_flags = bandwidth;
- } else {
- batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS;
- }
-
batadv_iv_ogm_slide_own_bcast_window(hard_iface);
batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
@@ -770,18 +933,18 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
if (dup_status != BATADV_NO_DUP)
continue;
- spin_lock_bh(&tmp_neigh_node->lq_update_lock);
- batadv_ring_buffer_set(tmp_neigh_node->tq_recv,
- &tmp_neigh_node->tq_index, 0);
- tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->tq_recv);
- tmp_neigh_node->tq_avg = tq_avg;
- spin_unlock_bh(&tmp_neigh_node->lq_update_lock);
+ spin_lock_bh(&tmp_neigh_node->bat_iv.lq_update_lock);
+ batadv_ring_buffer_set(tmp_neigh_node->bat_iv.tq_recv,
+ &tmp_neigh_node->bat_iv.tq_index, 0);
+ tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->bat_iv.tq_recv);
+ tmp_neigh_node->bat_iv.tq_avg = tq_avg;
+ spin_unlock_bh(&tmp_neigh_node->bat_iv.lq_update_lock);
}
if (!neigh_node) {
struct batadv_orig_node *orig_tmp;
- orig_tmp = batadv_get_orig_node(bat_priv, ethhdr->h_source);
+ orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source);
if (!orig_tmp)
goto unlock;
@@ -798,80 +961,55 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
rcu_read_unlock();
- orig_node->flags = batadv_ogm_packet->flags;
neigh_node->last_seen = jiffies;
- spin_lock_bh(&neigh_node->lq_update_lock);
- batadv_ring_buffer_set(neigh_node->tq_recv,
- &neigh_node->tq_index,
+ spin_lock_bh(&neigh_node->bat_iv.lq_update_lock);
+ batadv_ring_buffer_set(neigh_node->bat_iv.tq_recv,
+ &neigh_node->bat_iv.tq_index,
batadv_ogm_packet->tq);
- neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv);
- spin_unlock_bh(&neigh_node->lq_update_lock);
+ tq_avg = batadv_ring_buffer_avg(neigh_node->bat_iv.tq_recv);
+ neigh_node->bat_iv.tq_avg = tq_avg;
+ spin_unlock_bh(&neigh_node->bat_iv.lq_update_lock);
if (dup_status == BATADV_NO_DUP) {
orig_node->last_ttl = batadv_ogm_packet->header.ttl;
neigh_node->last_ttl = batadv_ogm_packet->header.ttl;
}
- batadv_bonding_candidate_add(orig_node, neigh_node);
+ batadv_bonding_candidate_add(bat_priv, orig_node, neigh_node);
/* if this neighbor already is our next hop there is nothing
* to change
*/
router = batadv_orig_node_get_router(orig_node);
if (router == neigh_node)
- goto update_tt;
+ goto out;
/* if this neighbor does not offer a better TQ we won't consider it */
- if (router && (router->tq_avg > neigh_node->tq_avg))
- goto update_tt;
+ if (router && (router->bat_iv.tq_avg > neigh_node->bat_iv.tq_avg))
+ goto out;
/* if the TQ is the same and the link not more symmetric we
* won't consider it either
*/
- if (router && (neigh_node->tq_avg == router->tq_avg)) {
+ if (router && (neigh_node->bat_iv.tq_avg == router->bat_iv.tq_avg)) {
orig_node_tmp = router->orig_node;
- spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
+ spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
if_num = router->if_incoming->if_num;
- sum_orig = orig_node_tmp->bcast_own_sum[if_num];
- spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
+ sum_orig = orig_node_tmp->bat_iv.bcast_own_sum[if_num];
+ spin_unlock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
orig_node_tmp = neigh_node->orig_node;
- spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
+ spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
if_num = neigh_node->if_incoming->if_num;
- sum_neigh = orig_node_tmp->bcast_own_sum[if_num];
- spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
+ sum_neigh = orig_node_tmp->bat_iv.bcast_own_sum[if_num];
+ spin_unlock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock);
if (sum_orig >= sum_neigh)
- goto update_tt;
+ goto out;
}
batadv_update_route(bat_priv, orig_node, neigh_node);
-
-update_tt:
- /* I have to check for transtable changes only if the OGM has been
- * sent through a primary interface
- */
- if (((batadv_ogm_packet->orig != ethhdr->h_source) &&
- (batadv_ogm_packet->header.ttl > 2)) ||
- (batadv_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP))
- batadv_tt_update_orig(bat_priv, orig_node, tt_buff,
- batadv_ogm_packet->tt_num_changes,
- batadv_ogm_packet->ttvn,
- ntohs(batadv_ogm_packet->tt_crc));
-
- if (orig_node->gw_flags != batadv_ogm_packet->gw_flags)
- batadv_gw_node_update(bat_priv, orig_node,
- batadv_ogm_packet->gw_flags);
-
- orig_node->gw_flags = batadv_ogm_packet->gw_flags;
-
- /* restart gateway selection if fast or late switching was enabled */
- if ((orig_node->gw_flags) &&
- (atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) &&
- (atomic_read(&bat_priv->gw_sel_class) > 2))
- batadv_gw_check_election(bat_priv, orig_node);
-
goto out;
unlock:
@@ -893,7 +1031,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
uint8_t total_count;
uint8_t orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own;
unsigned int neigh_rq_inv_cube, neigh_rq_max_cube;
- int tq_asym_penalty, inv_asym_penalty, ret = 0;
+ int tq_asym_penalty, inv_asym_penalty, if_num, ret = 0;
unsigned int combined_tq;
/* find corresponding one hop neighbor */
@@ -931,10 +1069,11 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node,
orig_node->last_seen = jiffies;
/* find packet count of corresponding one hop neighbor */
- spin_lock_bh(&orig_node->ogm_cnt_lock);
- orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num];
- neigh_rq_count = neigh_node->real_packet_count;
- spin_unlock_bh(&orig_node->ogm_cnt_lock);
+ spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
+ if_num = if_incoming->if_num;
+ orig_eq_count = orig_neigh_node->bat_iv.bcast_own_sum[if_num];
+ neigh_rq_count = neigh_node->bat_iv.real_packet_count;
+ spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
/* pay attention to not get a value bigger than 100 % */
if (orig_eq_count > neigh_rq_count)
@@ -1016,12 +1155,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
uint32_t seqno = ntohl(batadv_ogm_packet->seqno);
uint8_t *neigh_addr;
uint8_t packet_count;
+ unsigned long *bitmap;
- orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
+ orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig);
if (!orig_node)
return BATADV_NO_DUP;
- spin_lock_bh(&orig_node->ogm_cnt_lock);
+ spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
seq_diff = seqno - orig_node->last_real_seqno;
/* signalize caller that the packet is to be dropped. */
@@ -1036,7 +1176,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
hlist_for_each_entry_rcu(tmp_neigh_node,
&orig_node->neigh_list, list) {
neigh_addr = tmp_neigh_node->addr;
- is_dup = batadv_test_bit(tmp_neigh_node->real_bits,
+ is_dup = batadv_test_bit(tmp_neigh_node->bat_iv.real_bits,
orig_node->last_real_seqno,
seqno);
@@ -1052,13 +1192,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
}
/* if the window moved, set the update flag. */
- need_update |= batadv_bit_get_packet(bat_priv,
- tmp_neigh_node->real_bits,
+ bitmap = tmp_neigh_node->bat_iv.real_bits;
+ need_update |= batadv_bit_get_packet(bat_priv, bitmap,
seq_diff, set_mark);
- packet_count = bitmap_weight(tmp_neigh_node->real_bits,
+ packet_count = bitmap_weight(tmp_neigh_node->bat_iv.real_bits,
BATADV_TQ_LOCAL_WINDOW_SIZE);
- tmp_neigh_node->real_packet_count = packet_count;
+ tmp_neigh_node->bat_iv.real_packet_count = packet_count;
}
rcu_read_unlock();
@@ -1070,7 +1210,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
}
out:
- spin_unlock_bh(&orig_node->ogm_cnt_lock);
+ spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock);
batadv_orig_node_free_ref(orig_node);
return ret;
}
@@ -1082,7 +1222,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct batadv_hard_iface *hard_iface;
- struct batadv_orig_node *orig_neigh_node, *orig_node;
+ struct batadv_orig_node *orig_neigh_node, *orig_node, *orig_node_tmp;
struct batadv_neigh_node *router = NULL, *router_router = NULL;
struct batadv_neigh_node *orig_neigh_router = NULL;
int has_directlink_flag;
@@ -1122,13 +1262,11 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
is_single_hop_neigh = true;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, tq %d, TTL %d, V %d, IDF %d)\n",
+ "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, tq %d, TTL %d, V %d, IDF %d)\n",
ethhdr->h_source, if_incoming->net_dev->name,
if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig,
batadv_ogm_packet->prev_sender,
- ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->ttvn,
- ntohs(batadv_ogm_packet->tt_crc),
- batadv_ogm_packet->tt_num_changes, batadv_ogm_packet->tq,
+ ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->tq,
batadv_ogm_packet->header.ttl,
batadv_ogm_packet->header.version, has_directlink_flag);
@@ -1168,8 +1306,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
int16_t if_num;
uint8_t *weight;
- orig_neigh_node = batadv_get_orig_node(bat_priv,
- ethhdr->h_source);
+ orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
+ ethhdr->h_source);
if (!orig_neigh_node)
return;
@@ -1183,15 +1321,15 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
if_num = if_incoming->if_num;
offset = if_num * BATADV_NUM_WORDS;
- spin_lock_bh(&orig_neigh_node->ogm_cnt_lock);
- word = &(orig_neigh_node->bcast_own[offset]);
+ spin_lock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock);
+ word = &(orig_neigh_node->bat_iv.bcast_own[offset]);
bit_pos = if_incoming_seqno - 2;
bit_pos -= ntohl(batadv_ogm_packet->seqno);
batadv_set_bit(word, bit_pos);
- weight = &orig_neigh_node->bcast_own_sum[if_num];
+ weight = &orig_neigh_node->bat_iv.bcast_own_sum[if_num];
*weight = bitmap_weight(word,
BATADV_TQ_LOCAL_WINDOW_SIZE);
- spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock);
+ spin_unlock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock);
}
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
@@ -1214,7 +1352,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
return;
}
- orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
+ orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig);
if (!orig_node)
return;
@@ -1235,10 +1373,12 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
}
router = batadv_orig_node_get_router(orig_node);
- if (router)
- router_router = batadv_orig_node_get_router(router->orig_node);
+ if (router) {
+ orig_node_tmp = router->orig_node;
+ router_router = batadv_orig_node_get_router(orig_node_tmp);
+ }
- if ((router && router->tq_avg != 0) &&
+ if ((router && router->bat_iv.tq_avg != 0) &&
(batadv_compare_eth(router->addr, ethhdr->h_source)))
is_from_best_next_hop = true;
@@ -1254,14 +1394,16 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
goto out;
}
+ batadv_tvlv_ogm_receive(bat_priv, batadv_ogm_packet, orig_node);
+
/* if sender is a direct neighbor the sender mac equals
* originator mac
*/
if (is_single_hop_neigh)
orig_neigh_node = orig_node;
else
- orig_neigh_node = batadv_get_orig_node(bat_priv,
- ethhdr->h_source);
+ orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv,
+ ethhdr->h_source);
if (!orig_neigh_node)
goto out;
@@ -1350,9 +1492,9 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
struct batadv_ogm_packet *batadv_ogm_packet;
struct ethhdr *ethhdr;
int buff_pos = 0, packet_len;
- unsigned char *tt_buff, *packet_buff;
- bool ret;
+ unsigned char *tvlv_buff, *packet_buff;
uint8_t *packet_pos;
+ bool ret;
ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN);
if (!ret)
@@ -1375,14 +1517,14 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
/* unpack the aggregated packets and process them one by one */
while (batadv_iv_ogm_aggr_packet(buff_pos, packet_len,
- batadv_ogm_packet->tt_num_changes)) {
- tt_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
+ batadv_ogm_packet->tvlv_len)) {
+ tvlv_buff = packet_buff + buff_pos + BATADV_OGM_HLEN;
- batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, tt_buff,
- if_incoming);
+ batadv_iv_ogm_process(ethhdr, batadv_ogm_packet,
+ tvlv_buff, if_incoming);
buff_pos += BATADV_OGM_HLEN;
- buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes);
+ buff_pos += ntohs(batadv_ogm_packet->tvlv_len);
packet_pos = packet_buff + buff_pos;
batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
@@ -1392,6 +1534,106 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
return NET_RX_SUCCESS;
}
+/**
+ * batadv_iv_ogm_orig_print - print the originator table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @seq: debugfs table seq_file struct
+ */
+static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv,
+ struct seq_file *seq)
+{
+ struct batadv_neigh_node *neigh_node, *neigh_node_tmp;
+ struct batadv_hashtable *hash = bat_priv->orig_hash;
+ int last_seen_msecs, last_seen_secs;
+ struct batadv_orig_node *orig_node;
+ unsigned long last_seen_jiffies;
+ struct hlist_head *head;
+ int batman_count = 0;
+ uint32_t i;
+
+ seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
+ "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE,
+ "Nexthop", "outgoingIF", "Potential nexthops");
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
+ neigh_node = batadv_orig_node_get_router(orig_node);
+ if (!neigh_node)
+ continue;
+
+ if (neigh_node->bat_iv.tq_avg == 0)
+ goto next;
+
+ last_seen_jiffies = jiffies - orig_node->last_seen;
+ last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
+ last_seen_secs = last_seen_msecs / 1000;
+ last_seen_msecs = last_seen_msecs % 1000;
+
+ seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:",
+ orig_node->orig, last_seen_secs,
+ last_seen_msecs, neigh_node->bat_iv.tq_avg,
+ neigh_node->addr,
+ neigh_node->if_incoming->net_dev->name);
+
+ hlist_for_each_entry_rcu(neigh_node_tmp,
+ &orig_node->neigh_list, list) {
+ seq_printf(seq, " %pM (%3i)",
+ neigh_node_tmp->addr,
+ neigh_node_tmp->bat_iv.tq_avg);
+ }
+
+ seq_puts(seq, "\n");
+ batman_count++;
+
+next:
+ batadv_neigh_node_free_ref(neigh_node);
+ }
+ rcu_read_unlock();
+ }
+
+ if (batman_count == 0)
+ seq_puts(seq, "No batman nodes in range ...\n");
+}
+
+/**
+ * batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors
+ * @neigh1: the first neighbor object of the comparison
+ * @neigh2: the second neighbor object of the comparison
+ *
+ * Returns a value less, equal to or greater than 0 if the metric via neigh1 is
+ * lower, the same as or higher than the metric via neigh2
+ */
+static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
+ struct batadv_neigh_node *neigh2)
+{
+ uint8_t tq1, tq2;
+
+ tq1 = neigh1->bat_iv.tq_avg;
+ tq2 = neigh2->bat_iv.tq_avg;
+
+ return tq1 - tq2;
+}
+
+/**
+ * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than
+ * neigh2 from the metric prospective
+ * @neigh1: the first neighbor object of the comparison
+ * @neigh2: the second neighbor object of the comparison
+ *
+ * Returns true if the metric via neigh1 is equally good or better than the
+ * metric via neigh2, false otherwise.
+ */
+static bool batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1,
+ struct batadv_neigh_node *neigh2)
+{
+ int diff = batadv_iv_ogm_neigh_cmp(neigh1, neigh2);
+
+ return diff > -BATADV_TQ_SIMILARITY_THRESHOLD;
+}
+
static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.name = "BATMAN_IV",
.bat_iface_enable = batadv_iv_ogm_iface_enable,
@@ -1400,6 +1642,12 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.bat_primary_iface_set = batadv_iv_ogm_primary_iface_set,
.bat_ogm_schedule = batadv_iv_ogm_schedule,
.bat_ogm_emit = batadv_iv_ogm_emit,
+ .bat_neigh_cmp = batadv_iv_ogm_neigh_cmp,
+ .bat_neigh_is_equiv_or_better = batadv_iv_ogm_neigh_is_eob,
+ .bat_orig_print = batadv_iv_ogm_orig_print,
+ .bat_orig_free = batadv_iv_ogm_orig_free,
+ .bat_orig_add_if = batadv_iv_ogm_orig_add_if,
+ .bat_orig_del_if = batadv_iv_ogm_orig_del_if,
};
int __init batadv_iv_init(void)
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 264de88db320..28eb5e6d0a02 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -411,10 +411,10 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
return NULL;
}
- /* this is a gateway now, remove any tt entries */
+ /* this is a gateway now, remove any TT entry on this VLAN */
orig_node = batadv_orig_hash_find(bat_priv, orig);
if (orig_node) {
- batadv_tt_global_del_orig(bat_priv, orig_node,
+ batadv_tt_global_del_orig(bat_priv, orig_node, vid,
"became a backbone gateway");
batadv_orig_node_free_ref(orig_node);
}
@@ -858,30 +858,28 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
struct sk_buff *skb)
{
- struct ethhdr *ethhdr;
+ struct batadv_bla_claim_dst *bla_dst;
+ uint8_t *hw_src, *hw_dst;
struct vlan_ethhdr *vhdr;
+ struct ethhdr *ethhdr;
struct arphdr *arphdr;
- uint8_t *hw_src, *hw_dst;
- struct batadv_bla_claim_dst *bla_dst;
- uint16_t proto;
+ unsigned short vid;
+ __be16 proto;
int headlen;
- unsigned short vid = BATADV_NO_FLAGS;
int ret;
+ vid = batadv_get_vid(skb, 0);
ethhdr = eth_hdr(skb);
- if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+ proto = ethhdr->h_proto;
+ headlen = ETH_HLEN;
+ if (vid & BATADV_VLAN_HAS_TAG) {
vhdr = (struct vlan_ethhdr *)ethhdr;
- vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
- vid |= BATADV_VLAN_HAS_TAG;
- proto = ntohs(vhdr->h_vlan_encapsulated_proto);
- headlen = sizeof(*vhdr);
- } else {
- proto = ntohs(ethhdr->h_proto);
- headlen = ETH_HLEN;
+ proto = vhdr->h_vlan_encapsulated_proto;
+ headlen += VLAN_HLEN;
}
- if (proto != ETH_P_ARP)
+ if (proto != htons(ETH_P_ARP))
return 0; /* not a claim frame */
/* this must be a ARP frame. check if it is a claim. */
@@ -1317,12 +1315,14 @@ out:
/* @bat_priv: the bat priv with all the soft interface information
* @orig: originator mac address
+ * @vid: VLAN identifier
*
- * check if the originator is a gateway for any VLAN ID.
+ * Check if the originator is a gateway for the VLAN identified by vid.
*
- * returns 1 if it is found, 0 otherwise
+ * Returns true if orig is a backbone for this vid, false otherwise.
*/
-int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig)
+bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig,
+ unsigned short vid)
{
struct batadv_hashtable *hash = bat_priv->bla.backbone_hash;
struct hlist_head *head;
@@ -1330,25 +1330,26 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig)
int i;
if (!atomic_read(&bat_priv->bridge_loop_avoidance))
- return 0;
+ return false;
if (!hash)
- return 0;
+ return false;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) {
- if (batadv_compare_eth(backbone_gw->orig, orig)) {
+ if (batadv_compare_eth(backbone_gw->orig, orig) &&
+ backbone_gw->vid == vid) {
rcu_read_unlock();
- return 1;
+ return true;
}
}
rcu_read_unlock();
}
- return 0;
+ return false;
}
@@ -1365,10 +1366,8 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig)
int batadv_bla_is_backbone_gw(struct sk_buff *skb,
struct batadv_orig_node *orig_node, int hdr_size)
{
- struct ethhdr *ethhdr;
- struct vlan_ethhdr *vhdr;
struct batadv_bla_backbone_gw *backbone_gw;
- unsigned short vid = BATADV_NO_FLAGS;
+ unsigned short vid;
if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance))
return 0;
@@ -1377,16 +1376,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
if (!pskb_may_pull(skb, hdr_size + ETH_HLEN))
return 0;
- ethhdr = (struct ethhdr *)(((uint8_t *)skb->data) + hdr_size);
-
- if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
- if (!pskb_may_pull(skb, hdr_size + sizeof(struct vlan_ethhdr)))
- return 0;
-
- vhdr = (struct vlan_ethhdr *)(skb->data + hdr_size);
- vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
- vid |= BATADV_VLAN_HAS_TAG;
- }
+ vid = batadv_get_vid(skb, hdr_size);
/* see if this originator is a backbone gw for this VLAN */
backbone_gw = batadv_backbone_hash_find(orig_node->bat_priv,
diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h
index 4b102e71e5bd..da173e760e77 100644
--- a/net/batman-adv/bridge_loop_avoidance.h
+++ b/net/batman-adv/bridge_loop_avoidance.h
@@ -30,7 +30,8 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb,
int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset);
int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
void *offset);
-int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig);
+bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig,
+ unsigned short vid);
int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
struct sk_buff *skb);
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
@@ -74,10 +75,11 @@ static inline int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
return 0;
}
-static inline int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
- uint8_t *orig)
+static inline bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
+ uint8_t *orig,
+ unsigned short vid)
{
- return 0;
+ return false;
}
static inline int
diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index f186a55b23c3..049a7a2ac5b6 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -28,7 +28,6 @@
#include "gateway_common.h"
#include "gateway_client.h"
#include "soft-interface.h"
-#include "vis.h"
#include "icmp_socket.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
@@ -300,12 +299,6 @@ static int batadv_transtable_local_open(struct inode *inode, struct file *file)
return single_open(file, batadv_tt_local_seq_print_text, net_dev);
}
-static int batadv_vis_data_open(struct inode *inode, struct file *file)
-{
- struct net_device *net_dev = (struct net_device *)inode->i_private;
- return single_open(file, batadv_vis_seq_print_text, net_dev);
-}
-
struct batadv_debuginfo {
struct attribute attr;
const struct file_operations fops;
@@ -356,7 +349,6 @@ static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open);
#endif
static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
batadv_transtable_local_open);
-static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open);
#ifdef CONFIG_BATMAN_ADV_NC
static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
#endif
@@ -373,7 +365,6 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
&batadv_debuginfo_dat_cache,
#endif
&batadv_debuginfo_transtable_local,
- &batadv_debuginfo_vis_data,
#ifdef CONFIG_BATMAN_ADV_NC
&batadv_debuginfo_nc_nodes,
#endif
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index 06345d401588..6c8c3934bd7b 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -19,6 +19,7 @@
#include <linux/if_ether.h>
#include <linux/if_arp.h>
+#include <linux/if_vlan.h>
#include <net/arp.h>
#include "main.h"
@@ -29,7 +30,6 @@
#include "send.h"
#include "types.h"
#include "translation-table.h"
-#include "unicast.h"
static void batadv_dat_purge(struct work_struct *work);
@@ -206,15 +206,11 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
*/
static uint32_t batadv_hash_dat(const void *data, uint32_t size)
{
- const unsigned char *key = data;
uint32_t hash = 0;
- size_t i;
+ const struct batadv_dat_entry *dat = data;
- for (i = 0; i < 4; i++) {
- hash += key[i];
- hash += (hash << 10);
- hash ^= (hash >> 6);
- }
+ hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip));
+ hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid));
hash += (hash << 3);
hash ^= (hash >> 11);
@@ -228,21 +224,26 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)
* table
* @bat_priv: the bat priv with all the soft interface information
* @ip: search key
+ * @vid: VLAN identifier
*
* Returns the dat_entry if found, NULL otherwise.
*/
static struct batadv_dat_entry *
-batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
+batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip,
+ unsigned short vid)
{
struct hlist_head *head;
- struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL;
+ struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL;
struct batadv_hashtable *hash = bat_priv->dat.hash;
uint32_t index;
if (!hash)
return NULL;
- index = batadv_hash_dat(&ip, hash->size);
+ to_find.ip = ip;
+ to_find.vid = vid;
+
+ index = batadv_hash_dat(&to_find, hash->size);
head = &hash->table[index];
rcu_read_lock();
@@ -266,22 +267,24 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
* @bat_priv: the bat priv with all the soft interface information
* @ip: ipv4 to add/edit
* @mac_addr: mac address to assign to the given ipv4
+ * @vid: VLAN identifier
*/
static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
- uint8_t *mac_addr)
+ uint8_t *mac_addr, unsigned short vid)
{
struct batadv_dat_entry *dat_entry;
int hash_added;
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);
/* if this entry is already known, just update it */
if (dat_entry) {
if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
dat_entry->last_update = jiffies;
batadv_dbg(BATADV_DBG_DAT, bat_priv,
- "Entry updated: %pI4 %pM\n", &dat_entry->ip,
- dat_entry->mac_addr);
+ "Entry updated: %pI4 %pM (vid: %d)\n",
+ &dat_entry->ip, dat_entry->mac_addr,
+ BATADV_PRINT_VID(vid));
goto out;
}
@@ -290,12 +293,13 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
goto out;
dat_entry->ip = ip;
+ dat_entry->vid = vid;
memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
dat_entry->last_update = jiffies;
atomic_set(&dat_entry->refcount, 2);
hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
- batadv_hash_dat, &dat_entry->ip,
+ batadv_hash_dat, dat_entry,
&dat_entry->hash_entry);
if (unlikely(hash_added != 0)) {
@@ -304,8 +308,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
goto out;
}
- batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n",
- &dat_entry->ip, dat_entry->mac_addr);
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n",
+ &dat_entry->ip, dat_entry->mac_addr, BATADV_PRINT_VID(vid));
out:
if (dat_entry)
@@ -419,6 +423,10 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
bool ret = false;
int j;
+ /* check if orig node candidate is running DAT */
+ if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT))
+ goto out;
+
/* Check if this node has already been selected... */
for (j = 0; j < select; j++)
if (res[j].orig_node == candidate)
@@ -588,9 +596,9 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
goto free_orig;
tmp_skb = pskb_copy(skb, GFP_ATOMIC);
- if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb,
- cand[i].orig_node,
- packet_subtype)) {
+ if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb,
+ cand[i].orig_node,
+ packet_subtype)) {
kfree_skb(tmp_skb);
goto free_neigh;
}
@@ -626,6 +634,59 @@ out:
}
/**
+ * batadv_dat_tvlv_container_update - update the dat tvlv container after dat
+ * setting change
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv)
+{
+ char dat_mode;
+
+ dat_mode = atomic_read(&bat_priv->distributed_arp_table);
+
+ switch (dat_mode) {
+ case 0:
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
+ break;
+ case 1:
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_DAT, 1,
+ NULL, 0);
+ break;
+ }
+}
+
+/**
+ * batadv_dat_status_update - update the dat tvlv container after dat
+ * setting change
+ * @net_dev: the soft interface net device
+ */
+void batadv_dat_status_update(struct net_device *net_dev)
+{
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ batadv_dat_tvlv_container_update(bat_priv);
+}
+
+/**
+ * batadv_gw_tvlv_ogm_handler_v1 - process incoming dat tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
+ orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT;
+ else
+ orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT;
+}
+
+/**
* batadv_dat_hash_free - free the local DAT hash table
* @bat_priv: the bat priv with all the soft interface information
*/
@@ -657,6 +718,10 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
batadv_dat_start_timer(bat_priv);
+ batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1,
+ NULL, BATADV_TVLV_DAT, 1,
+ BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ batadv_dat_tvlv_container_update(bat_priv);
return 0;
}
@@ -666,6 +731,9 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
*/
void batadv_dat_free(struct batadv_priv *bat_priv)
{
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1);
+ batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1);
+
cancel_delayed_work_sync(&bat_priv->dat.work);
batadv_dat_hash_free(bat_priv);
@@ -693,8 +761,8 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
goto out;
seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name);
- seq_printf(seq, " %-7s %-13s %5s\n", "IPv4", "MAC",
- "last-seen");
+ seq_printf(seq, " %-7s %-9s %4s %11s\n", "IPv4",
+ "MAC", "VID", "last-seen");
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
@@ -707,8 +775,9 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
last_seen_msecs = last_seen_msecs % 60000;
last_seen_secs = last_seen_msecs / 1000;
- seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n",
+ seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n",
&dat_entry->ip, dat_entry->mac_addr,
+ BATADV_PRINT_VID(dat_entry->vid),
last_seen_mins, last_seen_secs);
}
rcu_read_unlock();
@@ -795,6 +864,31 @@ out:
}
/**
+ * batadv_dat_get_vid - extract the VLAN identifier from skb if any
+ * @skb: the buffer containing the packet to extract the VID from
+ * @hdr_size: the size of the batman-adv header encapsulating the packet
+ *
+ * If the packet embedded in the skb is vlan tagged this function returns the
+ * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
+ */
+static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size)
+{
+ unsigned short vid;
+
+ vid = batadv_get_vid(skb, *hdr_size);
+
+ /* ARP parsing functions jump forward of hdr_size + ETH_HLEN.
+ * If the header contained in the packet is a VLAN one (which is longer)
+ * hdr_size is updated so that the functions will still skip the
+ * correct amount of bytes.
+ */
+ if (vid & BATADV_VLAN_HAS_TAG)
+ *hdr_size += VLAN_HLEN;
+
+ return vid;
+}
+
+/**
* batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to
* answer using DAT
* @bat_priv: the bat priv with all the soft interface information
@@ -813,26 +907,31 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
bool ret = false;
struct batadv_dat_entry *dat_entry = NULL;
struct sk_buff *skb_new;
+ int hdr_size = 0;
+ unsigned short vid;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
- type = batadv_arp_get_type(bat_priv, skb, 0);
+ vid = batadv_dat_get_vid(skb, &hdr_size);
+
+ type = batadv_arp_get_type(bat_priv, skb, hdr_size);
/* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast
* message to the selected DHT candidates
*/
if (type != ARPOP_REQUEST)
goto out;
- batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST");
+ batadv_dbg_arp(bat_priv, skb, type, hdr_size,
+ "Parsing outgoing ARP REQUEST");
- ip_src = batadv_arp_ip_src(skb, 0);
- hw_src = batadv_arp_hw_src(skb, 0);
- ip_dst = batadv_arp_ip_dst(skb, 0);
+ ip_src = batadv_arp_ip_src(skb, hdr_size);
+ hw_src = batadv_arp_hw_src(skb, hdr_size);
+ ip_dst = batadv_arp_ip_dst(skb, hdr_size);
- batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
if (dat_entry) {
/* If the ARP request is destined for a local client the local
* client will answer itself. DAT would only generate a
@@ -842,7 +941,8 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
* additional DAT answer may trigger kernel warnings about
* a packet coming from the wrong port.
*/
- if (batadv_is_my_client(bat_priv, dat_entry->mac_addr)) {
+ if (batadv_is_my_client(bat_priv, dat_entry->mac_addr,
+ BATADV_NO_FLAGS)) {
ret = true;
goto out;
}
@@ -853,11 +953,15 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
if (!skb_new)
goto out;
+ if (vid & BATADV_VLAN_HAS_TAG)
+ skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
+ vid & VLAN_VID_MASK);
+
skb_reset_mac_header(skb_new);
skb_new->protocol = eth_type_trans(skb_new,
bat_priv->soft_iface);
bat_priv->stats.rx_packets++;
- bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
+ bat_priv->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size;
bat_priv->soft_iface->last_rx = jiffies;
netif_rx(skb_new);
@@ -892,11 +996,14 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb_new;
struct batadv_dat_entry *dat_entry = NULL;
bool ret = false;
+ unsigned short vid;
int err;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
+ vid = batadv_dat_get_vid(skb, &hdr_size);
+
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
if (type != ARPOP_REQUEST)
goto out;
@@ -908,9 +1015,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
batadv_dbg_arp(bat_priv, skb, type, hdr_size,
"Parsing incoming ARP REQUEST");
- batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
if (!dat_entry)
goto out;
@@ -921,17 +1028,22 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
if (!skb_new)
goto out;
+ if (vid & BATADV_VLAN_HAS_TAG)
+ skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
+ vid & VLAN_VID_MASK);
+
/* To preserve backwards compatibility, the node has choose the outgoing
* format based on the incoming request packet type. The assumption is
* that a node not using the 4addr packet format doesn't support it.
*/
if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
- err = batadv_unicast_4addr_send_skb(bat_priv, skb_new,
- BATADV_P_DAT_CACHE_REPLY);
+ err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new,
+ BATADV_P_DAT_CACHE_REPLY,
+ vid);
else
- err = batadv_unicast_send_skb(bat_priv, skb_new);
+ err = batadv_send_skb_via_tt(bat_priv, skb_new, vid);
- if (!err) {
+ if (err != NET_XMIT_DROP) {
batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);
ret = true;
}
@@ -954,23 +1066,28 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
uint16_t type;
__be32 ip_src, ip_dst;
uint8_t *hw_src, *hw_dst;
+ int hdr_size = 0;
+ unsigned short vid;
if (!atomic_read(&bat_priv->distributed_arp_table))
return;
- type = batadv_arp_get_type(bat_priv, skb, 0);
+ vid = batadv_dat_get_vid(skb, &hdr_size);
+
+ type = batadv_arp_get_type(bat_priv, skb, hdr_size);
if (type != ARPOP_REPLY)
return;
- batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY");
+ batadv_dbg_arp(bat_priv, skb, type, hdr_size,
+ "Parsing outgoing ARP REPLY");
- hw_src = batadv_arp_hw_src(skb, 0);
- ip_src = batadv_arp_ip_src(skb, 0);
- hw_dst = batadv_arp_hw_dst(skb, 0);
- ip_dst = batadv_arp_ip_dst(skb, 0);
+ hw_src = batadv_arp_hw_src(skb, hdr_size);
+ ip_src = batadv_arp_ip_src(skb, hdr_size);
+ hw_dst = batadv_arp_hw_dst(skb, hdr_size);
+ ip_dst = batadv_arp_ip_dst(skb, hdr_size);
- batadv_dat_entry_add(bat_priv, ip_src, hw_src);
- batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+ batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
/* Send the ARP reply to the candidates for both the IP addresses that
* the node obtained from the ARP reply
@@ -992,10 +1109,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
__be32 ip_src, ip_dst;
uint8_t *hw_src, *hw_dst;
bool ret = false;
+ unsigned short vid;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
+ vid = batadv_dat_get_vid(skb, &hdr_size);
+
type = batadv_arp_get_type(bat_priv, skb, hdr_size);
if (type != ARPOP_REPLY)
goto out;
@@ -1011,13 +1131,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
/* Update our internal cache with both the IP addresses the node got
* within the ARP reply
*/
- batadv_dat_entry_add(bat_priv, ip_src, hw_src);
- batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid);
+ batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);
/* if this REPLY is directed to a client of mine, let's deliver the
* packet to the interface
*/
- ret = !batadv_is_my_client(bat_priv, hw_dst);
+ ret = !batadv_is_my_client(bat_priv, hw_dst, vid);
out:
if (ret)
kfree_skb(skb);
@@ -1040,7 +1160,8 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
__be32 ip_dst;
struct batadv_dat_entry *dat_entry = NULL;
bool ret = false;
- const size_t bcast_len = sizeof(struct batadv_bcast_packet);
+ int hdr_size = sizeof(struct batadv_bcast_packet);
+ unsigned short vid;
if (!atomic_read(&bat_priv->distributed_arp_table))
goto out;
@@ -1051,12 +1172,14 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
if (forw_packet->num_packets)
goto out;
- type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len);
+ vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size);
+
+ type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size);
if (type != ARPOP_REQUEST)
goto out;
- ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len);
- dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+ ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);
/* check if the node already got this entry */
if (!dat_entry) {
batadv_dbg(BATADV_DBG_DAT, bat_priv,
diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h
index 125c8c6fcfad..60d853beb8d8 100644
--- a/net/batman-adv/distributed-arp-table.h
+++ b/net/batman-adv/distributed-arp-table.h
@@ -29,6 +29,7 @@
#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
+void batadv_dat_status_update(struct net_device *net_dev);
bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb);
bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
@@ -98,6 +99,10 @@ static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
#else
+static inline void batadv_dat_status_update(struct net_device *net_dev)
+{
+}
+
static inline bool
batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
struct sk_buff *skb)
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
new file mode 100644
index 000000000000..271d321b3a04
--- /dev/null
+++ b/net/batman-adv/fragmentation.c
@@ -0,0 +1,491 @@
+/* Copyright (C) 2013 B.A.T.M.A.N. contributors:
+ *
+ * Martin Hundebøll <martin@hundeboll.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "main.h"
+#include "fragmentation.h"
+#include "send.h"
+#include "originator.h"
+#include "routing.h"
+#include "hard-interface.h"
+#include "soft-interface.h"
+
+
+/**
+ * batadv_frag_clear_chain - delete entries in the fragment buffer chain
+ * @head: head of chain with entries.
+ *
+ * Free fragments in the passed hlist. Should be called with appropriate lock.
+ */
+static void batadv_frag_clear_chain(struct hlist_head *head)
+{
+ struct batadv_frag_list_entry *entry;
+ struct hlist_node *node;
+
+ hlist_for_each_entry_safe(entry, node, head, list) {
+ hlist_del(&entry->list);
+ kfree_skb(entry->skb);
+ kfree(entry);
+ }
+}
+
+/**
+ * batadv_frag_purge_orig - free fragments associated to an orig
+ * @orig_node: originator to free fragments from
+ * @check_cb: optional function to tell if an entry should be purged
+ */
+void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
+ bool (*check_cb)(struct batadv_frag_table_entry *))
+{
+ struct batadv_frag_table_entry *chain;
+ uint8_t i;
+
+ for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
+ chain = &orig_node->fragments[i];
+ spin_lock_bh(&orig_node->fragments[i].lock);
+
+ if (!check_cb || check_cb(chain)) {
+ batadv_frag_clear_chain(&orig_node->fragments[i].head);
+ orig_node->fragments[i].size = 0;
+ }
+
+ spin_unlock_bh(&orig_node->fragments[i].lock);
+ }
+}
+
+/**
+ * batadv_frag_size_limit - maximum possible size of packet to be fragmented
+ *
+ * Returns the maximum size of payload that can be fragmented.
+ */
+static int batadv_frag_size_limit(void)
+{
+ int limit = BATADV_FRAG_MAX_FRAG_SIZE;
+
+ limit -= sizeof(struct batadv_frag_packet);
+ limit *= BATADV_FRAG_MAX_FRAGMENTS;
+
+ return limit;
+}
+
+/**
+ * batadv_frag_init_chain - check and prepare fragment chain for new fragment
+ * @chain: chain in fragments table to init
+ * @seqno: sequence number of the received fragment
+ *
+ * Make chain ready for a fragment with sequence number "seqno". Delete existing
+ * entries if they have an "old" sequence number.
+ *
+ * Caller must hold chain->lock.
+ *
+ * Returns true if chain is empty and caller can just insert the new fragment
+ * without searching for the right position.
+ */
+static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
+ uint16_t seqno)
+{
+ if (chain->seqno == seqno)
+ return false;
+
+ if (!hlist_empty(&chain->head))
+ batadv_frag_clear_chain(&chain->head);
+
+ chain->size = 0;
+ chain->seqno = seqno;
+
+ return true;
+}
+
+/**
+ * batadv_frag_insert_packet - insert a fragment into a fragment chain
+ * @orig_node: originator that the fragment was received from
+ * @skb: skb to insert
+ * @chain_out: list head to attach complete chains of fragments to
+ *
+ * Insert a new fragment into the reverse ordered chain in the right table
+ * entry. The hash table entry is cleared if "old" fragments exist in it.
+ *
+ * Returns true if skb is buffered, false on error. If the chain has all the
+ * fragments needed to merge the packet, the chain is moved to the passed head
+ * to avoid locking the chain in the table.
+ */
+static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
+ struct sk_buff *skb,
+ struct hlist_head *chain_out)
+{
+ struct batadv_frag_table_entry *chain;
+ struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
+ struct batadv_frag_packet *frag_packet;
+ uint8_t bucket;
+ uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet);
+ bool ret = false;
+
+ /* Linearize packet to avoid linearizing 16 packets in a row when doing
+ * the later merge. Non-linear merge should be added to remove this
+ * linearization.
+ */
+ if (skb_linearize(skb) < 0)
+ goto err;
+
+ frag_packet = (struct batadv_frag_packet *)skb->data;
+ seqno = ntohs(frag_packet->seqno);
+ bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
+
+ frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC);
+ if (!frag_entry_new)
+ goto err;
+
+ frag_entry_new->skb = skb;
+ frag_entry_new->no = frag_packet->no;
+
+ /* Select entry in the "chain table" and delete any prior fragments
+ * with another sequence number. batadv_frag_init_chain() returns true,
+ * if the list is empty at return.
+ */
+ chain = &orig_node->fragments[bucket];
+ spin_lock_bh(&chain->lock);
+ if (batadv_frag_init_chain(chain, seqno)) {
+ hlist_add_head(&frag_entry_new->list, &chain->head);
+ chain->size = skb->len - hdr_size;
+ chain->timestamp = jiffies;
+ ret = true;
+ goto out;
+ }
+
+ /* Find the position for the new fragment. */
+ hlist_for_each_entry(frag_entry_curr, &chain->head, list) {
+ /* Drop packet if fragment already exists. */
+ if (frag_entry_curr->no == frag_entry_new->no)
+ goto err_unlock;
+
+ /* Order fragments from highest to lowest. */
+ if (frag_entry_curr->no < frag_entry_new->no) {
+ hlist_add_before(&frag_entry_new->list,
+ &frag_entry_curr->list);
+ chain->size += skb->len - hdr_size;
+ chain->timestamp = jiffies;
+ ret = true;
+ goto out;
+ }
+ }
+
+ /* Reached the end of the list, so insert after 'frag_entry_curr'. */
+ if (likely(frag_entry_curr)) {
+ hlist_add_after(&frag_entry_curr->list, &frag_entry_new->list);
+ chain->size += skb->len - hdr_size;
+ chain->timestamp = jiffies;
+ ret = true;
+ }
+
+out:
+ if (chain->size > batadv_frag_size_limit() ||
+ ntohs(frag_packet->total_size) > batadv_frag_size_limit()) {
+ /* Clear chain if total size of either the list or the packet
+ * exceeds the maximum size of one merged packet.
+ */
+ batadv_frag_clear_chain(&chain->head);
+ chain->size = 0;
+ } else if (ntohs(frag_packet->total_size) == chain->size) {
+ /* All fragments received. Hand over chain to caller. */
+ hlist_move_list(&chain->head, chain_out);
+ chain->size = 0;
+ }
+
+err_unlock:
+ spin_unlock_bh(&chain->lock);
+
+err:
+ if (!ret)
+ kfree(frag_entry_new);
+
+ return ret;
+}
+
+/**
+ * batadv_frag_merge_packets - merge a chain of fragments
+ * @chain: head of chain with fragments
+ * @skb: packet with total size of skb after merging
+ *
+ * Expand the first skb in the chain and copy the content of the remaining
+ * skb's into the expanded one. After doing so, clear the chain.
+ *
+ * Returns the merged skb or NULL on error.
+ */
+static struct sk_buff *
+batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
+{
+ struct batadv_frag_packet *packet;
+ struct batadv_frag_list_entry *entry;
+ struct sk_buff *skb_out = NULL;
+ int size, hdr_size = sizeof(struct batadv_frag_packet);
+
+ /* Make sure incoming skb has non-bogus data. */
+ packet = (struct batadv_frag_packet *)skb->data;
+ size = ntohs(packet->total_size);
+ if (size > batadv_frag_size_limit())
+ goto free;
+
+ /* Remove first entry, as this is the destination for the rest of the
+ * fragments.
+ */
+ entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list);
+ hlist_del(&entry->list);
+ skb_out = entry->skb;
+ kfree(entry);
+
+ /* Make room for the rest of the fragments. */
+ if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) {
+ kfree_skb(skb_out);
+ skb_out = NULL;
+ goto free;
+ }
+
+ /* Move the existing MAC header to just before the payload. (Override
+ * the fragment header.)
+ */
+ skb_pull_rcsum(skb_out, hdr_size);
+ memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
+ skb_set_mac_header(skb_out, -ETH_HLEN);
+ skb_reset_network_header(skb_out);
+ skb_reset_transport_header(skb_out);
+
+ /* Copy the payload of the each fragment into the last skb */
+ hlist_for_each_entry(entry, chain, list) {
+ size = entry->skb->len - hdr_size;
+ memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size,
+ size);
+ }
+
+free:
+ /* Locking is not needed, because 'chain' is not part of any orig. */
+ batadv_frag_clear_chain(chain);
+ return skb_out;
+}
+
+/**
+ * batadv_frag_skb_buffer - buffer fragment for later merge
+ * @skb: skb to buffer
+ * @orig_node_src: originator that the skb is received from
+ *
+ * Add fragment to buffer and merge fragments if possible.
+ *
+ * There are three possible outcomes: 1) Packet is merged: Return true and
+ * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
+ * to NULL; 3) Error: Return false and leave skb as is.
+ */
+bool batadv_frag_skb_buffer(struct sk_buff **skb,
+ struct batadv_orig_node *orig_node_src)
+{
+ struct sk_buff *skb_out = NULL;
+ struct hlist_head head = HLIST_HEAD_INIT;
+ bool ret = false;
+
+ /* Add packet to buffer and table entry if merge is possible. */
+ if (!batadv_frag_insert_packet(orig_node_src, *skb, &head))
+ goto out_err;
+
+ /* Leave if more fragments are needed to merge. */
+ if (hlist_empty(&head))
+ goto out;
+
+ skb_out = batadv_frag_merge_packets(&head, *skb);
+ if (!skb_out)
+ goto out_err;
+
+out:
+ *skb = skb_out;
+ ret = true;
+out_err:
+ return ret;
+}
+
+/**
+ * batadv_frag_skb_fwd - forward fragments that would exceed MTU when merged
+ * @skb: skb to forward
+ * @recv_if: interface that the skb is received on
+ * @orig_node_src: originator that the skb is received from
+ *
+ * Look up the next-hop of the fragments payload and check if the merged packet
+ * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
+ * without merging it.
+ *
+ * Returns true if the fragment is consumed/forwarded, false otherwise.
+ */
+bool batadv_frag_skb_fwd(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if,
+ struct batadv_orig_node *orig_node_src)
+{
+ struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+ struct batadv_orig_node *orig_node_dst = NULL;
+ struct batadv_neigh_node *neigh_node = NULL;
+ struct batadv_frag_packet *packet;
+ uint16_t total_size;
+ bool ret = false;
+
+ packet = (struct batadv_frag_packet *)skb->data;
+ orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest);
+ if (!orig_node_dst)
+ goto out;
+
+ neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if);
+ if (!neigh_node)
+ goto out;
+
+ /* Forward the fragment, if the merged packet would be too big to
+ * be assembled.
+ */
+ total_size = ntohs(packet->total_size);
+ if (total_size > neigh_node->if_incoming->net_dev->mtu) {
+ batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
+ batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
+ skb->len + ETH_HLEN);
+
+ packet->header.ttl--;
+ batadv_send_skb_packet(skb, neigh_node->if_incoming,
+ neigh_node->addr);
+ ret = true;
+ }
+
+out:
+ if (orig_node_dst)
+ batadv_orig_node_free_ref(orig_node_dst);
+ if (neigh_node)
+ batadv_neigh_node_free_ref(neigh_node);
+ return ret;
+}
+
+/**
+ * batadv_frag_create - create a fragment from skb
+ * @skb: skb to create fragment from
+ * @frag_head: header to use in new fragment
+ * @mtu: size of new fragment
+ *
+ * Split the passed skb into two fragments: A new one with size matching the
+ * passed mtu and the old one with the rest. The new skb contains data from the
+ * tail of the old skb.
+ *
+ * Returns the new fragment, NULL on error.
+ */
+static struct sk_buff *batadv_frag_create(struct sk_buff *skb,
+ struct batadv_frag_packet *frag_head,
+ unsigned int mtu)
+{
+ struct sk_buff *skb_fragment;
+ unsigned header_size = sizeof(*frag_head);
+ unsigned fragment_size = mtu - header_size;
+
+ skb_fragment = netdev_alloc_skb(NULL, mtu + ETH_HLEN);
+ if (!skb_fragment)
+ goto err;
+
+ skb->priority = TC_PRIO_CONTROL;
+
+ /* Eat the last mtu-bytes of the skb */
+ skb_reserve(skb_fragment, header_size + ETH_HLEN);
+ skb_split(skb, skb_fragment, skb->len - fragment_size);
+
+ /* Add the header */
+ skb_push(skb_fragment, header_size);
+ memcpy(skb_fragment->data, frag_head, header_size);
+
+err:
+ return skb_fragment;
+}
+
+/**
+ * batadv_frag_send_packet - create up to 16 fragments from the passed skb
+ * @skb: skb to create fragments from
+ * @orig_node: final destination of the created fragments
+ * @neigh_node: next-hop of the created fragments
+ *
+ * Returns true on success, false otherwise.
+ */
+bool batadv_frag_send_packet(struct sk_buff *skb,
+ struct batadv_orig_node *orig_node,
+ struct batadv_neigh_node *neigh_node)
+{
+ struct batadv_priv *bat_priv;
+ struct batadv_hard_iface *primary_if;
+ struct batadv_frag_packet frag_header;
+ struct sk_buff *skb_fragment;
+ unsigned mtu = neigh_node->if_incoming->net_dev->mtu;
+ unsigned header_size = sizeof(frag_header);
+ unsigned max_fragment_size, max_packet_size;
+
+ /* To avoid merge and refragmentation at next-hops we never send
+ * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE
+ */
+ mtu = min_t(unsigned, mtu, BATADV_FRAG_MAX_FRAG_SIZE);
+ max_fragment_size = (mtu - header_size - ETH_HLEN);
+ max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS;
+
+ /* Don't even try to fragment, if we need more than 16 fragments */
+ if (skb->len > max_packet_size)
+ goto out_err;
+
+ bat_priv = orig_node->bat_priv;
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out_err;
+
+ /* Create one header to be copied to all fragments */
+ frag_header.header.packet_type = BATADV_UNICAST_FRAG;
+ frag_header.header.version = BATADV_COMPAT_VERSION;
+ frag_header.header.ttl = BATADV_TTL;
+ frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno));
+ frag_header.reserved = 0;
+ frag_header.no = 0;
+ frag_header.total_size = htons(skb->len);
+ memcpy(frag_header.orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+ memcpy(frag_header.dest, orig_node->orig, ETH_ALEN);
+
+ /* Eat and send fragments from the tail of skb */
+ while (skb->len > max_fragment_size) {
+ skb_fragment = batadv_frag_create(skb, &frag_header, mtu);
+ if (!skb_fragment)
+ goto out_err;
+
+ batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
+ batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
+ skb_fragment->len + ETH_HLEN);
+ batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming,
+ neigh_node->addr);
+ frag_header.no++;
+
+ /* The initial check in this function should cover this case */
+ if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1)
+ goto out_err;
+ }
+
+ /* Make room for the fragment header. */
+ if (batadv_skb_head_push(skb, header_size) < 0 ||
+ pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0)
+ goto out_err;
+
+ memcpy(skb->data, &frag_header, header_size);
+
+ /* Send the last fragment */
+ batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX);
+ batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES,
+ skb->len + ETH_HLEN);
+ batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+
+ return true;
+out_err:
+ return false;
+}
diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
new file mode 100644
index 000000000000..ca029e2676e7
--- /dev/null
+++ b/net/batman-adv/fragmentation.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2013 B.A.T.M.A.N. contributors:
+ *
+ * Martin Hundebøll <martin@hundeboll.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_
+#define _NET_BATMAN_ADV_FRAGMENTATION_H_
+
+void batadv_frag_purge_orig(struct batadv_orig_node *orig,
+ bool (*check_cb)(struct batadv_frag_table_entry *));
+bool batadv_frag_skb_fwd(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if,
+ struct batadv_orig_node *orig_node_src);
+bool batadv_frag_skb_buffer(struct sk_buff **skb,
+ struct batadv_orig_node *orig_node);
+bool batadv_frag_send_packet(struct sk_buff *skb,
+ struct batadv_orig_node *orig_node,
+ struct batadv_neigh_node *neigh_node);
+
+/**
+ * batadv_frag_check_entry - check if a list of fragments has timed out
+ * @frags_entry: table entry to check
+ *
+ * Returns true if the frags entry has timed out, false otherwise.
+ */
+static inline bool
+batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry)
+{
+ if (!hlist_empty(&frags_entry->head) &&
+ batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT))
+ return true;
+ else
+ return false;
+}
+
+#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 1ce4b8763ef2..2449afaa7638 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -118,7 +118,6 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
uint32_t gw_divisor;
uint8_t max_tq = 0;
- int down, up;
uint8_t tq_avg;
struct batadv_orig_node *orig_node;
@@ -138,14 +137,13 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
if (!atomic_inc_not_zero(&gw_node->refcount))
goto next;
- tq_avg = router->tq_avg;
+ tq_avg = router->bat_iv.tq_avg;
switch (atomic_read(&bat_priv->gw_sel_class)) {
case 1: /* fast connection */
- batadv_gw_bandwidth_to_kbit(orig_node->gw_flags,
- &down, &up);
-
- tmp_gw_factor = tq_avg * tq_avg * down * 100 * 100;
+ tmp_gw_factor = tq_avg * tq_avg;
+ tmp_gw_factor *= gw_node->bandwidth_down;
+ tmp_gw_factor *= 100 * 100;
tmp_gw_factor /= gw_divisor;
if ((tmp_gw_factor > max_gw_factor) ||
@@ -223,11 +221,6 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
struct batadv_neigh_node *router = NULL;
char gw_addr[18] = { '\0' };
- /* The batman daemon checks here if we already passed a full originator
- * cycle in order to make sure we don't choose the first gateway we
- * hear about. This check is based on the daemon's uptime which we
- * don't have.
- */
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
goto out;
@@ -258,16 +251,22 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
NULL);
} else if ((!curr_gw) && (next_gw)) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n",
+ "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
next_gw->orig_node->orig,
- next_gw->orig_node->gw_flags, router->tq_avg);
+ next_gw->bandwidth_down / 10,
+ next_gw->bandwidth_down % 10,
+ next_gw->bandwidth_up / 10,
+ next_gw->bandwidth_up % 10, router->bat_iv.tq_avg);
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD,
gw_addr);
} else {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Changing route to gateway %pM (gw_flags: %i, tq: %i)\n",
+ "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n",
next_gw->orig_node->orig,
- next_gw->orig_node->gw_flags, router->tq_avg);
+ next_gw->bandwidth_down / 10,
+ next_gw->bandwidth_down % 10,
+ next_gw->bandwidth_up / 10,
+ next_gw->bandwidth_up % 10, router->bat_iv.tq_avg);
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE,
gw_addr);
}
@@ -306,8 +305,8 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv,
if (!router_orig)
goto out;
- gw_tq_avg = router_gw->tq_avg;
- orig_tq_avg = router_orig->tq_avg;
+ gw_tq_avg = router_gw->bat_iv.tq_avg;
+ orig_tq_avg = router_orig->bat_iv.tq_avg;
/* the TQ value has to be better */
if (orig_tq_avg < gw_tq_avg)
@@ -337,12 +336,20 @@ out:
return;
}
+/**
+ * batadv_gw_node_add - add gateway node to list of available gateways
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator announcing gateway capabilities
+ * @gateway: announced bandwidth information
+ */
static void batadv_gw_node_add(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- uint8_t new_gwflags)
+ struct batadv_tvlv_gateway_data *gateway)
{
struct batadv_gw_node *gw_node;
- int down, up;
+
+ if (gateway->bandwidth_down == 0)
+ return;
gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC);
if (!gw_node)
@@ -356,73 +363,116 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list);
spin_unlock_bh(&bat_priv->gw.list_lock);
- batadv_gw_bandwidth_to_kbit(new_gwflags, &down, &up);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n",
- orig_node->orig, new_gwflags,
- (down > 2048 ? down / 1024 : down),
- (down > 2048 ? "MBit" : "KBit"),
- (up > 2048 ? up / 1024 : up),
- (up > 2048 ? "MBit" : "KBit"));
+ "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
+ orig_node->orig,
+ ntohl(gateway->bandwidth_down) / 10,
+ ntohl(gateway->bandwidth_down) % 10,
+ ntohl(gateway->bandwidth_up) / 10,
+ ntohl(gateway->bandwidth_up) % 10);
}
-void batadv_gw_node_update(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- uint8_t new_gwflags)
+/**
+ * batadv_gw_node_get - retrieve gateway node from list of available gateways
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator announcing gateway capabilities
+ *
+ * Returns gateway node if found or NULL otherwise.
+ */
+static struct batadv_gw_node *
+batadv_gw_node_get(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node)
{
- struct batadv_gw_node *gw_node, *curr_gw;
-
- /* Note: We don't need a NULL check here, since curr_gw never gets
- * dereferenced. If curr_gw is NULL we also should not exit as we may
- * have this gateway in our list (duplication check!) even though we
- * have no currently selected gateway.
- */
- curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
+ struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
rcu_read_lock();
- hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
- if (gw_node->orig_node != orig_node)
+ hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) {
+ if (gw_node_tmp->orig_node != orig_node)
continue;
- batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Gateway class of originator %pM changed from %i to %i\n",
- orig_node->orig, gw_node->orig_node->gw_flags,
- new_gwflags);
+ if (gw_node_tmp->deleted)
+ continue;
- gw_node->deleted = 0;
+ if (!atomic_inc_not_zero(&gw_node_tmp->refcount))
+ continue;
- if (new_gwflags == BATADV_NO_FLAGS) {
- gw_node->deleted = jiffies;
- batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Gateway %pM removed from gateway list\n",
- orig_node->orig);
+ gw_node = gw_node_tmp;
+ break;
+ }
+ rcu_read_unlock();
- if (gw_node == curr_gw)
- goto deselect;
- }
+ return gw_node;
+}
- goto unlock;
+/**
+ * batadv_gw_node_update - update list of available gateways with changed
+ * bandwidth information
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator announcing gateway capabilities
+ * @gateway: announced bandwidth information
+ */
+void batadv_gw_node_update(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
+ struct batadv_tvlv_gateway_data *gateway)
+{
+ struct batadv_gw_node *gw_node, *curr_gw = NULL;
+
+ gw_node = batadv_gw_node_get(bat_priv, orig_node);
+ if (!gw_node) {
+ batadv_gw_node_add(bat_priv, orig_node, gateway);
+ goto out;
}
- if (new_gwflags == BATADV_NO_FLAGS)
- goto unlock;
+ if ((gw_node->bandwidth_down == ntohl(gateway->bandwidth_down)) &&
+ (gw_node->bandwidth_up == ntohl(gateway->bandwidth_up)))
+ goto out;
- batadv_gw_node_add(bat_priv, orig_node, new_gwflags);
- goto unlock;
+ batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+ "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n",
+ orig_node->orig,
+ gw_node->bandwidth_down / 10,
+ gw_node->bandwidth_down % 10,
+ gw_node->bandwidth_up / 10,
+ gw_node->bandwidth_up % 10,
+ ntohl(gateway->bandwidth_down) / 10,
+ ntohl(gateway->bandwidth_down) % 10,
+ ntohl(gateway->bandwidth_up) / 10,
+ ntohl(gateway->bandwidth_up) % 10);
+
+ gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
+ gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
+
+ gw_node->deleted = 0;
+ if (ntohl(gateway->bandwidth_down) == 0) {
+ gw_node->deleted = jiffies;
+ batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+ "Gateway %pM removed from gateway list\n",
+ orig_node->orig);
-deselect:
- batadv_gw_deselect(bat_priv);
-unlock:
- rcu_read_unlock();
+ /* Note: We don't need a NULL check here, since curr_gw never
+ * gets dereferenced.
+ */
+ curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
+ if (gw_node == curr_gw)
+ batadv_gw_deselect(bat_priv);
+ }
+out:
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
+ if (gw_node)
+ batadv_gw_node_free_ref(gw_node);
}
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node)
{
- batadv_gw_node_update(bat_priv, orig_node, 0);
+ struct batadv_tvlv_gateway_data gateway;
+
+ gateway.bandwidth_down = 0;
+ gateway.bandwidth_up = 0;
+
+ batadv_gw_node_update(bat_priv, orig_node, &gateway);
}
void batadv_gw_node_purge(struct batadv_priv *bat_priv)
@@ -467,9 +517,7 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
{
struct batadv_gw_node *curr_gw;
struct batadv_neigh_node *router;
- int down, up, ret = -1;
-
- batadv_gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
+ int ret = -1;
router = batadv_orig_node_get_router(gw_node->orig_node);
if (!router)
@@ -477,16 +525,15 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv,
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
- ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
+ ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n",
(curr_gw == gw_node ? "=>" : " "),
gw_node->orig_node->orig,
- router->tq_avg, router->addr,
+ router->bat_iv.tq_avg, router->addr,
router->if_incoming->net_dev->name,
- gw_node->orig_node->gw_flags,
- (down > 2048 ? down / 1024 : down),
- (down > 2048 ? "MBit" : "KBit"),
- (up > 2048 ? up / 1024 : up),
- (up > 2048 ? "MBit" : "KBit"));
+ gw_node->bandwidth_down / 10,
+ gw_node->bandwidth_down % 10,
+ gw_node->bandwidth_up / 10,
+ gw_node->bandwidth_up % 10);
batadv_neigh_node_free_ref(router);
if (curr_gw)
@@ -508,7 +555,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
goto out;
seq_printf(seq,
- " %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
+ " %-12s (%s/%i) %17s [%10s]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
"Gateway", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF",
BATADV_SOURCE_VERSION, primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name);
@@ -603,24 +650,29 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
struct iphdr *iphdr;
struct ipv6hdr *ipv6hdr;
struct udphdr *udphdr;
+ struct vlan_ethhdr *vhdr;
+ __be16 proto;
/* check for ethernet header */
if (!pskb_may_pull(skb, *header_len + ETH_HLEN))
return false;
ethhdr = (struct ethhdr *)skb->data;
+ proto = ethhdr->h_proto;
*header_len += ETH_HLEN;
/* check for initial vlan header */
- if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) {
+ if (proto == htons(ETH_P_8021Q)) {
if (!pskb_may_pull(skb, *header_len + VLAN_HLEN))
return false;
- ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN);
+
+ vhdr = (struct vlan_ethhdr *)skb->data;
+ proto = vhdr->h_vlan_encapsulated_proto;
*header_len += VLAN_HLEN;
}
/* check for ip header */
- switch (ntohs(ethhdr->h_proto)) {
- case ETH_P_IP:
+ switch (proto) {
+ case htons(ETH_P_IP):
if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr)))
return false;
iphdr = (struct iphdr *)(skb->data + *header_len);
@@ -631,7 +683,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
return false;
break;
- case ETH_P_IPV6:
+ case htons(ETH_P_IPV6):
if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr)))
return false;
ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len);
@@ -658,28 +710,44 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len)
*header_len += sizeof(*udphdr);
/* check for bootp port */
- if ((ntohs(ethhdr->h_proto) == ETH_P_IP) &&
- (ntohs(udphdr->dest) != 67))
+ if ((proto == htons(ETH_P_IP)) &&
+ (udphdr->dest != htons(67)))
return false;
- if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) &&
- (ntohs(udphdr->dest) != 547))
+ if ((proto == htons(ETH_P_IPV6)) &&
+ (udphdr->dest != htons(547)))
return false;
return true;
}
-/* this call might reallocate skb data */
+/**
+ * batadv_gw_out_of_range - check if the dhcp request destination is the best gw
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the outgoing packet
+ *
+ * Check if the skb is a DHCP request and if it is sent to the current best GW
+ * server. Due to topology changes it may be the case that the GW server
+ * previously selected is not the best one anymore.
+ *
+ * Returns true if the packet destination is unicast and it is not the best gw,
+ * false otherwise.
+ *
+ * This call might reallocate skb data.
+ */
bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL;
struct batadv_orig_node *orig_dst_node = NULL;
- struct batadv_gw_node *curr_gw = NULL;
+ struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL;
struct ethhdr *ethhdr;
bool ret, out_of_range = false;
unsigned int header_len = 0;
uint8_t curr_tq_avg;
+ unsigned short vid;
+
+ vid = batadv_get_vid(skb, 0);
ret = batadv_gw_is_dhcp_target(skb, &header_len);
if (!ret)
@@ -687,11 +755,12 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
ethhdr = (struct ethhdr *)skb->data;
orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
- ethhdr->h_dest);
+ ethhdr->h_dest, vid);
if (!orig_dst_node)
goto out;
- if (!orig_dst_node->gw_flags)
+ gw_node = batadv_gw_node_get(bat_priv, orig_dst_node);
+ if (!gw_node->bandwidth_down == 0)
goto out;
ret = batadv_is_type_dhcprequest(skb, header_len);
@@ -723,7 +792,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
if (!neigh_curr)
goto out;
- curr_tq_avg = neigh_curr->tq_avg;
+ curr_tq_avg = neigh_curr->bat_iv.tq_avg;
break;
case BATADV_GW_MODE_OFF:
default:
@@ -734,7 +803,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv,
if (!neigh_old)
goto out;
- if (curr_tq_avg - neigh_old->tq_avg > BATADV_GW_THRESHOLD)
+ if (curr_tq_avg - neigh_old->bat_iv.tq_avg > BATADV_GW_THRESHOLD)
out_of_range = true;
out:
@@ -742,6 +811,8 @@ out:
batadv_orig_node_free_ref(orig_dst_node);
if (curr_gw)
batadv_gw_node_free_ref(curr_gw);
+ if (gw_node)
+ batadv_gw_node_free_ref(gw_node);
if (neigh_old)
batadv_neigh_node_free_ref(neigh_old);
if (neigh_curr)
diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h
index ceef4ebe8bcd..d95c2d23195e 100644
--- a/net/batman-adv/gateway_client.h
+++ b/net/batman-adv/gateway_client.h
@@ -29,7 +29,7 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node);
void batadv_gw_node_update(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- uint8_t new_gwflags);
+ struct batadv_tvlv_gateway_data *gateway);
void batadv_gw_node_delete(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node);
void batadv_gw_node_purge(struct batadv_priv *bat_priv);
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c
index 84bb2b18d711..b211b0f9cb78 100644
--- a/net/batman-adv/gateway_common.c
+++ b/net/batman-adv/gateway_common.c
@@ -21,64 +21,23 @@
#include "gateway_common.h"
#include "gateway_client.h"
-/* calculates the gateway class from kbit */
-static void batadv_kbit_to_gw_bandwidth(int down, int up, long *gw_srv_class)
-{
- int mdown = 0, tdown, tup, difference;
- uint8_t sbit, part;
-
- *gw_srv_class = 0;
- difference = 0x0FFFFFFF;
-
- /* test all downspeeds */
- for (sbit = 0; sbit < 2; sbit++) {
- for (part = 0; part < 16; part++) {
- tdown = 32 * (sbit + 2) * (1 << part);
-
- if (abs(tdown - down) < difference) {
- *gw_srv_class = (sbit << 7) + (part << 3);
- difference = abs(tdown - down);
- mdown = tdown;
- }
- }
- }
-
- /* test all upspeeds */
- difference = 0x0FFFFFFF;
-
- for (part = 0; part < 8; part++) {
- tup = ((part + 1) * (mdown)) / 8;
-
- if (abs(tup - up) < difference) {
- *gw_srv_class = (*gw_srv_class & 0xF8) | part;
- difference = abs(tup - up);
- }
- }
-}
-
-/* returns the up and downspeeds in kbit, calculated from the class */
-void batadv_gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up)
-{
- int sbit = (gw_srv_class & 0x80) >> 7;
- int dpart = (gw_srv_class & 0x78) >> 3;
- int upart = (gw_srv_class & 0x07);
-
- if (!gw_srv_class) {
- *down = 0;
- *up = 0;
- return;
- }
-
- *down = 32 * (sbit + 2) * (1 << dpart);
- *up = ((upart + 1) * (*down)) / 8;
-}
-
+/**
+ * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
+ * and upload bandwidth information
+ * @net_dev: the soft interface net device
+ * @buff: string buffer to parse
+ * @down: pointer holding the returned download bandwidth information
+ * @up: pointer holding the returned upload bandwidth information
+ *
+ * Returns false on parse error and true otherwise.
+ */
static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
- int *up, int *down)
+ uint32_t *down, uint32_t *up)
{
- int ret, multi = 1;
+ enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
char *slash_ptr, *tmp_ptr;
long ldown, lup;
+ int ret;
slash_ptr = strchr(buff, '/');
if (slash_ptr)
@@ -88,10 +47,10 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
tmp_ptr = buff + strlen(buff) - 4;
if (strnicmp(tmp_ptr, "mbit", 4) == 0)
- multi = 1024;
+ bw_unit_type = BATADV_BW_UNIT_MBIT;
if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
- (multi > 1))
+ (bw_unit_type == BATADV_BW_UNIT_MBIT))
*tmp_ptr = '\0';
}
@@ -103,20 +62,28 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
return false;
}
- *down = ldown * multi;
+ switch (bw_unit_type) {
+ case BATADV_BW_UNIT_MBIT:
+ *down = ldown * 10;
+ break;
+ case BATADV_BW_UNIT_KBIT:
+ default:
+ *down = ldown / 100;
+ break;
+ }
/* we also got some upload info */
if (slash_ptr) {
- multi = 1;
+ bw_unit_type = BATADV_BW_UNIT_KBIT;
if (strlen(slash_ptr + 1) > 4) {
tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
if (strnicmp(tmp_ptr, "mbit", 4) == 0)
- multi = 1024;
+ bw_unit_type = BATADV_BW_UNIT_MBIT;
if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
- (multi > 1))
+ (bw_unit_type == BATADV_BW_UNIT_MBIT))
*tmp_ptr = '\0';
}
@@ -128,52 +95,149 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
return false;
}
- *up = lup * multi;
+ switch (bw_unit_type) {
+ case BATADV_BW_UNIT_MBIT:
+ *up = lup * 10;
+ break;
+ case BATADV_BW_UNIT_KBIT:
+ default:
+ *up = lup / 100;
+ break;
+ }
}
return true;
}
+/**
+ * batadv_gw_tvlv_container_update - update the gw tvlv container after gateway
+ * setting change
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv)
+{
+ struct batadv_tvlv_gateway_data gw;
+ uint32_t down, up;
+ char gw_mode;
+
+ gw_mode = atomic_read(&bat_priv->gw_mode);
+
+ switch (gw_mode) {
+ case BATADV_GW_MODE_OFF:
+ case BATADV_GW_MODE_CLIENT:
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
+ break;
+ case BATADV_GW_MODE_SERVER:
+ down = atomic_read(&bat_priv->gw.bandwidth_down);
+ up = atomic_read(&bat_priv->gw.bandwidth_up);
+ gw.bandwidth_down = htonl(down);
+ gw.bandwidth_up = htonl(up);
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1,
+ &gw, sizeof(gw));
+ break;
+ }
+}
+
ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
size_t count)
{
struct batadv_priv *bat_priv = netdev_priv(net_dev);
- long gw_bandwidth_tmp = 0;
- int up = 0, down = 0;
+ uint32_t down_curr, up_curr, down_new = 0, up_new = 0;
bool ret;
- ret = batadv_parse_gw_bandwidth(net_dev, buff, &up, &down);
+ down_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_down);
+ up_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_up);
+
+ ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new);
if (!ret)
goto end;
- if ((!down) || (down < 256))
- down = 2000;
-
- if (!up)
- up = down / 5;
+ if (!down_new)
+ down_new = 1;
- batadv_kbit_to_gw_bandwidth(down, up, &gw_bandwidth_tmp);
+ if (!up_new)
+ up_new = down_new / 5;
- /* the gw bandwidth we guessed above might not match the given
- * speeds, hence we need to calculate it back to show the number
- * that is going to be propagated
- */
- batadv_gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, &down, &up);
+ if (!up_new)
+ up_new = 1;
- if (atomic_read(&bat_priv->gw_bandwidth) == gw_bandwidth_tmp)
+ if ((down_curr == down_new) && (up_curr == up_new))
return count;
batadv_gw_deselect(bat_priv);
batadv_info(net_dev,
- "Changing gateway bandwidth from: '%i' to: '%ld' (propagating: %d%s/%d%s)\n",
- atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp,
- (down > 2048 ? down / 1024 : down),
- (down > 2048 ? "MBit" : "KBit"),
- (up > 2048 ? up / 1024 : up),
- (up > 2048 ? "MBit" : "KBit"));
+ "Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n",
+ down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10,
+ down_new / 10, down_new % 10, up_new / 10, up_new % 10);
- atomic_set(&bat_priv->gw_bandwidth, gw_bandwidth_tmp);
+ atomic_set(&bat_priv->gw.bandwidth_down, down_new);
+ atomic_set(&bat_priv->gw.bandwidth_up, up_new);
+ batadv_gw_tvlv_container_update(bat_priv);
end:
return count;
}
+
+/**
+ * batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
+
+ /* only fetch the tvlv value if the handler wasn't called via the
+ * CIFNOTFND flag and if there is data to fetch
+ */
+ if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) ||
+ (tvlv_value_len < sizeof(gateway))) {
+ gateway.bandwidth_down = 0;
+ gateway.bandwidth_up = 0;
+ } else {
+ gateway_ptr = tvlv_value;
+ gateway.bandwidth_down = gateway_ptr->bandwidth_down;
+ gateway.bandwidth_up = gateway_ptr->bandwidth_up;
+ if ((gateway.bandwidth_down == 0) ||
+ (gateway.bandwidth_up == 0)) {
+ gateway.bandwidth_down = 0;
+ gateway.bandwidth_up = 0;
+ }
+ }
+
+ batadv_gw_node_update(bat_priv, orig, &gateway);
+
+ /* restart gateway selection if fast or late switching was enabled */
+ if ((gateway.bandwidth_down != 0) &&
+ (atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) &&
+ (atomic_read(&bat_priv->gw_sel_class) > 2))
+ batadv_gw_check_election(bat_priv, orig);
+}
+
+/**
+ * batadv_gw_init - initialise the gateway handling internals
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_gw_init(struct batadv_priv *bat_priv)
+{
+ batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1,
+ NULL, BATADV_TVLV_GW, 1,
+ BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+}
+
+/**
+ * batadv_gw_free - free the gateway handling internals
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_gw_free(struct batadv_priv *bat_priv)
+{
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
+ batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1);
+}
diff --git a/net/batman-adv/gateway_common.h b/net/batman-adv/gateway_common.h
index 509b2bf8c2f4..56384a4cd18c 100644
--- a/net/batman-adv/gateway_common.h
+++ b/net/batman-adv/gateway_common.h
@@ -26,12 +26,24 @@ enum batadv_gw_modes {
BATADV_GW_MODE_SERVER,
};
+/**
+ * enum batadv_bandwidth_units - bandwidth unit types
+ * @BATADV_BW_UNIT_KBIT: unit type kbit
+ * @BATADV_BW_UNIT_MBIT: unit type mbit
+ */
+enum batadv_bandwidth_units {
+ BATADV_BW_UNIT_KBIT,
+ BATADV_BW_UNIT_MBIT,
+};
+
#define BATADV_GW_MODE_OFF_NAME "off"
#define BATADV_GW_MODE_CLIENT_NAME "client"
#define BATADV_GW_MODE_SERVER_NAME "server"
-void batadv_gw_bandwidth_to_kbit(uint8_t gw_class, int *down, int *up);
ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
size_t count);
+void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv);
+void batadv_gw_init(struct batadv_priv *bat_priv);
+void batadv_gw_free(struct batadv_priv *bat_priv);
#endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index c478e6bcf89b..57c2a19dcb5c 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -28,6 +28,7 @@
#include "originator.h"
#include "hash.h"
#include "bridge_loop_avoidance.h"
+#include "gateway_client.h"
#include <linux/if_arp.h>
#include <linux/if_ether.h>
@@ -124,8 +125,11 @@ static int batadv_is_valid_iface(const struct net_device *net_dev)
*
* Returns true if the net device is a 802.11 wireless device, false otherwise.
*/
-static bool batadv_is_wifi_netdev(struct net_device *net_device)
+bool batadv_is_wifi_netdev(struct net_device *net_device)
{
+ if (!net_device)
+ return false;
+
#ifdef CONFIG_WIRELESS_EXT
/* pre-cfg80211 drivers have to implement WEXT, so it is possible to
* check for wireless_handlers != NULL
@@ -141,34 +145,6 @@ static bool batadv_is_wifi_netdev(struct net_device *net_device)
return false;
}
-/**
- * batadv_is_wifi_iface - check if the given interface represented by ifindex
- * is a wifi interface
- * @ifindex: interface index to check
- *
- * Returns true if the interface represented by ifindex is a 802.11 wireless
- * device, false otherwise.
- */
-bool batadv_is_wifi_iface(int ifindex)
-{
- struct net_device *net_device = NULL;
- bool ret = false;
-
- if (ifindex == BATADV_NULL_IFINDEX)
- goto out;
-
- net_device = dev_get_by_index(&init_net, ifindex);
- if (!net_device)
- goto out;
-
- ret = batadv_is_wifi_netdev(net_device);
-
-out:
- if (net_device)
- dev_put(net_device);
- return ret;
-}
-
static struct batadv_hard_iface *
batadv_hardif_get_active(const struct net_device *soft_iface)
{
@@ -194,22 +170,13 @@ out:
static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
struct batadv_hard_iface *oldif)
{
- struct batadv_vis_packet *vis_packet;
struct batadv_hard_iface *primary_if;
- struct sk_buff *skb;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
batadv_dat_init_own_addr(bat_priv, primary_if);
-
- skb = bat_priv->vis.my_info->skb_packet;
- vis_packet = (struct batadv_vis_packet *)skb->data;
- memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- memcpy(vis_packet->sender_orig,
- primary_if->net_dev->dev_addr, ETH_ALEN);
-
batadv_bla_update_orig_address(bat_priv, primary_if, oldif);
out:
if (primary_if)
@@ -275,16 +242,10 @@ static void batadv_check_known_mac_addr(const struct net_device *net_dev)
int batadv_hardif_min_mtu(struct net_device *soft_iface)
{
- const struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+ struct batadv_priv *bat_priv = netdev_priv(soft_iface);
const struct batadv_hard_iface *hard_iface;
- /* allow big frames if all devices are capable to do so
- * (have MTU > 1500 + BAT_HEADER_LEN)
- */
int min_mtu = ETH_DATA_LEN;
- if (atomic_read(&bat_priv->fragmentation))
- goto out;
-
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if ((hard_iface->if_status != BATADV_IF_ACTIVE) &&
@@ -294,23 +255,40 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface)
if (hard_iface->soft_iface != soft_iface)
continue;
- min_mtu = min_t(int,
- hard_iface->net_dev->mtu - BATADV_HEADER_LEN,
- min_mtu);
+ min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu);
}
rcu_read_unlock();
+
+ atomic_set(&bat_priv->packet_size_max, min_mtu);
+
+ if (atomic_read(&bat_priv->fragmentation) == 0)
+ goto out;
+
+ /* with fragmentation enabled the maximum size of internally generated
+ * packets such as translation table exchanges or tvlv containers, etc
+ * has to be calculated
+ */
+ min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE);
+ min_mtu -= sizeof(struct batadv_frag_packet);
+ min_mtu *= BATADV_FRAG_MAX_FRAGMENTS;
+ atomic_set(&bat_priv->packet_size_max, min_mtu);
+
+ /* with fragmentation enabled we can fragment external packets easily */
+ min_mtu = min_t(int, min_mtu, ETH_DATA_LEN);
+
out:
- return min_mtu;
+ return min_mtu - batadv_max_header_len();
}
/* adjusts the MTU if a new interface with a smaller MTU appeared. */
void batadv_update_min_mtu(struct net_device *soft_iface)
{
- int min_mtu;
+ soft_iface->mtu = batadv_hardif_min_mtu(soft_iface);
- min_mtu = batadv_hardif_min_mtu(soft_iface);
- if (soft_iface->mtu != min_mtu)
- soft_iface->mtu = min_mtu;
+ /* Check if the local translate table should be cleaned up to match a
+ * new (and smaller) MTU.
+ */
+ batadv_tt_local_resize_to_mtu(soft_iface);
}
static void
@@ -388,7 +366,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
{
struct batadv_priv *bat_priv;
struct net_device *soft_iface, *master;
- __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+ __be16 ethertype = htons(ETH_P_BATMAN);
+ int max_header_len = batadv_max_header_len();
int ret;
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
@@ -453,23 +432,22 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
dev_add_pack(&hard_iface->batman_adv_ptype);
- atomic_set(&hard_iface->frag_seqno, 1);
batadv_info(hard_iface->soft_iface, "Adding interface: %s\n",
hard_iface->net_dev->name);
if (atomic_read(&bat_priv->fragmentation) &&
- hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN)
+ hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
batadv_info(hard_iface->soft_iface,
- "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %zi would solve the problem.\n",
+ "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n",
hard_iface->net_dev->name, hard_iface->net_dev->mtu,
- ETH_DATA_LEN + BATADV_HEADER_LEN);
+ ETH_DATA_LEN + max_header_len);
if (!atomic_read(&bat_priv->fragmentation) &&
- hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN)
+ hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len)
batadv_info(hard_iface->soft_iface,
- "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %zi.\n",
+ "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n",
hard_iface->net_dev->name, hard_iface->net_dev->mtu,
- ETH_DATA_LEN + BATADV_HEADER_LEN);
+ ETH_DATA_LEN + max_header_len);
if (batadv_hardif_is_iface_up(hard_iface))
batadv_hardif_activate_interface(hard_iface);
@@ -533,8 +511,12 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
dev_put(hard_iface->soft_iface);
/* nobody uses this interface anymore */
- if (!bat_priv->num_ifaces && autodel == BATADV_IF_CLEANUP_AUTO)
- batadv_softif_destroy_sysfs(hard_iface->soft_iface);
+ if (!bat_priv->num_ifaces) {
+ batadv_gw_check_client_stop(bat_priv);
+
+ if (autodel == BATADV_IF_CLEANUP_AUTO)
+ batadv_softif_destroy_sysfs(hard_iface->soft_iface);
+ }
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
hard_iface->soft_iface = NULL;
@@ -652,6 +634,8 @@ static int batadv_hard_if_event(struct notifier_block *this,
if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) {
batadv_sysfs_add_meshif(net_dev);
+ bat_priv = netdev_priv(net_dev);
+ batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
return NOTIFY_DONE;
}
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index 49892881a7c5..df4c8bd45c40 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -41,6 +41,7 @@ enum batadv_hard_if_cleanup {
extern struct notifier_block batadv_hard_if_notifier;
+bool batadv_is_wifi_netdev(struct net_device *net_device);
struct batadv_hard_iface*
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
@@ -51,7 +52,6 @@ void batadv_hardif_remove_interfaces(void);
int batadv_hardif_min_mtu(struct net_device *soft_iface);
void batadv_update_min_mtu(struct net_device *soft_iface);
void batadv_hardif_free_rcu(struct rcu_head *rcu);
-bool batadv_is_wifi_iface(int ifindex);
static inline void
batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface)
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index 5a99bb4b6b82..29ae4efe3543 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -29,7 +29,7 @@
static struct batadv_socket_client *batadv_socket_client_hash[256];
static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
- struct batadv_icmp_packet_rr *icmp_packet,
+ struct batadv_icmp_header *icmph,
size_t icmp_len);
void batadv_socket_init(void)
@@ -155,13 +155,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
struct batadv_priv *bat_priv = socket_client->bat_priv;
struct batadv_hard_iface *primary_if = NULL;
struct sk_buff *skb;
- struct batadv_icmp_packet_rr *icmp_packet;
-
+ struct batadv_icmp_packet_rr *icmp_packet_rr;
+ struct batadv_icmp_header *icmp_header;
struct batadv_orig_node *orig_node = NULL;
struct batadv_neigh_node *neigh_node = NULL;
size_t packet_len = sizeof(struct batadv_icmp_packet);
- if (len < sizeof(struct batadv_icmp_packet)) {
+ if (len < sizeof(struct batadv_icmp_header)) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Error - can't send packet from char device: invalid packet size\n");
return -EINVAL;
@@ -174,8 +174,10 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
goto out;
}
- if (len >= sizeof(struct batadv_icmp_packet_rr))
- packet_len = sizeof(struct batadv_icmp_packet_rr);
+ if (len >= BATADV_ICMP_MAX_PACKET_SIZE)
+ packet_len = BATADV_ICMP_MAX_PACKET_SIZE;
+ else
+ packet_len = len;
skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN);
if (!skb) {
@@ -185,67 +187,78 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
skb->priority = TC_PRIO_CONTROL;
skb_reserve(skb, ETH_HLEN);
- icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len);
+ icmp_header = (struct batadv_icmp_header *)skb_put(skb, packet_len);
- if (copy_from_user(icmp_packet, buff, packet_len)) {
+ if (copy_from_user(icmp_header, buff, packet_len)) {
len = -EFAULT;
goto free_skb;
}
- if (icmp_packet->header.packet_type != BATADV_ICMP) {
+ if (icmp_header->header.packet_type != BATADV_ICMP) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n");
len = -EINVAL;
goto free_skb;
}
- if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) {
+ switch (icmp_header->msg_type) {
+ case BATADV_ECHO_REQUEST:
+ if (len < sizeof(struct batadv_icmp_packet)) {
+ batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+ "Error - can't send packet from char device: invalid packet size\n");
+ len = -EINVAL;
+ goto free_skb;
+ }
+
+ if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
+ goto dst_unreach;
+
+ orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst);
+ if (!orig_node)
+ goto dst_unreach;
+
+ neigh_node = batadv_orig_node_get_router(orig_node);
+ if (!neigh_node)
+ goto dst_unreach;
+
+ if (!neigh_node->if_incoming)
+ goto dst_unreach;
+
+ if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
+ goto dst_unreach;
+
+ icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header;
+ if (packet_len == sizeof(*icmp_packet_rr))
+ memcpy(icmp_packet_rr->rr,
+ neigh_node->if_incoming->net_dev->dev_addr,
+ ETH_ALEN);
+
+ break;
+ default:
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n");
+ "Error - can't send packet from char device: got unknown message type\n");
len = -EINVAL;
goto free_skb;
}
- icmp_packet->uid = socket_client->index;
+ icmp_header->uid = socket_client->index;
- if (icmp_packet->header.version != BATADV_COMPAT_VERSION) {
- icmp_packet->msg_type = BATADV_PARAMETER_PROBLEM;
- icmp_packet->header.version = BATADV_COMPAT_VERSION;
- batadv_socket_add_packet(socket_client, icmp_packet,
+ if (icmp_header->header.version != BATADV_COMPAT_VERSION) {
+ icmp_header->msg_type = BATADV_PARAMETER_PROBLEM;
+ icmp_header->header.version = BATADV_COMPAT_VERSION;
+ batadv_socket_add_packet(socket_client, icmp_header,
packet_len);
goto free_skb;
}
- if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
- goto dst_unreach;
-
- orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst);
- if (!orig_node)
- goto dst_unreach;
-
- neigh_node = batadv_orig_node_get_router(orig_node);
- if (!neigh_node)
- goto dst_unreach;
-
- if (!neigh_node->if_incoming)
- goto dst_unreach;
-
- if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE)
- goto dst_unreach;
-
- memcpy(icmp_packet->orig,
- primary_if->net_dev->dev_addr, ETH_ALEN);
-
- if (packet_len == sizeof(struct batadv_icmp_packet_rr))
- memcpy(icmp_packet->rr,
- neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN);
+ memcpy(icmp_header->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
goto out;
dst_unreach:
- icmp_packet->msg_type = BATADV_DESTINATION_UNREACHABLE;
- batadv_socket_add_packet(socket_client, icmp_packet, packet_len);
+ icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE;
+ batadv_socket_add_packet(socket_client, icmp_header, packet_len);
free_skb:
kfree_skb(skb);
out:
@@ -298,27 +311,40 @@ err:
return -ENOMEM;
}
+/**
+ * batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace
+ * on an icmp socket.
+ * @socket_client: the socket this packet belongs to
+ * @icmph: pointer to the header of the icmp packet
+ * @icmp_len: total length of the icmp packet
+ */
static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
- struct batadv_icmp_packet_rr *icmp_packet,
+ struct batadv_icmp_header *icmph,
size_t icmp_len)
{
struct batadv_socket_packet *socket_packet;
+ size_t len;
socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC);
if (!socket_packet)
return;
+ len = icmp_len;
+ /* check the maximum length before filling the buffer */
+ if (len > sizeof(socket_packet->icmp_packet))
+ len = sizeof(socket_packet->icmp_packet);
+
INIT_LIST_HEAD(&socket_packet->list);
- memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len);
- socket_packet->icmp_len = icmp_len;
+ memcpy(&socket_packet->icmp_packet, icmph, len);
+ socket_packet->icmp_len = len;
spin_lock_bh(&socket_client->lock);
/* while waiting for the lock the socket_client could have been
* deleted
*/
- if (!batadv_socket_client_hash[icmp_packet->uid]) {
+ if (!batadv_socket_client_hash[icmph->uid]) {
spin_unlock_bh(&socket_client->lock);
kfree(socket_packet);
return;
@@ -342,12 +368,18 @@ static void batadv_socket_add_packet(struct batadv_socket_client *socket_client,
wake_up(&socket_client->queue_wait);
}
-void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet,
+/**
+ * batadv_socket_receive_packet - schedule an icmp packet to be received
+ * locally and sent to userspace.
+ * @icmph: pointer to the header of the icmp packet
+ * @icmp_len: total length of the icmp packet
+ */
+void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
size_t icmp_len)
{
struct batadv_socket_client *hash;
- hash = batadv_socket_client_hash[icmp_packet->uid];
+ hash = batadv_socket_client_hash[icmph->uid];
if (hash)
- batadv_socket_add_packet(hash, icmp_packet, icmp_len);
+ batadv_socket_add_packet(hash, icmph, icmp_len);
}
diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h
index 1fcca37b6223..6665080dff79 100644
--- a/net/batman-adv/icmp_socket.h
+++ b/net/batman-adv/icmp_socket.h
@@ -24,7 +24,7 @@
void batadv_socket_init(void);
int batadv_socket_setup(struct batadv_priv *bat_priv);
-void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet,
+void batadv_socket_receive_packet(struct batadv_icmp_header *icmph,
size_t icmp_len);
#endif /* _NET_BATMAN_ADV_ICMP_SOCKET_H_ */
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 1356af660b5b..c51a5e568f0a 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -36,10 +36,11 @@
#include "gateway_client.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
-#include "vis.h"
+#include "gateway_common.h"
#include "hash.h"
#include "bat_algo.h"
#include "network-coding.h"
+#include "fragmentation.h"
/* List manipulations on hardif_list have to be rtnl_lock()'ed,
@@ -109,9 +110,11 @@ int batadv_mesh_init(struct net_device *soft_iface)
spin_lock_init(&bat_priv->tt.req_list_lock);
spin_lock_init(&bat_priv->tt.roam_list_lock);
spin_lock_init(&bat_priv->tt.last_changeset_lock);
+ spin_lock_init(&bat_priv->tt.commit_lock);
spin_lock_init(&bat_priv->gw.list_lock);
- spin_lock_init(&bat_priv->vis.hash_lock);
- spin_lock_init(&bat_priv->vis.list_lock);
+ spin_lock_init(&bat_priv->tvlv.container_list_lock);
+ spin_lock_init(&bat_priv->tvlv.handler_list_lock);
+ spin_lock_init(&bat_priv->softif_vlan_list_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list);
INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
@@ -119,6 +122,9 @@ int batadv_mesh_init(struct net_device *soft_iface)
INIT_LIST_HEAD(&bat_priv->tt.changes_list);
INIT_LIST_HEAD(&bat_priv->tt.req_list);
INIT_LIST_HEAD(&bat_priv->tt.roam_list);
+ INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
+ INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
+ INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
ret = batadv_originator_init(bat_priv);
if (ret < 0)
@@ -128,13 +134,6 @@ int batadv_mesh_init(struct net_device *soft_iface)
if (ret < 0)
goto err;
- batadv_tt_local_add(soft_iface, soft_iface->dev_addr,
- BATADV_NULL_IFINDEX);
-
- ret = batadv_vis_init(bat_priv);
- if (ret < 0)
- goto err;
-
ret = batadv_bla_init(bat_priv);
if (ret < 0)
goto err;
@@ -147,6 +146,8 @@ int batadv_mesh_init(struct net_device *soft_iface)
if (ret < 0)
goto err;
+ batadv_gw_init(bat_priv);
+
atomic_set(&bat_priv->gw.reselect, 0);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
@@ -165,8 +166,6 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_purge_outstanding_packets(bat_priv, NULL);
- batadv_vis_quit(bat_priv);
-
batadv_gw_node_purge(bat_priv);
batadv_nc_mesh_free(bat_priv);
batadv_dat_free(bat_priv);
@@ -185,6 +184,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
*/
batadv_originator_free(bat_priv);
+ batadv_gw_free(bat_priv);
+
free_percpu(bat_priv->bat_counters);
bat_priv->bat_counters = NULL;
@@ -255,6 +256,31 @@ out:
}
/**
+ * batadv_max_header_len - calculate maximum encapsulation overhead for a
+ * payload packet
+ *
+ * Return the maximum encapsulation overhead in bytes.
+ */
+int batadv_max_header_len(void)
+{
+ int header_len = 0;
+
+ header_len = max_t(int, header_len,
+ sizeof(struct batadv_unicast_packet));
+ header_len = max_t(int, header_len,
+ sizeof(struct batadv_unicast_4addr_packet));
+ header_len = max_t(int, header_len,
+ sizeof(struct batadv_bcast_packet));
+
+#ifdef CONFIG_BATMAN_ADV_NC
+ header_len = max_t(int, header_len,
+ sizeof(struct batadv_coded_packet));
+#endif
+
+ return header_len;
+}
+
+/**
* batadv_skb_set_priority - sets skb priority according to packet content
* @skb: the packet to be sent
* @offset: offset to the packet content
@@ -392,22 +418,31 @@ static void batadv_recv_handler_init(void)
for (i = 0; i < ARRAY_SIZE(batadv_rx_handler); i++)
batadv_rx_handler[i] = batadv_recv_unhandled_packet;
- /* batman icmp packet */
- batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
+ for (i = BATADV_UNICAST_MIN; i <= BATADV_UNICAST_MAX; i++)
+ batadv_rx_handler[i] = batadv_recv_unhandled_unicast_packet;
+
+ /* compile time checks for struct member offsets */
+ BUILD_BUG_ON(offsetof(struct batadv_unicast_4addr_packet, src) != 10);
+ BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, dest) != 4);
+ BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, dst) != 4);
+ BUILD_BUG_ON(offsetof(struct batadv_frag_packet, dest) != 4);
+ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, icmph.dst) != 4);
+ BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, icmph.dst) != 4);
+
+ /* broadcast packet */
+ batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
+
+ /* unicast packets ... */
/* unicast with 4 addresses packet */
batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet;
/* unicast packet */
batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet;
- /* fragmented unicast packet */
- batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_ucast_frag_packet;
- /* broadcast packet */
- batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
- /* vis packet */
- batadv_rx_handler[BATADV_VIS] = batadv_recv_vis_packet;
- /* Translation table query (request or response) */
- batadv_rx_handler[BATADV_TT_QUERY] = batadv_recv_tt_query;
- /* Roaming advertisement */
- batadv_rx_handler[BATADV_ROAM_ADV] = batadv_recv_roam_adv;
+ /* unicast tvlv packet */
+ batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv;
+ /* batman icmp packet */
+ batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
+ /* Fragmented packets */
+ batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_frag_packet;
}
int
@@ -415,7 +450,12 @@ batadv_recv_handler_register(uint8_t packet_type,
int (*recv_handler)(struct sk_buff *,
struct batadv_hard_iface *))
{
- if (batadv_rx_handler[packet_type] != &batadv_recv_unhandled_packet)
+ int (*curr)(struct sk_buff *,
+ struct batadv_hard_iface *);
+ curr = batadv_rx_handler[packet_type];
+
+ if ((curr != batadv_recv_unhandled_packet) &&
+ (curr != batadv_recv_unhandled_unicast_packet))
return -EBUSY;
batadv_rx_handler[packet_type] = recv_handler;
@@ -461,7 +501,9 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
!bat_algo_ops->bat_iface_update_mac ||
!bat_algo_ops->bat_primary_iface_set ||
!bat_algo_ops->bat_ogm_schedule ||
- !bat_algo_ops->bat_ogm_emit) {
+ !bat_algo_ops->bat_ogm_emit ||
+ !bat_algo_ops->bat_neigh_cmp ||
+ !bat_algo_ops->bat_neigh_is_equiv_or_better) {
pr_info("Routing algo '%s' does not implement required ops\n",
bat_algo_ops->name);
ret = -EINVAL;
@@ -536,6 +578,601 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
return htonl(crc);
}
+/**
+ * batadv_tvlv_handler_free_ref - decrement the tvlv handler refcounter and
+ * possibly free it
+ * @tvlv_handler: the tvlv handler to free
+ */
+static void
+batadv_tvlv_handler_free_ref(struct batadv_tvlv_handler *tvlv_handler)
+{
+ if (atomic_dec_and_test(&tvlv_handler->refcount))
+ kfree_rcu(tvlv_handler, rcu);
+}
+
+/**
+ * batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list
+ * based on the provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv handler type to look for
+ * @version: tvlv handler version to look for
+ *
+ * Returns tvlv handler if found or NULL otherwise.
+ */
+static struct batadv_tvlv_handler
+*batadv_tvlv_handler_get(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version)
+{
+ struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tvlv_handler_tmp,
+ &bat_priv->tvlv.handler_list, list) {
+ if (tvlv_handler_tmp->type != type)
+ continue;
+
+ if (tvlv_handler_tmp->version != version)
+ continue;
+
+ if (!atomic_inc_not_zero(&tvlv_handler_tmp->refcount))
+ continue;
+
+ tvlv_handler = tvlv_handler_tmp;
+ break;
+ }
+ rcu_read_unlock();
+
+ return tvlv_handler;
+}
+
+/**
+ * batadv_tvlv_container_free_ref - decrement the tvlv container refcounter and
+ * possibly free it
+ * @tvlv_handler: the tvlv container to free
+ */
+static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv)
+{
+ if (atomic_dec_and_test(&tvlv->refcount))
+ kfree(tvlv);
+}
+
+/**
+ * batadv_tvlv_container_get - retrieve tvlv container from the tvlv container
+ * list based on the provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv container type to look for
+ * @version: tvlv container version to look for
+ *
+ * Has to be called with the appropriate locks being acquired
+ * (tvlv.container_list_lock).
+ *
+ * Returns tvlv container if found or NULL otherwise.
+ */
+static struct batadv_tvlv_container
+*batadv_tvlv_container_get(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version)
+{
+ struct batadv_tvlv_container *tvlv_tmp, *tvlv = NULL;
+
+ hlist_for_each_entry(tvlv_tmp, &bat_priv->tvlv.container_list, list) {
+ if (tvlv_tmp->tvlv_hdr.type != type)
+ continue;
+
+ if (tvlv_tmp->tvlv_hdr.version != version)
+ continue;
+
+ if (!atomic_inc_not_zero(&tvlv_tmp->refcount))
+ continue;
+
+ tvlv = tvlv_tmp;
+ break;
+ }
+
+ return tvlv;
+}
+
+/**
+ * batadv_tvlv_container_list_size - calculate the size of the tvlv container
+ * list entries
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Has to be called with the appropriate locks being acquired
+ * (tvlv.container_list_lock).
+ *
+ * Returns size of all currently registered tvlv containers in bytes.
+ */
+static uint16_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
+{
+ struct batadv_tvlv_container *tvlv;
+ uint16_t tvlv_len = 0;
+
+ hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
+ tvlv_len += sizeof(struct batadv_tvlv_hdr);
+ tvlv_len += ntohs(tvlv->tvlv_hdr.len);
+ }
+
+ return tvlv_len;
+}
+
+/**
+ * batadv_tvlv_container_remove - remove tvlv container from the tvlv container
+ * list
+ * @tvlv: the to be removed tvlv container
+ *
+ * Has to be called with the appropriate locks being acquired
+ * (tvlv.container_list_lock).
+ */
+static void batadv_tvlv_container_remove(struct batadv_tvlv_container *tvlv)
+{
+ if (!tvlv)
+ return;
+
+ hlist_del(&tvlv->list);
+
+ /* first call to decrement the counter, second call to free */
+ batadv_tvlv_container_free_ref(tvlv);
+ batadv_tvlv_container_free_ref(tvlv);
+}
+
+/**
+ * batadv_tvlv_container_unregister - unregister tvlv container based on the
+ * provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv container type to unregister
+ * @version: tvlv container type to unregister
+ */
+void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version)
+{
+ struct batadv_tvlv_container *tvlv;
+
+ spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+ tvlv = batadv_tvlv_container_get(bat_priv, type, version);
+ batadv_tvlv_container_remove(tvlv);
+ spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+}
+
+/**
+ * batadv_tvlv_container_register - register tvlv type, version and content
+ * to be propagated with each (primary interface) OGM
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv container type
+ * @version: tvlv container version
+ * @tvlv_value: tvlv container content
+ * @tvlv_value_len: tvlv container content length
+ *
+ * If a container of the same type and version was already registered the new
+ * content is going to replace the old one.
+ */
+void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version,
+ void *tvlv_value, uint16_t tvlv_value_len)
+{
+ struct batadv_tvlv_container *tvlv_old, *tvlv_new;
+
+ if (!tvlv_value)
+ tvlv_value_len = 0;
+
+ tvlv_new = kzalloc(sizeof(*tvlv_new) + tvlv_value_len, GFP_ATOMIC);
+ if (!tvlv_new)
+ return;
+
+ tvlv_new->tvlv_hdr.version = version;
+ tvlv_new->tvlv_hdr.type = type;
+ tvlv_new->tvlv_hdr.len = htons(tvlv_value_len);
+
+ memcpy(tvlv_new + 1, tvlv_value, ntohs(tvlv_new->tvlv_hdr.len));
+ INIT_HLIST_NODE(&tvlv_new->list);
+ atomic_set(&tvlv_new->refcount, 1);
+
+ spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+ tvlv_old = batadv_tvlv_container_get(bat_priv, type, version);
+ batadv_tvlv_container_remove(tvlv_old);
+ hlist_add_head(&tvlv_new->list, &bat_priv->tvlv.container_list);
+ spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+}
+
+/**
+ * batadv_tvlv_realloc_packet_buff - reallocate packet buffer to accomodate
+ * requested packet size
+ * @packet_buff: packet buffer
+ * @packet_buff_len: packet buffer size
+ * @packet_min_len: requested packet minimum size
+ * @additional_packet_len: requested additional packet size on top of minimum
+ * size
+ *
+ * Returns true of the packet buffer could be changed to the requested size,
+ * false otherwise.
+ */
+static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff,
+ int *packet_buff_len,
+ int min_packet_len,
+ int additional_packet_len)
+{
+ unsigned char *new_buff;
+
+ new_buff = kmalloc(min_packet_len + additional_packet_len, GFP_ATOMIC);
+
+ /* keep old buffer if kmalloc should fail */
+ if (new_buff) {
+ memcpy(new_buff, *packet_buff, min_packet_len);
+ kfree(*packet_buff);
+ *packet_buff = new_buff;
+ *packet_buff_len = min_packet_len + additional_packet_len;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * batadv_tvlv_container_ogm_append - append tvlv container content to given
+ * OGM packet buffer
+ * @bat_priv: the bat priv with all the soft interface information
+ * @packet_buff: ogm packet buffer
+ * @packet_buff_len: ogm packet buffer size including ogm header and tvlv
+ * content
+ * @packet_min_len: ogm header size to be preserved for the OGM itself
+ *
+ * The ogm packet might be enlarged or shrunk depending on the current size
+ * and the size of the to-be-appended tvlv containers.
+ *
+ * Returns size of all appended tvlv containers in bytes.
+ */
+uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ unsigned char **packet_buff,
+ int *packet_buff_len,
+ int packet_min_len)
+{
+ struct batadv_tvlv_container *tvlv;
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ uint16_t tvlv_value_len;
+ void *tvlv_value;
+ bool ret;
+
+ spin_lock_bh(&bat_priv->tvlv.container_list_lock);
+ tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
+
+ ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len,
+ packet_min_len, tvlv_value_len);
+
+ if (!ret)
+ goto end;
+
+ if (!tvlv_value_len)
+ goto end;
+
+ tvlv_value = (*packet_buff) + packet_min_len;
+
+ hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) {
+ tvlv_hdr = tvlv_value;
+ tvlv_hdr->type = tvlv->tvlv_hdr.type;
+ tvlv_hdr->version = tvlv->tvlv_hdr.version;
+ tvlv_hdr->len = tvlv->tvlv_hdr.len;
+ tvlv_value = tvlv_hdr + 1;
+ memcpy(tvlv_value, tvlv + 1, ntohs(tvlv->tvlv_hdr.len));
+ tvlv_value = (uint8_t *)tvlv_value + ntohs(tvlv->tvlv_hdr.len);
+ }
+
+end:
+ spin_unlock_bh(&bat_priv->tvlv.container_list_lock);
+ return tvlv_value_len;
+}
+
+/**
+ * batadv_tvlv_call_handler - parse the given tvlv buffer to call the
+ * appropriate handlers
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tvlv_handler: tvlv callback function handling the tvlv content
+ * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @orig_node: orig node emitting the ogm packet
+ * @src: source mac address of the unicast packet
+ * @dst: destination mac address of the unicast packet
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ *
+ * Returns success if handler was not found or the return value of the handler
+ * callback.
+ */
+static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
+ struct batadv_tvlv_handler *tvlv_handler,
+ bool ogm_source,
+ struct batadv_orig_node *orig_node,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value, uint16_t tvlv_value_len)
+{
+ if (!tvlv_handler)
+ return NET_RX_SUCCESS;
+
+ if (ogm_source) {
+ if (!tvlv_handler->ogm_handler)
+ return NET_RX_SUCCESS;
+
+ if (!orig_node)
+ return NET_RX_SUCCESS;
+
+ tvlv_handler->ogm_handler(bat_priv, orig_node,
+ BATADV_NO_FLAGS,
+ tvlv_value, tvlv_value_len);
+ tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+ } else {
+ if (!src)
+ return NET_RX_SUCCESS;
+
+ if (!dst)
+ return NET_RX_SUCCESS;
+
+ if (!tvlv_handler->unicast_handler)
+ return NET_RX_SUCCESS;
+
+ return tvlv_handler->unicast_handler(bat_priv, src,
+ dst, tvlv_value,
+ tvlv_value_len);
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_tvlv_containers_process - parse the given tvlv buffer to call the
+ * appropriate handlers
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @orig_node: orig node emitting the ogm packet
+ * @src: source mac address of the unicast packet
+ * @dst: destination mac address of the unicast packet
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ *
+ * Returns success when processing an OGM or the return value of all called
+ * handler callbacks.
+ */
+int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+ bool ogm_source,
+ struct batadv_orig_node *orig_node,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value, uint16_t tvlv_value_len)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ uint16_t tvlv_value_cont_len;
+ uint8_t cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+ int ret = NET_RX_SUCCESS;
+
+ while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
+ tvlv_hdr = tvlv_value;
+ tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+ tvlv_value = tvlv_hdr + 1;
+ tvlv_value_len -= sizeof(*tvlv_hdr);
+
+ if (tvlv_value_cont_len > tvlv_value_len)
+ break;
+
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+ tvlv_hdr->type,
+ tvlv_hdr->version);
+
+ ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
+ ogm_source, orig_node,
+ src, dst, tvlv_value,
+ tvlv_value_cont_len);
+ if (tvlv_handler)
+ batadv_tvlv_handler_free_ref(tvlv_handler);
+ tvlv_value = (uint8_t *)tvlv_value + tvlv_value_cont_len;
+ tvlv_value_len -= tvlv_value_cont_len;
+ }
+
+ if (!ogm_source)
+ return ret;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tvlv_handler,
+ &bat_priv->tvlv.handler_list, list) {
+ if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
+ !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+ tvlv_handler->ogm_handler(bat_priv, orig_node,
+ cifnotfound, NULL, 0);
+
+ tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
+ }
+ rcu_read_unlock();
+
+ return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate
+ * handlers
+ * @bat_priv: the bat priv with all the soft interface information
+ * @batadv_ogm_packet: ogm packet containing the tvlv containers
+ * @orig_node: orig node emitting the ogm packet
+ */
+void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+ struct batadv_ogm_packet *batadv_ogm_packet,
+ struct batadv_orig_node *orig_node)
+{
+ void *tvlv_value;
+ uint16_t tvlv_value_len;
+
+ if (!batadv_ogm_packet)
+ return;
+
+ tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len);
+ if (!tvlv_value_len)
+ return;
+
+ tvlv_value = batadv_ogm_packet + 1;
+
+ batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL,
+ tvlv_value, tvlv_value_len);
+}
+
+/**
+ * batadv_tvlv_handler_register - register tvlv handler based on the provided
+ * type and version (both need to match) for ogm tvlv payload and/or unicast
+ * payload
+ * @bat_priv: the bat priv with all the soft interface information
+ * @optr: ogm tvlv handler callback function. This function receives the orig
+ * node, flags and the tvlv content as argument to process.
+ * @uptr: unicast tvlv handler callback function. This function receives the
+ * source & destination of the unicast packet as well as the tvlv content
+ * to process.
+ * @type: tvlv handler type to be registered
+ * @version: tvlv handler version to be registered
+ * @flags: flags to enable or disable TVLV API behavior
+ */
+void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
+ void (*optr)(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value,
+ uint16_t tvlv_value_len),
+ int (*uptr)(struct batadv_priv *bat_priv,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value,
+ uint16_t tvlv_value_len),
+ uint8_t type, uint8_t version, uint8_t flags)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
+ if (tvlv_handler) {
+ batadv_tvlv_handler_free_ref(tvlv_handler);
+ return;
+ }
+
+ tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC);
+ if (!tvlv_handler)
+ return;
+
+ tvlv_handler->ogm_handler = optr;
+ tvlv_handler->unicast_handler = uptr;
+ tvlv_handler->type = type;
+ tvlv_handler->version = version;
+ tvlv_handler->flags = flags;
+ atomic_set(&tvlv_handler->refcount, 1);
+ INIT_HLIST_NODE(&tvlv_handler->list);
+
+ spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
+ hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list);
+ spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
+}
+
+/**
+ * batadv_tvlv_handler_unregister - unregister tvlv handler based on the
+ * provided type and version (both need to match)
+ * @bat_priv: the bat priv with all the soft interface information
+ * @type: tvlv handler type to be unregistered
+ * @version: tvlv handler version to be unregistered
+ */
+void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
+ if (!tvlv_handler)
+ return;
+
+ batadv_tvlv_handler_free_ref(tvlv_handler);
+ spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
+ hlist_del_rcu(&tvlv_handler->list);
+ spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
+ batadv_tvlv_handler_free_ref(tvlv_handler);
+}
+
+/**
+ * batadv_tvlv_unicast_send - send a unicast packet with tvlv payload to the
+ * specified host
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: source mac address of the unicast packet
+ * @dst: destination mac address of the unicast packet
+ * @type: tvlv type
+ * @version: tvlv version
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ */
+void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src,
+ uint8_t *dst, uint8_t type, uint8_t version,
+ void *tvlv_value, uint16_t tvlv_value_len)
+{
+ struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ struct batadv_orig_node *orig_node;
+ struct sk_buff *skb = NULL;
+ unsigned char *tvlv_buff;
+ unsigned int tvlv_len;
+ ssize_t hdr_len = sizeof(*unicast_tvlv_packet);
+ bool ret = false;
+
+ orig_node = batadv_orig_hash_find(bat_priv, dst);
+ if (!orig_node)
+ goto out;
+
+ tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len;
+
+ skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + hdr_len + tvlv_len);
+ if (!skb)
+ goto out;
+
+ skb->priority = TC_PRIO_CONTROL;
+ skb_reserve(skb, ETH_HLEN);
+ tvlv_buff = skb_put(skb, sizeof(*unicast_tvlv_packet) + tvlv_len);
+ unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)tvlv_buff;
+ unicast_tvlv_packet->header.packet_type = BATADV_UNICAST_TVLV;
+ unicast_tvlv_packet->header.version = BATADV_COMPAT_VERSION;
+ unicast_tvlv_packet->header.ttl = BATADV_TTL;
+ unicast_tvlv_packet->reserved = 0;
+ unicast_tvlv_packet->tvlv_len = htons(tvlv_len);
+ unicast_tvlv_packet->align = 0;
+ memcpy(unicast_tvlv_packet->src, src, ETH_ALEN);
+ memcpy(unicast_tvlv_packet->dst, dst, ETH_ALEN);
+
+ tvlv_buff = (unsigned char *)(unicast_tvlv_packet + 1);
+ tvlv_hdr = (struct batadv_tvlv_hdr *)tvlv_buff;
+ tvlv_hdr->version = version;
+ tvlv_hdr->type = type;
+ tvlv_hdr->len = htons(tvlv_value_len);
+ tvlv_buff += sizeof(*tvlv_hdr);
+ memcpy(tvlv_buff, tvlv_value, tvlv_value_len);
+
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
+ ret = true;
+
+out:
+ if (skb && !ret)
+ kfree_skb(skb);
+ if (orig_node)
+ batadv_orig_node_free_ref(orig_node);
+}
+
+/**
+ * batadv_get_vid - extract the VLAN identifier from skb if any
+ * @skb: the buffer containing the packet
+ * @header_len: length of the batman header preceding the ethernet header
+ *
+ * If the packet embedded in the skb is vlan tagged this function returns the
+ * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned.
+ */
+unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len)
+{
+ struct ethhdr *ethhdr = (struct ethhdr *)(skb->data + header_len);
+ struct vlan_ethhdr *vhdr;
+ unsigned short vid;
+
+ if (ethhdr->h_proto != htons(ETH_P_8021Q))
+ return BATADV_NO_FLAGS;
+
+ if (!pskb_may_pull(skb, header_len + VLAN_ETH_HLEN))
+ return BATADV_NO_FLAGS;
+
+ vhdr = (struct vlan_ethhdr *)(skb->data + header_len);
+ vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
+ vid |= BATADV_VLAN_HAS_TAG;
+
+ return vid;
+}
+
static int batadv_param_set_ra(const char *val, const struct kernel_param *kp)
{
struct batadv_algo_ops *bat_algo_ops;
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 24675523930f..f94f287b8670 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -20,13 +20,13 @@
#ifndef _NET_BATMAN_ADV_MAIN_H_
#define _NET_BATMAN_ADV_MAIN_H_
-#define BATADV_DRIVER_AUTHOR "Marek Lindner <lindner_marek@yahoo.de>, " \
- "Simon Wunderlich <siwu@hrz.tu-chemnitz.de>"
+#define BATADV_DRIVER_AUTHOR "Marek Lindner <mareklindner@neomailbox.ch>, " \
+ "Simon Wunderlich <sw@simonwunderlich.de>"
#define BATADV_DRIVER_DESC "B.A.T.M.A.N. advanced"
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2013.4.0"
+#define BATADV_SOURCE_VERSION "2013.5.0"
#endif
/* B.A.T.M.A.N. parameters */
@@ -86,7 +86,11 @@
/* numbers of originator to contact for any PUT/GET DHT operation */
#define BATADV_DAT_CANDIDATES_NUM 3
-#define BATADV_VIS_INTERVAL 5000 /* 5 seconds */
+/**
+ * BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ
+ * at most from the primary one in order to be still considered acceptable
+ */
+#define BATADV_TQ_SIMILARITY_THRESHOLD 50
/* how much worse secondary interfaces may be to be considered as bonding
* candidates
@@ -133,6 +137,15 @@ enum batadv_uev_type {
#define BATADV_GW_THRESHOLD 50
+/* Number of fragment chains for each orig_node */
+#define BATADV_FRAG_BUFFER_COUNT 8
+/* Maximum number of fragments for one packet */
+#define BATADV_FRAG_MAX_FRAGMENTS 16
+/* Maxumim size of each fragment */
+#define BATADV_FRAG_MAX_FRAG_SIZE 1400
+/* Time to keep fragments while waiting for rest of the fragments */
+#define BATADV_FRAG_TIMEOUT 10000
+
#define BATADV_DAT_CANDIDATE_NOT_FOUND 0
#define BATADV_DAT_CANDIDATE_ORIG 1
@@ -160,15 +173,9 @@ enum batadv_uev_type {
#include <net/rtnetlink.h>
#include <linux/jiffies.h>
#include <linux/seq_file.h>
-#include "types.h"
+#include <linux/if_vlan.h>
-/**
- * batadv_vlan_flags - flags for the four MSB of any vlan ID field
- * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
- */
-enum batadv_vlan_flags {
- BATADV_VLAN_HAS_TAG = BIT(15),
-};
+#include "types.h"
#define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \
(int)(vid & VLAN_VID_MASK) : -1)
@@ -184,6 +191,7 @@ void batadv_mesh_free(struct net_device *soft_iface);
int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr);
struct batadv_hard_iface *
batadv_seq_print_text_primary_if_get(struct seq_file *seq);
+int batadv_max_header_len(void);
void batadv_skb_set_priority(struct sk_buff *skb, int offset);
int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype,
@@ -326,4 +334,40 @@ static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv,
*/
#define BATADV_SKB_CB(__skb) ((struct batadv_skb_cb *)&((__skb)->cb[0]))
+void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version,
+ void *tvlv_value, uint16_t tvlv_value_len);
+uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
+ unsigned char **packet_buff,
+ int *packet_buff_len,
+ int packet_min_len);
+void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
+ struct batadv_ogm_packet *batadv_ogm_packet,
+ struct batadv_orig_node *orig_node);
+void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version);
+
+void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
+ void (*optr)(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value,
+ uint16_t tvlv_value_len),
+ int (*uptr)(struct batadv_priv *bat_priv,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value,
+ uint16_t tvlv_value_len),
+ uint8_t type, uint8_t version, uint8_t flags);
+void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
+ uint8_t type, uint8_t version);
+int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
+ bool ogm_source,
+ struct batadv_orig_node *orig_node,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_buff, uint16_t tvlv_buff_len);
+void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src,
+ uint8_t *dst, uint8_t type, uint8_t version,
+ void *tvlv_value, uint16_t tvlv_value_len);
+unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len);
+
#endif /* _NET_BATMAN_ADV_MAIN_H_ */
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index 4ecc0b6bf8ab..351e199bc0af 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -59,6 +59,59 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
}
/**
+ * batadv_nc_tvlv_container_update - update the network coding tvlv container
+ * after network coding setting change
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv)
+{
+ char nc_mode;
+
+ nc_mode = atomic_read(&bat_priv->network_coding);
+
+ switch (nc_mode) {
+ case 0:
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1);
+ break;
+ case 1:
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_NC, 1,
+ NULL, 0);
+ break;
+ }
+}
+
+/**
+ * batadv_nc_status_update - update the network coding tvlv container after
+ * network coding setting change
+ * @net_dev: the soft interface net device
+ */
+void batadv_nc_status_update(struct net_device *net_dev)
+{
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ batadv_nc_tvlv_container_update(bat_priv);
+}
+
+/**
+ * batadv_nc_tvlv_ogm_handler_v1 - process incoming nc tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
+ orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_NC;
+ else
+ orig->capabilities |= BATADV_ORIG_CAPA_HAS_NC;
+}
+
+/**
* batadv_nc_mesh_init - initialise coding hash table and start house keeping
* @bat_priv: the bat priv with all the soft interface information
*/
@@ -87,6 +140,10 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
batadv_nc_start_timer(bat_priv);
+ batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1,
+ NULL, BATADV_TVLV_NC, 1,
+ BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ batadv_nc_tvlv_container_update(bat_priv);
return 0;
err:
@@ -802,6 +859,10 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
if (!atomic_read(&bat_priv->network_coding))
goto out;
+ /* check if orig node is network coding enabled */
+ if (!(orig_node->capabilities & BATADV_ORIG_CAPA_HAS_NC))
+ goto out;
+
/* accept ogms from 'good' neighbors and single hop neighbors */
if (!batadv_can_nc_with_orig(bat_priv, orig_node, ogm_packet) &&
!is_single_hop_neigh)
@@ -942,7 +1003,7 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
struct batadv_nc_packet *nc_packet,
struct batadv_neigh_node *neigh_node)
{
- uint8_t tq_weighted_neigh, tq_weighted_coding;
+ uint8_t tq_weighted_neigh, tq_weighted_coding, tq_tmp;
struct sk_buff *skb_dest, *skb_src;
struct batadv_unicast_packet *packet1;
struct batadv_unicast_packet *packet2;
@@ -967,8 +1028,10 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
if (!router_coding)
goto out;
- tq_weighted_neigh = batadv_nc_random_weight_tq(router_neigh->tq_avg);
- tq_weighted_coding = batadv_nc_random_weight_tq(router_coding->tq_avg);
+ tq_tmp = batadv_nc_random_weight_tq(router_neigh->bat_iv.tq_avg);
+ tq_weighted_neigh = tq_tmp;
+ tq_tmp = batadv_nc_random_weight_tq(router_coding->bat_iv.tq_avg);
+ tq_weighted_coding = tq_tmp;
/* Select one destination for the MAC-header dst-field based on
* weighted TQ-values.
@@ -1735,6 +1798,8 @@ free_nc_packet:
*/
void batadv_nc_mesh_free(struct batadv_priv *bat_priv)
{
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1);
+ batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_NC, 1);
cancel_delayed_work_sync(&bat_priv->nc.work);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h
index ddfa618e80bf..d4fd315b5261 100644
--- a/net/batman-adv/network-coding.h
+++ b/net/batman-adv/network-coding.h
@@ -22,6 +22,7 @@
#ifdef CONFIG_BATMAN_ADV_NC
+void batadv_nc_status_update(struct net_device *net_dev);
int batadv_nc_init(void);
int batadv_nc_mesh_init(struct batadv_priv *bat_priv);
void batadv_nc_mesh_free(struct batadv_priv *bat_priv);
@@ -47,6 +48,10 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
#else /* ifdef CONFIG_BATMAN_ADV_NC */
+static inline void batadv_nc_status_update(struct net_device *net_dev)
+{
+}
+
static inline int batadv_nc_init(void)
{
return 0;
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index f50553a7de62..8ab14340d10f 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -25,10 +25,10 @@
#include "routing.h"
#include "gateway_client.h"
#include "hard-interface.h"
-#include "unicast.h"
#include "soft-interface.h"
#include "bridge_loop_avoidance.h"
#include "network-coding.h"
+#include "fragmentation.h"
/* hash class keys */
static struct lock_class_key batadv_orig_hash_lock_class_key;
@@ -36,7 +36,7 @@ static struct lock_class_key batadv_orig_hash_lock_class_key;
static void batadv_purge_orig(struct work_struct *work);
/* returns 1 if they are the same originator */
-static int batadv_compare_orig(const struct hlist_node *node, const void *data2)
+int batadv_compare_orig(const struct hlist_node *node, const void *data2)
{
const void *data1 = container_of(node, struct batadv_orig_node,
hash_entry);
@@ -44,6 +44,88 @@ static int batadv_compare_orig(const struct hlist_node *node, const void *data2)
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
+/**
+ * batadv_orig_node_vlan_get - get an orig_node_vlan object
+ * @orig_node: the originator serving the VLAN
+ * @vid: the VLAN identifier
+ *
+ * Returns the vlan object identified by vid and belonging to orig_node or NULL
+ * if it does not exist.
+ */
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
+ unsigned short vid)
+{
+ struct batadv_orig_node_vlan *vlan = NULL, *tmp;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(tmp, &orig_node->vlan_list, list) {
+ if (tmp->vid != vid)
+ continue;
+
+ if (!atomic_inc_not_zero(&tmp->refcount))
+ continue;
+
+ vlan = tmp;
+
+ break;
+ }
+ rcu_read_unlock();
+
+ return vlan;
+}
+
+/**
+ * batadv_orig_node_vlan_new - search and possibly create an orig_node_vlan
+ * object
+ * @orig_node: the originator serving the VLAN
+ * @vid: the VLAN identifier
+ *
+ * Returns NULL in case of failure or the vlan object identified by vid and
+ * belonging to orig_node otherwise. The object is created and added to the list
+ * if it does not exist.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
+ unsigned short vid)
+{
+ struct batadv_orig_node_vlan *vlan;
+
+ spin_lock_bh(&orig_node->vlan_list_lock);
+
+ /* first look if an object for this vid already exists */
+ vlan = batadv_orig_node_vlan_get(orig_node, vid);
+ if (vlan)
+ goto out;
+
+ vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
+ if (!vlan)
+ goto out;
+
+ atomic_set(&vlan->refcount, 2);
+ vlan->vid = vid;
+
+ list_add_rcu(&vlan->list, &orig_node->vlan_list);
+
+out:
+ spin_unlock_bh(&orig_node->vlan_list_lock);
+
+ return vlan;
+}
+
+/**
+ * batadv_orig_node_vlan_free_ref - decrement the refcounter and possibly free
+ * the originator-vlan object
+ * @orig_vlan: the originator-vlan object to release
+ */
+void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan)
+{
+ if (atomic_dec_and_test(&orig_vlan->refcount))
+ kfree_rcu(orig_vlan, rcu);
+}
+
int batadv_originator_init(struct batadv_priv *bat_priv)
{
if (bat_priv->orig_hash)
@@ -90,11 +172,20 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node)
return router;
}
+/**
+ * batadv_neigh_node_new - create and init a new neigh_node object
+ * @hard_iface: the interface where the neighbour is connected to
+ * @neigh_addr: the mac address of the neighbour interface
+ * @orig_node: originator object representing the neighbour
+ *
+ * Allocates a new neigh_node object and initialises all the generic fields.
+ * Returns the new object or NULL on failure.
+ */
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
- const uint8_t *neigh_addr)
+ const uint8_t *neigh_addr,
+ struct batadv_orig_node *orig_node)
{
- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct batadv_neigh_node *neigh_node;
neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
@@ -104,15 +195,14 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
INIT_HLIST_NODE(&neigh_node->list);
memcpy(neigh_node->addr, neigh_addr, ETH_ALEN);
- spin_lock_init(&neigh_node->lq_update_lock);
+ neigh_node->if_incoming = hard_iface;
+ neigh_node->orig_node = orig_node;
+
+ INIT_LIST_HEAD(&neigh_node->bonding_list);
/* extra reference for return */
atomic_set(&neigh_node->refcount, 2);
- batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Creating new neighbor %pM on interface %s\n", neigh_addr,
- hard_iface->net_dev->name);
-
out:
return neigh_node;
}
@@ -146,13 +236,15 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
/* Free nc_nodes */
batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
- batadv_frag_list_free(&orig_node->frag_list);
- batadv_tt_global_del_orig(orig_node->bat_priv, orig_node,
+ batadv_frag_purge_orig(orig_node, NULL);
+
+ batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, -1,
"originator timed out");
+ if (orig_node->bat_priv->bat_algo_ops->bat_orig_free)
+ orig_node->bat_priv->bat_algo_ops->bat_orig_free(orig_node);
+
kfree(orig_node->tt_buff);
- kfree(orig_node->bcast_own);
- kfree(orig_node->bcast_own_sum);
kfree(orig_node);
}
@@ -210,20 +302,22 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
batadv_hash_destroy(hash);
}
-/* this function finds or creates an originator entry for the given
- * address if it does not exits
+/**
+ * batadv_orig_node_new - creates a new orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the originator
+ *
+ * Creates a new originator object and initialise all the generic fields.
+ * The new object is not added to the originator list.
+ * Returns the newly created object or NULL on failure.
*/
-struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
+struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const uint8_t *addr)
{
struct batadv_orig_node *orig_node;
- int size;
- int hash_added;
+ struct batadv_orig_node_vlan *vlan;
unsigned long reset_time;
-
- orig_node = batadv_orig_hash_find(bat_priv, addr);
- if (orig_node)
- return orig_node;
+ int i;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Creating new originator: %pM\n", addr);
@@ -234,10 +328,12 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
INIT_HLIST_HEAD(&orig_node->neigh_list);
INIT_LIST_HEAD(&orig_node->bond_list);
- spin_lock_init(&orig_node->ogm_cnt_lock);
+ INIT_LIST_HEAD(&orig_node->vlan_list);
spin_lock_init(&orig_node->bcast_seqno_lock);
spin_lock_init(&orig_node->neigh_list_lock);
spin_lock_init(&orig_node->tt_buff_lock);
+ spin_lock_init(&orig_node->tt_lock);
+ spin_lock_init(&orig_node->vlan_list_lock);
batadv_nc_init_orig(orig_node);
@@ -249,43 +345,32 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
memcpy(orig_node->orig, addr, ETH_ALEN);
batadv_dat_init_orig_node_addr(orig_node);
orig_node->router = NULL;
- orig_node->tt_crc = 0;
atomic_set(&orig_node->last_ttvn, 0);
orig_node->tt_buff = NULL;
orig_node->tt_buff_len = 0;
- atomic_set(&orig_node->tt_size, 0);
reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
orig_node->bcast_seqno_reset = reset_time;
orig_node->batman_seqno_reset = reset_time;
atomic_set(&orig_node->bond_candidates, 0);
- size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS;
-
- orig_node->bcast_own = kzalloc(size, GFP_ATOMIC);
- if (!orig_node->bcast_own)
+ /* create a vlan object for the "untagged" LAN */
+ vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS);
+ if (!vlan)
goto free_orig_node;
+ /* batadv_orig_node_vlan_new() increases the refcounter.
+ * Immediately release vlan since it is not needed anymore in this
+ * context
+ */
+ batadv_orig_node_vlan_free_ref(vlan);
- size = bat_priv->num_ifaces * sizeof(uint8_t);
- orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
-
- INIT_LIST_HEAD(&orig_node->frag_list);
- orig_node->last_frag_packet = 0;
-
- if (!orig_node->bcast_own_sum)
- goto free_bcast_own;
-
- hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
- batadv_choose_orig, orig_node,
- &orig_node->hash_entry);
- if (hash_added != 0)
- goto free_bcast_own_sum;
+ for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
+ INIT_HLIST_HEAD(&orig_node->fragments[i].head);
+ spin_lock_init(&orig_node->fragments[i].lock);
+ orig_node->fragments[i].size = 0;
+ }
return orig_node;
-free_bcast_own_sum:
- kfree(orig_node->bcast_own_sum);
-free_bcast_own:
- kfree(orig_node->bcast_own);
free_orig_node:
kfree(orig_node);
return NULL;
@@ -294,15 +379,16 @@ free_orig_node:
static bool
batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- struct batadv_neigh_node **best_neigh_node)
+ struct batadv_neigh_node **best_neigh)
{
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
struct hlist_node *node_tmp;
struct batadv_neigh_node *neigh_node;
bool neigh_purged = false;
unsigned long last_seen;
struct batadv_hard_iface *if_incoming;
- *best_neigh_node = NULL;
+ *best_neigh = NULL;
spin_lock_bh(&orig_node->neigh_list_lock);
@@ -335,9 +421,12 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
batadv_bonding_candidate_del(orig_node, neigh_node);
batadv_neigh_node_free_ref(neigh_node);
} else {
- if ((!*best_neigh_node) ||
- (neigh_node->tq_avg > (*best_neigh_node)->tq_avg))
- *best_neigh_node = neigh_node;
+ /* store the best_neighbour if this is the first
+ * iteration or if a better neighbor has been found
+ */
+ if (!*best_neigh ||
+ bao->bat_neigh_cmp(neigh_node, *best_neigh) > 0)
+ *best_neigh = neigh_node;
}
}
@@ -388,17 +477,14 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv)
hlist_for_each_entry_safe(orig_node, node_tmp,
head, hash_entry) {
if (batadv_purge_orig_node(bat_priv, orig_node)) {
- if (orig_node->gw_flags)
- batadv_gw_node_delete(bat_priv,
- orig_node);
+ batadv_gw_node_delete(bat_priv, orig_node);
hlist_del_rcu(&orig_node->hash_entry);
batadv_orig_node_free_ref(orig_node);
continue;
}
- if (batadv_has_timed_out(orig_node->last_frag_packet,
- BATADV_FRAG_TIMEOUT))
- batadv_frag_list_free(&orig_node->frag_list);
+ batadv_frag_purge_orig(orig_node,
+ batadv_frag_check_entry);
}
spin_unlock_bh(list_lock);
}
@@ -429,100 +515,26 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct batadv_priv *bat_priv = netdev_priv(net_dev);
- struct batadv_hashtable *hash = bat_priv->orig_hash;
- struct hlist_head *head;
struct batadv_hard_iface *primary_if;
- struct batadv_orig_node *orig_node;
- struct batadv_neigh_node *neigh_node, *neigh_node_tmp;
- int batman_count = 0;
- int last_seen_secs;
- int last_seen_msecs;
- unsigned long last_seen_jiffies;
- uint32_t i;
primary_if = batadv_seq_print_text_primary_if_get(seq);
if (!primary_if)
- goto out;
+ return 0;
- seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
+ seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
BATADV_SOURCE_VERSION, primary_if->net_dev->name,
- primary_if->net_dev->dev_addr, net_dev->name);
- seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
- "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE,
- "Nexthop", "outgoingIF", "Potential nexthops");
-
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- neigh_node = batadv_orig_node_get_router(orig_node);
- if (!neigh_node)
- continue;
-
- if (neigh_node->tq_avg == 0)
- goto next;
-
- last_seen_jiffies = jiffies - orig_node->last_seen;
- last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
- last_seen_secs = last_seen_msecs / 1000;
- last_seen_msecs = last_seen_msecs % 1000;
-
- seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:",
- orig_node->orig, last_seen_secs,
- last_seen_msecs, neigh_node->tq_avg,
- neigh_node->addr,
- neigh_node->if_incoming->net_dev->name);
-
- hlist_for_each_entry_rcu(neigh_node_tmp,
- &orig_node->neigh_list, list) {
- seq_printf(seq, " %pM (%3i)",
- neigh_node_tmp->addr,
- neigh_node_tmp->tq_avg);
- }
+ primary_if->net_dev->dev_addr, net_dev->name,
+ bat_priv->bat_algo_ops->name);
- seq_puts(seq, "\n");
- batman_count++;
+ batadv_hardif_free_ref(primary_if);
-next:
- batadv_neigh_node_free_ref(neigh_node);
- }
- rcu_read_unlock();
+ if (!bat_priv->bat_algo_ops->bat_orig_print) {
+ seq_puts(seq,
+ "No printing function for this routing protocol\n");
+ return 0;
}
- if (batman_count == 0)
- seq_puts(seq, "No batman nodes in range ...\n");
-
-out:
- if (primary_if)
- batadv_hardif_free_ref(primary_if);
- return 0;
-}
-
-static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node,
- int max_if_num)
-{
- void *data_ptr;
- size_t data_size, old_size;
-
- data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS;
- old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS;
- data_ptr = kmalloc(data_size, GFP_ATOMIC);
- if (!data_ptr)
- return -ENOMEM;
-
- memcpy(data_ptr, orig_node->bcast_own, old_size);
- kfree(orig_node->bcast_own);
- orig_node->bcast_own = data_ptr;
-
- data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
- if (!data_ptr)
- return -ENOMEM;
-
- memcpy(data_ptr, orig_node->bcast_own_sum,
- (max_if_num - 1) * sizeof(uint8_t));
- kfree(orig_node->bcast_own_sum);
- orig_node->bcast_own_sum = data_ptr;
+ bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq);
return 0;
}
@@ -531,6 +543,7 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
int max_if_num)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct hlist_head *head;
struct batadv_orig_node *orig_node;
@@ -545,10 +558,10 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- spin_lock_bh(&orig_node->ogm_cnt_lock);
- ret = batadv_orig_node_add_if(orig_node, max_if_num);
- spin_unlock_bh(&orig_node->ogm_cnt_lock);
-
+ ret = 0;
+ if (bao->bat_orig_add_if)
+ ret = bao->bat_orig_add_if(orig_node,
+ max_if_num);
if (ret == -ENOMEM)
goto err;
}
@@ -562,54 +575,6 @@ err:
return -ENOMEM;
}
-static int batadv_orig_node_del_if(struct batadv_orig_node *orig_node,
- int max_if_num, int del_if_num)
-{
- void *data_ptr = NULL;
- int chunk_size;
-
- /* last interface was removed */
- if (max_if_num == 0)
- goto free_bcast_own;
-
- chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS;
- data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
- if (!data_ptr)
- return -ENOMEM;
-
- /* copy first part */
- memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size);
-
- /* copy second part */
- memcpy((char *)data_ptr + del_if_num * chunk_size,
- orig_node->bcast_own + ((del_if_num + 1) * chunk_size),
- (max_if_num - del_if_num) * chunk_size);
-
-free_bcast_own:
- kfree(orig_node->bcast_own);
- orig_node->bcast_own = data_ptr;
-
- if (max_if_num == 0)
- goto free_own_sum;
-
- data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
- if (!data_ptr)
- return -ENOMEM;
-
- memcpy(data_ptr, orig_node->bcast_own_sum,
- del_if_num * sizeof(uint8_t));
-
- memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t),
- orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)),
- (max_if_num - del_if_num) * sizeof(uint8_t));
-
-free_own_sum:
- kfree(orig_node->bcast_own_sum);
- orig_node->bcast_own_sum = data_ptr;
-
- return 0;
-}
-
int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
int max_if_num)
{
@@ -618,6 +583,7 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
struct hlist_head *head;
struct batadv_hard_iface *hard_iface_tmp;
struct batadv_orig_node *orig_node;
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
uint32_t i;
int ret;
@@ -629,11 +595,11 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- spin_lock_bh(&orig_node->ogm_cnt_lock);
- ret = batadv_orig_node_del_if(orig_node, max_if_num,
- hard_iface->if_num);
- spin_unlock_bh(&orig_node->ogm_cnt_lock);
-
+ ret = 0;
+ if (bao->bat_orig_del_if)
+ ret = bao->bat_orig_del_if(orig_node,
+ max_if_num,
+ hard_iface->if_num);
if (ret == -ENOMEM)
goto err;
}
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index 7887b84a9af4..6f77d808a916 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -22,16 +22,18 @@
#include "hash.h"
+int batadv_compare_orig(const struct hlist_node *node, const void *data2);
int batadv_originator_init(struct batadv_priv *bat_priv);
void batadv_originator_free(struct batadv_priv *bat_priv);
void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
-struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
+struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const uint8_t *addr);
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
- const uint8_t *neigh_addr);
+ const uint8_t *neigh_addr,
+ struct batadv_orig_node *orig_node);
void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node);
struct batadv_neigh_node *
batadv_orig_node_get_router(struct batadv_orig_node *orig_node);
@@ -40,6 +42,13 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
int max_if_num);
int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
int max_if_num);
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
+ unsigned short vid);
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
+ unsigned short vid);
+void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan);
/* hashfunction to choose an entry in a hash table of given size
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index a51ccfc39da4..207459b62966 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -20,17 +20,34 @@
#ifndef _NET_BATMAN_ADV_PACKET_H_
#define _NET_BATMAN_ADV_PACKET_H_
+/**
+ * enum batadv_packettype - types for batman-adv encapsulated packets
+ * @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV
+ * @BATADV_BCAST: broadcast packets carrying broadcast payload
+ * @BATADV_CODED: network coded packets
+ *
+ * @BATADV_UNICAST: unicast packets carrying unicast payload traffic
+ * @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original
+ * payload packet
+ * @BATADV_UNICAST_4ADDR: unicast packet including the originator address of
+ * the sender
+ * @BATADV_ICMP: unicast packet like IP ICMP used for ping or traceroute
+ * @BATADV_UNICAST_TVLV: unicast packet carrying TVLV containers
+ */
enum batadv_packettype {
- BATADV_IV_OGM = 0x01,
- BATADV_ICMP = 0x02,
- BATADV_UNICAST = 0x03,
- BATADV_BCAST = 0x04,
- BATADV_VIS = 0x05,
- BATADV_UNICAST_FRAG = 0x06,
- BATADV_TT_QUERY = 0x07,
- BATADV_ROAM_ADV = 0x08,
- BATADV_UNICAST_4ADDR = 0x09,
- BATADV_CODED = 0x0a,
+ /* 0x00 - 0x3f: local packets or special rules for handling */
+ BATADV_IV_OGM = 0x00,
+ BATADV_BCAST = 0x01,
+ BATADV_CODED = 0x02,
+ /* 0x40 - 0x7f: unicast */
+#define BATADV_UNICAST_MIN 0x40
+ BATADV_UNICAST = 0x40,
+ BATADV_UNICAST_FRAG = 0x41,
+ BATADV_UNICAST_4ADDR = 0x42,
+ BATADV_ICMP = 0x43,
+ BATADV_UNICAST_TVLV = 0x44,
+#define BATADV_UNICAST_MAX 0x7f
+ /* 0x80 - 0xff: reserved */
};
/**
@@ -48,13 +65,21 @@ enum batadv_subtype {
};
/* this file is included by batctl which needs these defines */
-#define BATADV_COMPAT_VERSION 14
+#define BATADV_COMPAT_VERSION 15
+/**
+ * enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets
+ * @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was
+ * previously received from someone else than the best neighbor.
+ * @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address
+ * is used, and the packet travels its first hop.
+ * @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a
+ * one hop neighbor on the interface where it was originally received.
+ */
enum batadv_iv_flags {
- BATADV_NOT_BEST_NEXT_HOP = BIT(3),
- BATADV_PRIMARIES_FIRST_HOP = BIT(4),
- BATADV_VIS_SERVER = BIT(5),
- BATADV_DIRECTLINK = BIT(6),
+ BATADV_NOT_BEST_NEXT_HOP = BIT(0),
+ BATADV_PRIMARIES_FIRST_HOP = BIT(1),
+ BATADV_DIRECTLINK = BIT(2),
};
/* ICMP message types */
@@ -66,43 +91,44 @@ enum batadv_icmp_packettype {
BATADV_PARAMETER_PROBLEM = 12,
};
-/* vis defines */
-enum batadv_vis_packettype {
- BATADV_VIS_TYPE_SERVER_SYNC = 0,
- BATADV_VIS_TYPE_CLIENT_UPDATE = 1,
-};
-
-/* fragmentation defines */
-enum batadv_unicast_frag_flags {
- BATADV_UNI_FRAG_HEAD = BIT(0),
- BATADV_UNI_FRAG_LARGETAIL = BIT(1),
-};
+/* tt data subtypes */
+#define BATADV_TT_DATA_TYPE_MASK 0x0F
-/* TT_QUERY subtypes */
-#define BATADV_TT_QUERY_TYPE_MASK 0x3
-
-enum batadv_tt_query_packettype {
- BATADV_TT_REQUEST = 0,
- BATADV_TT_RESPONSE = 1,
-};
-
-/* TT_QUERY flags */
-enum batadv_tt_query_flags {
- BATADV_TT_FULL_TABLE = BIT(2),
+/**
+ * enum batadv_tt_data_flags - flags for tt data tvlv
+ * @BATADV_TT_OGM_DIFF: TT diff propagated through OGM
+ * @BATADV_TT_REQUEST: TT request message
+ * @BATADV_TT_RESPONSE: TT response message
+ * @BATADV_TT_FULL_TABLE: contains full table to replace existing table
+ */
+enum batadv_tt_data_flags {
+ BATADV_TT_OGM_DIFF = BIT(0),
+ BATADV_TT_REQUEST = BIT(1),
+ BATADV_TT_RESPONSE = BIT(2),
+ BATADV_TT_FULL_TABLE = BIT(4),
};
/* BATADV_TT_CLIENT flags.
* Flags from BIT(0) to BIT(7) are sent on the wire, while flags from BIT(8) to
- * BIT(15) are used for local computation only
+ * BIT(15) are used for local computation only.
+ * Flags from BIT(4) to BIT(7) are kept in sync with the rest of the network.
*/
enum batadv_tt_client_flags {
BATADV_TT_CLIENT_DEL = BIT(0),
BATADV_TT_CLIENT_ROAM = BIT(1),
- BATADV_TT_CLIENT_WIFI = BIT(2),
- BATADV_TT_CLIENT_TEMP = BIT(3),
+ BATADV_TT_CLIENT_WIFI = BIT(4),
BATADV_TT_CLIENT_NOPURGE = BIT(8),
BATADV_TT_CLIENT_NEW = BIT(9),
BATADV_TT_CLIENT_PENDING = BIT(10),
+ BATADV_TT_CLIENT_TEMP = BIT(11),
+};
+
+/**
+ * batadv_vlan_flags - flags for the four MSB of any vlan ID field
+ * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not
+ */
+enum batadv_vlan_flags {
+ BATADV_VLAN_HAS_TAG = BIT(15),
};
/* claim frame types for the bridge loop avoidance */
@@ -113,6 +139,22 @@ enum batadv_bla_claimframe {
BATADV_CLAIM_TYPE_REQUEST = 0x03,
};
+/**
+ * enum batadv_tvlv_type - tvlv type definitions
+ * @BATADV_TVLV_GW: gateway tvlv
+ * @BATADV_TVLV_DAT: distributed arp table tvlv
+ * @BATADV_TVLV_NC: network coding tvlv
+ * @BATADV_TVLV_TT: translation table tvlv
+ * @BATADV_TVLV_ROAM: roaming advertisement tvlv
+ */
+enum batadv_tvlv_type {
+ BATADV_TVLV_GW = 0x01,
+ BATADV_TVLV_DAT = 0x02,
+ BATADV_TVLV_NC = 0x03,
+ BATADV_TVLV_TT = 0x04,
+ BATADV_TVLV_ROAM = 0x05,
+};
+
/* the destination hardware field in the ARP frame is used to
* transport the claim type and the group id
*/
@@ -131,47 +173,74 @@ struct batadv_header {
*/
};
+/**
+ * struct batadv_ogm_packet - ogm (routing protocol) packet
+ * @header: common batman packet header
+ * @flags: contains routing relevant flags - see enum batadv_iv_flags
+ * @tvlv_len: length of tvlv data following the ogm header
+ */
struct batadv_ogm_packet {
struct batadv_header header;
- uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */
+ uint8_t flags;
__be32 seqno;
uint8_t orig[ETH_ALEN];
uint8_t prev_sender[ETH_ALEN];
- uint8_t gw_flags; /* flags related to gateway class */
+ uint8_t reserved;
uint8_t tq;
- uint8_t tt_num_changes;
- uint8_t ttvn; /* translation table version number */
- __be16 tt_crc;
-} __packed;
+ __be16 tvlv_len;
+ /* __packed is not needed as the struct size is divisible by 4,
+ * and the largest data type in this struct has a size of 4.
+ */
+};
#define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet)
-struct batadv_icmp_packet {
+/**
+ * batadv_icmp_header - common ICMP header
+ * @header: common batman header
+ * @msg_type: ICMP packet type
+ * @dst: address of the destination node
+ * @orig: address of the source node
+ * @uid: local ICMP socket identifier
+ */
+struct batadv_icmp_header {
struct batadv_header header;
uint8_t msg_type; /* see ICMP message types above */
uint8_t dst[ETH_ALEN];
uint8_t orig[ETH_ALEN];
- __be16 seqno;
uint8_t uid;
+};
+
+/**
+ * batadv_icmp_packet - ICMP packet
+ * @icmph: common ICMP header
+ * @reserved: not used - useful for alignment
+ * @seqno: ICMP sequence number
+ */
+struct batadv_icmp_packet {
+ struct batadv_icmp_header icmph;
uint8_t reserved;
+ __be16 seqno;
};
#define BATADV_RR_LEN 16
-/* icmp_packet_rr must start with all fields from imcp_packet
- * as this is assumed by code that handles ICMP packets
+/**
+ * batadv_icmp_packet_rr - ICMP RouteRecord packet
+ * @icmph: common ICMP header
+ * @rr_cur: number of entries the rr array
+ * @seqno: ICMP sequence number
+ * @rr: route record array
*/
struct batadv_icmp_packet_rr {
- struct batadv_header header;
- uint8_t msg_type; /* see ICMP message types above */
- uint8_t dst[ETH_ALEN];
- uint8_t orig[ETH_ALEN];
- __be16 seqno;
- uint8_t uid;
+ struct batadv_icmp_header icmph;
uint8_t rr_cur;
+ __be16 seqno;
uint8_t rr[BATADV_RR_LEN][ETH_ALEN];
};
+#define BATADV_ICMP_MAX_PACKET_SIZE sizeof(struct batadv_icmp_packet_rr)
+
/* All packet headers in front of an ethernet header have to be completely
* divisible by 2 but not by 4 to make the payload after the ethernet
* header again 4 bytes boundary aligned.
@@ -209,15 +278,32 @@ struct batadv_unicast_4addr_packet {
*/
};
-struct batadv_unicast_frag_packet {
- struct batadv_header header;
- uint8_t ttvn; /* destination translation table version number */
- uint8_t dest[ETH_ALEN];
- uint8_t flags;
- uint8_t align;
- uint8_t orig[ETH_ALEN];
- __be16 seqno;
-} __packed;
+/**
+ * struct batadv_frag_packet - fragmented packet
+ * @header: common batman packet header with type, compatversion, and ttl
+ * @dest: final destination used when routing fragments
+ * @orig: originator of the fragment used when merging the packet
+ * @no: fragment number within this sequence
+ * @reserved: reserved byte for alignment
+ * @seqno: sequence identification
+ * @total_size: size of the merged packet
+ */
+struct batadv_frag_packet {
+ struct batadv_header header;
+#if defined(__BIG_ENDIAN_BITFIELD)
+ uint8_t no:4;
+ uint8_t reserved:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ uint8_t reserved:4;
+ uint8_t no:4;
+#else
+#error "unknown bitfield endianess"
+#endif
+ uint8_t dest[ETH_ALEN];
+ uint8_t orig[ETH_ALEN];
+ __be16 seqno;
+ __be16 total_size;
+};
struct batadv_bcast_packet {
struct batadv_header header;
@@ -231,54 +317,6 @@ struct batadv_bcast_packet {
#pragma pack()
-struct batadv_vis_packet {
- struct batadv_header header;
- uint8_t vis_type; /* which type of vis-participant sent this? */
- __be32 seqno; /* sequence number */
- uint8_t entries; /* number of entries behind this struct */
- uint8_t reserved;
- uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */
- uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */
- uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */
-};
-
-struct batadv_tt_query_packet {
- struct batadv_header header;
- /* the flag field is a combination of:
- * - TT_REQUEST or TT_RESPONSE
- * - TT_FULL_TABLE
- */
- uint8_t flags;
- uint8_t dst[ETH_ALEN];
- uint8_t src[ETH_ALEN];
- /* the ttvn field is:
- * if TT_REQUEST: ttvn that triggered the
- * request
- * if TT_RESPONSE: new ttvn for the src
- * orig_node
- */
- uint8_t ttvn;
- /* tt_data field is:
- * if TT_REQUEST: crc associated with the
- * ttvn
- * if TT_RESPONSE: table_size
- */
- __be16 tt_data;
-} __packed;
-
-struct batadv_roam_adv_packet {
- struct batadv_header header;
- uint8_t reserved;
- uint8_t dst[ETH_ALEN];
- uint8_t src[ETH_ALEN];
- uint8_t client[ETH_ALEN];
-} __packed;
-
-struct batadv_tt_change {
- uint8_t flags;
- uint8_t addr[ETH_ALEN];
-} __packed;
-
/**
* struct batadv_coded_packet - network coded packet
* @header: common batman packet header and ttl of first included packet
@@ -311,4 +349,96 @@ struct batadv_coded_packet {
__be16 coded_len;
};
+/**
+ * struct batadv_unicast_tvlv - generic unicast packet with tvlv payload
+ * @header: common batman packet header
+ * @reserved: reserved field (for packet alignment)
+ * @src: address of the source
+ * @dst: address of the destination
+ * @tvlv_len: length of tvlv data following the unicast tvlv header
+ * @align: 2 bytes to align the header to a 4 byte boundry
+ */
+struct batadv_unicast_tvlv_packet {
+ struct batadv_header header;
+ uint8_t reserved;
+ uint8_t dst[ETH_ALEN];
+ uint8_t src[ETH_ALEN];
+ __be16 tvlv_len;
+ uint16_t align;
+};
+
+/**
+ * struct batadv_tvlv_hdr - base tvlv header struct
+ * @type: tvlv container type (see batadv_tvlv_type)
+ * @version: tvlv container version
+ * @len: tvlv container length
+ */
+struct batadv_tvlv_hdr {
+ uint8_t type;
+ uint8_t version;
+ __be16 len;
+};
+
+/**
+ * struct batadv_tvlv_gateway_data - gateway data propagated through gw tvlv
+ * container
+ * @bandwidth_down: advertised uplink download bandwidth
+ * @bandwidth_up: advertised uplink upload bandwidth
+ */
+struct batadv_tvlv_gateway_data {
+ __be32 bandwidth_down;
+ __be32 bandwidth_up;
+};
+
+/**
+ * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container
+ * @flags: translation table flags (see batadv_tt_data_flags)
+ * @ttvn: translation table version number
+ * @vlan_num: number of announced VLANs. In the TVLV this struct is followed by
+ * one batadv_tvlv_tt_vlan_data object per announced vlan
+ */
+struct batadv_tvlv_tt_data {
+ uint8_t flags;
+ uint8_t ttvn;
+ __be16 num_vlan;
+};
+
+/**
+ * struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through
+ * the tt tvlv container
+ * @crc: crc32 checksum of the entries belonging to this vlan
+ * @vid: vlan identifier
+ * @reserved: unused, useful for alignment purposes
+ */
+struct batadv_tvlv_tt_vlan_data {
+ __be32 crc;
+ __be16 vid;
+ uint16_t reserved;
+};
+
+/**
+ * struct batadv_tvlv_tt_change - translation table diff data
+ * @flags: status indicators concerning the non-mesh client (see
+ * batadv_tt_client_flags)
+ * @reserved: reserved field
+ * @addr: mac address of non-mesh client that triggered this tt change
+ * @vid: VLAN identifier
+ */
+struct batadv_tvlv_tt_change {
+ uint8_t flags;
+ uint8_t reserved;
+ uint8_t addr[ETH_ALEN];
+ __be16 vid;
+};
+
+/**
+ * struct batadv_tvlv_roam_adv - roaming advertisement
+ * @client: mac address of roaming client
+ * @vid: VLAN identifier
+ */
+struct batadv_tvlv_roam_adv {
+ uint8_t client[ETH_ALEN];
+ __be16 vid;
+};
+
#endif /* _NET_BATMAN_ADV_PACKET_H_ */
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 0439395d7ba5..d4114d775ad6 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -25,11 +25,12 @@
#include "icmp_socket.h"
#include "translation-table.h"
#include "originator.h"
-#include "vis.h"
-#include "unicast.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"
#include "network-coding.h"
+#include "fragmentation.h"
+
+#include <linux/if_vlan.h>
static int batadv_route_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
@@ -46,7 +47,7 @@ static void _batadv_update_route(struct batadv_priv *bat_priv,
if ((curr_router) && (!neigh_node)) {
batadv_dbg(BATADV_DBG_ROUTES, bat_priv,
"Deleting route towards: %pM\n", orig_node->orig);
- batadv_tt_global_del_orig(bat_priv, orig_node,
+ batadv_tt_global_del_orig(bat_priv, orig_node, -1,
"Deleted route towards originator");
/* route added */
@@ -114,9 +115,19 @@ out:
return;
}
-void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node,
+/**
+ * batadv_bonding_candidate_add - consider a new link for bonding mode towards
+ * the given originator
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the target node
+ * @neigh_node: the neighbor representing the new link to consider for bonding
+ * mode
+ */
+void batadv_bonding_candidate_add(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node)
{
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
struct batadv_neigh_node *tmp_neigh_node, *router = NULL;
uint8_t interference_candidate = 0;
@@ -131,8 +142,9 @@ void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node,
if (!router)
goto candidate_del;
+
/* ... and is good enough to be considered */
- if (neigh_node->tq_avg < router->tq_avg - BATADV_BONDING_TQ_THRESHOLD)
+ if (bao->bat_neigh_is_equiv_or_better(neigh_node, router))
goto candidate_del;
/* check if we have another candidate with the same mac address or
@@ -248,46 +260,65 @@ bool batadv_check_management_packet(struct sk_buff *skb,
return true;
}
+/**
+ * batadv_recv_my_icmp_packet - receive an icmp packet locally
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: icmp packet to process
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
- struct sk_buff *skb, size_t icmp_len)
+ struct sk_buff *skb)
{
struct batadv_hard_iface *primary_if = NULL;
struct batadv_orig_node *orig_node = NULL;
- struct batadv_icmp_packet_rr *icmp_packet;
- int ret = NET_RX_DROP;
+ struct batadv_icmp_header *icmph;
+ int res, ret = NET_RX_DROP;
- icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+ icmph = (struct batadv_icmp_header *)skb->data;
- /* add data to device queue */
- if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) {
- batadv_socket_receive_packet(icmp_packet, icmp_len);
- goto out;
- }
+ switch (icmph->msg_type) {
+ case BATADV_ECHO_REPLY:
+ case BATADV_DESTINATION_UNREACHABLE:
+ case BATADV_TTL_EXCEEDED:
+ /* receive the packet */
+ if (skb_linearize(skb) < 0)
+ break;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
+ batadv_socket_receive_packet(icmph, skb->len);
+ break;
+ case BATADV_ECHO_REQUEST:
+ /* answer echo request (ping) */
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
- /* answer echo request (ping) */
- /* get routing information */
- orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig);
- if (!orig_node)
- goto out;
+ /* get routing information */
+ orig_node = batadv_orig_hash_find(bat_priv, icmph->orig);
+ if (!orig_node)
+ goto out;
- /* create a copy of the skb, if needed, to modify it. */
- if (skb_cow(skb, ETH_HLEN) < 0)
- goto out;
+ /* create a copy of the skb, if needed, to modify it. */
+ if (skb_cow(skb, ETH_HLEN) < 0)
+ goto out;
- icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+ icmph = (struct batadv_icmp_header *)skb->data;
- memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
- memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- icmp_packet->msg_type = BATADV_ECHO_REPLY;
- icmp_packet->header.ttl = BATADV_TTL;
+ memcpy(icmph->dst, icmph->orig, ETH_ALEN);
+ memcpy(icmph->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
+ icmph->msg_type = BATADV_ECHO_REPLY;
+ icmph->header.ttl = BATADV_TTL;
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
- ret = NET_RX_SUCCESS;
+ res = batadv_send_skb_to_orig(skb, orig_node, NULL);
+ if (res != NET_XMIT_DROP)
+ ret = NET_RX_SUCCESS;
+ break;
+ default:
+ /* drop unknown type */
+ goto out;
+ }
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
@@ -307,9 +338,9 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
icmp_packet = (struct batadv_icmp_packet *)skb->data;
/* send TTL exceeded if packet is an echo request (traceroute) */
- if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) {
+ if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) {
pr_debug("Warning - can't forward icmp packet from %pM to %pM: ttl exceeded\n",
- icmp_packet->orig, icmp_packet->dst);
+ icmp_packet->icmph.orig, icmp_packet->icmph.dst);
goto out;
}
@@ -318,7 +349,7 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
goto out;
/* get routing information */
- orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig);
+ orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.orig);
if (!orig_node)
goto out;
@@ -328,10 +359,11 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
icmp_packet = (struct batadv_icmp_packet *)skb->data;
- memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
- memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- icmp_packet->msg_type = BATADV_TTL_EXCEEDED;
- icmp_packet->header.ttl = BATADV_TTL;
+ memcpy(icmp_packet->icmph.dst, icmp_packet->icmph.orig, ETH_ALEN);
+ memcpy(icmp_packet->icmph.orig, primary_if->net_dev->dev_addr,
+ ETH_ALEN);
+ icmp_packet->icmph.msg_type = BATADV_TTL_EXCEEDED;
+ icmp_packet->icmph.header.ttl = BATADV_TTL;
if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
ret = NET_RX_SUCCESS;
@@ -349,16 +381,13 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
- struct batadv_icmp_packet_rr *icmp_packet;
+ struct batadv_icmp_header *icmph;
+ struct batadv_icmp_packet_rr *icmp_packet_rr;
struct ethhdr *ethhdr;
struct batadv_orig_node *orig_node = NULL;
- int hdr_size = sizeof(struct batadv_icmp_packet);
+ int hdr_size = sizeof(struct batadv_icmp_header);
int ret = NET_RX_DROP;
- /* we truncate all incoming icmp packets if they don't match our size */
- if (skb->len >= sizeof(struct batadv_icmp_packet_rr))
- hdr_size = sizeof(struct batadv_icmp_packet_rr);
-
/* drop packet if it has not necessary minimum size */
if (unlikely(!pskb_may_pull(skb, hdr_size)))
goto out;
@@ -377,26 +406,39 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
goto out;
- icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+ icmph = (struct batadv_icmp_header *)skb->data;
/* add record route information if not full */
- if ((hdr_size == sizeof(struct batadv_icmp_packet_rr)) &&
- (icmp_packet->rr_cur < BATADV_RR_LEN)) {
- memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]),
+ if ((icmph->msg_type == BATADV_ECHO_REPLY ||
+ icmph->msg_type == BATADV_ECHO_REQUEST) &&
+ (skb->len >= sizeof(struct batadv_icmp_packet_rr))) {
+ if (skb_linearize(skb) < 0)
+ goto out;
+
+ /* create a copy of the skb, if needed, to modify it. */
+ if (skb_cow(skb, ETH_HLEN) < 0)
+ goto out;
+
+ icmph = (struct batadv_icmp_header *)skb->data;
+ icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph;
+ if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN)
+ goto out;
+
+ memcpy(&(icmp_packet_rr->rr[icmp_packet_rr->rr_cur]),
ethhdr->h_dest, ETH_ALEN);
- icmp_packet->rr_cur++;
+ icmp_packet_rr->rr_cur++;
}
/* packet for me */
- if (batadv_is_my_mac(bat_priv, icmp_packet->dst))
- return batadv_recv_my_icmp_packet(bat_priv, skb, hdr_size);
+ if (batadv_is_my_mac(bat_priv, icmph->dst))
+ return batadv_recv_my_icmp_packet(bat_priv, skb);
/* TTL exceeded */
- if (icmp_packet->header.ttl < 2)
+ if (icmph->header.ttl < 2)
return batadv_recv_icmp_ttl_exceeded(bat_priv, skb);
/* get routing information */
- orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst);
+ orig_node = batadv_orig_hash_find(bat_priv, icmph->dst);
if (!orig_node)
goto out;
@@ -404,10 +446,10 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
- icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+ icmph = (struct batadv_icmp_header *)skb->data;
/* decrement ttl */
- icmp_packet->header.ttl--;
+ icmph->header.ttl--;
/* route it */
if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP)
@@ -474,18 +516,25 @@ out:
return router;
}
-/* Interface Alternating: Use the best of the
- * remaining candidates which are not using
- * this interface.
+/**
+ * batadv_find_ifalter_router - find the best of the remaining candidates which
+ * are not using this interface
+ * @bat_priv: the bat priv with all the soft interface information
+ * @primary_orig: the destination
+ * @recv_if: the interface that the router returned by this function has to not
+ * use
*
- * Increases the returned router's refcount
+ * Returns the best candidate towards primary_orig that is not using recv_if.
+ * Increases the returned neighbor's refcount
*/
static struct batadv_neigh_node *
-batadv_find_ifalter_router(struct batadv_orig_node *primary_orig,
+batadv_find_ifalter_router(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *primary_orig,
const struct batadv_hard_iface *recv_if)
{
- struct batadv_neigh_node *tmp_neigh_node;
struct batadv_neigh_node *router = NULL, *first_candidate = NULL;
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
+ struct batadv_neigh_node *tmp_neigh_node;
rcu_read_lock();
list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list,
@@ -497,7 +546,7 @@ batadv_find_ifalter_router(struct batadv_orig_node *primary_orig,
if (tmp_neigh_node->if_incoming == recv_if)
continue;
- if (router && tmp_neigh_node->tq_avg <= router->tq_avg)
+ if (router && bao->bat_neigh_cmp(tmp_neigh_node, router))
continue;
if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
@@ -557,126 +606,6 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
return 0;
}
-int batadv_recv_tt_query(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
-{
- struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
- struct batadv_tt_query_packet *tt_query;
- uint16_t tt_size;
- int hdr_size = sizeof(*tt_query);
- char tt_flag;
- size_t packet_size;
-
- if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
- return NET_RX_DROP;
-
- /* I could need to modify it */
- if (skb_cow(skb, sizeof(struct batadv_tt_query_packet)) < 0)
- goto out;
-
- tt_query = (struct batadv_tt_query_packet *)skb->data;
-
- switch (tt_query->flags & BATADV_TT_QUERY_TYPE_MASK) {
- case BATADV_TT_REQUEST:
- batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
-
- /* If we cannot provide an answer the tt_request is
- * forwarded
- */
- if (!batadv_send_tt_response(bat_priv, tt_query)) {
- if (tt_query->flags & BATADV_TT_FULL_TABLE)
- tt_flag = 'F';
- else
- tt_flag = '.';
-
- batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Routing TT_REQUEST to %pM [%c]\n",
- tt_query->dst,
- tt_flag);
- return batadv_route_unicast_packet(skb, recv_if);
- }
- break;
- case BATADV_TT_RESPONSE:
- batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
-
- if (batadv_is_my_mac(bat_priv, tt_query->dst)) {
- /* packet needs to be linearized to access the TT
- * changes
- */
- if (skb_linearize(skb) < 0)
- goto out;
- /* skb_linearize() possibly changed skb->data */
- tt_query = (struct batadv_tt_query_packet *)skb->data;
-
- tt_size = batadv_tt_len(ntohs(tt_query->tt_data));
-
- /* Ensure we have all the claimed data */
- packet_size = sizeof(struct batadv_tt_query_packet);
- packet_size += tt_size;
- if (unlikely(skb_headlen(skb) < packet_size))
- goto out;
-
- batadv_handle_tt_response(bat_priv, tt_query);
- } else {
- if (tt_query->flags & BATADV_TT_FULL_TABLE)
- tt_flag = 'F';
- else
- tt_flag = '.';
- batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Routing TT_RESPONSE to %pM [%c]\n",
- tt_query->dst,
- tt_flag);
- return batadv_route_unicast_packet(skb, recv_if);
- }
- break;
- }
-
-out:
- /* returning NET_RX_DROP will make the caller function kfree the skb */
- return NET_RX_DROP;
-}
-
-int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
-{
- struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
- struct batadv_roam_adv_packet *roam_adv_packet;
- struct batadv_orig_node *orig_node;
-
- if (batadv_check_unicast_packet(bat_priv, skb,
- sizeof(*roam_adv_packet)) < 0)
- goto out;
-
- batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
-
- roam_adv_packet = (struct batadv_roam_adv_packet *)skb->data;
-
- if (!batadv_is_my_mac(bat_priv, roam_adv_packet->dst))
- return batadv_route_unicast_packet(skb, recv_if);
-
- /* check if it is a backbone gateway. we don't accept
- * roaming advertisement from it, as it has the same
- * entries as we have.
- */
- if (batadv_bla_is_backbone_gw_orig(bat_priv, roam_adv_packet->src))
- goto out;
-
- orig_node = batadv_orig_hash_find(bat_priv, roam_adv_packet->src);
- if (!orig_node)
- goto out;
-
- batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Received ROAMING_ADV from %pM (client %pM)\n",
- roam_adv_packet->src, roam_adv_packet->client);
-
- batadv_tt_global_add(bat_priv, orig_node, roam_adv_packet->client,
- BATADV_TT_CLIENT_ROAM,
- atomic_read(&orig_node->last_ttvn) + 1);
-
- batadv_orig_node_free_ref(orig_node);
-out:
- /* returning NET_RX_DROP will make the caller function kfree the skb */
- return NET_RX_DROP;
-}
-
/* find a suitable router for this originator, and use
* bonding if possible. increases the found neighbors
* refcount.
@@ -751,7 +680,8 @@ batadv_find_router(struct batadv_priv *bat_priv,
if (bonding_enabled)
router = batadv_find_bond_router(primary_orig_node, recv_if);
else
- router = batadv_find_ifalter_router(primary_orig_node, recv_if);
+ router = batadv_find_ifalter_router(bat_priv, primary_orig_node,
+ recv_if);
return_router:
if (router && router->if_incoming->if_status != BATADV_IF_ACTIVE)
@@ -772,11 +702,9 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *neigh_node = NULL;
struct batadv_unicast_packet *unicast_packet;
struct ethhdr *ethhdr = eth_hdr(skb);
int res, hdr_len, ret = NET_RX_DROP;
- struct sk_buff *new_skb;
unicast_packet = (struct batadv_unicast_packet *)skb->data;
@@ -793,46 +721,12 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
if (!orig_node)
goto out;
- /* find_router() increases neigh_nodes refcount if found. */
- neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
-
- if (!neigh_node)
- goto out;
-
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
-
- if (unicast_packet->header.packet_type == BATADV_UNICAST &&
- atomic_read(&bat_priv->fragmentation) &&
- skb->len > neigh_node->if_incoming->net_dev->mtu) {
- ret = batadv_frag_send_skb(skb, bat_priv,
- neigh_node->if_incoming,
- neigh_node->addr);
- goto out;
- }
-
- if (unicast_packet->header.packet_type == BATADV_UNICAST_FRAG &&
- batadv_frag_can_reassemble(skb,
- neigh_node->if_incoming->net_dev->mtu)) {
- ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb);
-
- if (ret == NET_RX_DROP)
- goto out;
-
- /* packet was buffered for late merge */
- if (!new_skb) {
- ret = NET_RX_SUCCESS;
- goto out;
- }
-
- skb = new_skb;
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
- }
-
/* decrement ttl */
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
unicast_packet->header.ttl--;
switch (unicast_packet->header.packet_type) {
@@ -867,8 +761,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
}
out:
- if (neigh_node)
- batadv_neigh_node_free_ref(neigh_node);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
return ret;
@@ -879,6 +771,7 @@ out:
* @bat_priv: the bat priv with all the soft interface information
* @unicast_packet: the unicast header to be updated
* @dst_addr: the payload destination
+ * @vid: VLAN identifier
*
* Search the translation table for dst_addr and update the unicast header with
* the new corresponding information (originator address where the destination
@@ -889,21 +782,22 @@ out:
static bool
batadv_reroute_unicast_packet(struct batadv_priv *bat_priv,
struct batadv_unicast_packet *unicast_packet,
- uint8_t *dst_addr)
+ uint8_t *dst_addr, unsigned short vid)
{
struct batadv_orig_node *orig_node = NULL;
struct batadv_hard_iface *primary_if = NULL;
bool ret = false;
uint8_t *orig_addr, orig_ttvn;
- if (batadv_is_my_client(bat_priv, dst_addr)) {
+ if (batadv_is_my_client(bat_priv, dst_addr, vid)) {
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
orig_addr = primary_if->net_dev->dev_addr;
orig_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
} else {
- orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr);
+ orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr,
+ vid);
if (!orig_node)
goto out;
@@ -930,11 +824,12 @@ out:
static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
struct sk_buff *skb, int hdr_len) {
- uint8_t curr_ttvn, old_ttvn;
+ struct batadv_unicast_packet *unicast_packet;
+ struct batadv_hard_iface *primary_if;
struct batadv_orig_node *orig_node;
+ uint8_t curr_ttvn, old_ttvn;
struct ethhdr *ethhdr;
- struct batadv_hard_iface *primary_if;
- struct batadv_unicast_packet *unicast_packet;
+ unsigned short vid;
int is_old_ttvn;
/* check if there is enough data before accessing it */
@@ -946,6 +841,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
return 0;
unicast_packet = (struct batadv_unicast_packet *)skb->data;
+ vid = batadv_get_vid(skb, hdr_len);
ethhdr = (struct ethhdr *)(skb->data + hdr_len);
/* check if the destination client was served by this node and it is now
@@ -953,9 +849,9 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
* message and that it knows the new destination in the mesh to re-route
* the packet to
*/
- if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest)) {
+ if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) {
if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
- ethhdr->h_dest))
+ ethhdr->h_dest, vid))
net_ratelimited_function(batadv_dbg, BATADV_DBG_TT,
bat_priv,
"Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n",
@@ -1001,7 +897,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
* target host
*/
if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
- ethhdr->h_dest)) {
+ ethhdr->h_dest, vid)) {
net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv,
"Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n",
unicast_packet->dest, ethhdr->h_dest,
@@ -1013,7 +909,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
* currently served by this node or there is no destination at all and
* it is possible to drop the packet
*/
- if (!batadv_is_my_client(bat_priv, ethhdr->h_dest))
+ if (!batadv_is_my_client(bat_priv, ethhdr->h_dest, vid))
return 0;
/* update the header in order to let the packet be delivered to this
@@ -1032,6 +928,34 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
return 1;
}
+/**
+ * batadv_recv_unhandled_unicast_packet - receive and process packets which
+ * are in the unicast number space but not yet known to the implementation
+ * @skb: unicast tvlv packet to process
+ * @recv_if: pointer to interface this packet was received on
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
+int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if)
+{
+ struct batadv_unicast_packet *unicast_packet;
+ struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+ int check, hdr_size = sizeof(*unicast_packet);
+
+ check = batadv_check_unicast_packet(bat_priv, skb, hdr_size);
+ if (check < 0)
+ return NET_RX_DROP;
+
+ /* we don't know about this type, drop it. */
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
+ if (batadv_is_my_mac(bat_priv, unicast_packet->dest))
+ return NET_RX_DROP;
+
+ return batadv_route_unicast_packet(skb, recv_if);
+}
+
int batadv_recv_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
@@ -1094,51 +1018,112 @@ rx_success:
return batadv_route_unicast_packet(skb, recv_if);
}
-int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
- struct batadv_hard_iface *recv_if)
+/**
+ * batadv_recv_unicast_tvlv - receive and process unicast tvlv packets
+ * @skb: unicast tvlv packet to process
+ * @recv_if: pointer to interface this packet was received on
+ * @dst_addr: the payload destination
+ *
+ * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
+ * otherwise.
+ */
+int batadv_recv_unicast_tvlv(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if)
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
- struct batadv_unicast_frag_packet *unicast_packet;
- int hdr_size = sizeof(*unicast_packet);
- struct sk_buff *new_skb = NULL;
- int ret;
+ struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
+ unsigned char *tvlv_buff;
+ uint16_t tvlv_buff_len;
+ int hdr_size = sizeof(*unicast_tvlv_packet);
+ int ret = NET_RX_DROP;
if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
return NET_RX_DROP;
- if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size))
+ /* the header is likely to be modified while forwarding */
+ if (skb_cow(skb, hdr_size) < 0)
return NET_RX_DROP;
- unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
+ /* packet needs to be linearized to access the tvlv content */
+ if (skb_linearize(skb) < 0)
+ return NET_RX_DROP;
- /* packet for me */
- if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) {
- ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb);
+ unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data;
- if (ret == NET_RX_DROP)
- return NET_RX_DROP;
+ tvlv_buff = (unsigned char *)(skb->data + hdr_size);
+ tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len);
- /* packet was buffered for late merge */
- if (!new_skb)
- return NET_RX_SUCCESS;
+ if (tvlv_buff_len > skb->len - hdr_size)
+ return NET_RX_DROP;
- if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb,
- hdr_size))
- goto rx_success;
- if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb,
- hdr_size))
- goto rx_success;
+ ret = batadv_tvlv_containers_process(bat_priv, false, NULL,
+ unicast_tvlv_packet->src,
+ unicast_tvlv_packet->dst,
+ tvlv_buff, tvlv_buff_len);
- batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if,
- sizeof(struct batadv_unicast_packet), NULL);
+ if (ret != NET_RX_SUCCESS)
+ ret = batadv_route_unicast_packet(skb, recv_if);
-rx_success:
- return NET_RX_SUCCESS;
+ return ret;
+}
+
+/**
+ * batadv_recv_frag_packet - process received fragment
+ * @skb: the received fragment
+ * @recv_if: interface that the skb is received on
+ *
+ * This function does one of the three following things: 1) Forward fragment, if
+ * the assembled packet will exceed our MTU; 2) Buffer fragment, if we till
+ * lack further fragments; 3) Merge fragments, if we have all needed parts.
+ *
+ * Return NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
+ */
+int batadv_recv_frag_packet(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if)
+{
+ struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
+ struct batadv_orig_node *orig_node_src = NULL;
+ struct batadv_frag_packet *frag_packet;
+ int ret = NET_RX_DROP;
+
+ if (batadv_check_unicast_packet(bat_priv, skb,
+ sizeof(*frag_packet)) < 0)
+ goto out;
+
+ frag_packet = (struct batadv_frag_packet *)skb->data;
+ orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig);
+ if (!orig_node_src)
+ goto out;
+
+ /* Route the fragment if it is not for us and too big to be merged. */
+ if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
+ batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
+ ret = NET_RX_SUCCESS;
+ goto out;
}
- return batadv_route_unicast_packet(skb, recv_if);
-}
+ batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX);
+ batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, skb->len);
+
+ /* Add fragment to buffer and merge if possible. */
+ if (!batadv_frag_skb_buffer(&skb, orig_node_src))
+ goto out;
+ /* Deliver merged packet to the appropriate handler, if it was
+ * merged
+ */
+ if (skb)
+ batadv_batman_skb_recv(skb, recv_if->net_dev,
+ &recv_if->batman_adv_ptype, NULL);
+
+ ret = NET_RX_SUCCESS;
+
+out:
+ if (orig_node_src)
+ batadv_orig_node_free_ref(orig_node_src);
+
+ return ret;
+}
int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
@@ -1240,53 +1225,3 @@ out:
batadv_orig_node_free_ref(orig_node);
return ret;
}
-
-int batadv_recv_vis_packet(struct sk_buff *skb,
- struct batadv_hard_iface *recv_if)
-{
- struct batadv_vis_packet *vis_packet;
- struct ethhdr *ethhdr;
- struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
- int hdr_size = sizeof(*vis_packet);
-
- /* keep skb linear */
- if (skb_linearize(skb) < 0)
- return NET_RX_DROP;
-
- if (unlikely(!pskb_may_pull(skb, hdr_size)))
- return NET_RX_DROP;
-
- vis_packet = (struct batadv_vis_packet *)skb->data;
- ethhdr = eth_hdr(skb);
-
- /* not for me */
- if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest))
- return NET_RX_DROP;
-
- /* ignore own packets */
- if (batadv_is_my_mac(bat_priv, vis_packet->vis_orig))
- return NET_RX_DROP;
-
- if (batadv_is_my_mac(bat_priv, vis_packet->sender_orig))
- return NET_RX_DROP;
-
- switch (vis_packet->vis_type) {
- case BATADV_VIS_TYPE_SERVER_SYNC:
- batadv_receive_server_sync_packet(bat_priv, vis_packet,
- skb_headlen(skb));
- break;
-
- case BATADV_VIS_TYPE_CLIENT_UPDATE:
- batadv_receive_client_update_packet(bat_priv, vis_packet,
- skb_headlen(skb));
- break;
-
- default: /* ignore unknown packet */
- break;
- }
-
- /* We take a copy of the data in the packet, so we should
- * always free the skbuf.
- */
- return NET_RX_DROP;
-}
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h
index 72a29bde2010..19544ddb81b5 100644
--- a/net/batman-adv/routing.h
+++ b/net/batman-adv/routing.h
@@ -30,23 +30,26 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
-int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
- struct batadv_hard_iface *recv_if);
+int batadv_recv_frag_packet(struct sk_buff *skb,
+ struct batadv_hard_iface *iface);
int batadv_recv_bcast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
-int batadv_recv_vis_packet(struct sk_buff *skb,
- struct batadv_hard_iface *recv_if);
int batadv_recv_tt_query(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
int batadv_recv_roam_adv(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
+int batadv_recv_unicast_tvlv(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if);
+int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb,
+ struct batadv_hard_iface *recv_if);
struct batadv_neigh_node *
batadv_find_router(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
const struct batadv_hard_iface *recv_if);
void batadv_bonding_candidate_del(struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node);
-void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node,
+void batadv_bonding_candidate_add(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node);
void batadv_bonding_save_primary(const struct batadv_orig_node *orig_node,
struct batadv_orig_node *orig_neigh_node,
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 0266edd0fa7f..c83be5ebaa28 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -24,12 +24,11 @@
#include "translation-table.h"
#include "soft-interface.h"
#include "hard-interface.h"
-#include "vis.h"
#include "gateway_common.h"
+#include "gateway_client.h"
#include "originator.h"
#include "network-coding.h"
-
-#include <linux/if_ether.h>
+#include "fragmentation.h"
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
@@ -64,10 +63,10 @@ int batadv_send_skb_packet(struct sk_buff *skb,
ethhdr = eth_hdr(skb);
memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
- ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
+ ethhdr->h_proto = htons(ETH_P_BATMAN);
skb_set_network_header(skb, ETH_HLEN);
- skb->protocol = __constant_htons(ETH_P_BATMAN);
+ skb->protocol = htons(ETH_P_BATMAN);
skb->dev = hard_iface->net_dev;
@@ -109,7 +108,19 @@ int batadv_send_skb_to_orig(struct sk_buff *skb,
/* batadv_find_router() increases neigh_nodes refcount if found. */
neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
if (!neigh_node)
- return ret;
+ goto out;
+
+ /* Check if the skb is too large to send in one piece and fragment
+ * it if needed.
+ */
+ if (atomic_read(&bat_priv->fragmentation) &&
+ skb->len > neigh_node->if_incoming->net_dev->mtu) {
+ /* Fragment and send packet. */
+ if (batadv_frag_send_packet(skb, orig_node, neigh_node))
+ ret = NET_XMIT_SUCCESS;
+
+ goto out;
+ }
/* try to network code the packet, if it is received on an interface
* (i.e. being forwarded). If the packet originates from this node or if
@@ -123,11 +134,225 @@ int batadv_send_skb_to_orig(struct sk_buff *skb,
ret = NET_XMIT_SUCCESS;
}
- batadv_neigh_node_free_ref(neigh_node);
+out:
+ if (neigh_node)
+ batadv_neigh_node_free_ref(neigh_node);
+
+ return ret;
+}
+
+/**
+ * batadv_send_skb_push_fill_unicast - extend the buffer and initialize the
+ * common fields for unicast packets
+ * @skb: the skb carrying the unicast header to initialize
+ * @hdr_size: amount of bytes to push at the beginning of the skb
+ * @orig_node: the destination node
+ *
+ * Returns false if the buffer extension was not possible or true otherwise.
+ */
+static bool
+batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size,
+ struct batadv_orig_node *orig_node)
+{
+ struct batadv_unicast_packet *unicast_packet;
+ uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
+
+ if (batadv_skb_head_push(skb, hdr_size) < 0)
+ return false;
+
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
+ unicast_packet->header.version = BATADV_COMPAT_VERSION;
+ /* batman packet type: unicast */
+ unicast_packet->header.packet_type = BATADV_UNICAST;
+ /* set unicast ttl */
+ unicast_packet->header.ttl = BATADV_TTL;
+ /* copy the destination for faster routing */
+ memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
+ /* set the destination tt version number */
+ unicast_packet->ttvn = ttvn;
+
+ return true;
+}
+
+/**
+ * batadv_send_skb_prepare_unicast - encapsulate an skb with a unicast header
+ * @skb: the skb containing the payload to encapsulate
+ * @orig_node: the destination node
+ *
+ * Returns false if the payload could not be encapsulated or true otherwise.
+ */
+static bool batadv_send_skb_prepare_unicast(struct sk_buff *skb,
+ struct batadv_orig_node *orig_node)
+{
+ size_t uni_size = sizeof(struct batadv_unicast_packet);
+
+ return batadv_send_skb_push_fill_unicast(skb, uni_size, orig_node);
+}
+
+/**
+ * batadv_send_skb_prepare_unicast_4addr - encapsulate an skb with a
+ * unicast 4addr header
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the skb containing the payload to encapsulate
+ * @orig_node: the destination node
+ * @packet_subtype: the unicast 4addr packet subtype to use
+ *
+ * Returns false if the payload could not be encapsulated or true otherwise.
+ */
+bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ struct batadv_orig_node *orig,
+ int packet_subtype)
+{
+ struct batadv_hard_iface *primary_if;
+ struct batadv_unicast_4addr_packet *uc_4addr_packet;
+ bool ret = false;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ /* Pull the header space and fill the unicast_packet substructure.
+ * We can do that because the first member of the uc_4addr_packet
+ * is of type struct unicast_packet
+ */
+ if (!batadv_send_skb_push_fill_unicast(skb, sizeof(*uc_4addr_packet),
+ orig))
+ goto out;
+
+ uc_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
+ uc_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR;
+ memcpy(uc_4addr_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
+ uc_4addr_packet->subtype = packet_subtype;
+ uc_4addr_packet->reserved = 0;
+
+ ret = true;
+out:
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
+ return ret;
+}
+
+/**
+ * batadv_send_skb_unicast - encapsulate and send an skb via unicast
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @packet_type: the batman unicast packet type to use
+ * @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
+ * 4addr packets)
+ * @orig_node: the originator to send the packet to
+ * @vid: the vid to be used to search the translation table
+ *
+ * Wrap the given skb into a batman-adv unicast or unicast-4addr header
+ * depending on whether BATADV_UNICAST or BATADV_UNICAST_4ADDR was supplied
+ * as packet_type. Then send this frame to the given orig_node and release a
+ * reference to this orig_node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+static int batadv_send_skb_unicast(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype,
+ struct batadv_orig_node *orig_node,
+ unsigned short vid)
+{
+ struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+ struct batadv_unicast_packet *unicast_packet;
+ int ret = NET_XMIT_DROP;
+
+ if (!orig_node)
+ goto out;
+
+ switch (packet_type) {
+ case BATADV_UNICAST:
+ if (!batadv_send_skb_prepare_unicast(skb, orig_node))
+ goto out;
+ break;
+ case BATADV_UNICAST_4ADDR:
+ if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb,
+ orig_node,
+ packet_subtype))
+ goto out;
+ break;
+ default:
+ /* this function supports UNICAST and UNICAST_4ADDR only. It
+ * should never be invoked with any other packet type
+ */
+ goto out;
+ }
+
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
+
+ /* inform the destination node that we are still missing a correct route
+ * for this client. The destination will receive this packet and will
+ * try to reroute it because the ttvn contained in the header is less
+ * than the current one
+ */
+ if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest, vid))
+ unicast_packet->ttvn = unicast_packet->ttvn - 1;
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
+ ret = NET_XMIT_SUCCESS;
+
+out:
+ if (orig_node)
+ batadv_orig_node_free_ref(orig_node);
+ if (ret == NET_XMIT_DROP)
+ kfree_skb(skb);
return ret;
}
+/**
+ * batadv_send_skb_via_tt_generic - send an skb via TT lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @packet_type: the batman unicast packet type to use
+ * @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast
+ * 4addr packets)
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the recipient node for the destination address in the ethernet
+ * header via the translation table. Wrap the given skb into a batman-adv
+ * unicast or unicast-4addr header depending on whether BATADV_UNICAST or
+ * BATADV_UNICAST_4ADDR was supplied as packet_type. Then send this frame
+ * to the according destination node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype, unsigned short vid)
+{
+ struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+ struct batadv_orig_node *orig_node;
+
+ orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
+ ethhdr->h_dest, vid);
+ return batadv_send_skb_unicast(bat_priv, skb, packet_type,
+ packet_subtype, orig_node, vid);
+}
+
+/**
+ * batadv_send_skb_via_gw - send an skb via gateway lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the currently selected gateway. Wrap the given skb into a batman-adv
+ * unicast header and send this frame to this gateway node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ unsigned short vid)
+{
+ struct batadv_orig_node *orig_node;
+
+ orig_node = batadv_gw_get_selected_orig(bat_priv);
+ return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
+ orig_node, vid);
+}
+
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h
index e7b17880fca4..aa2e2537a739 100644
--- a/net/batman-adv/send.h
+++ b/net/batman-adv/send.h
@@ -34,5 +34,58 @@ void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work);
void
batadv_purge_outstanding_packets(struct batadv_priv *bat_priv,
const struct batadv_hard_iface *hard_iface);
+bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ struct batadv_orig_node *orig_node,
+ int packet_subtype);
+int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype, unsigned short vid);
+int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ unsigned short vid);
+
+/**
+ * batadv_send_skb_via_tt - send an skb via TT lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the payload to send
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the recipient node for the destination address in the ethernet
+ * header via the translation table. Wrap the given skb into a batman-adv
+ * unicast header. Then send this frame to the according destination node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ unsigned short vid)
+{
+ return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST, 0,
+ vid);
+}
+
+/**
+ * batadv_send_skb_via_tt_4addr - send an skb via TT lookup
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the payload to send
+ * @packet_subtype: the unicast 4addr packet subtype to use
+ * @vid: the vid to be used to search the translation table
+ *
+ * Look up the recipient node for the destination address in the ethernet
+ * header via the translation table. Wrap the given skb into a batman-adv
+ * unicast-4addr header. Then send this frame to the according destination
+ * node.
+ *
+ * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise.
+ */
+static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ int packet_subtype,
+ unsigned short vid)
+{
+ return batadv_send_skb_via_tt_generic(bat_priv, skb,
+ BATADV_UNICAST_4ADDR,
+ packet_subtype, vid);
+}
#endif /* _NET_BATMAN_ADV_SEND_H_ */
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 813db4e64602..36f050876f82 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -34,8 +34,6 @@
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
-#include <linux/if_ether.h>
-#include "unicast.h"
#include "bridge_loop_avoidance.h"
#include "network-coding.h"
@@ -120,9 +118,10 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
/* only modify transtable if it has been initialized before */
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
- batadv_tt_local_remove(bat_priv, old_addr,
+ batadv_tt_local_remove(bat_priv, old_addr, BATADV_NO_FLAGS,
"mac address changed", false);
- batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX);
+ batadv_tt_local_add(dev, addr->sa_data, BATADV_NO_FLAGS,
+ BATADV_NULL_IFINDEX);
}
return 0;
@@ -139,36 +138,48 @@ static int batadv_interface_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+/**
+ * batadv_interface_set_rx_mode - set the rx mode of a device
+ * @dev: registered network device to modify
+ *
+ * We do not actually need to set any rx filters for the virtual batman
+ * soft interface. However a dummy handler enables a user to set static
+ * multicast listeners for instance.
+ */
+static void batadv_interface_set_rx_mode(struct net_device *dev)
+{
+}
+
static int batadv_interface_tx(struct sk_buff *skb,
struct net_device *soft_iface)
{
- struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+ struct ethhdr *ethhdr;
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
struct batadv_hard_iface *primary_if = NULL;
struct batadv_bcast_packet *bcast_packet;
- struct vlan_ethhdr *vhdr;
- __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+ __be16 ethertype = htons(ETH_P_BATMAN);
static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00,
0x00, 0x00};
static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00,
0x00, 0x00};
+ struct vlan_ethhdr *vhdr;
unsigned int header_len = 0;
int data_len = skb->len, ret;
- unsigned short vid __maybe_unused = BATADV_NO_FLAGS;
- bool do_bcast = false;
- uint32_t seqno;
unsigned long brd_delay = 1;
+ bool do_bcast = false, client_added;
+ unsigned short vid;
+ uint32_t seqno;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
soft_iface->trans_start = jiffies;
+ vid = batadv_get_vid(skb, 0);
+ ethhdr = (struct ethhdr *)skb->data;
switch (ntohs(ethhdr->h_proto)) {
case ETH_P_8021Q:
vhdr = (struct vlan_ethhdr *)skb->data;
- vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
- vid |= BATADV_VLAN_HAS_TAG;
if (vhdr->h_vlan_encapsulated_proto != ethertype)
break;
@@ -185,8 +196,12 @@ static int batadv_interface_tx(struct sk_buff *skb,
ethhdr = (struct ethhdr *)skb->data;
/* Register the client MAC in the transtable */
- if (!is_multicast_ether_addr(ethhdr->h_source))
- batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif);
+ if (!is_multicast_ether_addr(ethhdr->h_source)) {
+ client_added = batadv_tt_local_add(soft_iface, ethhdr->h_source,
+ vid, skb->skb_iif);
+ if (!client_added)
+ goto dropped;
+ }
/* don't accept stp packets. STP does not help in meshes.
* better use the bridge loop avoidance ...
@@ -286,8 +301,12 @@ static int batadv_interface_tx(struct sk_buff *skb,
batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb);
- ret = batadv_unicast_send_skb(bat_priv, skb);
- if (ret != 0)
+ if (is_multicast_ether_addr(ethhdr->h_dest))
+ ret = batadv_send_skb_via_gw(bat_priv, skb, vid);
+ else
+ ret = batadv_send_skb_via_tt(bat_priv, skb, vid);
+
+ if (ret == NET_XMIT_DROP)
goto dropped_freed;
}
@@ -309,12 +328,12 @@ void batadv_interface_rx(struct net_device *soft_iface,
struct sk_buff *skb, struct batadv_hard_iface *recv_if,
int hdr_size, struct batadv_orig_node *orig_node)
{
+ struct batadv_header *batadv_header = (struct batadv_header *)skb->data;
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
- struct ethhdr *ethhdr;
+ __be16 ethertype = htons(ETH_P_BATMAN);
struct vlan_ethhdr *vhdr;
- struct batadv_header *batadv_header = (struct batadv_header *)skb->data;
- unsigned short vid __maybe_unused = BATADV_NO_FLAGS;
- __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+ struct ethhdr *ethhdr;
+ unsigned short vid;
bool is_bcast;
is_bcast = (batadv_header->packet_type == BATADV_BCAST);
@@ -326,13 +345,12 @@ void batadv_interface_rx(struct net_device *soft_iface,
skb_pull_rcsum(skb, hdr_size);
skb_reset_mac_header(skb);
+ vid = batadv_get_vid(skb, hdr_size);
ethhdr = eth_hdr(skb);
switch (ntohs(ethhdr->h_proto)) {
case ETH_P_8021Q:
vhdr = (struct vlan_ethhdr *)skb->data;
- vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK;
- vid |= BATADV_VLAN_HAS_TAG;
if (vhdr->h_vlan_encapsulated_proto != ethertype)
break;
@@ -368,9 +386,10 @@ void batadv_interface_rx(struct net_device *soft_iface,
if (orig_node)
batadv_tt_add_temporary_global_entry(bat_priv, orig_node,
- ethhdr->h_source);
+ ethhdr->h_source, vid);
- if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest))
+ if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest,
+ vid))
goto dropped;
netif_rx(skb);
@@ -382,6 +401,177 @@ out:
return;
}
+/**
+ * batadv_softif_vlan_free_ref - decrease the vlan object refcounter and
+ * possibly free it
+ * @softif_vlan: the vlan object to release
+ */
+void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan)
+{
+ if (atomic_dec_and_test(&softif_vlan->refcount))
+ kfree_rcu(softif_vlan, rcu);
+}
+
+/**
+ * batadv_softif_vlan_get - get the vlan object for a specific vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the identifier of the vlan object to retrieve
+ *
+ * Returns the private data of the vlan matching the vid passed as argument or
+ * NULL otherwise. The refcounter of the returned object is incremented by 1.
+ */
+struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
+ unsigned short vid)
+{
+ struct batadv_softif_vlan *vlan_tmp, *vlan = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) {
+ if (vlan_tmp->vid != vid)
+ continue;
+
+ if (!atomic_inc_not_zero(&vlan_tmp->refcount))
+ continue;
+
+ vlan = vlan_tmp;
+ break;
+ }
+ rcu_read_unlock();
+
+ return vlan;
+}
+
+/**
+ * batadv_create_vlan - allocate the needed resources for a new vlan
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ *
+ * Returns 0 on success, a negative error otherwise.
+ */
+int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
+{
+ struct batadv_softif_vlan *vlan;
+ int err;
+
+ vlan = batadv_softif_vlan_get(bat_priv, vid);
+ if (vlan) {
+ batadv_softif_vlan_free_ref(vlan);
+ return -EEXIST;
+ }
+
+ vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
+ if (!vlan)
+ return -ENOMEM;
+
+ vlan->vid = vid;
+ atomic_set(&vlan->refcount, 1);
+
+ atomic_set(&vlan->ap_isolation, 0);
+
+ err = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan);
+ if (err) {
+ kfree(vlan);
+ return err;
+ }
+
+ /* add a new TT local entry. This one will be marked with the NOPURGE
+ * flag
+ */
+ batadv_tt_local_add(bat_priv->soft_iface,
+ bat_priv->soft_iface->dev_addr, vid,
+ BATADV_NULL_IFINDEX);
+
+ spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+ hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
+ spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
+
+ return 0;
+}
+
+/**
+ * batadv_softif_destroy_vlan - remove and destroy a softif_vlan object
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: the object to remove
+ */
+static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan)
+{
+ spin_lock_bh(&bat_priv->softif_vlan_list_lock);
+ hlist_del_rcu(&vlan->list);
+ spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
+
+ batadv_sysfs_del_vlan(bat_priv, vlan);
+
+ /* explicitly remove the associated TT local entry because it is marked
+ * with the NOPURGE flag
+ */
+ batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr,
+ vlan->vid, "vlan interface destroyed", false);
+
+ batadv_softif_vlan_free_ref(vlan);
+}
+
+/**
+ * batadv_interface_add_vid - ndo_add_vid API implementation
+ * @dev: the netdev of the mesh interface
+ * @vid: identifier of the new vlan
+ *
+ * Set up all the internal structures for handling the new vlan on top of the
+ * mesh interface
+ *
+ * Returns 0 on success or a negative error code in case of failure.
+ */
+static int batadv_interface_add_vid(struct net_device *dev, __be16 proto,
+ unsigned short vid)
+{
+ struct batadv_priv *bat_priv = netdev_priv(dev);
+
+ /* only 802.1Q vlans are supported.
+ * batman-adv does not know how to handle other types
+ */
+ if (proto != htons(ETH_P_8021Q))
+ return -EINVAL;
+
+ vid |= BATADV_VLAN_HAS_TAG;
+
+ return batadv_softif_create_vlan(bat_priv, vid);
+}
+
+/**
+ * batadv_interface_kill_vid - ndo_kill_vid API implementation
+ * @dev: the netdev of the mesh interface
+ * @vid: identifier of the deleted vlan
+ *
+ * Destroy all the internal structures used to handle the vlan identified by vid
+ * on top of the mesh interface
+ *
+ * Returns 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q
+ * or -ENOENT if the specified vlan id wasn't registered.
+ */
+static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
+ unsigned short vid)
+{
+ struct batadv_priv *bat_priv = netdev_priv(dev);
+ struct batadv_softif_vlan *vlan;
+
+ /* only 802.1Q vlans are supported. batman-adv does not know how to
+ * handle other types
+ */
+ if (proto != htons(ETH_P_8021Q))
+ return -EINVAL;
+
+ vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
+ if (!vlan)
+ return -ENOENT;
+
+ batadv_softif_destroy_vlan(bat_priv, vlan);
+
+ /* finally free the vlan object */
+ batadv_softif_vlan_free_ref(vlan);
+
+ return 0;
+}
+
/* batman-adv network devices have devices nesting below it and are a special
* "super class" of normal network devices; split their locks off into a
* separate class since they always nest.
@@ -421,6 +611,7 @@ static void batadv_set_lockdep_class(struct net_device *dev)
*/
static void batadv_softif_destroy_finish(struct work_struct *work)
{
+ struct batadv_softif_vlan *vlan;
struct batadv_priv *bat_priv;
struct net_device *soft_iface;
@@ -428,6 +619,13 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
cleanup_work);
soft_iface = bat_priv->soft_iface;
+ /* destroy the "untagged" VLAN */
+ vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+ if (vlan) {
+ batadv_softif_destroy_vlan(bat_priv, vlan);
+ batadv_softif_vlan_free_ref(vlan);
+ }
+
batadv_sysfs_del_meshif(soft_iface);
rtnl_lock();
@@ -444,6 +642,7 @@ static void batadv_softif_destroy_finish(struct work_struct *work)
static int batadv_softif_init_late(struct net_device *dev)
{
struct batadv_priv *bat_priv;
+ uint32_t random_seqno;
int ret;
size_t cnt_len = sizeof(uint64_t) * BATADV_CNT_NUM;
@@ -468,17 +667,17 @@ static int batadv_softif_init_late(struct net_device *dev)
#ifdef CONFIG_BATMAN_ADV_DAT
atomic_set(&bat_priv->distributed_arp_table, 1);
#endif
- atomic_set(&bat_priv->ap_isolation, 0);
- atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE);
atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
atomic_set(&bat_priv->gw_sel_class, 20);
- atomic_set(&bat_priv->gw_bandwidth, 41);
+ atomic_set(&bat_priv->gw.bandwidth_down, 100);
+ atomic_set(&bat_priv->gw.bandwidth_up, 20);
atomic_set(&bat_priv->orig_interval, 1000);
atomic_set(&bat_priv->hop_penalty, 30);
#ifdef CONFIG_BATMAN_ADV_DEBUG
atomic_set(&bat_priv->log_level, 0);
#endif
atomic_set(&bat_priv->fragmentation, 1);
+ atomic_set(&bat_priv->packet_size_max, ETH_DATA_LEN);
atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN);
atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN);
@@ -493,6 +692,10 @@ static int batadv_softif_init_late(struct net_device *dev)
bat_priv->tt.last_changeset = NULL;
bat_priv->tt.last_changeset_len = 0;
+ /* randomize initial seqno to avoid collision */
+ get_random_bytes(&random_seqno, sizeof(random_seqno));
+ atomic_set(&bat_priv->frag_seqno, random_seqno);
+
bat_priv->primary_if = NULL;
bat_priv->num_ifaces = 0;
@@ -578,8 +781,11 @@ static const struct net_device_ops batadv_netdev_ops = {
.ndo_open = batadv_interface_open,
.ndo_stop = batadv_interface_release,
.ndo_get_stats = batadv_interface_stats,
+ .ndo_vlan_rx_add_vid = batadv_interface_add_vid,
+ .ndo_vlan_rx_kill_vid = batadv_interface_kill_vid,
.ndo_set_mac_address = batadv_interface_set_mac_addr,
.ndo_change_mtu = batadv_interface_change_mtu,
+ .ndo_set_rx_mode = batadv_interface_set_rx_mode,
.ndo_start_xmit = batadv_interface_tx,
.ndo_validate_addr = eth_validate_addr,
.ndo_add_slave = batadv_softif_slave_add,
@@ -616,6 +822,7 @@ static void batadv_softif_init_early(struct net_device *dev)
dev->netdev_ops = &batadv_netdev_ops;
dev->destructor = batadv_softif_free;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
dev->tx_queue_len = 0;
/* can't call min_mtu, because the needed variables
@@ -623,7 +830,7 @@ static void batadv_softif_init_early(struct net_device *dev)
*/
dev->mtu = ETH_DATA_LEN;
/* reserve more space in the skbuff for our header */
- dev->hard_header_len = BATADV_HEADER_LEN;
+ dev->hard_header_len = batadv_max_header_len();
/* generate random address */
eth_hw_addr_random(dev);
@@ -760,6 +967,12 @@ static const struct {
{ "mgmt_tx_bytes" },
{ "mgmt_rx" },
{ "mgmt_rx_bytes" },
+ { "frag_tx" },
+ { "frag_tx_bytes" },
+ { "frag_rx" },
+ { "frag_rx_bytes" },
+ { "frag_fwd" },
+ { "frag_fwd_bytes" },
{ "tt_request_tx" },
{ "tt_request_rx" },
{ "tt_response_tx" },
diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h
index 2f2472c2ea0d..06fc91ff5a02 100644
--- a/net/batman-adv/soft-interface.h
+++ b/net/batman-adv/soft-interface.h
@@ -28,5 +28,9 @@ struct net_device *batadv_softif_create(const char *name);
void batadv_softif_destroy_sysfs(struct net_device *soft_iface);
int batadv_softif_is_valid(const struct net_device *net_dev);
extern struct rtnl_link_ops batadv_link_ops;
+int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid);
+void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan);
+struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
+ unsigned short vid);
#endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */
diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
index 4114b961bc2c..6335433310af 100644
--- a/net/batman-adv/sysfs.c
+++ b/net/batman-adv/sysfs.c
@@ -21,11 +21,12 @@
#include "sysfs.h"
#include "translation-table.h"
#include "distributed-arp-table.h"
+#include "network-coding.h"
#include "originator.h"
#include "hard-interface.h"
+#include "soft-interface.h"
#include "gateway_common.h"
#include "gateway_client.h"
-#include "vis.h"
static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
{
@@ -39,6 +40,53 @@ static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj)
return netdev_priv(net_dev);
}
+/**
+ * batadv_vlan_kobj_to_batpriv - convert a vlan kobj in the associated batpriv
+ * @obj: kobject to covert
+ *
+ * Returns the associated batadv_priv struct.
+ */
+static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj)
+{
+ /* VLAN specific attributes are located in the root sysfs folder if they
+ * refer to the untagged VLAN..
+ */
+ if (!strcmp(BATADV_SYSFS_IF_MESH_SUBDIR, obj->name))
+ return batadv_kobj_to_batpriv(obj);
+
+ /* ..while the attributes for the tagged vlans are located in
+ * the in the corresponding "vlan%VID" subfolder
+ */
+ return batadv_kobj_to_batpriv(obj->parent);
+}
+
+/**
+ * batadv_kobj_to_vlan - convert a kobj in the associated softif_vlan struct
+ * @obj: kobject to covert
+ *
+ * Returns the associated softif_vlan struct if found, NULL otherwise.
+ */
+static struct batadv_softif_vlan *
+batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj)
+{
+ struct batadv_softif_vlan *vlan_tmp, *vlan = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) {
+ if (vlan_tmp->kobj != obj)
+ continue;
+
+ if (!atomic_inc_not_zero(&vlan_tmp->refcount))
+ continue;
+
+ vlan = vlan_tmp;
+ break;
+ }
+ rcu_read_unlock();
+
+ return vlan;
+}
+
#define BATADV_UEV_TYPE_VAR "BATTYPE="
#define BATADV_UEV_ACTION_VAR "BATACTION="
#define BATADV_UEV_DATA_VAR "BATDATA="
@@ -53,6 +101,15 @@ static char *batadv_uev_type_str[] = {
"gw"
};
+/* Use this, if you have customized show and store functions for vlan attrs */
+#define BATADV_ATTR_VLAN(_name, _mode, _show, _store) \
+struct batadv_attribute batadv_attr_vlan_##_name = { \
+ .attr = {.name = __stringify(_name), \
+ .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+};
+
/* Use this, if you have customized show and store functions */
#define BATADV_ATTR(_name, _mode, _show, _store) \
struct batadv_attribute batadv_attr_##_name = { \
@@ -122,6 +179,41 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \
static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
batadv_store_##_name)
+#define BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \
+ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \
+ struct attribute *attr, char *buff, \
+ size_t count) \
+{ \
+ struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\
+ struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \
+ kobj); \
+ size_t res = __batadv_store_bool_attr(buff, count, _post_func, \
+ attr, &vlan->_name, \
+ bat_priv->soft_iface); \
+ batadv_softif_vlan_free_ref(vlan); \
+ return res; \
+}
+
+#define BATADV_ATTR_VLAN_SHOW_BOOL(_name) \
+ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
+ struct attribute *attr, char *buff) \
+{ \
+ struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\
+ struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \
+ kobj); \
+ size_t res = sprintf(buff, "%s\n", \
+ atomic_read(&vlan->_name) == 0 ? \
+ "disabled" : "enabled"); \
+ batadv_softif_vlan_free_ref(vlan); \
+ return res; \
+}
+
+/* Use this, if you are going to turn a [name] in the vlan struct on or off */
+#define BATADV_ATTR_VLAN_BOOL(_name, _mode, _post_func) \
+ static BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \
+ static BATADV_ATTR_VLAN_SHOW_BOOL(_name) \
+ static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \
+ batadv_store_vlan_##_name)
static int batadv_store_bool_attr(char *buff, size_t count,
struct net_device *net_dev,
@@ -230,74 +322,6 @@ __batadv_store_uint_attr(const char *buff, size_t count,
return ret;
}
-static ssize_t batadv_show_vis_mode(struct kobject *kobj,
- struct attribute *attr, char *buff)
-{
- struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
- int vis_mode = atomic_read(&bat_priv->vis_mode);
- const char *mode;
-
- if (vis_mode == BATADV_VIS_TYPE_CLIENT_UPDATE)
- mode = "client";
- else
- mode = "server";
-
- return sprintf(buff, "%s\n", mode);
-}
-
-static ssize_t batadv_store_vis_mode(struct kobject *kobj,
- struct attribute *attr, char *buff,
- size_t count)
-{
- struct net_device *net_dev = batadv_kobj_to_netdev(kobj);
- struct batadv_priv *bat_priv = netdev_priv(net_dev);
- unsigned long val;
- int ret, vis_mode_tmp = -1;
- const char *old_mode, *new_mode;
-
- ret = kstrtoul(buff, 10, &val);
-
- if (((count == 2) && (!ret) &&
- (val == BATADV_VIS_TYPE_CLIENT_UPDATE)) ||
- (strncmp(buff, "client", 6) == 0) ||
- (strncmp(buff, "off", 3) == 0))
- vis_mode_tmp = BATADV_VIS_TYPE_CLIENT_UPDATE;
-
- if (((count == 2) && (!ret) &&
- (val == BATADV_VIS_TYPE_SERVER_SYNC)) ||
- (strncmp(buff, "server", 6) == 0))
- vis_mode_tmp = BATADV_VIS_TYPE_SERVER_SYNC;
-
- if (vis_mode_tmp < 0) {
- if (buff[count - 1] == '\n')
- buff[count - 1] = '\0';
-
- batadv_info(net_dev,
- "Invalid parameter for 'vis mode' setting received: %s\n",
- buff);
- return -EINVAL;
- }
-
- if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp)
- return count;
-
- if (atomic_read(&bat_priv->vis_mode) == BATADV_VIS_TYPE_CLIENT_UPDATE)
- old_mode = "client";
- else
- old_mode = "server";
-
- if (vis_mode_tmp == BATADV_VIS_TYPE_CLIENT_UPDATE)
- new_mode = "client";
- else
- new_mode = "server";
-
- batadv_info(net_dev, "Changing vis mode from: %s to: %s\n", old_mode,
- new_mode);
-
- atomic_set(&bat_priv->vis_mode, (unsigned int)vis_mode_tmp);
- return count;
-}
-
static ssize_t batadv_show_bat_algo(struct kobject *kobj,
struct attribute *attr, char *buff)
{
@@ -390,6 +414,7 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj,
*/
batadv_gw_check_client_stop(bat_priv);
atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp);
+ batadv_gw_tvlv_container_update(bat_priv);
return count;
}
@@ -397,15 +422,13 @@ static ssize_t batadv_show_gw_bwidth(struct kobject *kobj,
struct attribute *attr, char *buff)
{
struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
- int down, up;
- int gw_bandwidth = atomic_read(&bat_priv->gw_bandwidth);
-
- batadv_gw_bandwidth_to_kbit(gw_bandwidth, &down, &up);
- return sprintf(buff, "%i%s/%i%s\n",
- (down > 2048 ? down / 1024 : down),
- (down > 2048 ? "MBit" : "KBit"),
- (up > 2048 ? up / 1024 : up),
- (up > 2048 ? "MBit" : "KBit"));
+ uint32_t down, up;
+
+ down = atomic_read(&bat_priv->gw.bandwidth_down);
+ up = atomic_read(&bat_priv->gw.bandwidth_up);
+
+ return sprintf(buff, "%u.%u/%u.%u MBit\n", down / 10,
+ down % 10, up / 10, up % 10);
}
static ssize_t batadv_store_gw_bwidth(struct kobject *kobj,
@@ -426,12 +449,10 @@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
-BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, NULL);
+BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR,
+ batadv_dat_status_update);
#endif
BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu);
-BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
-static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode,
- batadv_store_vis_mode);
static BATADV_ATTR(routing_algo, S_IRUGO, batadv_show_bat_algo, NULL);
static BATADV_ATTR(gw_mode, S_IRUGO | S_IWUSR, batadv_show_gw_mode,
batadv_store_gw_mode);
@@ -447,7 +468,8 @@ static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL);
#endif
#ifdef CONFIG_BATMAN_ADV_NC
-BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, NULL);
+BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR,
+ batadv_nc_status_update);
#endif
static struct batadv_attribute *batadv_mesh_attrs[] = {
@@ -460,8 +482,6 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
&batadv_attr_distributed_arp_table,
#endif
&batadv_attr_fragmentation,
- &batadv_attr_ap_isolation,
- &batadv_attr_vis_mode,
&batadv_attr_routing_algo,
&batadv_attr_gw_mode,
&batadv_attr_orig_interval,
@@ -477,6 +497,16 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
NULL,
};
+BATADV_ATTR_VLAN_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
+
+/**
+ * batadv_vlan_attrs - array of vlan specific sysfs attributes
+ */
+static struct batadv_attribute *batadv_vlan_attrs[] = {
+ &batadv_attr_vlan_ap_isolation,
+ NULL,
+};
+
int batadv_sysfs_add_meshif(struct net_device *dev)
{
struct kobject *batif_kobject = &dev->dev.kobj;
@@ -527,6 +557,80 @@ void batadv_sysfs_del_meshif(struct net_device *dev)
bat_priv->mesh_obj = NULL;
}
+/**
+ * batadv_sysfs_add_vlan - add all the needed sysfs objects for the new vlan
+ * @dev: netdev of the mesh interface
+ * @vlan: private data of the newly added VLAN interface
+ *
+ * Returns 0 on success and -ENOMEM if any of the structure allocations fails.
+ */
+int batadv_sysfs_add_vlan(struct net_device *dev,
+ struct batadv_softif_vlan *vlan)
+{
+ char vlan_subdir[sizeof(BATADV_SYSFS_VLAN_SUBDIR_PREFIX) + 5];
+ struct batadv_priv *bat_priv = netdev_priv(dev);
+ struct batadv_attribute **bat_attr;
+ int err;
+
+ if (vlan->vid & BATADV_VLAN_HAS_TAG) {
+ sprintf(vlan_subdir, BATADV_SYSFS_VLAN_SUBDIR_PREFIX "%hu",
+ vlan->vid & VLAN_VID_MASK);
+
+ vlan->kobj = kobject_create_and_add(vlan_subdir,
+ bat_priv->mesh_obj);
+ if (!vlan->kobj) {
+ batadv_err(dev, "Can't add sysfs directory: %s/%s\n",
+ dev->name, vlan_subdir);
+ goto out;
+ }
+ } else {
+ /* the untagged LAN uses the root folder to store its "VLAN
+ * specific attributes"
+ */
+ vlan->kobj = bat_priv->mesh_obj;
+ kobject_get(bat_priv->mesh_obj);
+ }
+
+ for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) {
+ err = sysfs_create_file(vlan->kobj,
+ &((*bat_attr)->attr));
+ if (err) {
+ batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n",
+ dev->name, vlan_subdir,
+ ((*bat_attr)->attr).name);
+ goto rem_attr;
+ }
+ }
+
+ return 0;
+
+rem_attr:
+ for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
+ sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
+
+ kobject_put(vlan->kobj);
+ vlan->kobj = NULL;
+out:
+ return -ENOMEM;
+}
+
+/**
+ * batadv_sysfs_del_vlan - remove all the sysfs objects for a given VLAN
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vlan: the private data of the VLAN to destroy
+ */
+void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan)
+{
+ struct batadv_attribute **bat_attr;
+
+ for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr)
+ sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr));
+
+ kobject_put(vlan->kobj);
+ vlan->kobj = NULL;
+}
+
static ssize_t batadv_show_mesh_iface(struct kobject *kobj,
struct attribute *attr, char *buff)
{
diff --git a/net/batman-adv/sysfs.h b/net/batman-adv/sysfs.h
index 479acf4c16f4..c7d725de50ad 100644
--- a/net/batman-adv/sysfs.h
+++ b/net/batman-adv/sysfs.h
@@ -22,6 +22,12 @@
#define BATADV_SYSFS_IF_MESH_SUBDIR "mesh"
#define BATADV_SYSFS_IF_BAT_SUBDIR "batman_adv"
+/**
+ * BATADV_SYSFS_VLAN_SUBDIR_PREFIX - prefix of the subfolder that will be
+ * created in the sysfs hierarchy for each VLAN interface. The subfolder will
+ * be named "BATADV_SYSFS_VLAN_SUBDIR_PREFIX%vid".
+ */
+#define BATADV_SYSFS_VLAN_SUBDIR_PREFIX "vlan"
struct batadv_attribute {
struct attribute attr;
@@ -36,6 +42,10 @@ void batadv_sysfs_del_meshif(struct net_device *dev);
int batadv_sysfs_add_hardif(struct kobject **hardif_obj,
struct net_device *dev);
void batadv_sysfs_del_hardif(struct kobject **hardif_obj);
+int batadv_sysfs_add_vlan(struct net_device *dev,
+ struct batadv_softif_vlan *vlan);
+void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,
+ struct batadv_softif_vlan *vlan);
int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
enum batadv_uev_action action, const char *data);
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 34510f38708f..4add57d4857f 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -27,13 +27,14 @@
#include "routing.h"
#include "bridge_loop_avoidance.h"
-#include <linux/crc16.h>
+#include <linux/crc32c.h>
/* hash class keys */
static struct lock_class_key batadv_tt_local_hash_lock_class_key;
static struct lock_class_key batadv_tt_global_hash_lock_class_key;
static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
+ unsigned short vid,
struct batadv_orig_node *orig_node);
static void batadv_tt_purge(struct work_struct *work);
static void
@@ -41,7 +42,8 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry);
static void batadv_tt_global_del(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
const unsigned char *addr,
- const char *message, bool roaming);
+ unsigned short vid, const char *message,
+ bool roaming);
/* returns 1 if they are the same mac addr */
static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
@@ -52,43 +54,93 @@ static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
+/**
+ * batadv_choose_tt - return the index of the tt entry in the hash table
+ * @data: pointer to the tt_common_entry object to map
+ * @size: the size of the hash table
+ *
+ * Returns the hash index where the object represented by 'data' should be
+ * stored at.
+ */
+static inline uint32_t batadv_choose_tt(const void *data, uint32_t size)
+{
+ struct batadv_tt_common_entry *tt;
+ uint32_t hash = 0;
+
+ tt = (struct batadv_tt_common_entry *)data;
+ hash = batadv_hash_bytes(hash, &tt->addr, ETH_ALEN);
+ hash = batadv_hash_bytes(hash, &tt->vid, sizeof(tt->vid));
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash % size;
+}
+
+/**
+ * batadv_tt_hash_find - look for a client in the given hash table
+ * @hash: the hash table to search
+ * @addr: the mac address of the client to look for
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the tt_common struct belonging to the searched client if
+ * found, NULL otherwise.
+ */
static struct batadv_tt_common_entry *
-batadv_tt_hash_find(struct batadv_hashtable *hash, const void *data)
+batadv_tt_hash_find(struct batadv_hashtable *hash, const uint8_t *addr,
+ unsigned short vid)
{
struct hlist_head *head;
- struct batadv_tt_common_entry *tt_common_entry;
- struct batadv_tt_common_entry *tt_common_entry_tmp = NULL;
+ struct batadv_tt_common_entry to_search, *tt, *tt_tmp = NULL;
uint32_t index;
if (!hash)
return NULL;
- index = batadv_choose_orig(data, hash->size);
+ memcpy(to_search.addr, addr, ETH_ALEN);
+ to_search.vid = vid;
+
+ index = batadv_choose_tt(&to_search, hash->size);
head = &hash->table[index];
rcu_read_lock();
- hlist_for_each_entry_rcu(tt_common_entry, head, hash_entry) {
- if (!batadv_compare_eth(tt_common_entry, data))
+ hlist_for_each_entry_rcu(tt, head, hash_entry) {
+ if (!batadv_compare_eth(tt, addr))
continue;
- if (!atomic_inc_not_zero(&tt_common_entry->refcount))
+ if (tt->vid != vid)
continue;
- tt_common_entry_tmp = tt_common_entry;
+ if (!atomic_inc_not_zero(&tt->refcount))
+ continue;
+
+ tt_tmp = tt;
break;
}
rcu_read_unlock();
- return tt_common_entry_tmp;
+ return tt_tmp;
}
+/**
+ * batadv_tt_local_hash_find - search the local table for a given client
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the client to look for
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the corresponding tt_local_entry struct if the client is
+ * found, NULL otherwise.
+ */
static struct batadv_tt_local_entry *
-batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const void *data)
+batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const uint8_t *addr,
+ unsigned short vid)
{
struct batadv_tt_common_entry *tt_common_entry;
struct batadv_tt_local_entry *tt_local_entry = NULL;
- tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, data);
+ tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, addr,
+ vid);
if (tt_common_entry)
tt_local_entry = container_of(tt_common_entry,
struct batadv_tt_local_entry,
@@ -96,13 +148,24 @@ batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const void *data)
return tt_local_entry;
}
+/**
+ * batadv_tt_global_hash_find - search the global table for a given client
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the client to look for
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the corresponding tt_global_entry struct if the client
+ * is found, NULL otherwise.
+ */
static struct batadv_tt_global_entry *
-batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const void *data)
+batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const uint8_t *addr,
+ unsigned short vid)
{
struct batadv_tt_common_entry *tt_common_entry;
struct batadv_tt_global_entry *tt_global_entry = NULL;
- tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, data);
+ tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, addr,
+ vid);
if (tt_common_entry)
tt_global_entry = container_of(tt_common_entry,
struct batadv_tt_global_entry,
@@ -117,25 +180,17 @@ batadv_tt_local_entry_free_ref(struct batadv_tt_local_entry *tt_local_entry)
kfree_rcu(tt_local_entry, common.rcu);
}
-static void batadv_tt_global_entry_free_rcu(struct rcu_head *rcu)
-{
- struct batadv_tt_common_entry *tt_common_entry;
- struct batadv_tt_global_entry *tt_global_entry;
-
- tt_common_entry = container_of(rcu, struct batadv_tt_common_entry, rcu);
- tt_global_entry = container_of(tt_common_entry,
- struct batadv_tt_global_entry, common);
-
- kfree(tt_global_entry);
-}
-
+/**
+ * batadv_tt_global_entry_free_ref - decrement the refcounter for a
+ * tt_global_entry and possibly free it
+ * @tt_global_entry: the object to free
+ */
static void
batadv_tt_global_entry_free_ref(struct batadv_tt_global_entry *tt_global_entry)
{
if (atomic_dec_and_test(&tt_global_entry->common.refcount)) {
batadv_tt_global_del_orig_list(tt_global_entry);
- call_rcu(&tt_global_entry->common.rcu,
- batadv_tt_global_entry_free_rcu);
+ kfree_rcu(tt_global_entry, common.rcu);
}
}
@@ -153,13 +208,107 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
kfree(orig_entry);
}
+/**
+ * batadv_tt_local_size_mod - change the size by v of the local table identified
+ * by vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier of the sub-table to change
+ * @v: the amount to sum to the local table size
+ */
+static void batadv_tt_local_size_mod(struct batadv_priv *bat_priv,
+ unsigned short vid, int v)
+{
+ struct batadv_softif_vlan *vlan;
+
+ vlan = batadv_softif_vlan_get(bat_priv, vid);
+ if (!vlan)
+ return;
+
+ atomic_add(v, &vlan->tt.num_entries);
+
+ batadv_softif_vlan_free_ref(vlan);
+}
+
+/**
+ * batadv_tt_local_size_inc - increase by one the local table size for the given
+ * vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ */
+static void batadv_tt_local_size_inc(struct batadv_priv *bat_priv,
+ unsigned short vid)
+{
+ batadv_tt_local_size_mod(bat_priv, vid, 1);
+}
+
+/**
+ * batadv_tt_local_size_dec - decrease by one the local table size for the given
+ * vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ */
+static void batadv_tt_local_size_dec(struct batadv_priv *bat_priv,
+ unsigned short vid)
+{
+ batadv_tt_local_size_mod(bat_priv, vid, -1);
+}
+
+/**
+ * batadv_tt_global_size_mod - change the size by v of the local table
+ * identified by vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ * @v: the amount to sum to the global table size
+ */
+static void batadv_tt_global_size_mod(struct batadv_orig_node *orig_node,
+ unsigned short vid, int v)
+{
+ struct batadv_orig_node_vlan *vlan;
+
+ vlan = batadv_orig_node_vlan_new(orig_node, vid);
+ if (!vlan)
+ return;
+
+ if (atomic_add_return(v, &vlan->tt.num_entries) == 0) {
+ spin_lock_bh(&orig_node->vlan_list_lock);
+ list_del_rcu(&vlan->list);
+ spin_unlock_bh(&orig_node->vlan_list_lock);
+ batadv_orig_node_vlan_free_ref(vlan);
+ }
+
+ batadv_orig_node_vlan_free_ref(vlan);
+}
+
+/**
+ * batadv_tt_global_size_inc - increase by one the global table size for the
+ * given vid
+ * @orig_node: the originator which global table size has to be decreased
+ * @vid: the vlan identifier
+ */
+static void batadv_tt_global_size_inc(struct batadv_orig_node *orig_node,
+ unsigned short vid)
+{
+ batadv_tt_global_size_mod(orig_node, vid, 1);
+}
+
+/**
+ * batadv_tt_global_size_dec - decrease by one the global table size for the
+ * given vid
+ * @orig_node: the originator which global table size has to be decreased
+ * @vid: the vlan identifier
+ */
+static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node,
+ unsigned short vid)
+{
+ batadv_tt_global_size_mod(orig_node, vid, -1);
+}
+
static void
batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry)
{
if (!atomic_dec_and_test(&orig_entry->refcount))
return;
- /* to avoid race conditions, immediately decrease the tt counter */
- atomic_dec(&orig_entry->orig_node->tt_size);
+
call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu);
}
@@ -180,12 +329,13 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
bool del_op_requested, del_op_entry;
tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC);
-
if (!tt_change_node)
return;
tt_change_node->change.flags = flags;
+ tt_change_node->change.reserved = 0;
memcpy(tt_change_node->change.addr, common->addr, ETH_ALEN);
+ tt_change_node->change.vid = htons(common->vid);
del_op_requested = flags & BATADV_TT_CLIENT_DEL;
@@ -208,6 +358,13 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
goto del;
if (del_op_requested && !del_op_entry)
goto del;
+
+ /* this is a second add in the same originator interval. It
+ * means that flags have been changed: update them!
+ */
+ if (!del_op_requested && !del_op_entry)
+ entry->change.flags = flags;
+
continue;
del:
list_del(&entry->list);
@@ -229,9 +386,55 @@ unlock:
atomic_inc(&bat_priv->tt.local_changes);
}
-int batadv_tt_len(int changes_num)
+/**
+ * batadv_tt_len - compute length in bytes of given number of tt changes
+ * @changes_num: number of tt changes
+ *
+ * Returns computed length in bytes.
+ */
+static int batadv_tt_len(int changes_num)
{
- return changes_num * sizeof(struct batadv_tt_change);
+ return changes_num * sizeof(struct batadv_tvlv_tt_change);
+}
+
+/**
+ * batadv_tt_entries - compute the number of entries fitting in tt_len bytes
+ * @tt_len: available space
+ *
+ * Returns the number of entries.
+ */
+static uint16_t batadv_tt_entries(uint16_t tt_len)
+{
+ return tt_len / batadv_tt_len(1);
+}
+
+/**
+ * batadv_tt_local_table_transmit_size - calculates the local translation table
+ * size when transmitted over the air
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Returns local translation table size in bytes.
+ */
+static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv)
+{
+ uint16_t num_vlan = 0, tt_local_entries = 0;
+ struct batadv_softif_vlan *vlan;
+ int hdr_size;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+ num_vlan++;
+ tt_local_entries += atomic_read(&vlan->tt.num_entries);
+ }
+ rcu_read_unlock();
+
+ /* header size of tvlv encapsulated tt response payload */
+ hdr_size = sizeof(struct batadv_unicast_tvlv_packet);
+ hdr_size += sizeof(struct batadv_tvlv_hdr);
+ hdr_size += sizeof(struct batadv_tvlv_tt_data);
+ hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data);
+
+ return hdr_size + batadv_tt_len(tt_local_entries);
}
static int batadv_tt_local_init(struct batadv_priv *bat_priv)
@@ -255,33 +458,51 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv,
const char *message)
{
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Deleting global tt entry %pM: %s\n",
- tt_global->common.addr, message);
+ "Deleting global tt entry %pM (vid: %d): %s\n",
+ tt_global->common.addr,
+ BATADV_PRINT_VID(tt_global->common.vid), message);
batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
- batadv_choose_orig, tt_global->common.addr);
+ batadv_choose_tt, &tt_global->common);
batadv_tt_global_entry_free_ref(tt_global);
}
-void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
- int ifindex)
+/**
+ * batadv_tt_local_add - add a new client to the local table or update an
+ * existing client
+ * @soft_iface: netdev struct of the mesh interface
+ * @addr: the mac address of the client to add
+ * @vid: VLAN identifier
+ * @ifindex: index of the interface where the client is connected to (useful to
+ * identify wireless clients)
+ *
+ * Returns true if the client was successfully added, false otherwise.
+ */
+bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
+ unsigned short vid, int ifindex)
{
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
struct batadv_tt_local_entry *tt_local;
struct batadv_tt_global_entry *tt_global;
+ struct net_device *in_dev = NULL;
struct hlist_head *head;
struct batadv_tt_orig_list_entry *orig_entry;
- int hash_added;
- bool roamed_back = false;
+ int hash_added, table_size, packet_size_max;
+ bool ret = false, roamed_back = false;
+ uint8_t remote_flags;
+
+ if (ifindex != BATADV_NULL_IFINDEX)
+ in_dev = dev_get_by_index(&init_net, ifindex);
- tt_local = batadv_tt_local_hash_find(bat_priv, addr);
- tt_global = batadv_tt_global_hash_find(bat_priv, addr);
+ tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid);
+ tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (tt_local) {
tt_local->last_seen = jiffies;
if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Re-adding pending client %pM\n", addr);
+ "Re-adding pending client %pM (vid: %d)\n",
+ addr, BATADV_PRINT_VID(vid));
/* whatever the reason why the PENDING flag was set,
* this is a client which was enqueued to be removed in
* this orig_interval. Since it popped up again, the
@@ -293,8 +514,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Roaming client %pM came back to its original location\n",
- addr);
+ "Roaming client %pM (vid: %d) came back to its original location\n",
+ addr, BATADV_PRINT_VID(vid));
/* the ROAM flag is set because this client roamed away
* and the node got a roaming_advertisement message. Now
* that the client popped up again at its original
@@ -306,12 +527,24 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
goto check_roaming;
}
+ /* Ignore the client if we cannot send it in a full table response. */
+ table_size = batadv_tt_local_table_transmit_size(bat_priv);
+ table_size += batadv_tt_len(1);
+ packet_size_max = atomic_read(&bat_priv->packet_size_max);
+ if (table_size > packet_size_max) {
+ net_ratelimited_function(batadv_info, soft_iface,
+ "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
+ table_size, packet_size_max, addr);
+ goto out;
+ }
+
tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC);
if (!tt_local)
goto out;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Creating new local tt entry: %pM (ttvn: %d)\n", addr,
+ "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n",
+ addr, BATADV_PRINT_VID(vid),
(uint8_t)atomic_read(&bat_priv->tt.vn));
memcpy(tt_local->common.addr, addr, ETH_ALEN);
@@ -320,7 +553,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
* (consistency check)
*/
tt_local->common.flags = BATADV_TT_CLIENT_NEW;
- if (batadv_is_wifi_iface(ifindex))
+ tt_local->common.vid = vid;
+ if (batadv_is_wifi_netdev(in_dev))
tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
atomic_set(&tt_local->common.refcount, 2);
tt_local->last_seen = jiffies;
@@ -331,7 +565,7 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
- batadv_choose_orig, &tt_local->common,
+ batadv_choose_tt, &tt_local->common,
&tt_local->common.hash_entry);
if (unlikely(hash_added != 0)) {
@@ -353,6 +587,7 @@ check_roaming:
rcu_read_lock();
hlist_for_each_entry_rcu(orig_entry, head, list) {
batadv_send_roam_adv(bat_priv, tt_global->common.addr,
+ tt_global->common.vid,
orig_entry->orig_node);
}
rcu_read_unlock();
@@ -369,78 +604,219 @@ check_roaming:
}
}
+ /* store the current remote flags before altering them. This helps
+ * understanding is flags are changing or not
+ */
+ remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK;
+
+ if (batadv_is_wifi_netdev(in_dev))
+ tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
+ else
+ tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI;
+
+ /* if any "dynamic" flag has been modified, resend an ADD event for this
+ * entry so that all the nodes can get the new flags
+ */
+ if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK))
+ batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
+
+ ret = true;
out:
+ if (in_dev)
+ dev_put(in_dev);
if (tt_local)
batadv_tt_local_entry_free_ref(tt_local);
if (tt_global)
batadv_tt_global_entry_free_ref(tt_global);
+ return ret;
}
-static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff,
- int *packet_buff_len,
- int min_packet_len,
- int new_packet_len)
+/**
+ * batadv_tt_prepare_tvlv_global_data - prepare the TVLV TT header to send
+ * within a TT Response directed to another node
+ * @orig_node: originator for which the TT data has to be prepared
+ * @tt_data: uninitialised pointer to the address of the TVLV buffer
+ * @tt_change: uninitialised pointer to the address of the area where the TT
+ * changed can be stored
+ * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
+ * function reserves the amount of space needed to send the entire global TT
+ * table. In case of success the value is updated with the real amount of
+ * reserved bytes
+
+ * Allocate the needed amount of memory for the entire TT TVLV and write its
+ * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
+ * objects, one per active VLAN served by the originator node.
+ *
+ * Return the size of the allocated buffer or 0 in case of failure.
+ */
+static uint16_t
+batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
+ struct batadv_tvlv_tt_data **tt_data,
+ struct batadv_tvlv_tt_change **tt_change,
+ int32_t *tt_len)
{
- unsigned char *new_buff;
+ uint16_t num_vlan = 0, num_entries = 0, change_offset, tvlv_len;
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
+ struct batadv_orig_node_vlan *vlan;
+ uint8_t *tt_change_ptr;
- new_buff = kmalloc(new_packet_len, GFP_ATOMIC);
+ rcu_read_lock();
+ list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
+ num_vlan++;
+ num_entries += atomic_read(&vlan->tt.num_entries);
+ }
- /* keep old buffer if kmalloc should fail */
- if (new_buff) {
- memcpy(new_buff, *packet_buff, min_packet_len);
- kfree(*packet_buff);
- *packet_buff = new_buff;
- *packet_buff_len = new_packet_len;
+ change_offset = sizeof(**tt_data);
+ change_offset += num_vlan * sizeof(*tt_vlan);
+
+ /* if tt_len is negative, allocate the space needed by the full table */
+ if (*tt_len < 0)
+ *tt_len = batadv_tt_len(num_entries);
+
+ tvlv_len = *tt_len;
+ tvlv_len += change_offset;
+
+ *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+ if (!*tt_data) {
+ *tt_len = 0;
+ goto out;
}
+
+ (*tt_data)->flags = BATADV_NO_FLAGS;
+ (*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn);
+ (*tt_data)->num_vlan = htons(num_vlan);
+
+ tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
+ list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
+ tt_vlan->vid = htons(vlan->vid);
+ tt_vlan->crc = htonl(vlan->tt.crc);
+
+ tt_vlan++;
+ }
+
+ tt_change_ptr = (uint8_t *)*tt_data + change_offset;
+ *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+
+out:
+ rcu_read_unlock();
+ return tvlv_len;
}
-static void batadv_tt_prepare_packet_buff(struct batadv_priv *bat_priv,
- unsigned char **packet_buff,
- int *packet_buff_len,
- int min_packet_len)
-{
- int req_len;
+/**
+ * batadv_tt_prepare_tvlv_local_data - allocate and prepare the TT TVLV for this
+ * node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: uninitialised pointer to the address of the TVLV buffer
+ * @tt_change: uninitialised pointer to the address of the area where the TT
+ * changes can be stored
+ * @tt_len: pointer to the length to reserve to the tt_change. if -1 this
+ * function reserves the amount of space needed to send the entire local TT
+ * table. In case of success the value is updated with the real amount of
+ * reserved bytes
+ *
+ * Allocate the needed amount of memory for the entire TT TVLV and write its
+ * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data
+ * objects, one per active VLAN.
+ *
+ * Return the size of the allocated buffer or 0 in case of failure.
+ */
+static uint16_t
+batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
+ struct batadv_tvlv_tt_data **tt_data,
+ struct batadv_tvlv_tt_change **tt_change,
+ int32_t *tt_len)
+{
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
+ struct batadv_softif_vlan *vlan;
+ uint16_t num_vlan = 0, num_entries = 0, tvlv_len;
+ uint8_t *tt_change_ptr;
+ int change_offset;
- req_len = min_packet_len;
- req_len += batadv_tt_len(atomic_read(&bat_priv->tt.local_changes));
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+ num_vlan++;
+ num_entries += atomic_read(&vlan->tt.num_entries);
+ }
- /* if we have too many changes for one packet don't send any
- * and wait for the tt table request which will be fragmented
- */
- if (req_len > bat_priv->soft_iface->mtu)
- req_len = min_packet_len;
+ change_offset = sizeof(**tt_data);
+ change_offset += num_vlan * sizeof(*tt_vlan);
+
+ /* if tt_len is negative, allocate the space needed by the full table */
+ if (*tt_len < 0)
+ *tt_len = batadv_tt_len(num_entries);
+
+ tvlv_len = *tt_len;
+ tvlv_len += change_offset;
+
+ *tt_data = kmalloc(tvlv_len, GFP_ATOMIC);
+ if (!*tt_data) {
+ tvlv_len = 0;
+ goto out;
+ }
+
+ (*tt_data)->flags = BATADV_NO_FLAGS;
+ (*tt_data)->ttvn = atomic_read(&bat_priv->tt.vn);
+ (*tt_data)->num_vlan = htons(num_vlan);
+
+ tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1);
+ hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+ tt_vlan->vid = htons(vlan->vid);
+ tt_vlan->crc = htonl(vlan->tt.crc);
- batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len,
- min_packet_len, req_len);
+ tt_vlan++;
+ }
+
+ tt_change_ptr = (uint8_t *)*tt_data + change_offset;
+ *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
+
+out:
+ rcu_read_unlock();
+ return tvlv_len;
}
-static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv,
- unsigned char **packet_buff,
- int *packet_buff_len,
- int min_packet_len)
+/**
+ * batadv_tt_tvlv_container_update - update the translation table tvlv container
+ * after local tt changes have been committed
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv)
{
struct batadv_tt_change_node *entry, *safe;
- int count = 0, tot_changes = 0, new_len;
- unsigned char *tt_buff;
+ struct batadv_tvlv_tt_data *tt_data;
+ struct batadv_tvlv_tt_change *tt_change;
+ int tt_diff_len, tt_change_len = 0;
+ int tt_diff_entries_num = 0, tt_diff_entries_count = 0;
+ uint16_t tvlv_len;
- batadv_tt_prepare_packet_buff(bat_priv, packet_buff,
- packet_buff_len, min_packet_len);
+ tt_diff_entries_num = atomic_read(&bat_priv->tt.local_changes);
+ tt_diff_len = batadv_tt_len(tt_diff_entries_num);
- new_len = *packet_buff_len - min_packet_len;
- tt_buff = *packet_buff + min_packet_len;
+ /* if we have too many changes for one packet don't send any
+ * and wait for the tt table request which will be fragmented
+ */
+ if (tt_diff_len > bat_priv->soft_iface->mtu)
+ tt_diff_len = 0;
+
+ tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data,
+ &tt_change, &tt_diff_len);
+ if (!tvlv_len)
+ return;
- if (new_len > 0)
- tot_changes = new_len / batadv_tt_len(1);
+ tt_data->flags = BATADV_TT_OGM_DIFF;
+
+ if (tt_diff_len == 0)
+ goto container_register;
spin_lock_bh(&bat_priv->tt.changes_list_lock);
atomic_set(&bat_priv->tt.local_changes, 0);
list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list,
list) {
- if (count < tot_changes) {
- memcpy(tt_buff + batadv_tt_len(count),
- &entry->change, sizeof(struct batadv_tt_change));
- count++;
+ if (tt_diff_entries_count < tt_diff_entries_num) {
+ memcpy(tt_change + tt_diff_entries_count,
+ &entry->change,
+ sizeof(struct batadv_tvlv_tt_change));
+ tt_diff_entries_count++;
}
list_del(&entry->list);
kfree(entry);
@@ -452,20 +828,25 @@ static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv,
kfree(bat_priv->tt.last_changeset);
bat_priv->tt.last_changeset_len = 0;
bat_priv->tt.last_changeset = NULL;
+ tt_change_len = batadv_tt_len(tt_diff_entries_count);
/* check whether this new OGM has no changes due to size problems */
- if (new_len > 0) {
+ if (tt_diff_entries_count > 0) {
/* if kmalloc() fails we will reply with the full table
* instead of providing the diff
*/
- bat_priv->tt.last_changeset = kmalloc(new_len, GFP_ATOMIC);
+ bat_priv->tt.last_changeset = kzalloc(tt_diff_len, GFP_ATOMIC);
if (bat_priv->tt.last_changeset) {
- memcpy(bat_priv->tt.last_changeset, tt_buff, new_len);
- bat_priv->tt.last_changeset_len = new_len;
+ memcpy(bat_priv->tt.last_changeset,
+ tt_change, tt_change_len);
+ bat_priv->tt.last_changeset_len = tt_diff_len;
}
}
spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
- return count;
+container_register:
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_TT, 1, tt_data,
+ tvlv_len);
+ kfree(tt_data);
}
int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
@@ -476,7 +857,9 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_tt_common_entry *tt_common_entry;
struct batadv_tt_local_entry *tt_local;
struct batadv_hard_iface *primary_if;
+ struct batadv_softif_vlan *vlan;
struct hlist_head *head;
+ unsigned short vid;
uint32_t i;
int last_seen_secs;
int last_seen_msecs;
@@ -489,11 +872,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
goto out;
seq_printf(seq,
- "Locally retrieved addresses (from %s) announced via TT (TTVN: %u CRC: %#.4x):\n",
- net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn),
- bat_priv->tt.local_crc);
- seq_printf(seq, " %-13s %-7s %-10s\n", "Client", "Flags",
- "Last seen");
+ "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n",
+ net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn));
+ seq_printf(seq, " %-13s %s %-7s %-9s (%-10s)\n", "Client", "VID",
+ "Flags", "Last seen", "CRC");
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
@@ -504,6 +886,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
tt_local = container_of(tt_common_entry,
struct batadv_tt_local_entry,
common);
+ vid = tt_common_entry->vid;
last_seen_jiffies = jiffies - tt_local->last_seen;
last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
last_seen_secs = last_seen_msecs / 1000;
@@ -511,8 +894,17 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
no_purge = tt_common_entry->flags & np_flag;
- seq_printf(seq, " * %pM [%c%c%c%c%c] %3u.%03u\n",
+ vlan = batadv_softif_vlan_get(bat_priv, vid);
+ if (!vlan) {
+ seq_printf(seq, "Cannot retrieve VLAN %d\n",
+ BATADV_PRINT_VID(vid));
+ continue;
+ }
+
+ seq_printf(seq,
+ " * %pM %4i [%c%c%c%c%c] %3u.%03u (%#.8x)\n",
tt_common_entry->addr,
+ BATADV_PRINT_VID(tt_common_entry->vid),
(tt_common_entry->flags &
BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
no_purge ? 'P' : '.',
@@ -523,7 +915,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
(tt_common_entry->flags &
BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
no_purge ? 0 : last_seen_secs,
- no_purge ? 0 : last_seen_msecs);
+ no_purge ? 0 : last_seen_msecs,
+ vlan->tt.crc);
+
+ batadv_softif_vlan_free_ref(vlan);
}
rcu_read_unlock();
}
@@ -547,27 +942,29 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Local tt entry (%pM) pending to be removed: %s\n",
- tt_local_entry->common.addr, message);
+ "Local tt entry (%pM, vid: %d) pending to be removed: %s\n",
+ tt_local_entry->common.addr,
+ BATADV_PRINT_VID(tt_local_entry->common.vid), message);
}
/**
* batadv_tt_local_remove - logically remove an entry from the local table
* @bat_priv: the bat priv with all the soft interface information
* @addr: the MAC address of the client to remove
+ * @vid: VLAN identifier
* @message: message to append to the log on deletion
* @roaming: true if the deletion is due to a roaming event
*
* Returns the flags assigned to the local entry before being deleted
*/
uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
- const uint8_t *addr, const char *message,
- bool roaming)
+ const uint8_t *addr, unsigned short vid,
+ const char *message, bool roaming)
{
struct batadv_tt_local_entry *tt_local_entry;
uint16_t flags, curr_flags = BATADV_NO_FLAGS;
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!tt_local_entry)
goto out;
@@ -603,8 +1000,16 @@ out:
return curr_flags;
}
+/**
+ * batadv_tt_local_purge_list - purge inactive tt local entries
+ * @bat_priv: the bat priv with all the soft interface information
+ * @head: pointer to the list containing the local tt entries
+ * @timeout: parameter deciding whether a given tt local entry is considered
+ * inactive or not
+ */
static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
- struct hlist_head *head)
+ struct hlist_head *head,
+ int timeout)
{
struct batadv_tt_local_entry *tt_local_entry;
struct batadv_tt_common_entry *tt_common_entry;
@@ -622,8 +1027,7 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
continue;
- if (!batadv_has_timed_out(tt_local_entry->last_seen,
- BATADV_TT_LOCAL_TIMEOUT))
+ if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout))
continue;
batadv_tt_local_set_pending(bat_priv, tt_local_entry,
@@ -631,7 +1035,14 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
}
}
-static void batadv_tt_local_purge(struct batadv_priv *bat_priv)
+/**
+ * batadv_tt_local_purge - purge inactive tt local entries
+ * @bat_priv: the bat priv with all the soft interface information
+ * @timeout: parameter deciding whether a given tt local entry is considered
+ * inactive or not
+ */
+static void batadv_tt_local_purge(struct batadv_priv *bat_priv,
+ int timeout)
{
struct batadv_hashtable *hash = bat_priv->tt.local_hash;
struct hlist_head *head;
@@ -643,7 +1054,7 @@ static void batadv_tt_local_purge(struct batadv_priv *bat_priv)
list_lock = &hash->list_locks[i];
spin_lock_bh(list_lock);
- batadv_tt_local_purge_list(bat_priv, head);
+ batadv_tt_local_purge_list(bat_priv, head, timeout);
spin_unlock_bh(list_lock);
}
}
@@ -784,7 +1195,7 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global,
INIT_HLIST_NODE(&orig_entry->list);
atomic_inc(&orig_node->refcount);
- atomic_inc(&orig_node->tt_size);
+ batadv_tt_global_size_inc(orig_node, tt_global->common.vid);
orig_entry->orig_node = orig_node;
orig_entry->ttvn = ttvn;
atomic_set(&orig_entry->refcount, 2);
@@ -803,6 +1214,7 @@ out:
* @bat_priv: the bat priv with all the soft interface information
* @orig_node: the originator announcing the client
* @tt_addr: the mac address of the non-mesh client
+ * @vid: VLAN identifier
* @flags: TT flags that have to be set for this non-mesh client
* @ttvn: the tt version number ever announcing this non-mesh client
*
@@ -813,21 +1225,28 @@ out:
* If a TT local entry exists for this non-mesh client remove it.
*
* The caller must hold orig_node refcount.
+ *
+ * Return true if the new entry has been added, false otherwise
*/
-int batadv_tt_global_add(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- const unsigned char *tt_addr, uint16_t flags,
- uint8_t ttvn)
+static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
+ const unsigned char *tt_addr,
+ unsigned short vid, uint16_t flags,
+ uint8_t ttvn)
{
struct batadv_tt_global_entry *tt_global_entry;
struct batadv_tt_local_entry *tt_local_entry;
- int ret = 0;
+ bool ret = false;
int hash_added;
struct batadv_tt_common_entry *common;
uint16_t local_flags;
- tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr);
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr);
+ /* ignore global entries from backbone nodes */
+ if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid))
+ return true;
+
+ tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr, vid);
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr, vid);
/* if the node already has a local client for this entry, it has to wait
* for a roaming advertisement instead of manually messing up the global
@@ -844,6 +1263,7 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
common = &tt_global_entry->common;
memcpy(common->addr, tt_addr, ETH_ALEN);
+ common->vid = vid;
common->flags = flags;
tt_global_entry->roam_at = 0;
@@ -861,7 +1281,7 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
hash_added = batadv_hash_add(bat_priv->tt.global_hash,
batadv_compare_tt,
- batadv_choose_orig, common,
+ batadv_choose_tt, common,
&common->hash_entry);
if (unlikely(hash_added != 0)) {
@@ -920,14 +1340,15 @@ add_orig_entry:
batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn);
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Creating new global tt entry: %pM (via %pM)\n",
- common->addr, orig_node->orig);
- ret = 1;
+ "Creating new global tt entry: %pM (vid: %d, via %pM)\n",
+ common->addr, BATADV_PRINT_VID(common->vid),
+ orig_node->orig);
+ ret = true;
out_remove:
/* remove address from local hash if present */
- local_flags = batadv_tt_local_remove(bat_priv, tt_addr,
+ local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid,
"global tt received",
flags & BATADV_TT_CLIENT_ROAM);
tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
@@ -947,18 +1368,20 @@ out:
}
/* batadv_transtable_best_orig - Get best originator list entry from tt entry
+ * @bat_priv: the bat priv with all the soft interface information
* @tt_global_entry: global translation table entry to be analyzed
*
* This functon assumes the caller holds rcu_read_lock().
* Returns best originator list entry or NULL on errors.
*/
static struct batadv_tt_orig_list_entry *
-batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry)
+batadv_transtable_best_orig(struct batadv_priv *bat_priv,
+ struct batadv_tt_global_entry *tt_global_entry)
{
- struct batadv_neigh_node *router = NULL;
+ struct batadv_neigh_node *router, *best_router = NULL;
+ struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
struct hlist_head *head;
struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL;
- int best_tq = 0;
head = &tt_global_entry->orig_list;
hlist_for_each_entry_rcu(orig_entry, head, list) {
@@ -966,64 +1389,104 @@ batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry)
if (!router)
continue;
- if (router->tq_avg > best_tq) {
- best_entry = orig_entry;
- best_tq = router->tq_avg;
+ if (best_router &&
+ bao->bat_neigh_cmp(router, best_router) <= 0) {
+ batadv_neigh_node_free_ref(router);
+ continue;
}
- batadv_neigh_node_free_ref(router);
+ /* release the refcount for the "old" best */
+ if (best_router)
+ batadv_neigh_node_free_ref(best_router);
+
+ best_entry = orig_entry;
+ best_router = router;
}
+ if (best_router)
+ batadv_neigh_node_free_ref(best_router);
+
return best_entry;
}
/* batadv_tt_global_print_entry - print all orig nodes who announce the address
* for this global entry
+ * @bat_priv: the bat priv with all the soft interface information
* @tt_global_entry: global translation table entry to be printed
* @seq: debugfs table seq_file struct
*
* This functon assumes the caller holds rcu_read_lock().
*/
static void
-batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry,
+batadv_tt_global_print_entry(struct batadv_priv *bat_priv,
+ struct batadv_tt_global_entry *tt_global_entry,
struct seq_file *seq)
{
- struct hlist_head *head;
struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
struct batadv_tt_common_entry *tt_common_entry;
- uint16_t flags;
+ struct batadv_orig_node_vlan *vlan;
+ struct hlist_head *head;
uint8_t last_ttvn;
+ uint16_t flags;
tt_common_entry = &tt_global_entry->common;
flags = tt_common_entry->flags;
- best_entry = batadv_transtable_best_orig(tt_global_entry);
+ best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
if (best_entry) {
+ vlan = batadv_orig_node_vlan_get(best_entry->orig_node,
+ tt_common_entry->vid);
+ if (!vlan) {
+ seq_printf(seq,
+ " * Cannot retrieve VLAN %d for originator %pM\n",
+ BATADV_PRINT_VID(tt_common_entry->vid),
+ best_entry->orig_node->orig);
+ goto print_list;
+ }
+
last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn);
seq_printf(seq,
- " %c %pM (%3u) via %pM (%3u) (%#.4x) [%c%c%c]\n",
+ " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n",
'*', tt_global_entry->common.addr,
+ BATADV_PRINT_VID(tt_global_entry->common.vid),
best_entry->ttvn, best_entry->orig_node->orig,
- last_ttvn, best_entry->orig_node->tt_crc,
+ last_ttvn, vlan->tt.crc,
(flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
(flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
(flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
+
+ batadv_orig_node_vlan_free_ref(vlan);
}
+print_list:
head = &tt_global_entry->orig_list;
hlist_for_each_entry_rcu(orig_entry, head, list) {
if (best_entry == orig_entry)
continue;
+ vlan = batadv_orig_node_vlan_get(orig_entry->orig_node,
+ tt_common_entry->vid);
+ if (!vlan) {
+ seq_printf(seq,
+ " + Cannot retrieve VLAN %d for originator %pM\n",
+ BATADV_PRINT_VID(tt_common_entry->vid),
+ orig_entry->orig_node->orig);
+ continue;
+ }
+
last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn);
- seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n",
+ seq_printf(seq,
+ " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n",
'+', tt_global_entry->common.addr,
+ BATADV_PRINT_VID(tt_global_entry->common.vid),
orig_entry->ttvn, orig_entry->orig_node->orig,
- last_ttvn,
+ last_ttvn, vlan->tt.crc,
(flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
(flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
(flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
+
+ batadv_orig_node_vlan_free_ref(vlan);
}
}
@@ -1045,9 +1508,9 @@ int batadv_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 (%-6s) %s\n",
- "Client", "(TTVN)", "Originator", "(Curr TTVN)", "CRC",
- "Flags");
+ seq_printf(seq, " %-13s %s %s %-15s %s (%-10s) %s\n",
+ "Client", "VID", "(TTVN)", "Originator", "(Curr TTVN)",
+ "CRC", "Flags");
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
@@ -1058,7 +1521,7 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
tt_global = container_of(tt_common_entry,
struct batadv_tt_global_entry,
common);
- batadv_tt_global_print_entry(tt_global, seq);
+ batadv_tt_global_print_entry(bat_priv, tt_global, seq);
}
rcu_read_unlock();
}
@@ -1080,6 +1543,8 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry)
head = &tt_global_entry->orig_list;
hlist_for_each_entry_safe(orig_entry, safe, head, list) {
hlist_del_rcu(&orig_entry->list);
+ batadv_tt_global_size_dec(orig_entry->orig_node,
+ tt_global_entry->common.vid);
batadv_tt_orig_list_entry_free_ref(orig_entry);
}
spin_unlock_bh(&tt_global_entry->list_lock);
@@ -1094,16 +1559,21 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
struct hlist_head *head;
struct hlist_node *safe;
struct batadv_tt_orig_list_entry *orig_entry;
+ unsigned short vid;
spin_lock_bh(&tt_global_entry->list_lock);
head = &tt_global_entry->orig_list;
hlist_for_each_entry_safe(orig_entry, safe, head, list) {
if (orig_entry->orig_node == orig_node) {
+ vid = tt_global_entry->common.vid;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Deleting %pM from global tt entry %pM: %s\n",
+ "Deleting %pM from global tt entry %pM (vid: %d): %s\n",
orig_node->orig,
- tt_global_entry->common.addr, message);
+ tt_global_entry->common.addr,
+ BATADV_PRINT_VID(vid), message);
hlist_del_rcu(&orig_entry->list);
+ batadv_tt_global_size_dec(orig_node,
+ tt_global_entry->common.vid);
batadv_tt_orig_list_entry_free_ref(orig_entry);
}
}
@@ -1150,17 +1620,25 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv,
orig_node, message);
}
-
-
+/**
+ * batadv_tt_global_del - remove a client from the global table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: an originator serving this client
+ * @addr: the mac address of the client
+ * @vid: VLAN identifier
+ * @message: a message explaining the reason for deleting the client to print
+ * for debugging purpose
+ * @roaming: true if the deletion has been triggered by a roaming event
+ */
static void batadv_tt_global_del(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- const unsigned char *addr,
+ const unsigned char *addr, unsigned short vid,
const char *message, bool roaming)
{
struct batadv_tt_global_entry *tt_global_entry;
struct batadv_tt_local_entry *local_entry = NULL;
- tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+ tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (!tt_global_entry)
goto out;
@@ -1189,7 +1667,8 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
* the global entry, since it is useless now.
*/
local_entry = batadv_tt_local_hash_find(bat_priv,
- tt_global_entry->common.addr);
+ tt_global_entry->common.addr,
+ vid);
if (local_entry) {
/* local entry exists, case 2: client roamed to us. */
batadv_tt_global_del_orig_list(tt_global_entry);
@@ -1207,8 +1686,18 @@ out:
batadv_tt_local_entry_free_ref(local_entry);
}
+/**
+ * batadv_tt_global_del_orig - remove all the TT global entries belonging to the
+ * given originator matching the provided vid
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the originator owning the entries to remove
+ * @match_vid: the VLAN identifier to match. If negative all the entries will be
+ * removed
+ * @message: debug message to print as "reason"
+ */
void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
+ int32_t match_vid,
const char *message)
{
struct batadv_tt_global_entry *tt_global;
@@ -1218,6 +1707,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct hlist_node *safe;
struct hlist_head *head;
spinlock_t *list_lock; /* protects write access to the hash lists */
+ unsigned short vid;
if (!hash)
return;
@@ -1229,6 +1719,10 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(tt_common_entry, safe,
head, hash_entry) {
+ /* remove only matching entries */
+ if (match_vid >= 0 && tt_common_entry->vid != match_vid)
+ continue;
+
tt_global = container_of(tt_common_entry,
struct batadv_tt_global_entry,
common);
@@ -1237,9 +1731,11 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
orig_node, message);
if (hlist_empty(&tt_global->orig_list)) {
+ vid = tt_global->common.vid;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Deleting global tt entry %pM: %s\n",
- tt_global->common.addr, message);
+ "Deleting global tt entry %pM (vid: %d): %s\n",
+ tt_global->common.addr,
+ BATADV_PRINT_VID(vid), message);
hlist_del_rcu(&tt_common_entry->hash_entry);
batadv_tt_global_entry_free_ref(tt_global);
}
@@ -1297,8 +1793,10 @@ static void batadv_tt_global_purge(struct batadv_priv *bat_priv)
continue;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Deleting global tt entry (%pM): %s\n",
- tt_global->common.addr, msg);
+ "Deleting global tt entry %pM (vid: %d): %s\n",
+ tt_global->common.addr,
+ BATADV_PRINT_VID(tt_global->common.vid),
+ msg);
hlist_del_rcu(&tt_common->hash_entry);
@@ -1357,23 +1855,49 @@ _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry,
return ret;
}
+/**
+ * batadv_transtable_search - get the mesh destination for a given client
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: mac address of the source client
+ * @addr: mac address of the destination client
+ * @vid: VLAN identifier
+ *
+ * Returns a pointer to the originator that was selected as destination in the
+ * mesh for contacting the client 'addr', NULL otherwise.
+ * In case of multiple originators serving the same client, the function returns
+ * the best one (best in terms of metric towards the destination node).
+ *
+ * If the two clients are AP isolated the function returns NULL.
+ */
struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
const uint8_t *src,
- const uint8_t *addr)
+ const uint8_t *addr,
+ unsigned short vid)
{
struct batadv_tt_local_entry *tt_local_entry = NULL;
struct batadv_tt_global_entry *tt_global_entry = NULL;
struct batadv_orig_node *orig_node = NULL;
struct batadv_tt_orig_list_entry *best_entry;
+ bool ap_isolation_enabled = false;
+ struct batadv_softif_vlan *vlan;
- if (src && atomic_read(&bat_priv->ap_isolation)) {
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, src);
+ /* if the AP isolation is requested on a VLAN, then check for its
+ * setting in the proper VLAN private data structure
+ */
+ vlan = batadv_softif_vlan_get(bat_priv, vid);
+ if (vlan) {
+ ap_isolation_enabled = atomic_read(&vlan->ap_isolation);
+ batadv_softif_vlan_free_ref(vlan);
+ }
+
+ if (src && ap_isolation_enabled) {
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid);
if (!tt_local_entry ||
(tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
goto out;
}
- tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+ tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (!tt_global_entry)
goto out;
@@ -1385,7 +1909,7 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
goto out;
rcu_read_lock();
- best_entry = batadv_transtable_best_orig(tt_global_entry);
+ best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry);
/* found anything? */
if (best_entry)
orig_node = best_entry->orig_node;
@@ -1402,17 +1926,40 @@ out:
return orig_node;
}
-/* Calculates the checksum of the local table of a given orig_node */
-static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node)
+/**
+ * batadv_tt_global_crc - calculates the checksum of the local table belonging
+ * to the given orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: originator for which the CRC should be computed
+ * @vid: VLAN identifier for which the CRC32 has to be computed
+ *
+ * This function computes the checksum for the global table corresponding to a
+ * specific originator. In particular, the checksum is computed as follows: For
+ * each client connected to the originator the CRC32C of the MAC address and the
+ * VID is computed and then all the CRC32Cs of the various clients are xor'ed
+ * together.
+ *
+ * The idea behind is that CRC32C should be used as much as possible in order to
+ * produce a unique hash of the table, but since the order which is used to feed
+ * the CRC32C function affects the result and since every node in the network
+ * probably sorts the clients differently, the hash function cannot be directly
+ * computed over the entire table. Hence the CRC32C is used only on
+ * the single client entry, while all the results are then xor'ed together
+ * because the XOR operation can combine them all while trying to reduce the
+ * noise as much as possible.
+ *
+ * Returns the checksum of the global table of a given originator.
+ */
+static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
+ unsigned short vid)
{
- uint16_t total = 0, total_one;
struct batadv_hashtable *hash = bat_priv->tt.global_hash;
struct batadv_tt_common_entry *tt_common;
struct batadv_tt_global_entry *tt_global;
struct hlist_head *head;
- uint32_t i;
- int j;
+ uint32_t i, crc_tmp, crc = 0;
+ uint8_t flags;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
@@ -1422,6 +1969,12 @@ static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
tt_global = container_of(tt_common,
struct batadv_tt_global_entry,
common);
+ /* compute the CRC only for entries belonging to the
+ * VLAN identified by the vid passed as parameter
+ */
+ if (tt_common->vid != vid)
+ continue;
+
/* Roaming clients are in the global table for
* consistency only. They don't have to be
* taken into account while computing the
@@ -1443,48 +1996,74 @@ static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv,
orig_node))
continue;
- total_one = 0;
- for (j = 0; j < ETH_ALEN; j++)
- total_one = crc16_byte(total_one,
- tt_common->addr[j]);
- total ^= total_one;
+ crc_tmp = crc32c(0, &tt_common->vid,
+ sizeof(tt_common->vid));
+
+ /* compute the CRC on flags that have to be kept in sync
+ * among nodes
+ */
+ flags = tt_common->flags & BATADV_TT_SYNC_MASK;
+ crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
+
+ crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
}
rcu_read_unlock();
}
- return total;
+ return crc;
}
-/* Calculates the checksum of the local table */
-static uint16_t batadv_tt_local_crc(struct batadv_priv *bat_priv)
+/**
+ * batadv_tt_local_crc - calculates the checksum of the local table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: VLAN identifier for which the CRC32 has to be computed
+ *
+ * For details about the computation, please refer to the documentation for
+ * batadv_tt_global_crc().
+ *
+ * Returns the checksum of the local table
+ */
+static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv,
+ unsigned short vid)
{
- uint16_t total = 0, total_one;
struct batadv_hashtable *hash = bat_priv->tt.local_hash;
struct batadv_tt_common_entry *tt_common;
struct hlist_head *head;
- uint32_t i;
- int j;
+ uint32_t i, crc_tmp, crc = 0;
+ uint8_t flags;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
rcu_read_lock();
hlist_for_each_entry_rcu(tt_common, head, hash_entry) {
+ /* compute the CRC only for entries belonging to the
+ * VLAN identified by vid
+ */
+ if (tt_common->vid != vid)
+ continue;
+
/* not yet committed clients have not to be taken into
* account while computing the CRC
*/
if (tt_common->flags & BATADV_TT_CLIENT_NEW)
continue;
- total_one = 0;
- for (j = 0; j < ETH_ALEN; j++)
- total_one = crc16_byte(total_one,
- tt_common->addr[j]);
- total ^= total_one;
+
+ crc_tmp = crc32c(0, &tt_common->vid,
+ sizeof(tt_common->vid));
+
+ /* compute the CRC on flags that have to be kept in sync
+ * among nodes
+ */
+ flags = tt_common->flags & BATADV_TT_SYNC_MASK;
+ crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags));
+
+ crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN);
}
rcu_read_unlock();
}
- return total;
+ return crc;
}
static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
@@ -1503,11 +2082,9 @@ static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
static void batadv_tt_save_orig_buffer(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- const unsigned char *tt_buff,
- uint8_t tt_num_changes)
+ const void *tt_buff,
+ uint16_t tt_buff_len)
{
- uint16_t tt_buff_len = batadv_tt_len(tt_num_changes);
-
/* Replace the old buffer only if I received something in the
* last OGM (the OGM could carry no changes)
*/
@@ -1569,9 +2146,14 @@ unlock:
return tt_req_node;
}
-/* data_ptr is useless here, but has to be kept to respect the prototype */
-static int batadv_tt_local_valid_entry(const void *entry_ptr,
- const void *data_ptr)
+/**
+ * batadv_tt_local_valid - verify that given tt entry is a valid one
+ * @entry_ptr: to be checked local tt entry
+ * @data_ptr: not used but definition required to satisfy the callback prototype
+ *
+ * Returns 1 if the entry is a valid, 0 otherwise.
+ */
+static int batadv_tt_local_valid(const void *entry_ptr, const void *data_ptr)
{
const struct batadv_tt_common_entry *tt_common_entry = entry_ptr;
@@ -1598,41 +2180,30 @@ static int batadv_tt_global_valid(const void *entry_ptr,
return batadv_tt_global_entry_has_orig(tt_global_entry, orig_node);
}
-static struct sk_buff *
-batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
- struct batadv_hashtable *hash,
- struct batadv_priv *bat_priv,
- int (*valid_cb)(const void *, const void *),
- void *cb_data)
+/**
+ * batadv_tt_tvlv_generate - fill the tvlv buff with the tt entries from the
+ * specified tt hash
+ * @bat_priv: the bat priv with all the soft interface information
+ * @hash: hash table containing the tt entries
+ * @tt_len: expected tvlv tt data buffer length in number of bytes
+ * @tvlv_buff: pointer to the buffer to fill with the TT data
+ * @valid_cb: function to filter tt change entries
+ * @cb_data: data passed to the filter function as argument
+ */
+static void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv,
+ struct batadv_hashtable *hash,
+ void *tvlv_buff, uint16_t tt_len,
+ int (*valid_cb)(const void *, const void *),
+ void *cb_data)
{
struct batadv_tt_common_entry *tt_common_entry;
- struct batadv_tt_query_packet *tt_response;
- struct batadv_tt_change *tt_change;
+ struct batadv_tvlv_tt_change *tt_change;
struct hlist_head *head;
- struct sk_buff *skb = NULL;
- uint16_t tt_tot, tt_count;
- ssize_t tt_query_size = sizeof(struct batadv_tt_query_packet);
+ uint16_t tt_tot, tt_num_entries = 0;
uint32_t i;
- size_t len;
-
- if (tt_query_size + tt_len > bat_priv->soft_iface->mtu) {
- tt_len = bat_priv->soft_iface->mtu - tt_query_size;
- tt_len -= tt_len % sizeof(struct batadv_tt_change);
- }
- tt_tot = tt_len / sizeof(struct batadv_tt_change);
-
- len = tt_query_size + tt_len;
- skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
- if (!skb)
- goto out;
-
- skb->priority = TC_PRIO_CONTROL;
- skb_reserve(skb, ETH_HLEN);
- tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len);
- tt_response->ttvn = ttvn;
- tt_change = (struct batadv_tt_change *)(skb->data + tt_query_size);
- tt_count = 0;
+ tt_tot = batadv_tt_entries(tt_len);
+ tt_change = (struct batadv_tvlv_tt_change *)tvlv_buff;
rcu_read_lock();
for (i = 0; i < hash->size; i++) {
@@ -1640,7 +2211,7 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
hlist_for_each_entry_rcu(tt_common_entry,
head, hash_entry) {
- if (tt_count == tt_tot)
+ if (tt_tot == tt_num_entries)
break;
if ((valid_cb) && (!valid_cb(tt_common_entry, cb_data)))
@@ -1649,33 +2220,123 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
memcpy(tt_change->addr, tt_common_entry->addr,
ETH_ALEN);
tt_change->flags = tt_common_entry->flags;
+ tt_change->vid = htons(tt_common_entry->vid);
+ tt_change->reserved = 0;
- tt_count++;
+ tt_num_entries++;
tt_change++;
}
}
rcu_read_unlock();
+}
- /* store in the message the number of entries we have successfully
- * copied
- */
- tt_response->tt_data = htons(tt_count);
+/**
+ * batadv_tt_global_check_crc - check if all the CRCs are correct
+ * @orig_node: originator for which the CRCs have to be checked
+ * @tt_vlan: pointer to the first tvlv VLAN entry
+ * @num_vlan: number of tvlv VLAN entries
+ * @create: if true, create VLAN objects if not found
+ *
+ * Return true if all the received CRCs match the locally stored ones, false
+ * otherwise
+ */
+static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
+ struct batadv_tvlv_tt_vlan_data *tt_vlan,
+ uint16_t num_vlan)
+{
+ struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
+ struct batadv_orig_node_vlan *vlan;
+ int i;
-out:
- return skb;
+ /* check if each received CRC matches the locally stored one */
+ for (i = 0; i < num_vlan; i++) {
+ tt_vlan_tmp = tt_vlan + i;
+
+ /* if orig_node is a backbone node for this VLAN, don't check
+ * the CRC as we ignore all the global entries over it
+ */
+ if (batadv_bla_is_backbone_gw_orig(orig_node->bat_priv,
+ orig_node->orig,
+ ntohs(tt_vlan_tmp->vid)))
+ continue;
+
+ vlan = batadv_orig_node_vlan_get(orig_node,
+ ntohs(tt_vlan_tmp->vid));
+ if (!vlan)
+ return false;
+
+ if (vlan->tt.crc != ntohl(tt_vlan_tmp->crc))
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * batadv_tt_local_update_crc - update all the local CRCs
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_tt_local_update_crc(struct batadv_priv *bat_priv)
+{
+ struct batadv_softif_vlan *vlan;
+
+ /* recompute the global CRC for each VLAN */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+ vlan->tt.crc = batadv_tt_local_crc(bat_priv, vlan->vid);
+ }
+ rcu_read_unlock();
}
+/**
+ * batadv_tt_global_update_crc - update all the global CRCs for this orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: the orig_node for which the CRCs have to be updated
+ */
+static void batadv_tt_global_update_crc(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node)
+{
+ struct batadv_orig_node_vlan *vlan;
+ uint32_t crc;
+
+ /* recompute the global CRC for each VLAN */
+ rcu_read_lock();
+ list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
+ /* if orig_node is a backbone node for this VLAN, don't compute
+ * the CRC as we ignore all the global entries over it
+ */
+ if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig,
+ vlan->vid))
+ continue;
+
+ crc = batadv_tt_global_crc(bat_priv, orig_node, vlan->vid);
+ vlan->tt.crc = crc;
+ }
+ rcu_read_unlock();
+}
+
+/**
+ * batadv_send_tt_request - send a TT Request message to a given node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @dst_orig_node: the destination of the message
+ * @ttvn: the version number that the source of the message is looking for
+ * @tt_vlan: pointer to the first tvlv VLAN object to request
+ * @num_vlan: number of tvlv VLAN entries
+ * @full_table: ask for the entire translation table if true, while only for the
+ * last TT diff otherwise
+ */
static int batadv_send_tt_request(struct batadv_priv *bat_priv,
struct batadv_orig_node *dst_orig_node,
- uint8_t ttvn, uint16_t tt_crc,
- bool full_table)
+ uint8_t ttvn,
+ struct batadv_tvlv_tt_vlan_data *tt_vlan,
+ uint16_t num_vlan, bool full_table)
{
- struct sk_buff *skb = NULL;
- struct batadv_tt_query_packet *tt_request;
- struct batadv_hard_iface *primary_if;
+ struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
struct batadv_tt_req_node *tt_req_node = NULL;
- int ret = 1;
- size_t tt_req_len;
+ struct batadv_tvlv_tt_vlan_data *tt_vlan_req;
+ struct batadv_hard_iface *primary_if;
+ bool ret = false;
+ int i, size;
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
@@ -1688,157 +2349,171 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
if (!tt_req_node)
goto out;
- skb = netdev_alloc_skb_ip_align(NULL, sizeof(*tt_request) + ETH_HLEN);
- if (!skb)
+ size = sizeof(*tvlv_tt_data) + sizeof(*tt_vlan_req) * num_vlan;
+ tvlv_tt_data = kzalloc(size, GFP_ATOMIC);
+ if (!tvlv_tt_data)
goto out;
- skb->priority = TC_PRIO_CONTROL;
- skb_reserve(skb, ETH_HLEN);
+ tvlv_tt_data->flags = BATADV_TT_REQUEST;
+ tvlv_tt_data->ttvn = ttvn;
+ tvlv_tt_data->num_vlan = htons(num_vlan);
- tt_req_len = sizeof(*tt_request);
- tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len);
+ /* send all the CRCs within the request. This is needed by intermediate
+ * nodes to ensure they have the correct table before replying
+ */
+ tt_vlan_req = (struct batadv_tvlv_tt_vlan_data *)(tvlv_tt_data + 1);
+ for (i = 0; i < num_vlan; i++) {
+ tt_vlan_req->vid = tt_vlan->vid;
+ tt_vlan_req->crc = tt_vlan->crc;
- tt_request->header.packet_type = BATADV_TT_QUERY;
- tt_request->header.version = BATADV_COMPAT_VERSION;
- memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN);
- memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN);
- tt_request->header.ttl = BATADV_TTL;
- tt_request->ttvn = ttvn;
- tt_request->tt_data = htons(tt_crc);
- tt_request->flags = BATADV_TT_REQUEST;
+ tt_vlan_req++;
+ tt_vlan++;
+ }
if (full_table)
- tt_request->flags |= BATADV_TT_FULL_TABLE;
+ tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n",
- dst_orig_node->orig, (full_table ? 'F' : '.'));
+ dst_orig_node->orig, full_table ? 'F' : '.');
batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
-
- if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL) != NET_XMIT_DROP)
- ret = 0;
+ batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
+ dst_orig_node->orig, BATADV_TVLV_TT, 1,
+ tvlv_tt_data, size);
+ ret = true;
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- if (ret)
- kfree_skb(skb);
if (ret && tt_req_node) {
spin_lock_bh(&bat_priv->tt.req_list_lock);
list_del(&tt_req_node->list);
spin_unlock_bh(&bat_priv->tt.req_list_lock);
kfree(tt_req_node);
}
+ kfree(tvlv_tt_data);
return ret;
}
-static bool
-batadv_send_other_tt_response(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_request)
+/**
+ * batadv_send_other_tt_response - send reply to tt request concerning another
+ * node's translation table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @req_src: mac address of tt request sender
+ * @req_dst: mac address of tt request recipient
+ *
+ * Returns true if tt request reply was sent, false otherwise.
+ */
+static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv,
+ struct batadv_tvlv_tt_data *tt_data,
+ uint8_t *req_src, uint8_t *req_dst)
{
struct batadv_orig_node *req_dst_orig_node;
struct batadv_orig_node *res_dst_orig_node = NULL;
- uint8_t orig_ttvn, req_ttvn, ttvn;
- int res, ret = false;
- unsigned char *tt_buff;
- bool full_table;
- uint16_t tt_len, tt_tot;
- struct sk_buff *skb = NULL;
- struct batadv_tt_query_packet *tt_response;
- uint8_t *packet_pos;
- size_t len;
+ struct batadv_tvlv_tt_change *tt_change;
+ struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
+ bool ret = false, full_table;
+ uint8_t orig_ttvn, req_ttvn;
+ uint16_t tvlv_len;
+ int32_t tt_len;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Received TT_REQUEST from %pM for ttvn: %u (%pM) [%c]\n",
- tt_request->src, tt_request->ttvn, tt_request->dst,
- (tt_request->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+ req_src, tt_data->ttvn, req_dst,
+ (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
/* Let's get the orig node of the REAL destination */
- req_dst_orig_node = batadv_orig_hash_find(bat_priv, tt_request->dst);
+ req_dst_orig_node = batadv_orig_hash_find(bat_priv, req_dst);
if (!req_dst_orig_node)
goto out;
- res_dst_orig_node = batadv_orig_hash_find(bat_priv, tt_request->src);
+ res_dst_orig_node = batadv_orig_hash_find(bat_priv, req_src);
if (!res_dst_orig_node)
goto out;
orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
- req_ttvn = tt_request->ttvn;
+ req_ttvn = tt_data->ttvn;
- /* I don't have the requested data */
+ tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
+ /* this node doesn't have the requested data */
if (orig_ttvn != req_ttvn ||
- tt_request->tt_data != htons(req_dst_orig_node->tt_crc))
+ !batadv_tt_global_check_crc(req_dst_orig_node, tt_vlan,
+ ntohs(tt_data->num_vlan)))
goto out;
/* If the full table has been explicitly requested */
- if (tt_request->flags & BATADV_TT_FULL_TABLE ||
+ if (tt_data->flags & BATADV_TT_FULL_TABLE ||
!req_dst_orig_node->tt_buff)
full_table = true;
else
full_table = false;
- /* In this version, fragmentation is not implemented, then
- * I'll send only one packet with as much TT entries as I can
+ /* TT fragmentation hasn't been implemented yet, so send as many
+ * TT entries fit a single packet as possible only
*/
if (!full_table) {
spin_lock_bh(&req_dst_orig_node->tt_buff_lock);
tt_len = req_dst_orig_node->tt_buff_len;
- tt_tot = tt_len / sizeof(struct batadv_tt_change);
- len = sizeof(*tt_response) + tt_len;
- skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
- if (!skb)
+ tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
+ &tvlv_tt_data,
+ &tt_change,
+ &tt_len);
+ if (!tt_len)
goto unlock;
- skb->priority = TC_PRIO_CONTROL;
- skb_reserve(skb, ETH_HLEN);
- packet_pos = skb_put(skb, len);
- tt_response = (struct batadv_tt_query_packet *)packet_pos;
- tt_response->ttvn = req_ttvn;
- tt_response->tt_data = htons(tt_tot);
-
- tt_buff = skb->data + sizeof(*tt_response);
/* Copy the last orig_node's OGM buffer */
- memcpy(tt_buff, req_dst_orig_node->tt_buff,
+ memcpy(tt_change, req_dst_orig_node->tt_buff,
req_dst_orig_node->tt_buff_len);
-
spin_unlock_bh(&req_dst_orig_node->tt_buff_lock);
} else {
- tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size);
- tt_len *= sizeof(struct batadv_tt_change);
- ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn);
-
- skb = batadv_tt_response_fill_table(tt_len, ttvn,
- bat_priv->tt.global_hash,
- bat_priv,
- batadv_tt_global_valid,
- req_dst_orig_node);
- if (!skb)
+ /* allocate the tvlv, put the tt_data and all the tt_vlan_data
+ * in the initial part
+ */
+ tt_len = -1;
+ tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node,
+ &tvlv_tt_data,
+ &tt_change,
+ &tt_len);
+ if (!tt_len)
goto out;
- tt_response = (struct batadv_tt_query_packet *)skb->data;
+ /* fill the rest of the tvlv with the real TT entries */
+ batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.global_hash,
+ tt_change, tt_len,
+ batadv_tt_global_valid,
+ req_dst_orig_node);
}
- tt_response->header.packet_type = BATADV_TT_QUERY;
- tt_response->header.version = BATADV_COMPAT_VERSION;
- tt_response->header.ttl = BATADV_TTL;
- memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN);
- memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
- tt_response->flags = BATADV_TT_RESPONSE;
+ /* Don't send the response, if larger than fragmented packet. */
+ tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len;
+ if (tt_len > atomic_read(&bat_priv->packet_size_max)) {
+ net_ratelimited_function(batadv_info, bat_priv->soft_iface,
+ "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n",
+ res_dst_orig_node->orig);
+ goto out;
+ }
+
+ tvlv_tt_data->flags = BATADV_TT_RESPONSE;
+ tvlv_tt_data->ttvn = req_ttvn;
if (full_table)
- tt_response->flags |= BATADV_TT_FULL_TABLE;
+ tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending TT_RESPONSE %pM for %pM (ttvn: %u)\n",
- res_dst_orig_node->orig, req_dst_orig_node->orig, req_ttvn);
+ "Sending TT_RESPONSE %pM for %pM [%c] (ttvn: %u)\n",
+ res_dst_orig_node->orig, req_dst_orig_node->orig,
+ full_table ? 'F' : '.', req_ttvn);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- res = batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL);
- if (res != NET_XMIT_DROP)
- ret = true;
+ batadv_tvlv_unicast_send(bat_priv, req_dst_orig_node->orig,
+ req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
+ tvlv_len);
+ ret = true;
goto out;
unlock:
@@ -1849,37 +2524,43 @@ out:
batadv_orig_node_free_ref(res_dst_orig_node);
if (req_dst_orig_node)
batadv_orig_node_free_ref(req_dst_orig_node);
- if (!ret)
- kfree_skb(skb);
+ kfree(tvlv_tt_data);
return ret;
}
-static bool
-batadv_send_my_tt_response(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_request)
+/**
+ * batadv_send_my_tt_response - send reply to tt request concerning this node's
+ * translation table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @req_src: mac address of tt request sender
+ *
+ * Returns true if tt request reply was sent, false otherwise.
+ */
+static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
+ struct batadv_tvlv_tt_data *tt_data,
+ uint8_t *req_src)
{
- struct batadv_orig_node *orig_node;
+ struct batadv_tvlv_tt_data *tvlv_tt_data = NULL;
struct batadv_hard_iface *primary_if = NULL;
- uint8_t my_ttvn, req_ttvn, ttvn;
- int ret = false;
- unsigned char *tt_buff;
+ struct batadv_tvlv_tt_change *tt_change;
+ struct batadv_orig_node *orig_node;
+ uint8_t my_ttvn, req_ttvn;
+ uint16_t tvlv_len;
bool full_table;
- uint16_t tt_len, tt_tot;
- struct sk_buff *skb = NULL;
- struct batadv_tt_query_packet *tt_response;
- uint8_t *packet_pos;
- size_t len;
+ int32_t tt_len;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n",
- tt_request->src, tt_request->ttvn,
- (tt_request->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+ req_src, tt_data->ttvn,
+ (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+ spin_lock_bh(&bat_priv->tt.commit_lock);
my_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
- req_ttvn = tt_request->ttvn;
+ req_ttvn = tt_data->ttvn;
- orig_node = batadv_orig_hash_find(bat_priv, tt_request->src);
+ orig_node = batadv_orig_hash_find(bat_priv, req_src);
if (!orig_node)
goto out;
@@ -1890,103 +2571,104 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
/* If the full table has been explicitly requested or the gap
* is too big send the whole local translation table
*/
- if (tt_request->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn ||
+ if (tt_data->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn ||
!bat_priv->tt.last_changeset)
full_table = true;
else
full_table = false;
- /* In this version, fragmentation is not implemented, then
- * I'll send only one packet with as much TT entries as I can
+ /* TT fragmentation hasn't been implemented yet, so send as many
+ * TT entries fit a single packet as possible only
*/
if (!full_table) {
spin_lock_bh(&bat_priv->tt.last_changeset_lock);
- tt_len = bat_priv->tt.last_changeset_len;
- tt_tot = tt_len / sizeof(struct batadv_tt_change);
- len = sizeof(*tt_response) + tt_len;
- skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
- if (!skb)
+ tt_len = bat_priv->tt.last_changeset_len;
+ tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
+ &tvlv_tt_data,
+ &tt_change,
+ &tt_len);
+ if (!tt_len)
goto unlock;
- skb->priority = TC_PRIO_CONTROL;
- skb_reserve(skb, ETH_HLEN);
- packet_pos = skb_put(skb, len);
- tt_response = (struct batadv_tt_query_packet *)packet_pos;
- tt_response->ttvn = req_ttvn;
- tt_response->tt_data = htons(tt_tot);
-
- tt_buff = skb->data + sizeof(*tt_response);
- memcpy(tt_buff, bat_priv->tt.last_changeset,
+ /* Copy the last orig_node's OGM buffer */
+ memcpy(tt_change, bat_priv->tt.last_changeset,
bat_priv->tt.last_changeset_len);
spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
} else {
- tt_len = (uint16_t)atomic_read(&bat_priv->tt.local_entry_num);
- tt_len *= sizeof(struct batadv_tt_change);
- ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
-
- skb = batadv_tt_response_fill_table(tt_len, ttvn,
- bat_priv->tt.local_hash,
- bat_priv,
- batadv_tt_local_valid_entry,
- NULL);
- if (!skb)
+ req_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
+
+ /* allocate the tvlv, put the tt_data and all the tt_vlan_data
+ * in the initial part
+ */
+ tt_len = -1;
+ tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv,
+ &tvlv_tt_data,
+ &tt_change,
+ &tt_len);
+ if (!tt_len)
goto out;
- tt_response = (struct batadv_tt_query_packet *)skb->data;
+ /* fill the rest of the tvlv with the real TT entries */
+ batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.local_hash,
+ tt_change, tt_len,
+ batadv_tt_local_valid, NULL);
}
- tt_response->header.packet_type = BATADV_TT_QUERY;
- tt_response->header.version = BATADV_COMPAT_VERSION;
- tt_response->header.ttl = BATADV_TTL;
- memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN);
- memcpy(tt_response->dst, tt_request->src, ETH_ALEN);
- tt_response->flags = BATADV_TT_RESPONSE;
+ tvlv_tt_data->flags = BATADV_TT_RESPONSE;
+ tvlv_tt_data->ttvn = req_ttvn;
if (full_table)
- tt_response->flags |= BATADV_TT_FULL_TABLE;
+ tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending TT_RESPONSE to %pM [%c]\n",
- orig_node->orig,
- (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
+ "Sending TT_RESPONSE to %pM [%c] (ttvn: %u)\n",
+ orig_node->orig, full_table ? 'F' : '.', req_ttvn);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
- ret = true;
+ batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
+ req_src, BATADV_TVLV_TT, 1, tvlv_tt_data,
+ tvlv_len);
+
goto out;
unlock:
spin_unlock_bh(&bat_priv->tt.last_changeset_lock);
out:
+ spin_unlock_bh(&bat_priv->tt.commit_lock);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
if (primary_if)
batadv_hardif_free_ref(primary_if);
- if (!ret)
- kfree_skb(skb);
- /* This packet was for me, so it doesn't need to be re-routed */
+ kfree(tvlv_tt_data);
+ /* The packet was for this host, so it doesn't need to be re-routed */
return true;
}
-bool batadv_send_tt_response(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_request)
+/**
+ * batadv_send_tt_response - send reply to tt request
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @req_src: mac address of tt request sender
+ * @req_dst: mac address of tt request recipient
+ *
+ * Returns true if tt request reply was sent, false otherwise.
+ */
+static bool batadv_send_tt_response(struct batadv_priv *bat_priv,
+ struct batadv_tvlv_tt_data *tt_data,
+ uint8_t *req_src, uint8_t *req_dst)
{
- if (batadv_is_my_mac(bat_priv, tt_request->dst)) {
- /* don't answer backbone gws! */
- if (batadv_bla_is_backbone_gw_orig(bat_priv, tt_request->src))
- return true;
-
- return batadv_send_my_tt_response(bat_priv, tt_request);
- } else {
- return batadv_send_other_tt_response(bat_priv, tt_request);
- }
+ if (batadv_is_my_mac(bat_priv, req_dst))
+ return batadv_send_my_tt_response(bat_priv, tt_data, req_src);
+ else
+ return batadv_send_other_tt_response(bat_priv, tt_data,
+ req_src, req_dst);
}
static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- struct batadv_tt_change *tt_change,
+ struct batadv_tvlv_tt_change *tt_change,
uint16_t tt_num_changes, uint8_t ttvn)
{
int i;
@@ -1997,11 +2679,13 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM;
batadv_tt_global_del(bat_priv, orig_node,
(tt_change + i)->addr,
+ ntohs((tt_change + i)->vid),
"tt removed by changes",
roams);
} else {
if (!batadv_tt_global_add(bat_priv, orig_node,
(tt_change + i)->addr,
+ ntohs((tt_change + i)->vid),
(tt_change + i)->flags, ttvn))
/* In case of problem while storing a
* global_entry, we stop the updating
@@ -2016,21 +2700,22 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
}
static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_response)
+ struct batadv_tvlv_tt_change *tt_change,
+ uint8_t ttvn, uint8_t *resp_src,
+ uint16_t num_entries)
{
struct batadv_orig_node *orig_node;
- orig_node = batadv_orig_hash_find(bat_priv, tt_response->src);
+ orig_node = batadv_orig_hash_find(bat_priv, resp_src);
if (!orig_node)
goto out;
/* Purge the old table first.. */
- batadv_tt_global_del_orig(bat_priv, orig_node, "Received full table");
+ batadv_tt_global_del_orig(bat_priv, orig_node, -1,
+ "Received full table");
- _batadv_tt_update_changes(bat_priv, orig_node,
- (struct batadv_tt_change *)(tt_response + 1),
- ntohs(tt_response->tt_data),
- tt_response->ttvn);
+ _batadv_tt_update_changes(bat_priv, orig_node, tt_change, num_entries,
+ ttvn);
spin_lock_bh(&orig_node->tt_buff_lock);
kfree(orig_node->tt_buff);
@@ -2038,7 +2723,7 @@ static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
orig_node->tt_buff = NULL;
spin_unlock_bh(&orig_node->tt_buff_lock);
- atomic_set(&orig_node->last_ttvn, tt_response->ttvn);
+ atomic_set(&orig_node->last_ttvn, ttvn);
out:
if (orig_node)
@@ -2048,22 +2733,31 @@ out:
static void batadv_tt_update_changes(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
uint16_t tt_num_changes, uint8_t ttvn,
- struct batadv_tt_change *tt_change)
+ struct batadv_tvlv_tt_change *tt_change)
{
_batadv_tt_update_changes(bat_priv, orig_node, tt_change,
tt_num_changes, ttvn);
- batadv_tt_save_orig_buffer(bat_priv, orig_node,
- (unsigned char *)tt_change, tt_num_changes);
+ batadv_tt_save_orig_buffer(bat_priv, orig_node, tt_change,
+ batadv_tt_len(tt_num_changes));
atomic_set(&orig_node->last_ttvn, ttvn);
}
-bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr)
+/**
+ * batadv_is_my_client - check if a client is served by the local node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac adress of the client to check
+ * @vid: VLAN identifier
+ *
+ * Returns true if the client is served by this node, false otherwise.
+ */
+bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr,
+ unsigned short vid)
{
struct batadv_tt_local_entry *tt_local_entry;
bool ret = false;
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!tt_local_entry)
goto out;
/* Check if the client has been logically deleted (but is kept for
@@ -2079,72 +2773,68 @@ out:
return ret;
}
-void batadv_handle_tt_response(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_response)
+/**
+ * batadv_handle_tt_response - process incoming tt reply
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tt_data: tt data containing the tt request information
+ * @resp_src: mac address of tt reply sender
+ * @num_entries: number of tt change entries appended to the tt data
+ */
+static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
+ struct batadv_tvlv_tt_data *tt_data,
+ uint8_t *resp_src, uint16_t num_entries)
{
struct batadv_tt_req_node *node, *safe;
struct batadv_orig_node *orig_node = NULL;
- struct batadv_tt_change *tt_change;
+ struct batadv_tvlv_tt_change *tt_change;
+ uint8_t *tvlv_ptr = (uint8_t *)tt_data;
+ uint16_t change_offset;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n",
- tt_response->src, tt_response->ttvn,
- ntohs(tt_response->tt_data),
- (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
-
- /* we should have never asked a backbone gw */
- if (batadv_bla_is_backbone_gw_orig(bat_priv, tt_response->src))
- goto out;
+ resp_src, tt_data->ttvn, num_entries,
+ (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
- orig_node = batadv_orig_hash_find(bat_priv, tt_response->src);
+ orig_node = batadv_orig_hash_find(bat_priv, resp_src);
if (!orig_node)
goto out;
- if (tt_response->flags & BATADV_TT_FULL_TABLE) {
- batadv_tt_fill_gtable(bat_priv, tt_response);
+ spin_lock_bh(&orig_node->tt_lock);
+
+ change_offset = sizeof(struct batadv_tvlv_tt_vlan_data);
+ change_offset *= ntohs(tt_data->num_vlan);
+ change_offset += sizeof(*tt_data);
+ tvlv_ptr += change_offset;
+
+ tt_change = (struct batadv_tvlv_tt_change *)tvlv_ptr;
+ if (tt_data->flags & BATADV_TT_FULL_TABLE) {
+ batadv_tt_fill_gtable(bat_priv, tt_change, tt_data->ttvn,
+ resp_src, num_entries);
} else {
- tt_change = (struct batadv_tt_change *)(tt_response + 1);
- batadv_tt_update_changes(bat_priv, orig_node,
- ntohs(tt_response->tt_data),
- tt_response->ttvn, tt_change);
+ batadv_tt_update_changes(bat_priv, orig_node, num_entries,
+ tt_data->ttvn, tt_change);
}
+ /* Recalculate the CRC for this orig_node and store it */
+ batadv_tt_global_update_crc(bat_priv, orig_node);
+
+ spin_unlock_bh(&orig_node->tt_lock);
+
/* Delete the tt_req_node from pending tt_requests list */
spin_lock_bh(&bat_priv->tt.req_list_lock);
list_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
- if (!batadv_compare_eth(node->addr, tt_response->src))
+ if (!batadv_compare_eth(node->addr, resp_src))
continue;
list_del(&node->list);
kfree(node);
}
- spin_unlock_bh(&bat_priv->tt.req_list_lock);
- /* Recalculate the CRC for this orig_node and store it */
- orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
+ spin_unlock_bh(&bat_priv->tt.req_list_lock);
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
}
-int batadv_tt_init(struct batadv_priv *bat_priv)
-{
- int ret;
-
- ret = batadv_tt_local_init(bat_priv);
- if (ret < 0)
- return ret;
-
- ret = batadv_tt_global_init(bat_priv);
- if (ret < 0)
- return ret;
-
- INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge);
- queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
- msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
-
- return 1;
-}
-
static void batadv_tt_roam_list_free(struct batadv_priv *bat_priv)
{
struct batadv_tt_roam_node *node, *safe;
@@ -2225,14 +2915,28 @@ unlock:
return ret;
}
+/**
+ * batadv_send_roam_adv - send a roaming advertisement message
+ * @bat_priv: the bat priv with all the soft interface information
+ * @client: mac address of the roaming client
+ * @vid: VLAN identifier
+ * @orig_node: message destination
+ *
+ * Send a ROAMING_ADV message to the node which was previously serving this
+ * client. This is done to inform the node that from now on all traffic destined
+ * for this particular roamed client has to be forwarded to the sender of the
+ * roaming message.
+ */
static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
+ unsigned short vid,
struct batadv_orig_node *orig_node)
{
- struct sk_buff *skb = NULL;
- struct batadv_roam_adv_packet *roam_adv_packet;
- int ret = 1;
struct batadv_hard_iface *primary_if;
- size_t len = sizeof(*roam_adv_packet);
+ struct batadv_tvlv_roam_adv tvlv_roam;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
/* before going on we have to check whether the client has
* already roamed to us too many times
@@ -2240,40 +2944,22 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
if (!batadv_tt_check_roam_count(bat_priv, client))
goto out;
- skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
- if (!skb)
- goto out;
-
- skb->priority = TC_PRIO_CONTROL;
- skb_reserve(skb, ETH_HLEN);
-
- roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len);
-
- roam_adv_packet->header.packet_type = BATADV_ROAM_ADV;
- roam_adv_packet->header.version = BATADV_COMPAT_VERSION;
- roam_adv_packet->header.ttl = BATADV_TTL;
- roam_adv_packet->reserved = 0;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
- memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN);
- batadv_hardif_free_ref(primary_if);
- memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN);
- memcpy(roam_adv_packet->client, client, ETH_ALEN);
-
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending ROAMING_ADV to %pM (client %pM)\n",
- orig_node->orig, client);
+ "Sending ROAMING_ADV to %pM (client %pM, vid: %d)\n",
+ orig_node->orig, client, BATADV_PRINT_VID(vid));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
- ret = 0;
+ memcpy(tvlv_roam.client, client, sizeof(tvlv_roam.client));
+ tvlv_roam.vid = htons(vid);
+
+ batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr,
+ orig_node->orig, BATADV_TVLV_ROAM, 1,
+ &tvlv_roam, sizeof(tvlv_roam));
out:
- if (ret && skb)
- kfree_skb(skb);
- return;
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
}
static void batadv_tt_purge(struct work_struct *work)
@@ -2286,7 +2972,7 @@ static void batadv_tt_purge(struct work_struct *work)
priv_tt = container_of(delayed_work, struct batadv_priv_tt, work);
bat_priv = container_of(priv_tt, struct batadv_priv, tt);
- batadv_tt_local_purge(bat_priv);
+ batadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT);
batadv_tt_global_purge(bat_priv);
batadv_tt_req_purge(bat_priv);
batadv_tt_roam_purge(bat_priv);
@@ -2297,6 +2983,9 @@ static void batadv_tt_purge(struct work_struct *work)
void batadv_tt_free(struct batadv_priv *bat_priv)
{
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_TT, 1);
+ batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_TT, 1);
+
cancel_delayed_work_sync(&bat_priv->tt.work);
batadv_tt_local_table_free(bat_priv);
@@ -2308,19 +2997,25 @@ void batadv_tt_free(struct batadv_priv *bat_priv)
kfree(bat_priv->tt.last_changeset);
}
-/* This function will enable or disable the specified flags for all the entries
- * in the given hash table and returns the number of modified entries
+/**
+ * batadv_tt_local_set_flags - set or unset the specified flags on the local
+ * table and possibly count them in the TT size
+ * @bat_priv: the bat priv with all the soft interface information
+ * @flags: the flag to switch
+ * @enable: whether to set or unset the flag
+ * @count: whether to increase the TT size by the number of changed entries
*/
-static uint16_t batadv_tt_set_flags(struct batadv_hashtable *hash,
- uint16_t flags, bool enable)
+static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv,
+ uint16_t flags, bool enable, bool count)
{
- uint32_t i;
+ struct batadv_hashtable *hash = bat_priv->tt.local_hash;
+ struct batadv_tt_common_entry *tt_common_entry;
uint16_t changed_num = 0;
struct hlist_head *head;
- struct batadv_tt_common_entry *tt_common_entry;
+ uint32_t i;
if (!hash)
- goto out;
+ return;
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
@@ -2338,11 +3033,15 @@ static uint16_t batadv_tt_set_flags(struct batadv_hashtable *hash,
tt_common_entry->flags &= ~flags;
}
changed_num++;
+
+ if (!count)
+ continue;
+
+ batadv_tt_local_size_inc(bat_priv,
+ tt_common_entry->vid);
}
rcu_read_unlock();
}
-out:
- return changed_num;
}
/* Purge out all the tt local entries marked with BATADV_TT_CLIENT_PENDING */
@@ -2370,10 +3069,11 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
continue;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Deleting local tt entry (%pM): pending\n",
- tt_common->addr);
+ "Deleting local tt entry (%pM, vid: %d): pending\n",
+ tt_common->addr,
+ BATADV_PRINT_VID(tt_common->vid));
- atomic_dec(&bat_priv->tt.local_entry_num);
+ batadv_tt_local_size_dec(bat_priv, tt_common->vid);
hlist_del_rcu(&tt_common->hash_entry);
tt_local = container_of(tt_common,
struct batadv_tt_local_entry,
@@ -2384,22 +3084,25 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
}
}
-static int batadv_tt_commit_changes(struct batadv_priv *bat_priv,
- unsigned char **packet_buff,
- int *packet_buff_len, int packet_min_len)
+/**
+ * batadv_tt_local_commit_changes_nolock - commit all pending local tt changes
+ * which have been queued in the time since the last commit
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Caller must hold tt->commit_lock.
+ */
+static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv)
{
- uint16_t changed_num = 0;
-
- if (atomic_read(&bat_priv->tt.local_changes) < 1)
- return -ENOENT;
+ if (atomic_read(&bat_priv->tt.local_changes) < 1) {
+ if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))
+ batadv_tt_tvlv_container_update(bat_priv);
+ return;
+ }
- changed_num = batadv_tt_set_flags(bat_priv->tt.local_hash,
- BATADV_TT_CLIENT_NEW, false);
+ batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true);
- /* all reset entries have to be counted as local entries */
- atomic_add(changed_num, &bat_priv->tt.local_entry_num);
batadv_tt_local_purge_pending_clients(bat_priv);
- bat_priv->tt.local_crc = batadv_tt_local_crc(bat_priv);
+ batadv_tt_local_update_crc(bat_priv);
/* Increment the TTVN only once per OGM interval */
atomic_inc(&bat_priv->tt.vn);
@@ -2409,49 +3112,38 @@ static int batadv_tt_commit_changes(struct batadv_priv *bat_priv,
/* reset the sending counter */
atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
-
- return batadv_tt_changes_fill_buff(bat_priv, packet_buff,
- packet_buff_len, packet_min_len);
+ batadv_tt_tvlv_container_update(bat_priv);
}
-/* when calling this function (hard_iface == primary_if) has to be true */
-int batadv_tt_append_diff(struct batadv_priv *bat_priv,
- unsigned char **packet_buff, int *packet_buff_len,
- int packet_min_len)
+/**
+ * batadv_tt_local_commit_changes - commit all pending local tt changes which
+ * have been queued in the time since the last commit
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv)
{
- int tt_num_changes;
-
- /* if at least one change happened */
- tt_num_changes = batadv_tt_commit_changes(bat_priv, packet_buff,
- packet_buff_len,
- packet_min_len);
-
- /* if the changes have been sent often enough */
- if ((tt_num_changes < 0) &&
- (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))) {
- batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len,
- packet_min_len, packet_min_len);
- tt_num_changes = 0;
- }
-
- return tt_num_changes;
+ spin_lock_bh(&bat_priv->tt.commit_lock);
+ batadv_tt_local_commit_changes_nolock(bat_priv);
+ spin_unlock_bh(&bat_priv->tt.commit_lock);
}
bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
- uint8_t *dst)
+ uint8_t *dst, unsigned short vid)
{
struct batadv_tt_local_entry *tt_local_entry = NULL;
struct batadv_tt_global_entry *tt_global_entry = NULL;
+ struct batadv_softif_vlan *vlan;
bool ret = false;
- if (!atomic_read(&bat_priv->ap_isolation))
+ vlan = batadv_softif_vlan_get(bat_priv, vid);
+ if (!vlan || !atomic_read(&vlan->ap_isolation))
goto out;
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst);
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid);
if (!tt_local_entry)
goto out;
- tt_global_entry = batadv_tt_global_hash_find(bat_priv, src);
+ tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid);
if (!tt_global_entry)
goto out;
@@ -2461,6 +3153,8 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
ret = true;
out:
+ if (vlan)
+ batadv_softif_vlan_free_ref(vlan);
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
if (tt_local_entry)
@@ -2468,19 +3162,29 @@ out:
return ret;
}
-void batadv_tt_update_orig(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- const unsigned char *tt_buff, uint8_t tt_num_changes,
- uint8_t ttvn, uint16_t tt_crc)
+/**
+ * batadv_tt_update_orig - update global translation table with new tt
+ * information received via ogms
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @tt_vlan: pointer to the first tvlv VLAN entry
+ * @tt_num_vlan: number of tvlv VLAN entries
+ * @tt_change: pointer to the first entry in the TT buffer
+ * @tt_num_changes: number of tt changes inside the tt buffer
+ * @ttvn: translation table version number of this changeset
+ * @tt_crc: crc32 checksum of orig node's translation table
+ */
+static void batadv_tt_update_orig(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig_node,
+ const void *tt_buff, uint16_t tt_num_vlan,
+ struct batadv_tvlv_tt_change *tt_change,
+ uint16_t tt_num_changes, uint8_t ttvn)
{
uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
bool full_table = true;
- struct batadv_tt_change *tt_change;
-
- /* don't care about a backbone gateways updates. */
- if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
- return;
+ tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff;
/* orig table not initialised AND first diff is in the OGM OR the ttvn
* increased by one -> we can apply the attached changes
*/
@@ -2496,7 +3200,9 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
goto request_table;
}
- tt_change = (struct batadv_tt_change *)tt_buff;
+ spin_lock_bh(&orig_node->tt_lock);
+
+ tt_change = (struct batadv_tvlv_tt_change *)tt_buff;
batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes,
ttvn, tt_change);
@@ -2504,7 +3210,9 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
* prefer to recompute it to spot any possible inconsistency
* in the global table
*/
- orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
+ batadv_tt_global_update_crc(bat_priv, orig_node);
+
+ spin_unlock_bh(&orig_node->tt_lock);
/* The ttvn alone is not enough to guarantee consistency
* because a single value could represent different states
@@ -2515,37 +3223,46 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
* checking the CRC value is mandatory to detect the
* inconsistency
*/
- if (orig_node->tt_crc != tt_crc)
+ if (!batadv_tt_global_check_crc(orig_node, tt_vlan,
+ tt_num_vlan))
goto request_table;
} else {
/* if we missed more than one change or our tables are not
* in sync anymore -> request fresh tt data
*/
if (!orig_node->tt_initialised || ttvn != orig_ttvn ||
- orig_node->tt_crc != tt_crc) {
+ !batadv_tt_global_check_crc(orig_node, tt_vlan,
+ tt_num_vlan)) {
request_table:
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u crc: %#.4x last_crc: %#.4x num_changes: %u)\n",
- orig_node->orig, ttvn, orig_ttvn, tt_crc,
- orig_node->tt_crc, tt_num_changes);
+ "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u num_changes: %u)\n",
+ orig_node->orig, ttvn, orig_ttvn,
+ tt_num_changes);
batadv_send_tt_request(bat_priv, orig_node, ttvn,
- tt_crc, full_table);
+ tt_vlan, tt_num_vlan,
+ full_table);
return;
}
}
}
-/* returns true whether we know that the client has moved from its old
- * originator to another one. This entry is kept is still kept for consistency
- * purposes
+/**
+ * batadv_tt_global_client_is_roaming - check if a client is marked as roaming
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the client to check
+ * @vid: VLAN identifier
+ *
+ * Returns true if we know that the client has moved from its old originator
+ * to another one. This entry is still kept for consistency purposes and will be
+ * deleted later by a DEL or because of timeout
*/
bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
- uint8_t *addr)
+ uint8_t *addr, unsigned short vid)
{
struct batadv_tt_global_entry *tt_global_entry;
bool ret = false;
- tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+ tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid);
if (!tt_global_entry)
goto out;
@@ -2558,19 +3275,20 @@ out:
/**
* batadv_tt_local_client_is_roaming - tells whether the client is roaming
* @bat_priv: the bat priv with all the soft interface information
- * @addr: the MAC address of the local client to query
+ * @addr: the mac address of the local client to query
+ * @vid: VLAN identifier
*
* Returns true if the local client is known to be roaming (it is not served by
* this node anymore) or not. If yes, the client is still present in the table
* to keep the latter consistent with the node TTVN
*/
bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
- uint8_t *addr)
+ uint8_t *addr, unsigned short vid)
{
struct batadv_tt_local_entry *tt_local_entry;
bool ret = false;
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
if (!tt_local_entry)
goto out;
@@ -2582,26 +3300,268 @@ out:
bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- const unsigned char *addr)
+ const unsigned char *addr,
+ unsigned short vid)
{
bool ret = false;
- /* if the originator is a backbone node (meaning it belongs to the same
- * LAN of this node) the temporary client must not be added because to
- * reach such destination the node must use the LAN instead of the mesh
- */
- if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
- goto out;
-
- if (!batadv_tt_global_add(bat_priv, orig_node, addr,
+ if (!batadv_tt_global_add(bat_priv, orig_node, addr, vid,
BATADV_TT_CLIENT_TEMP,
atomic_read(&orig_node->last_ttvn)))
goto out;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Added temporary global client (addr: %pM orig: %pM)\n",
- addr, orig_node->orig);
+ "Added temporary global client (addr: %pM, vid: %d, orig: %pM)\n",
+ addr, BATADV_PRINT_VID(vid), orig_node->orig);
ret = true;
out:
return ret;
}
+
+/**
+ * batadv_tt_local_resize_to_mtu - resize the local translation table fit the
+ * maximum packet size that can be transported through the mesh
+ * @soft_iface: netdev struct of the mesh interface
+ *
+ * Remove entries older than 'timeout' and half timeout if more entries need
+ * to be removed.
+ */
+void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface)
+{
+ struct batadv_priv *bat_priv = netdev_priv(soft_iface);
+ int packet_size_max = atomic_read(&bat_priv->packet_size_max);
+ int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2;
+ bool reduced = false;
+
+ spin_lock_bh(&bat_priv->tt.commit_lock);
+
+ while (true) {
+ table_size = batadv_tt_local_table_transmit_size(bat_priv);
+ if (packet_size_max >= table_size)
+ break;
+
+ batadv_tt_local_purge(bat_priv, timeout);
+ batadv_tt_local_purge_pending_clients(bat_priv);
+
+ timeout /= 2;
+ reduced = true;
+ net_ratelimited_function(batadv_info, soft_iface,
+ "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n",
+ packet_size_max);
+ }
+
+ /* commit these changes immediately, to avoid synchronization problem
+ * with the TTVN
+ */
+ if (reduced)
+ batadv_tt_local_commit_changes_nolock(bat_priv);
+
+ spin_unlock_bh(&bat_priv->tt.commit_lock);
+}
+
+/**
+ * batadv_tt_tvlv_ogm_handler_v1 - process incoming tt tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig: the orig_node of the ogm
+ * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
+ * @tvlv_value: tvlv buffer containing the gateway data
+ * @tvlv_value_len: tvlv buffer length
+ */
+static void batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags, void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ struct batadv_tvlv_tt_vlan_data *tt_vlan;
+ struct batadv_tvlv_tt_change *tt_change;
+ struct batadv_tvlv_tt_data *tt_data;
+ uint16_t num_entries, num_vlan;
+
+ if (tvlv_value_len < sizeof(*tt_data))
+ return;
+
+ tt_data = (struct batadv_tvlv_tt_data *)tvlv_value;
+ tvlv_value_len -= sizeof(*tt_data);
+
+ num_vlan = ntohs(tt_data->num_vlan);
+
+ if (tvlv_value_len < sizeof(*tt_vlan) * num_vlan)
+ return;
+
+ tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
+ tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan);
+ tvlv_value_len -= sizeof(*tt_vlan) * num_vlan;
+
+ num_entries = batadv_tt_entries(tvlv_value_len);
+
+ batadv_tt_update_orig(bat_priv, orig, tt_vlan, num_vlan, tt_change,
+ num_entries, tt_data->ttvn);
+}
+
+/**
+ * batadv_tt_tvlv_unicast_handler_v1 - process incoming (unicast) tt tvlv
+ * container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: mac address of tt tvlv sender
+ * @dst: mac address of tt tvlv recipient
+ * @tvlv_value: tvlv buffer containing the tt data
+ * @tvlv_value_len: tvlv buffer length
+ *
+ * Returns NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS
+ * otherwise.
+ */
+static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ struct batadv_tvlv_tt_data *tt_data;
+ uint16_t tt_vlan_len, tt_num_entries;
+ char tt_flag;
+ bool ret;
+
+ if (tvlv_value_len < sizeof(*tt_data))
+ return NET_RX_SUCCESS;
+
+ tt_data = (struct batadv_tvlv_tt_data *)tvlv_value;
+ tvlv_value_len -= sizeof(*tt_data);
+
+ tt_vlan_len = sizeof(struct batadv_tvlv_tt_vlan_data);
+ tt_vlan_len *= ntohs(tt_data->num_vlan);
+
+ if (tvlv_value_len < tt_vlan_len)
+ return NET_RX_SUCCESS;
+
+ tvlv_value_len -= tt_vlan_len;
+ tt_num_entries = batadv_tt_entries(tvlv_value_len);
+
+ switch (tt_data->flags & BATADV_TT_DATA_TYPE_MASK) {
+ case BATADV_TT_REQUEST:
+ batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX);
+
+ /* If this node cannot provide a TT response the tt_request is
+ * forwarded
+ */
+ ret = batadv_send_tt_response(bat_priv, tt_data, src, dst);
+ if (!ret) {
+ if (tt_data->flags & BATADV_TT_FULL_TABLE)
+ tt_flag = 'F';
+ else
+ tt_flag = '.';
+
+ batadv_dbg(BATADV_DBG_TT, bat_priv,
+ "Routing TT_REQUEST to %pM [%c]\n",
+ dst, tt_flag);
+ /* tvlv API will re-route the packet */
+ return NET_RX_DROP;
+ }
+ break;
+ case BATADV_TT_RESPONSE:
+ batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX);
+
+ if (batadv_is_my_mac(bat_priv, dst)) {
+ batadv_handle_tt_response(bat_priv, tt_data,
+ src, tt_num_entries);
+ return NET_RX_SUCCESS;
+ }
+
+ if (tt_data->flags & BATADV_TT_FULL_TABLE)
+ tt_flag = 'F';
+ else
+ tt_flag = '.';
+
+ batadv_dbg(BATADV_DBG_TT, bat_priv,
+ "Routing TT_RESPONSE to %pM [%c]\n", dst, tt_flag);
+
+ /* tvlv API will re-route the packet */
+ return NET_RX_DROP;
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_roam_tvlv_unicast_handler_v1 - process incoming tt roam tvlv container
+ * @bat_priv: the bat priv with all the soft interface information
+ * @src: mac address of tt tvlv sender
+ * @dst: mac address of tt tvlv recipient
+ * @tvlv_value: tvlv buffer containing the tt data
+ * @tvlv_value_len: tvlv buffer length
+ *
+ * Returns NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS
+ * otherwise.
+ */
+static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value,
+ uint16_t tvlv_value_len)
+{
+ struct batadv_tvlv_roam_adv *roaming_adv;
+ struct batadv_orig_node *orig_node = NULL;
+
+ /* If this node is not the intended recipient of the
+ * roaming advertisement the packet is forwarded
+ * (the tvlv API will re-route the packet).
+ */
+ if (!batadv_is_my_mac(bat_priv, dst))
+ return NET_RX_DROP;
+
+ if (tvlv_value_len < sizeof(*roaming_adv))
+ goto out;
+
+ orig_node = batadv_orig_hash_find(bat_priv, src);
+ if (!orig_node)
+ goto out;
+
+ batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
+ roaming_adv = (struct batadv_tvlv_roam_adv *)tvlv_value;
+
+ batadv_dbg(BATADV_DBG_TT, bat_priv,
+ "Received ROAMING_ADV from %pM (client %pM)\n",
+ src, roaming_adv->client);
+
+ batadv_tt_global_add(bat_priv, orig_node, roaming_adv->client,
+ ntohs(roaming_adv->vid), BATADV_TT_CLIENT_ROAM,
+ atomic_read(&orig_node->last_ttvn) + 1);
+
+out:
+ if (orig_node)
+ batadv_orig_node_free_ref(orig_node);
+ return NET_RX_SUCCESS;
+}
+
+/**
+ * batadv_tt_init - initialise the translation table internals
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Return 0 on success or negative error number in case of failure.
+ */
+int batadv_tt_init(struct batadv_priv *bat_priv)
+{
+ int ret;
+
+ /* synchronized flags must be remote */
+ BUILD_BUG_ON(!(BATADV_TT_SYNC_MASK & BATADV_TT_REMOTE_MASK));
+
+ ret = batadv_tt_local_init(bat_priv);
+ if (ret < 0)
+ return ret;
+
+ ret = batadv_tt_global_init(bat_priv);
+ if (ret < 0)
+ return ret;
+
+ batadv_tvlv_handler_register(bat_priv, batadv_tt_tvlv_ogm_handler_v1,
+ batadv_tt_tvlv_unicast_handler_v1,
+ BATADV_TVLV_TT, 1, BATADV_NO_FLAGS);
+
+ batadv_tvlv_handler_register(bat_priv, NULL,
+ batadv_roam_tvlv_unicast_handler_v1,
+ BATADV_TVLV_ROAM, 1, BATADV_NO_FLAGS);
+
+ INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge);
+ queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work,
+ msecs_to_jiffies(BATADV_TT_WORK_PERIOD));
+
+ return 1;
+}
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index 659a3bb759ce..026b1ffa6746 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -20,49 +20,35 @@
#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_
-int batadv_tt_len(int changes_num);
int batadv_tt_init(struct batadv_priv *bat_priv);
-void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
- int ifindex);
+bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
+ unsigned short vid, int ifindex);
uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
- const uint8_t *addr, const char *message,
- bool roaming);
+ const uint8_t *addr, unsigned short vid,
+ const char *message, bool roaming);
int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset);
-void batadv_tt_global_add_orig(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- const unsigned char *tt_buff, int tt_buff_len);
-int batadv_tt_global_add(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- const unsigned char *addr, uint16_t flags,
- uint8_t ttvn);
int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset);
void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- const char *message);
+ int32_t match_vid, const char *message);
struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
const uint8_t *src,
- const uint8_t *addr);
+ const uint8_t *addr,
+ unsigned short vid);
void batadv_tt_free(struct batadv_priv *bat_priv);
-bool batadv_send_tt_response(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_request);
-bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr);
-void batadv_handle_tt_response(struct batadv_priv *bat_priv,
- struct batadv_tt_query_packet *tt_response);
+bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr,
+ unsigned short vid);
bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src,
- uint8_t *dst);
-void batadv_tt_update_orig(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- const unsigned char *tt_buff, uint8_t tt_num_changes,
- uint8_t ttvn, uint16_t tt_crc);
-int batadv_tt_append_diff(struct batadv_priv *bat_priv,
- unsigned char **packet_buff, int *packet_buff_len,
- int packet_min_len);
+ uint8_t *dst, unsigned short vid);
+void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv);
bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
- uint8_t *addr);
+ uint8_t *addr, unsigned short vid);
bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
- uint8_t *addr);
+ uint8_t *addr, unsigned short vid);
+void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface);
bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
- const unsigned char *addr);
+ const unsigned char *addr,
+ unsigned short vid);
#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index b2c94e139319..91dd369b0ff2 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -24,13 +24,6 @@
#include "bitarray.h"
#include <linux/kernel.h>
-/**
- * Maximum overhead for the encapsulation for a payload packet
- */
-#define BATADV_HEADER_LEN \
- (ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \
- sizeof(struct batadv_bcast_packet)))
-
#ifdef CONFIG_BATMAN_ADV_DAT
/* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed,
@@ -43,6 +36,18 @@
#endif /* CONFIG_BATMAN_ADV_DAT */
/**
+ * BATADV_TT_REMOTE_MASK - bitmask selecting the flags that are sent over the
+ * wire only
+ */
+#define BATADV_TT_REMOTE_MASK 0x00FF
+
+/**
+ * BATADV_TT_SYNC_MASK - bitmask of the flags that need to be kept in sync
+ * among the nodes. These flags are used to compute the global/local CRC
+ */
+#define BATADV_TT_SYNC_MASK 0x00F0
+
+/**
* struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data
* @ogm_buff: buffer holding the OGM packet
* @ogm_buff_len: length of the OGM packet buffer
@@ -60,7 +65,6 @@ struct batadv_hard_iface_bat_iv {
* @if_num: identificator of the interface
* @if_status: status of the interface for batman-adv
* @net_dev: pointer to the net_device
- * @frag_seqno: last fragment sequence number sent by this interface
* @num_bcasts: number of payload re-broadcasts on this interface (ARQ)
* @hardif_obj: kobject of the per interface sysfs "mesh" directory
* @refcount: number of contexts the object is used
@@ -76,7 +80,6 @@ struct batadv_hard_iface {
int16_t if_num;
char if_status;
struct net_device *net_dev;
- atomic_t frag_seqno;
uint8_t num_bcasts;
struct kobject *hardif_obj;
atomic_t refcount;
@@ -88,28 +91,97 @@ struct batadv_hard_iface {
};
/**
+ * struct batadv_frag_table_entry - head in the fragment buffer table
+ * @head: head of list with fragments
+ * @lock: lock to protect the list of fragments
+ * @timestamp: time (jiffie) of last received fragment
+ * @seqno: sequence number of the fragments in the list
+ * @size: accumulated size of packets in list
+ */
+struct batadv_frag_table_entry {
+ struct hlist_head head;
+ spinlock_t lock; /* protects head */
+ unsigned long timestamp;
+ uint16_t seqno;
+ uint16_t size;
+};
+
+/**
+ * struct batadv_frag_list_entry - entry in a list of fragments
+ * @list: list node information
+ * @skb: fragment
+ * @no: fragment number in the set
+ */
+struct batadv_frag_list_entry {
+ struct hlist_node list;
+ struct sk_buff *skb;
+ uint8_t no;
+};
+
+/**
+ * struct batadv_vlan_tt - VLAN specific TT attributes
+ * @crc: CRC32 checksum of the entries belonging to this vlan
+ * @num_entries: number of TT entries for this VLAN
+ */
+struct batadv_vlan_tt {
+ uint32_t crc;
+ atomic_t num_entries;
+};
+
+/**
+ * batadv_orig_node_vlan - VLAN specific data per orig_node
+ * @vid: the VLAN identifier
+ * @tt: VLAN specific TT attributes
+ * @list: list node for orig_node::vlan_list
+ * @refcount: number of context where this object is currently in use
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_orig_node_vlan {
+ unsigned short vid;
+ struct batadv_vlan_tt tt;
+ struct list_head list;
+ atomic_t refcount;
+ struct rcu_head rcu;
+};
+
+/**
+ * struct batadv_orig_bat_iv - B.A.T.M.A.N. IV private orig_node members
+ * @bcast_own: bitfield containing the number of our OGMs this orig_node
+ * rebroadcasted "back" to us (relative to last_real_seqno)
+ * @bcast_own_sum: counted result of bcast_own
+ * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum,
+ * neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count
+ */
+struct batadv_orig_bat_iv {
+ unsigned long *bcast_own;
+ uint8_t *bcast_own_sum;
+ /* ogm_cnt_lock protects: bcast_own, bcast_own_sum,
+ * neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count
+ */
+ spinlock_t ogm_cnt_lock;
+};
+
+/**
* struct batadv_orig_node - structure for orig_list maintaining nodes of mesh
* @orig: originator ethernet address
* @primary_addr: hosts primary interface address
* @router: router that should be used to reach this originator
* @batadv_dat_addr_t: address of the orig node in the distributed hash
- * @bcast_own: bitfield containing the number of our OGMs this orig_node
- * rebroadcasted "back" to us (relative to last_real_seqno)
- * @bcast_own_sum: counted result of bcast_own
* @last_seen: time when last packet from this node was received
* @bcast_seqno_reset: time when the broadcast seqno window was reset
* @batman_seqno_reset: time when the batman seqno window was reset
- * @gw_flags: flags related to gateway class
- * @flags: for now only VIS_SERVER flag
+ * @capabilities: announced capabilities of this originator
* @last_ttvn: last seen translation table version number
- * @tt_crc: CRC of the translation table
* @tt_buff: last tt changeset this node received from the orig node
* @tt_buff_len: length of the last tt changeset this node received from the
* orig node
* @tt_buff_lock: lock that protects tt_buff and tt_buff_len
- * @tt_size: number of global TT entries announced by the orig node
* @tt_initialised: bool keeping track of whether or not this node have received
* any translation table information from the orig node yet
+ * @tt_lock: prevents from updating the table while reading it. Table update is
+ * made up by two operations (data structure update and metdata -CRC/TTVN-
+ * recalculation) and they have to be executed atomically in order to avoid
+ * another thread to read the table/metadata between those.
* @last_real_seqno: last and best known sequence number
* @last_ttl: ttl of last received packet
* @bcast_bits: bitfield containing the info which payload broadcast originated
@@ -117,14 +189,9 @@ struct batadv_hard_iface {
* last_bcast_seqno)
* @last_bcast_seqno: last broadcast sequence number received by this host
* @neigh_list: list of potential next hop neighbor towards this orig node
- * @frag_list: fragmentation buffer list for fragment re-assembly
- * @last_frag_packet: time when last fragmented packet from this node was
- * received
* @neigh_list_lock: lock protecting neigh_list, router and bonding_list
* @hash_entry: hlist node for batadv_priv::orig_hash
* @bat_priv: pointer to soft_iface this orig node belongs to
- * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum,
- * neigh_node->real_bits & neigh_node->real_packet_count
* @bcast_seqno_lock: lock protecting bcast_bits & last_bcast_seqno
* @bond_candidates: how many candidates are available
* @bond_list: list of bonding candidates
@@ -134,6 +201,11 @@ struct batadv_hard_iface {
* @out_coding_list: list of nodes that can hear this orig
* @in_coding_list_lock: protects in_coding_list
* @out_coding_list_lock: protects out_coding_list
+ * @fragments: array with heads for fragment chains
+ * @vlan_list: a list of orig_node_vlan structs, one per VLAN served by the
+ * originator represented by this object
+ * @vlan_list_lock: lock protecting vlan_list
+ * @bat_iv: B.A.T.M.A.N. IV private structure
*/
struct batadv_orig_node {
uint8_t orig[ETH_ALEN];
@@ -142,35 +214,26 @@ struct batadv_orig_node {
#ifdef CONFIG_BATMAN_ADV_DAT
batadv_dat_addr_t dat_addr;
#endif
- unsigned long *bcast_own;
- uint8_t *bcast_own_sum;
unsigned long last_seen;
unsigned long bcast_seqno_reset;
unsigned long batman_seqno_reset;
- uint8_t gw_flags;
- uint8_t flags;
+ uint8_t capabilities;
atomic_t last_ttvn;
- uint16_t tt_crc;
unsigned char *tt_buff;
int16_t tt_buff_len;
spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */
- atomic_t tt_size;
bool tt_initialised;
+ /* prevents from changing the table while reading it */
+ spinlock_t tt_lock;
uint32_t last_real_seqno;
uint8_t last_ttl;
DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
uint32_t last_bcast_seqno;
struct hlist_head neigh_list;
- struct list_head frag_list;
- unsigned long last_frag_packet;
/* neigh_list_lock protects: neigh_list, router & bonding_list */
spinlock_t neigh_list_lock;
struct hlist_node hash_entry;
struct batadv_priv *bat_priv;
- /* ogm_cnt_lock protects: bcast_own, bcast_own_sum,
- * neigh_node->real_bits & neigh_node->real_packet_count
- */
- spinlock_t ogm_cnt_lock;
/* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */
spinlock_t bcast_seqno_lock;
atomic_t bond_candidates;
@@ -183,12 +246,28 @@ struct batadv_orig_node {
spinlock_t in_coding_list_lock; /* Protects in_coding_list */
spinlock_t out_coding_list_lock; /* Protects out_coding_list */
#endif
+ struct batadv_frag_table_entry fragments[BATADV_FRAG_BUFFER_COUNT];
+ struct list_head vlan_list;
+ spinlock_t vlan_list_lock; /* protects vlan_list */
+ struct batadv_orig_bat_iv bat_iv;
+};
+
+/**
+ * enum batadv_orig_capabilities - orig node capabilities
+ * @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled
+ * @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled
+ */
+enum batadv_orig_capabilities {
+ BATADV_ORIG_CAPA_HAS_DAT = BIT(0),
+ BATADV_ORIG_CAPA_HAS_NC = BIT(1),
};
/**
* struct batadv_gw_node - structure for orig nodes announcing gw capabilities
* @list: list node for batadv_priv_gw::list
* @orig_node: pointer to corresponding orig node
+ * @bandwidth_down: advertised uplink download bandwidth
+ * @bandwidth_up: advertised uplink upload bandwidth
* @deleted: this struct is scheduled for deletion
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
@@ -196,46 +275,57 @@ struct batadv_orig_node {
struct batadv_gw_node {
struct hlist_node list;
struct batadv_orig_node *orig_node;
+ uint32_t bandwidth_down;
+ uint32_t bandwidth_up;
unsigned long deleted;
atomic_t refcount;
struct rcu_head rcu;
};
/**
- * struct batadv_neigh_node - structure for single hop neighbors
- * @list: list node for batadv_orig_node::neigh_list
- * @addr: mac address of neigh node
+ * struct batadv_neigh_bat_iv - B.A.T.M.A.N. IV specific structure for single
+ * hop neighbors
* @tq_recv: ring buffer of received TQ values from this neigh node
* @tq_index: ring buffer index
* @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv)
- * @last_ttl: last received ttl from this neigh node
- * @bonding_list: list node for batadv_orig_node::bond_list
- * @last_seen: when last packet via this neighbor was received
* @real_bits: bitfield containing the number of OGMs received from this neigh
* node (relative to orig_node->last_real_seqno)
* @real_packet_count: counted result of real_bits
+ * @lq_update_lock: lock protecting tq_recv & tq_index
+ */
+struct batadv_neigh_bat_iv {
+ uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE];
+ uint8_t tq_index;
+ uint8_t tq_avg;
+ DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
+ uint8_t real_packet_count;
+ spinlock_t lq_update_lock; /* protects tq_recv & tq_index */
+};
+
+/**
+ * struct batadv_neigh_node - structure for single hops neighbors
+ * @list: list node for batadv_orig_node::neigh_list
* @orig_node: pointer to corresponding orig_node
+ * @addr: the MAC address of the neighboring interface
* @if_incoming: pointer to incoming hard interface
- * @lq_update_lock: lock protecting tq_recv & tq_index
+ * @last_seen: when last packet via this neighbor was received
+ * @last_ttl: last received ttl from this neigh node
+ * @bonding_list: list node for batadv_orig_node::bond_list
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
+ * @bat_iv: B.A.T.M.A.N. IV private structure
*/
struct batadv_neigh_node {
struct hlist_node list;
+ struct batadv_orig_node *orig_node;
uint8_t addr[ETH_ALEN];
- uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE];
- uint8_t tq_index;
- uint8_t tq_avg;
+ struct batadv_hard_iface *if_incoming;
+ unsigned long last_seen;
uint8_t last_ttl;
struct list_head bonding_list;
- unsigned long last_seen;
- DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
- uint8_t real_packet_count;
- struct batadv_orig_node *orig_node;
- struct batadv_hard_iface *if_incoming;
- spinlock_t lq_update_lock; /* protects tq_recv & tq_index */
atomic_t refcount;
struct rcu_head rcu;
+ struct batadv_neigh_bat_iv bat_iv;
};
/**
@@ -265,6 +355,12 @@ struct batadv_bcast_duplist_entry {
* @BATADV_CNT_MGMT_TX_BYTES: transmitted routing protocol traffic bytes counter
* @BATADV_CNT_MGMT_RX: received routing protocol traffic packet counter
* @BATADV_CNT_MGMT_RX_BYTES: received routing protocol traffic bytes counter
+ * @BATADV_CNT_FRAG_TX: transmitted fragment traffic packet counter
+ * @BATADV_CNT_FRAG_TX_BYTES: transmitted fragment traffic bytes counter
+ * @BATADV_CNT_FRAG_RX: received fragment traffic packet counter
+ * @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter
+ * @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter
+ * @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter
* @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter
* @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter
* @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter
@@ -302,6 +398,12 @@ enum batadv_counters {
BATADV_CNT_MGMT_TX_BYTES,
BATADV_CNT_MGMT_RX,
BATADV_CNT_MGMT_RX_BYTES,
+ BATADV_CNT_FRAG_TX,
+ BATADV_CNT_FRAG_TX_BYTES,
+ BATADV_CNT_FRAG_RX,
+ BATADV_CNT_FRAG_RX_BYTES,
+ BATADV_CNT_FRAG_FWD,
+ BATADV_CNT_FRAG_FWD_BYTES,
BATADV_CNT_TT_REQUEST_TX,
BATADV_CNT_TT_REQUEST_RX,
BATADV_CNT_TT_RESPONSE_TX,
@@ -343,11 +445,14 @@ enum batadv_counters {
* @changes_list_lock: lock protecting changes_list
* @req_list_lock: lock protecting req_list
* @roam_list_lock: lock protecting roam_list
- * @local_entry_num: number of entries in the local hash table
- * @local_crc: Checksum of the local table, recomputed before sending a new OGM
* @last_changeset: last tt changeset this host has generated
* @last_changeset_len: length of last tt changeset this host has generated
* @last_changeset_lock: lock protecting last_changeset & last_changeset_len
+ * @commit_lock: prevents from executing a local TT commit while reading the
+ * local table. The local TT commit is made up by two operations (data
+ * structure update and metdata -CRC/TTVN- recalculation) and they have to be
+ * executed atomically in order to avoid another thread to read the
+ * table/metadata between those.
* @work: work queue callback item for translation table purging
*/
struct batadv_priv_tt {
@@ -362,12 +467,12 @@ struct batadv_priv_tt {
spinlock_t changes_list_lock; /* protects changes */
spinlock_t req_list_lock; /* protects req_list */
spinlock_t roam_list_lock; /* protects roam_list */
- atomic_t local_entry_num;
- uint16_t local_crc;
unsigned char *last_changeset;
int16_t last_changeset_len;
/* protects last_changeset & last_changeset_len */
spinlock_t last_changeset_lock;
+ /* prevents from executing a commit while reading the table */
+ spinlock_t commit_lock;
struct delayed_work work;
};
@@ -420,31 +525,31 @@ struct batadv_priv_debug_log {
* @list: list of available gateway nodes
* @list_lock: lock protecting gw_list & curr_gw
* @curr_gw: pointer to currently selected gateway node
+ * @bandwidth_down: advertised uplink download bandwidth (if gw_mode server)
+ * @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server)
* @reselect: bool indicating a gateway re-selection is in progress
*/
struct batadv_priv_gw {
struct hlist_head list;
spinlock_t list_lock; /* protects gw_list & curr_gw */
struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */
+ atomic_t bandwidth_down;
+ atomic_t bandwidth_up;
atomic_t reselect;
};
/**
- * struct batadv_priv_vis - per mesh interface vis data
- * @send_list: list of batadv_vis_info packets to sent
- * @hash: hash table containing vis data from other nodes in the network
- * @hash_lock: lock protecting the hash table
- * @list_lock: lock protecting my_info::recv_list
- * @work: work queue callback item for vis packet sending
- * @my_info: holds this node's vis data sent on a regular basis
+ * struct batadv_priv_tvlv - per mesh interface tvlv data
+ * @container_list: list of registered tvlv containers to be sent with each OGM
+ * @handler_list: list of the various tvlv content handlers
+ * @container_list_lock: protects tvlv container list access
+ * @handler_list_lock: protects handler list access
*/
-struct batadv_priv_vis {
- struct list_head send_list;
- struct batadv_hashtable *hash;
- spinlock_t hash_lock; /* protects hash */
- spinlock_t list_lock; /* protects my_info::recv_list */
- struct delayed_work work;
- struct batadv_vis_info *my_info;
+struct batadv_priv_tvlv {
+ struct hlist_head container_list;
+ struct hlist_head handler_list;
+ spinlock_t container_list_lock; /* protects container_list */
+ spinlock_t handler_list_lock; /* protects handler_list */
};
/**
@@ -491,6 +596,26 @@ struct batadv_priv_nc {
};
/**
+ * struct batadv_softif_vlan - per VLAN attributes set
+ * @vid: VLAN identifier
+ * @kobj: kobject for sysfs vlan subdirectory
+ * @ap_isolation: AP isolation state
+ * @tt: TT private attributes (VLAN specific)
+ * @list: list node for bat_priv::softif_vlan_list
+ * @refcount: number of context where this object is currently in use
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_softif_vlan {
+ unsigned short vid;
+ struct kobject *kobj;
+ atomic_t ap_isolation; /* boolean */
+ struct batadv_vlan_tt tt;
+ struct hlist_node list;
+ atomic_t refcount;
+ struct rcu_head rcu;
+};
+
+/**
* struct batadv_priv - per mesh interface data
* @mesh_state: current status of the mesh (inactive/active/deactivating)
* @soft_iface: net device which holds this struct as private data
@@ -499,15 +624,15 @@ struct batadv_priv_nc {
* @aggregated_ogms: bool indicating whether OGM aggregation is enabled
* @bonding: bool indicating whether traffic bonding is enabled
* @fragmentation: bool indicating whether traffic fragmentation is enabled
- * @ap_isolation: bool indicating whether ap isolation is enabled
+ * @packet_size_max: max packet size that can be transmitted via
+ * multiple fragmented skbs or a single frame if fragmentation is disabled
+ * @frag_seqno: incremental counter to identify chains of egress fragments
* @bridge_loop_avoidance: bool indicating whether bridge loop avoidance is
* enabled
* @distributed_arp_table: bool indicating whether distributed ARP table is
* enabled
- * @vis_mode: vis operation: client or server (see batadv_vis_packettype)
* @gw_mode: gateway operation: off, client or server (see batadv_gw_modes)
* @gw_sel_class: gateway selection class (applies if gw_mode client)
- * @gw_bandwidth: gateway announced bandwidth (applies if gw_mode server)
* @orig_interval: OGM broadcast interval in milliseconds
* @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop
* @log_level: configured log level (see batadv_dbg_level)
@@ -527,11 +652,14 @@ struct batadv_priv_nc {
* @primary_if: one of the hard interfaces assigned to this mesh interface
* becomes the primary interface
* @bat_algo_ops: routing algorithm used by this mesh interface
+ * @softif_vlan_list: a list of softif_vlan structs, one per VLAN created on top
+ * of the mesh interface represented by this object
+ * @softif_vlan_list_lock: lock protecting softif_vlan_list
* @bla: bridge loope avoidance data
* @debug_log: holding debug logging relevant data
* @gw: gateway data
* @tt: translation table data
- * @vis: vis data
+ * @tvlv: type-version-length-value data
* @dat: distributed arp table data
* @network_coding: bool indicating whether network coding is enabled
* @batadv_priv_nc: network coding data
@@ -544,17 +672,16 @@ struct batadv_priv {
atomic_t aggregated_ogms;
atomic_t bonding;
atomic_t fragmentation;
- atomic_t ap_isolation;
+ atomic_t packet_size_max;
+ atomic_t frag_seqno;
#ifdef CONFIG_BATMAN_ADV_BLA
atomic_t bridge_loop_avoidance;
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
atomic_t distributed_arp_table;
#endif
- atomic_t vis_mode;
atomic_t gw_mode;
atomic_t gw_sel_class;
- atomic_t gw_bandwidth;
atomic_t orig_interval;
atomic_t hop_penalty;
#ifdef CONFIG_BATMAN_ADV_DEBUG
@@ -575,6 +702,8 @@ struct batadv_priv {
struct work_struct cleanup_work;
struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */
struct batadv_algo_ops *bat_algo_ops;
+ struct hlist_head softif_vlan_list;
+ spinlock_t softif_vlan_list_lock; /* protects softif_vlan_list */
#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_priv_bla bla;
#endif
@@ -583,7 +712,7 @@ struct batadv_priv {
#endif
struct batadv_priv_gw gw;
struct batadv_priv_tt tt;
- struct batadv_priv_vis vis;
+ struct batadv_priv_tvlv tvlv;
#ifdef CONFIG_BATMAN_ADV_DAT
struct batadv_priv_dat dat;
#endif
@@ -620,7 +749,7 @@ struct batadv_socket_client {
struct batadv_socket_packet {
struct list_head list;
size_t icmp_len;
- struct batadv_icmp_packet_rr icmp_packet;
+ uint8_t icmp_packet[BATADV_ICMP_MAX_PACKET_SIZE];
};
/**
@@ -677,6 +806,7 @@ struct batadv_bla_claim {
/**
* struct batadv_tt_common_entry - tt local & tt global common data
* @addr: mac address of non-mesh client
+ * @vid: VLAN identifier
* @hash_entry: hlist node for batadv_priv_tt::local_hash or for
* batadv_priv_tt::global_hash
* @flags: various state handling flags (see batadv_tt_client_flags)
@@ -686,6 +816,7 @@ struct batadv_bla_claim {
*/
struct batadv_tt_common_entry {
uint8_t addr[ETH_ALEN];
+ unsigned short vid;
struct hlist_node hash_entry;
uint16_t flags;
unsigned long added_at;
@@ -740,7 +871,7 @@ struct batadv_tt_orig_list_entry {
*/
struct batadv_tt_change_node {
struct list_head list;
- struct batadv_tt_change change;
+ struct batadv_tvlv_tt_change change;
};
/**
@@ -866,78 +997,6 @@ struct batadv_forw_packet {
};
/**
- * struct batadv_frag_packet_list_entry - storage for fragment packet
- * @list: list node for orig_node::frag_list
- * @seqno: sequence number of the fragment
- * @skb: fragment's skb buffer
- */
-struct batadv_frag_packet_list_entry {
- struct list_head list;
- uint16_t seqno;
- struct sk_buff *skb;
-};
-
-/**
- * struct batadv_vis_info - local data for vis information
- * @first_seen: timestamp used for purging stale vis info entries
- * @recv_list: List of server-neighbors we have received this packet from. This
- * packet should not be re-forward to them again. List elements are struct
- * batadv_vis_recvlist_node
- * @send_list: list of packets to be forwarded
- * @refcount: number of contexts the object is used
- * @hash_entry: hlist node for batadv_priv_vis::hash
- * @bat_priv: pointer to soft_iface this orig node belongs to
- * @skb_packet: contains the vis packet
- */
-struct batadv_vis_info {
- unsigned long first_seen;
- struct list_head recv_list;
- struct list_head send_list;
- struct kref refcount;
- struct hlist_node hash_entry;
- struct batadv_priv *bat_priv;
- struct sk_buff *skb_packet;
-} __packed;
-
-/**
- * struct batadv_vis_info_entry - contains link information for vis
- * @src: source MAC of the link, all zero for local TT entry
- * @dst: destination MAC of the link, client mac address for local TT entry
- * @quality: transmission quality of the link, or 0 for local TT entry
- */
-struct batadv_vis_info_entry {
- uint8_t src[ETH_ALEN];
- uint8_t dest[ETH_ALEN];
- uint8_t quality;
-} __packed;
-
-/**
- * struct batadv_vis_recvlist_node - list entry for batadv_vis_info::recv_list
- * @list: list node for batadv_vis_info::recv_list
- * @mac: MAC address of the originator from where the vis_info was received
- */
-struct batadv_vis_recvlist_node {
- struct list_head list;
- uint8_t mac[ETH_ALEN];
-};
-
-/**
- * struct batadv_vis_if_list_entry - auxiliary data for vis data generation
- * @addr: MAC address of the interface
- * @primary: true if this interface is the primary interface
- * @list: list node the interface list
- *
- * While scanning for vis-entries of a particular vis-originator
- * this list collects its interfaces to create a subgraph/cluster
- * out of them later
- */
-struct batadv_vis_if_list_entry {
- uint8_t addr[ETH_ALEN];
- bool primary;
- struct hlist_node list;
-};
-
-/**
* struct batadv_algo_ops - mesh algorithm callbacks
* @list: list node for the batadv_algo_list
* @name: name of the algorithm
@@ -948,6 +1007,16 @@ struct batadv_vis_if_list_entry {
* @bat_primary_iface_set: called when primary interface is selected / changed
* @bat_ogm_schedule: prepare a new outgoing OGM for the send queue
* @bat_ogm_emit: send scheduled OGM
+ * @bat_neigh_cmp: compare the metrics of two neighbors
+ * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or
+ * better than neigh2 from the metric prospective
+ * @bat_orig_print: print the originator table (optional)
+ * @bat_orig_free: free the resources allocated by the routing algorithm for an
+ * orig_node object
+ * @bat_orig_add_if: ask the routing algorithm to apply the needed changes to
+ * the orig_node due to a new hard-interface being added into the mesh
+ * @bat_orig_del_if: ask the routing algorithm to apply the needed changes to
+ * the orig_node due to an hard-interface being removed from the mesh
*/
struct batadv_algo_ops {
struct hlist_node list;
@@ -958,6 +1027,17 @@ struct batadv_algo_ops {
void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface);
void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface);
void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
+ int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1,
+ struct batadv_neigh_node *neigh2);
+ bool (*bat_neigh_is_equiv_or_better)(struct batadv_neigh_node *neigh1,
+ struct batadv_neigh_node *neigh2);
+ /* orig_node handling API */
+ void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq);
+ void (*bat_orig_free)(struct batadv_orig_node *orig_node);
+ int (*bat_orig_add_if)(struct batadv_orig_node *orig_node,
+ int max_if_num);
+ int (*bat_orig_del_if)(struct batadv_orig_node *orig_node,
+ int max_if_num, int del_if_num);
};
/**
@@ -965,6 +1045,7 @@ struct batadv_algo_ops {
* is used to stored ARP entries needed for the global DAT cache
* @ip: the IPv4 corresponding to this DAT/ARP entry
* @mac_addr: the MAC address associated to the stored IPv4
+ * @vid: the vlan ID associated to this entry
* @last_update: time in jiffies when this entry was refreshed last time
* @hash_entry: hlist node for batadv_priv_dat::hash
* @refcount: number of contexts the object is used
@@ -973,6 +1054,7 @@ struct batadv_algo_ops {
struct batadv_dat_entry {
__be32 ip;
uint8_t mac_addr[ETH_ALEN];
+ unsigned short vid;
unsigned long last_update;
struct hlist_node hash_entry;
atomic_t refcount;
@@ -992,4 +1074,60 @@ struct batadv_dat_candidate {
struct batadv_orig_node *orig_node;
};
+/**
+ * struct batadv_tvlv_container - container for tvlv appended to OGMs
+ * @list: hlist node for batadv_priv_tvlv::container_list
+ * @tvlv_hdr: tvlv header information needed to construct the tvlv
+ * @value_len: length of the buffer following this struct which contains
+ * the actual tvlv payload
+ * @refcount: number of contexts the object is used
+ */
+struct batadv_tvlv_container {
+ struct hlist_node list;
+ struct batadv_tvlv_hdr tvlv_hdr;
+ atomic_t refcount;
+};
+
+/**
+ * struct batadv_tvlv_handler - handler for specific tvlv type and version
+ * @list: hlist node for batadv_priv_tvlv::handler_list
+ * @ogm_handler: handler callback which is given the tvlv payload to process on
+ * incoming OGM packets
+ * @unicast_handler: handler callback which is given the tvlv payload to process
+ * on incoming unicast tvlv packets
+ * @type: tvlv type this handler feels responsible for
+ * @version: tvlv version this handler feels responsible for
+ * @flags: tvlv handler flags
+ * @refcount: number of contexts the object is used
+ * @rcu: struct used for freeing in an RCU-safe manner
+ */
+struct batadv_tvlv_handler {
+ struct hlist_node list;
+ void (*ogm_handler)(struct batadv_priv *bat_priv,
+ struct batadv_orig_node *orig,
+ uint8_t flags,
+ void *tvlv_value, uint16_t tvlv_value_len);
+ int (*unicast_handler)(struct batadv_priv *bat_priv,
+ uint8_t *src, uint8_t *dst,
+ void *tvlv_value, uint16_t tvlv_value_len);
+ uint8_t type;
+ uint8_t version;
+ uint8_t flags;
+ atomic_t refcount;
+ struct rcu_head rcu;
+};
+
+/**
+ * enum batadv_tvlv_handler_flags - tvlv handler flags definitions
+ * @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call
+ * this handler even if its type was not found (with no data)
+ * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks
+ * a handler as being called, so it won't be called if the
+ * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+ */
+enum batadv_tvlv_handler_flags {
+ BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
+ BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+};
+
#endif /* _NET_BATMAN_ADV_TYPES_H_ */
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c
deleted file mode 100644
index 48b31d33ce6b..000000000000
--- a/net/batman-adv/unicast.c
+++ /dev/null
@@ -1,491 +0,0 @@
-/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors:
- *
- * Andreas Langer
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include "main.h"
-#include "unicast.h"
-#include "send.h"
-#include "soft-interface.h"
-#include "gateway_client.h"
-#include "originator.h"
-#include "hash.h"
-#include "translation-table.h"
-#include "routing.h"
-#include "hard-interface.h"
-
-
-static struct sk_buff *
-batadv_frag_merge_packet(struct list_head *head,
- struct batadv_frag_packet_list_entry *tfp,
- struct sk_buff *skb)
-{
- struct batadv_unicast_frag_packet *up;
- struct sk_buff *tmp_skb;
- struct batadv_unicast_packet *unicast_packet;
- int hdr_len = sizeof(*unicast_packet);
- int uni_diff = sizeof(*up) - hdr_len;
- uint8_t *packet_pos;
-
- up = (struct batadv_unicast_frag_packet *)skb->data;
- /* set skb to the first part and tmp_skb to the second part */
- if (up->flags & BATADV_UNI_FRAG_HEAD) {
- tmp_skb = tfp->skb;
- } else {
- tmp_skb = skb;
- skb = tfp->skb;
- }
-
- if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0)
- goto err;
-
- skb_pull(tmp_skb, sizeof(*up));
- if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0)
- goto err;
-
- /* move free entry to end */
- tfp->skb = NULL;
- tfp->seqno = 0;
- list_move_tail(&tfp->list, head);
-
- memcpy(skb_put(skb, tmp_skb->len), tmp_skb->data, tmp_skb->len);
- kfree_skb(tmp_skb);
-
- memmove(skb->data + uni_diff, skb->data, hdr_len);
- packet_pos = skb_pull(skb, uni_diff);
- unicast_packet = (struct batadv_unicast_packet *)packet_pos;
- unicast_packet->header.packet_type = BATADV_UNICAST;
-
- return skb;
-
-err:
- /* free buffered skb, skb will be freed later */
- kfree_skb(tfp->skb);
- return NULL;
-}
-
-static void batadv_frag_create_entry(struct list_head *head,
- struct sk_buff *skb)
-{
- struct batadv_frag_packet_list_entry *tfp;
- struct batadv_unicast_frag_packet *up;
-
- up = (struct batadv_unicast_frag_packet *)skb->data;
-
- /* free and oldest packets stand at the end */
- tfp = list_entry((head)->prev, typeof(*tfp), list);
- kfree_skb(tfp->skb);
-
- tfp->seqno = ntohs(up->seqno);
- tfp->skb = skb;
- list_move(&tfp->list, head);
- return;
-}
-
-static int batadv_frag_create_buffer(struct list_head *head)
-{
- int i;
- struct batadv_frag_packet_list_entry *tfp;
-
- for (i = 0; i < BATADV_FRAG_BUFFER_SIZE; i++) {
- tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC);
- if (!tfp) {
- batadv_frag_list_free(head);
- return -ENOMEM;
- }
- tfp->skb = NULL;
- tfp->seqno = 0;
- INIT_LIST_HEAD(&tfp->list);
- list_add(&tfp->list, head);
- }
-
- return 0;
-}
-
-static struct batadv_frag_packet_list_entry *
-batadv_frag_search_packet(struct list_head *head,
- const struct batadv_unicast_frag_packet *up)
-{
- struct batadv_frag_packet_list_entry *tfp;
- struct batadv_unicast_frag_packet *tmp_up = NULL;
- bool is_head_tmp, is_head;
- uint16_t search_seqno;
-
- if (up->flags & BATADV_UNI_FRAG_HEAD)
- search_seqno = ntohs(up->seqno)+1;
- else
- search_seqno = ntohs(up->seqno)-1;
-
- is_head = up->flags & BATADV_UNI_FRAG_HEAD;
-
- list_for_each_entry(tfp, head, list) {
- if (!tfp->skb)
- continue;
-
- if (tfp->seqno == ntohs(up->seqno))
- goto mov_tail;
-
- tmp_up = (struct batadv_unicast_frag_packet *)tfp->skb->data;
-
- if (tfp->seqno == search_seqno) {
- is_head_tmp = tmp_up->flags & BATADV_UNI_FRAG_HEAD;
- if (is_head_tmp != is_head)
- return tfp;
- else
- goto mov_tail;
- }
- }
- return NULL;
-
-mov_tail:
- list_move_tail(&tfp->list, head);
- return NULL;
-}
-
-void batadv_frag_list_free(struct list_head *head)
-{
- struct batadv_frag_packet_list_entry *pf, *tmp_pf;
-
- if (!list_empty(head)) {
- list_for_each_entry_safe(pf, tmp_pf, head, list) {
- kfree_skb(pf->skb);
- list_del(&pf->list);
- kfree(pf);
- }
- }
- return;
-}
-
-/* frag_reassemble_skb():
- * returns NET_RX_DROP if the operation failed - skb is left intact
- * returns NET_RX_SUCCESS if the fragment was buffered (skb_new will be NULL)
- * or the skb could be reassembled (skb_new will point to the new packet and
- * skb was freed)
- */
-int batadv_frag_reassemble_skb(struct sk_buff *skb,
- struct batadv_priv *bat_priv,
- struct sk_buff **new_skb)
-{
- struct batadv_orig_node *orig_node;
- struct batadv_frag_packet_list_entry *tmp_frag_entry;
- int ret = NET_RX_DROP;
- struct batadv_unicast_frag_packet *unicast_packet;
-
- unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
- *new_skb = NULL;
-
- orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->orig);
- if (!orig_node)
- goto out;
-
- orig_node->last_frag_packet = jiffies;
-
- if (list_empty(&orig_node->frag_list) &&
- batadv_frag_create_buffer(&orig_node->frag_list)) {
- pr_debug("couldn't create frag buffer\n");
- goto out;
- }
-
- tmp_frag_entry = batadv_frag_search_packet(&orig_node->frag_list,
- unicast_packet);
-
- if (!tmp_frag_entry) {
- batadv_frag_create_entry(&orig_node->frag_list, skb);
- ret = NET_RX_SUCCESS;
- goto out;
- }
-
- *new_skb = batadv_frag_merge_packet(&orig_node->frag_list,
- tmp_frag_entry, skb);
- /* if not, merge failed */
- if (*new_skb)
- ret = NET_RX_SUCCESS;
-
-out:
- if (orig_node)
- batadv_orig_node_free_ref(orig_node);
- return ret;
-}
-
-int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv,
- struct batadv_hard_iface *hard_iface,
- const uint8_t dstaddr[])
-{
- struct batadv_unicast_packet tmp_uc, *unicast_packet;
- struct batadv_hard_iface *primary_if;
- struct sk_buff *frag_skb;
- struct batadv_unicast_frag_packet *frag1, *frag2;
- int uc_hdr_len = sizeof(*unicast_packet);
- int ucf_hdr_len = sizeof(*frag1);
- int data_len = skb->len - uc_hdr_len;
- int large_tail = 0, ret = NET_RX_DROP;
- uint16_t seqno;
-
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto dropped;
-
- frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len);
- if (!frag_skb)
- goto dropped;
-
- skb->priority = TC_PRIO_CONTROL;
- skb_reserve(frag_skb, ucf_hdr_len);
-
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
- memcpy(&tmp_uc, unicast_packet, uc_hdr_len);
- skb_split(skb, frag_skb, data_len / 2 + uc_hdr_len);
-
- if (batadv_skb_head_push(skb, ucf_hdr_len - uc_hdr_len) < 0 ||
- batadv_skb_head_push(frag_skb, ucf_hdr_len) < 0)
- goto drop_frag;
-
- frag1 = (struct batadv_unicast_frag_packet *)skb->data;
- frag2 = (struct batadv_unicast_frag_packet *)frag_skb->data;
-
- memcpy(frag1, &tmp_uc, sizeof(tmp_uc));
-
- frag1->header.ttl--;
- frag1->header.version = BATADV_COMPAT_VERSION;
- frag1->header.packet_type = BATADV_UNICAST_FRAG;
-
- memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- memcpy(frag2, frag1, sizeof(*frag2));
-
- if (data_len & 1)
- large_tail = BATADV_UNI_FRAG_LARGETAIL;
-
- frag1->flags = BATADV_UNI_FRAG_HEAD | large_tail;
- frag2->flags = large_tail;
-
- seqno = atomic_add_return(2, &hard_iface->frag_seqno);
- frag1->seqno = htons(seqno - 1);
- frag2->seqno = htons(seqno);
-
- batadv_send_skb_packet(skb, hard_iface, dstaddr);
- batadv_send_skb_packet(frag_skb, hard_iface, dstaddr);
- ret = NET_RX_SUCCESS;
- goto out;
-
-drop_frag:
- kfree_skb(frag_skb);
-dropped:
- kfree_skb(skb);
-out:
- if (primary_if)
- batadv_hardif_free_ref(primary_if);
- return ret;
-}
-
-/**
- * batadv_unicast_push_and_fill_skb - extends the buffer and initializes the
- * common fields for unicast packets
- * @skb: packet
- * @hdr_size: amount of bytes to push at the beginning of the skb
- * @orig_node: the destination node
- *
- * Returns false if the buffer extension was not possible or true otherwise
- */
-static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size,
- struct batadv_orig_node *orig_node)
-{
- struct batadv_unicast_packet *unicast_packet;
- uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
-
- if (batadv_skb_head_push(skb, hdr_size) < 0)
- return false;
-
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
- unicast_packet->header.version = BATADV_COMPAT_VERSION;
- /* batman packet type: unicast */
- unicast_packet->header.packet_type = BATADV_UNICAST;
- /* set unicast ttl */
- unicast_packet->header.ttl = BATADV_TTL;
- /* copy the destination for faster routing */
- memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
- /* set the destination tt version number */
- unicast_packet->ttvn = ttvn;
-
- return true;
-}
-
-/**
- * batadv_unicast_prepare_skb - encapsulate an skb with a unicast header
- * @skb: the skb containing the payload to encapsulate
- * @orig_node: the destination node
- *
- * Returns false if the payload could not be encapsulated or true otherwise.
- *
- * This call might reallocate skb data.
- */
-static bool batadv_unicast_prepare_skb(struct sk_buff *skb,
- struct batadv_orig_node *orig_node)
-{
- size_t uni_size = sizeof(struct batadv_unicast_packet);
- return batadv_unicast_push_and_fill_skb(skb, uni_size, orig_node);
-}
-
-/**
- * batadv_unicast_4addr_prepare_skb - encapsulate an skb with a unicast4addr
- * header
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: the skb containing the payload to encapsulate
- * @orig_node: the destination node
- * @packet_subtype: the batman 4addr packet subtype to use
- *
- * Returns false if the payload could not be encapsulated or true otherwise.
- *
- * This call might reallocate skb data.
- */
-bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
- struct batadv_orig_node *orig,
- int packet_subtype)
-{
- struct batadv_hard_iface *primary_if;
- struct batadv_unicast_4addr_packet *unicast_4addr_packet;
- bool ret = false;
-
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
-
- /* pull the header space and fill the unicast_packet substructure.
- * We can do that because the first member of the unicast_4addr_packet
- * is of type struct unicast_packet
- */
- if (!batadv_unicast_push_and_fill_skb(skb,
- sizeof(*unicast_4addr_packet),
- orig))
- goto out;
-
- unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
- unicast_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR;
- memcpy(unicast_4addr_packet->src, primary_if->net_dev->dev_addr,
- ETH_ALEN);
- unicast_4addr_packet->subtype = packet_subtype;
- unicast_4addr_packet->reserved = 0;
-
- ret = true;
-out:
- if (primary_if)
- batadv_hardif_free_ref(primary_if);
- return ret;
-}
-
-/**
- * batadv_unicast_generic_send_skb - send an skb as unicast
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: payload to send
- * @packet_type: the batman unicast packet type to use
- * @packet_subtype: the batman packet subtype. It is ignored if packet_type is
- * not BATADV_UNICAT_4ADDR
- *
- * Returns 1 in case of error or 0 otherwise
- */
-int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv,
- struct sk_buff *skb, int packet_type,
- int packet_subtype)
-{
- struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
- struct batadv_unicast_packet *unicast_packet;
- struct batadv_orig_node *orig_node;
- struct batadv_neigh_node *neigh_node;
- int data_len = skb->len;
- int ret = NET_RX_DROP;
- unsigned int dev_mtu, header_len;
-
- /* get routing information */
- if (is_multicast_ether_addr(ethhdr->h_dest)) {
- orig_node = batadv_gw_get_selected_orig(bat_priv);
- if (orig_node)
- goto find_router;
- }
-
- /* check for tt host - increases orig_node refcount.
- * returns NULL in case of AP isolation
- */
- orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
- ethhdr->h_dest);
-
-find_router:
- /* find_router():
- * - if orig_node is NULL it returns NULL
- * - increases neigh_nodes refcount if found.
- */
- neigh_node = batadv_find_router(bat_priv, orig_node, NULL);
-
- if (!neigh_node)
- goto out;
-
- switch (packet_type) {
- case BATADV_UNICAST:
- if (!batadv_unicast_prepare_skb(skb, orig_node))
- goto out;
-
- header_len = sizeof(struct batadv_unicast_packet);
- break;
- case BATADV_UNICAST_4ADDR:
- if (!batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
- packet_subtype))
- goto out;
-
- header_len = sizeof(struct batadv_unicast_4addr_packet);
- break;
- default:
- /* this function supports UNICAST and UNICAST_4ADDR only. It
- * should never be invoked with any other packet type
- */
- goto out;
- }
-
- ethhdr = (struct ethhdr *)(skb->data + header_len);
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
-
- /* inform the destination node that we are still missing a correct route
- * for this client. The destination will receive this packet and will
- * try to reroute it because the ttvn contained in the header is less
- * than the current one
- */
- if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest))
- unicast_packet->ttvn = unicast_packet->ttvn - 1;
-
- dev_mtu = neigh_node->if_incoming->net_dev->mtu;
- /* fragmentation mechanism only works for UNICAST (now) */
- if (packet_type == BATADV_UNICAST &&
- atomic_read(&bat_priv->fragmentation) &&
- data_len + sizeof(*unicast_packet) > dev_mtu) {
- /* send frag skb decreases ttl */
- unicast_packet->header.ttl++;
- ret = batadv_frag_send_skb(skb, bat_priv,
- neigh_node->if_incoming,
- neigh_node->addr);
- goto out;
- }
-
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
- ret = 0;
-
-out:
- if (neigh_node)
- batadv_neigh_node_free_ref(neigh_node);
- if (orig_node)
- batadv_orig_node_free_ref(orig_node);
- if (ret == NET_RX_DROP)
- kfree_skb(skb);
- return ret;
-}
diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h
deleted file mode 100644
index 429cf8a4a31e..000000000000
--- a/net/batman-adv/unicast.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors:
- *
- * Andreas Langer
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef _NET_BATMAN_ADV_UNICAST_H_
-#define _NET_BATMAN_ADV_UNICAST_H_
-
-#include "packet.h"
-
-#define BATADV_FRAG_TIMEOUT 10000 /* purge frag list entries after time in ms */
-#define BATADV_FRAG_BUFFER_SIZE 6 /* number of list elements in buffer */
-
-int batadv_frag_reassemble_skb(struct sk_buff *skb,
- struct batadv_priv *bat_priv,
- struct sk_buff **new_skb);
-void batadv_frag_list_free(struct list_head *head);
-int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv,
- struct batadv_hard_iface *hard_iface,
- const uint8_t dstaddr[]);
-bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
- struct batadv_orig_node *orig_node,
- int packet_subtype);
-int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv,
- struct sk_buff *skb, int packet_type,
- int packet_subtype);
-
-
-/**
- * batadv_unicast_send_skb - send the skb encapsulated in a unicast packet
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: the payload to send
- */
-static inline int batadv_unicast_send_skb(struct batadv_priv *bat_priv,
- struct sk_buff *skb)
-{
- return batadv_unicast_generic_send_skb(bat_priv, skb, BATADV_UNICAST,
- 0);
-}
-
-/**
- * batadv_unicast_send_skb - send the skb encapsulated in a unicast4addr packet
- * @bat_priv: the bat priv with all the soft interface information
- * @skb: the payload to send
- * @packet_subtype: the batman 4addr packet subtype to use
- */
-static inline int batadv_unicast_4addr_send_skb(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
- int packet_subtype)
-{
- return batadv_unicast_generic_send_skb(bat_priv, skb,
- BATADV_UNICAST_4ADDR,
- packet_subtype);
-}
-
-static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu)
-{
- const struct batadv_unicast_frag_packet *unicast_packet;
- int uneven_correction = 0;
- unsigned int merged_size;
-
- unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
-
- if (unicast_packet->flags & BATADV_UNI_FRAG_LARGETAIL) {
- if (unicast_packet->flags & BATADV_UNI_FRAG_HEAD)
- uneven_correction = 1;
- else
- uneven_correction = -1;
- }
-
- merged_size = (skb->len - sizeof(*unicast_packet)) * 2;
- merged_size += sizeof(struct batadv_unicast_packet) + uneven_correction;
-
- return merged_size <= mtu;
-}
-
-#endif /* _NET_BATMAN_ADV_UNICAST_H_ */
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
deleted file mode 100644
index d8ea31a58457..000000000000
--- a/net/batman-adv/vis.c
+++ /dev/null
@@ -1,938 +0,0 @@
-/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors:
- *
- * Simon Wunderlich
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#include "main.h"
-#include "send.h"
-#include "translation-table.h"
-#include "vis.h"
-#include "soft-interface.h"
-#include "hard-interface.h"
-#include "hash.h"
-#include "originator.h"
-
-#define BATADV_MAX_VIS_PACKET_SIZE 1000
-
-/* hash class keys */
-static struct lock_class_key batadv_vis_hash_lock_class_key;
-
-/* free the info */
-static void batadv_free_info(struct kref *ref)
-{
- struct batadv_vis_info *info;
- struct batadv_priv *bat_priv;
- struct batadv_vis_recvlist_node *entry, *tmp;
-
- info = container_of(ref, struct batadv_vis_info, refcount);
- bat_priv = info->bat_priv;
-
- list_del_init(&info->send_list);
- spin_lock_bh(&bat_priv->vis.list_lock);
- list_for_each_entry_safe(entry, tmp, &info->recv_list, list) {
- list_del(&entry->list);
- kfree(entry);
- }
-
- spin_unlock_bh(&bat_priv->vis.list_lock);
- kfree_skb(info->skb_packet);
- kfree(info);
-}
-
-/* Compare two vis packets, used by the hashing algorithm */
-static int batadv_vis_info_cmp(const struct hlist_node *node, const void *data2)
-{
- const struct batadv_vis_info *d1, *d2;
- const struct batadv_vis_packet *p1, *p2;
-
- d1 = container_of(node, struct batadv_vis_info, hash_entry);
- d2 = data2;
- p1 = (struct batadv_vis_packet *)d1->skb_packet->data;
- p2 = (struct batadv_vis_packet *)d2->skb_packet->data;
- return batadv_compare_eth(p1->vis_orig, p2->vis_orig);
-}
-
-/* hash function to choose an entry in a hash table of given size
- * hash algorithm from http://en.wikipedia.org/wiki/Hash_table
- */
-static uint32_t batadv_vis_info_choose(const void *data, uint32_t size)
-{
- const struct batadv_vis_info *vis_info = data;
- const struct batadv_vis_packet *packet;
- const unsigned char *key;
- uint32_t hash = 0;
- size_t i;
-
- packet = (struct batadv_vis_packet *)vis_info->skb_packet->data;
- key = packet->vis_orig;
- for (i = 0; i < ETH_ALEN; i++) {
- hash += key[i];
- hash += (hash << 10);
- hash ^= (hash >> 6);
- }
-
- hash += (hash << 3);
- hash ^= (hash >> 11);
- hash += (hash << 15);
-
- return hash % size;
-}
-
-static struct batadv_vis_info *
-batadv_vis_hash_find(struct batadv_priv *bat_priv, const void *data)
-{
- struct batadv_hashtable *hash = bat_priv->vis.hash;
- struct hlist_head *head;
- struct batadv_vis_info *vis_info, *vis_info_tmp = NULL;
- uint32_t index;
-
- if (!hash)
- return NULL;
-
- index = batadv_vis_info_choose(data, hash->size);
- head = &hash->table[index];
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(vis_info, head, hash_entry) {
- if (!batadv_vis_info_cmp(&vis_info->hash_entry, data))
- continue;
-
- vis_info_tmp = vis_info;
- break;
- }
- rcu_read_unlock();
-
- return vis_info_tmp;
-}
-
-/* insert interface to the list of interfaces of one originator, if it
- * does not already exist in the list
- */
-static void batadv_vis_data_insert_interface(const uint8_t *interface,
- struct hlist_head *if_list,
- bool primary)
-{
- struct batadv_vis_if_list_entry *entry;
-
- hlist_for_each_entry(entry, if_list, list) {
- if (batadv_compare_eth(entry->addr, interface))
- return;
- }
-
- /* it's a new address, add it to the list */
- entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
- if (!entry)
- return;
- memcpy(entry->addr, interface, ETH_ALEN);
- entry->primary = primary;
- hlist_add_head(&entry->list, if_list);
-}
-
-static void batadv_vis_data_read_prim_sec(struct seq_file *seq,
- const struct hlist_head *if_list)
-{
- struct batadv_vis_if_list_entry *entry;
-
- hlist_for_each_entry(entry, if_list, list) {
- if (entry->primary)
- seq_puts(seq, "PRIMARY, ");
- else
- seq_printf(seq, "SEC %pM, ", entry->addr);
- }
-}
-
-/* read an entry */
-static ssize_t
-batadv_vis_data_read_entry(struct seq_file *seq,
- const struct batadv_vis_info_entry *entry,
- const uint8_t *src, bool primary)
-{
- if (primary && entry->quality == 0)
- return seq_printf(seq, "TT %pM, ", entry->dest);
- else if (batadv_compare_eth(entry->src, src))
- return seq_printf(seq, "TQ %pM %d, ", entry->dest,
- entry->quality);
-
- return 0;
-}
-
-static void
-batadv_vis_data_insert_interfaces(struct hlist_head *list,
- struct batadv_vis_packet *packet,
- struct batadv_vis_info_entry *entries)
-{
- int i;
-
- for (i = 0; i < packet->entries; i++) {
- if (entries[i].quality == 0)
- continue;
-
- if (batadv_compare_eth(entries[i].src, packet->vis_orig))
- continue;
-
- batadv_vis_data_insert_interface(entries[i].src, list, false);
- }
-}
-
-static void batadv_vis_data_read_entries(struct seq_file *seq,
- struct hlist_head *list,
- struct batadv_vis_packet *packet,
- struct batadv_vis_info_entry *entries)
-{
- int i;
- struct batadv_vis_if_list_entry *entry;
-
- hlist_for_each_entry(entry, list, list) {
- seq_printf(seq, "%pM,", entry->addr);
-
- for (i = 0; i < packet->entries; i++)
- batadv_vis_data_read_entry(seq, &entries[i],
- entry->addr, entry->primary);
-
- /* add primary/secondary records */
- if (batadv_compare_eth(entry->addr, packet->vis_orig))
- batadv_vis_data_read_prim_sec(seq, list);
-
- seq_puts(seq, "\n");
- }
-}
-
-static void batadv_vis_seq_print_text_bucket(struct seq_file *seq,
- const struct hlist_head *head)
-{
- struct batadv_vis_info *info;
- struct batadv_vis_packet *packet;
- uint8_t *entries_pos;
- struct batadv_vis_info_entry *entries;
- struct batadv_vis_if_list_entry *entry;
- struct hlist_node *n;
-
- HLIST_HEAD(vis_if_list);
-
- hlist_for_each_entry_rcu(info, head, hash_entry) {
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
- entries_pos = (uint8_t *)packet + sizeof(*packet);
- entries = (struct batadv_vis_info_entry *)entries_pos;
-
- batadv_vis_data_insert_interface(packet->vis_orig, &vis_if_list,
- true);
- batadv_vis_data_insert_interfaces(&vis_if_list, packet,
- entries);
- batadv_vis_data_read_entries(seq, &vis_if_list, packet,
- entries);
-
- hlist_for_each_entry_safe(entry, n, &vis_if_list, list) {
- hlist_del(&entry->list);
- kfree(entry);
- }
- }
-}
-
-int batadv_vis_seq_print_text(struct seq_file *seq, void *offset)
-{
- struct batadv_hard_iface *primary_if;
- struct hlist_head *head;
- struct net_device *net_dev = (struct net_device *)seq->private;
- struct batadv_priv *bat_priv = netdev_priv(net_dev);
- struct batadv_hashtable *hash = bat_priv->vis.hash;
- uint32_t i;
- int ret = 0;
- int vis_server = atomic_read(&bat_priv->vis_mode);
-
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
-
- if (vis_server == BATADV_VIS_TYPE_CLIENT_UPDATE)
- goto out;
-
- spin_lock_bh(&bat_priv->vis.hash_lock);
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
- batadv_vis_seq_print_text_bucket(seq, head);
- }
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-
-out:
- if (primary_if)
- batadv_hardif_free_ref(primary_if);
- return ret;
-}
-
-/* add the info packet to the send list, if it was not
- * already linked in.
- */
-static void batadv_send_list_add(struct batadv_priv *bat_priv,
- struct batadv_vis_info *info)
-{
- if (list_empty(&info->send_list)) {
- kref_get(&info->refcount);
- list_add_tail(&info->send_list, &bat_priv->vis.send_list);
- }
-}
-
-/* delete the info packet from the send list, if it was
- * linked in.
- */
-static void batadv_send_list_del(struct batadv_vis_info *info)
-{
- if (!list_empty(&info->send_list)) {
- list_del_init(&info->send_list);
- kref_put(&info->refcount, batadv_free_info);
- }
-}
-
-/* tries to add one entry to the receive list. */
-static void batadv_recv_list_add(struct batadv_priv *bat_priv,
- struct list_head *recv_list, const char *mac)
-{
- struct batadv_vis_recvlist_node *entry;
-
- entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
- if (!entry)
- return;
-
- memcpy(entry->mac, mac, ETH_ALEN);
- spin_lock_bh(&bat_priv->vis.list_lock);
- list_add_tail(&entry->list, recv_list);
- spin_unlock_bh(&bat_priv->vis.list_lock);
-}
-
-/* returns 1 if this mac is in the recv_list */
-static int batadv_recv_list_is_in(struct batadv_priv *bat_priv,
- const struct list_head *recv_list,
- const char *mac)
-{
- const struct batadv_vis_recvlist_node *entry;
-
- spin_lock_bh(&bat_priv->vis.list_lock);
- list_for_each_entry(entry, recv_list, list) {
- if (batadv_compare_eth(entry->mac, mac)) {
- spin_unlock_bh(&bat_priv->vis.list_lock);
- return 1;
- }
- }
- spin_unlock_bh(&bat_priv->vis.list_lock);
- return 0;
-}
-
-/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old,
- * broken.. ). vis hash must be locked outside. is_new is set when the packet
- * is newer than old entries in the hash.
- */
-static struct batadv_vis_info *
-batadv_add_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_packet *vis_packet, int vis_info_len,
- int *is_new, int make_broadcast)
-{
- struct batadv_vis_info *info, *old_info;
- struct batadv_vis_packet *search_packet, *old_packet;
- struct batadv_vis_info search_elem;
- struct batadv_vis_packet *packet;
- struct sk_buff *tmp_skb;
- int hash_added;
- size_t len;
- size_t max_entries;
-
- *is_new = 0;
- /* sanity check */
- if (!bat_priv->vis.hash)
- return NULL;
-
- /* see if the packet is already in vis_hash */
- search_elem.skb_packet = dev_alloc_skb(sizeof(*search_packet));
- if (!search_elem.skb_packet)
- return NULL;
- len = sizeof(*search_packet);
- tmp_skb = search_elem.skb_packet;
- search_packet = (struct batadv_vis_packet *)skb_put(tmp_skb, len);
-
- memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN);
- old_info = batadv_vis_hash_find(bat_priv, &search_elem);
- kfree_skb(search_elem.skb_packet);
-
- if (old_info) {
- tmp_skb = old_info->skb_packet;
- old_packet = (struct batadv_vis_packet *)tmp_skb->data;
- if (!batadv_seq_after(ntohl(vis_packet->seqno),
- ntohl(old_packet->seqno))) {
- if (old_packet->seqno == vis_packet->seqno) {
- batadv_recv_list_add(bat_priv,
- &old_info->recv_list,
- vis_packet->sender_orig);
- return old_info;
- } else {
- /* newer packet is already in hash. */
- return NULL;
- }
- }
- /* remove old entry */
- batadv_hash_remove(bat_priv->vis.hash, batadv_vis_info_cmp,
- batadv_vis_info_choose, old_info);
- batadv_send_list_del(old_info);
- kref_put(&old_info->refcount, batadv_free_info);
- }
-
- info = kmalloc(sizeof(*info), GFP_ATOMIC);
- if (!info)
- return NULL;
-
- len = sizeof(*packet) + vis_info_len;
- info->skb_packet = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
- if (!info->skb_packet) {
- kfree(info);
- return NULL;
- }
- info->skb_packet->priority = TC_PRIO_CONTROL;
- skb_reserve(info->skb_packet, ETH_HLEN);
- packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len);
-
- kref_init(&info->refcount);
- INIT_LIST_HEAD(&info->send_list);
- INIT_LIST_HEAD(&info->recv_list);
- info->first_seen = jiffies;
- info->bat_priv = bat_priv;
- memcpy(packet, vis_packet, len);
-
- /* initialize and add new packet. */
- *is_new = 1;
-
- /* Make it a broadcast packet, if required */
- if (make_broadcast)
- memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN);
-
- /* repair if entries is longer than packet. */
- max_entries = vis_info_len / sizeof(struct batadv_vis_info_entry);
- if (packet->entries > max_entries)
- packet->entries = max_entries;
-
- batadv_recv_list_add(bat_priv, &info->recv_list, packet->sender_orig);
-
- /* try to add it */
- hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp,
- batadv_vis_info_choose, info,
- &info->hash_entry);
- if (hash_added != 0) {
- /* did not work (for some reason) */
- kref_put(&info->refcount, batadv_free_info);
- info = NULL;
- }
-
- return info;
-}
-
-/* handle the server sync packet, forward if needed. */
-void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_packet *vis_packet,
- int vis_info_len)
-{
- struct batadv_vis_info *info;
- int is_new, make_broadcast;
- int vis_server = atomic_read(&bat_priv->vis_mode);
-
- make_broadcast = (vis_server == BATADV_VIS_TYPE_SERVER_SYNC);
-
- spin_lock_bh(&bat_priv->vis.hash_lock);
- info = batadv_add_packet(bat_priv, vis_packet, vis_info_len,
- &is_new, make_broadcast);
- if (!info)
- goto end;
-
- /* only if we are server ourselves and packet is newer than the one in
- * hash.
- */
- if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC && is_new)
- batadv_send_list_add(bat_priv, info);
-end:
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-}
-
-/* handle an incoming client update packet and schedule forward if needed. */
-void batadv_receive_client_update_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_packet *vis_packet,
- int vis_info_len)
-{
- struct batadv_vis_info *info;
- struct batadv_vis_packet *packet;
- int is_new;
- int vis_server = atomic_read(&bat_priv->vis_mode);
- int are_target = 0;
-
- /* clients shall not broadcast. */
- if (is_broadcast_ether_addr(vis_packet->target_orig))
- return;
-
- /* Are we the target for this VIS packet? */
- if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC &&
- batadv_is_my_mac(bat_priv, vis_packet->target_orig))
- are_target = 1;
-
- spin_lock_bh(&bat_priv->vis.hash_lock);
- info = batadv_add_packet(bat_priv, vis_packet, vis_info_len,
- &is_new, are_target);
-
- if (!info)
- goto end;
- /* note that outdated packets will be dropped at this point. */
-
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
- /* send only if we're the target server or ... */
- if (are_target && is_new) {
- packet->vis_type = BATADV_VIS_TYPE_SERVER_SYNC; /* upgrade! */
- batadv_send_list_add(bat_priv, info);
-
- /* ... we're not the recipient (and thus need to forward). */
- } else if (!batadv_is_my_mac(bat_priv, packet->target_orig)) {
- batadv_send_list_add(bat_priv, info);
- }
-
-end:
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-}
-
-/* Walk the originators and find the VIS server with the best tq. Set the packet
- * address to its address and return the best_tq.
- *
- * Must be called with the originator hash locked
- */
-static int batadv_find_best_vis_server(struct batadv_priv *bat_priv,
- struct batadv_vis_info *info)
-{
- struct batadv_hashtable *hash = bat_priv->orig_hash;
- struct batadv_neigh_node *router;
- struct hlist_head *head;
- struct batadv_orig_node *orig_node;
- struct batadv_vis_packet *packet;
- int best_tq = -1;
- uint32_t i;
-
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
- continue;
-
- if ((orig_node->flags & BATADV_VIS_SERVER) &&
- (router->tq_avg > best_tq)) {
- best_tq = router->tq_avg;
- memcpy(packet->target_orig, orig_node->orig,
- ETH_ALEN);
- }
- batadv_neigh_node_free_ref(router);
- }
- rcu_read_unlock();
- }
-
- return best_tq;
-}
-
-/* Return true if the vis packet is full. */
-static bool batadv_vis_packet_full(const struct batadv_vis_info *info)
-{
- const struct batadv_vis_packet *packet;
- size_t num;
-
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
- num = BATADV_MAX_VIS_PACKET_SIZE / sizeof(struct batadv_vis_info_entry);
-
- if (num < packet->entries + 1)
- return true;
- return false;
-}
-
-/* generates a packet of own vis data,
- * returns 0 on success, -1 if no packet could be generated
- */
-static int batadv_generate_vis_packet(struct batadv_priv *bat_priv)
-{
- struct batadv_hashtable *hash = bat_priv->orig_hash;
- struct hlist_head *head;
- struct batadv_orig_node *orig_node;
- struct batadv_neigh_node *router;
- struct batadv_vis_info *info = bat_priv->vis.my_info;
- struct batadv_vis_packet *packet;
- struct batadv_vis_info_entry *entry;
- struct batadv_tt_common_entry *tt_common_entry;
- uint8_t *packet_pos;
- int best_tq = -1;
- uint32_t i;
-
- info->first_seen = jiffies;
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
- packet->vis_type = atomic_read(&bat_priv->vis_mode);
-
- memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN);
- packet->header.ttl = BATADV_TTL;
- packet->seqno = htonl(ntohl(packet->seqno) + 1);
- packet->entries = 0;
- packet->reserved = 0;
- skb_trim(info->skb_packet, sizeof(*packet));
-
- if (packet->vis_type == BATADV_VIS_TYPE_CLIENT_UPDATE) {
- best_tq = batadv_find_best_vis_server(bat_priv, info);
-
- if (best_tq < 0)
- return best_tq;
- }
-
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
- continue;
-
- if (!batadv_compare_eth(router->addr, orig_node->orig))
- goto next;
-
- if (router->if_incoming->if_status != BATADV_IF_ACTIVE)
- goto next;
-
- if (router->tq_avg < 1)
- goto next;
-
- /* fill one entry into buffer. */
- packet_pos = skb_put(info->skb_packet, sizeof(*entry));
- entry = (struct batadv_vis_info_entry *)packet_pos;
- memcpy(entry->src,
- router->if_incoming->net_dev->dev_addr,
- ETH_ALEN);
- memcpy(entry->dest, orig_node->orig, ETH_ALEN);
- entry->quality = router->tq_avg;
- packet->entries++;
-
-next:
- batadv_neigh_node_free_ref(router);
-
- if (batadv_vis_packet_full(info))
- goto unlock;
- }
- rcu_read_unlock();
- }
-
- hash = bat_priv->tt.local_hash;
-
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(tt_common_entry, head,
- hash_entry) {
- packet_pos = skb_put(info->skb_packet, sizeof(*entry));
- entry = (struct batadv_vis_info_entry *)packet_pos;
- memset(entry->src, 0, ETH_ALEN);
- memcpy(entry->dest, tt_common_entry->addr, ETH_ALEN);
- entry->quality = 0; /* 0 means TT */
- packet->entries++;
-
- if (batadv_vis_packet_full(info))
- goto unlock;
- }
- rcu_read_unlock();
- }
-
- return 0;
-
-unlock:
- rcu_read_unlock();
- return 0;
-}
-
-/* free old vis packets. Must be called with this vis_hash_lock
- * held
- */
-static void batadv_purge_vis_packets(struct batadv_priv *bat_priv)
-{
- uint32_t i;
- struct batadv_hashtable *hash = bat_priv->vis.hash;
- struct hlist_node *node_tmp;
- struct hlist_head *head;
- struct batadv_vis_info *info;
-
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
-
- hlist_for_each_entry_safe(info, node_tmp,
- head, hash_entry) {
- /* never purge own data. */
- if (info == bat_priv->vis.my_info)
- continue;
-
- if (batadv_has_timed_out(info->first_seen,
- BATADV_VIS_TIMEOUT)) {
- hlist_del(&info->hash_entry);
- batadv_send_list_del(info);
- kref_put(&info->refcount, batadv_free_info);
- }
- }
- }
-}
-
-static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_info *info)
-{
- struct batadv_hashtable *hash = bat_priv->orig_hash;
- struct hlist_head *head;
- struct batadv_orig_node *orig_node;
- struct batadv_vis_packet *packet;
- struct sk_buff *skb;
- uint32_t i, res;
-
-
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
- /* send to all routers in range. */
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- /* if it's a vis server and reachable, send it. */
- if (!(orig_node->flags & BATADV_VIS_SERVER))
- continue;
-
- /* don't send it if we already received the packet from
- * this node.
- */
- if (batadv_recv_list_is_in(bat_priv, &info->recv_list,
- orig_node->orig))
- continue;
-
- memcpy(packet->target_orig, orig_node->orig, ETH_ALEN);
- skb = skb_clone(info->skb_packet, GFP_ATOMIC);
- if (!skb)
- continue;
-
- res = batadv_send_skb_to_orig(skb, orig_node, NULL);
- if (res == NET_XMIT_DROP)
- kfree_skb(skb);
- }
- rcu_read_unlock();
- }
-}
-
-static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_info *info)
-{
- struct batadv_orig_node *orig_node;
- struct sk_buff *skb;
- struct batadv_vis_packet *packet;
-
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
-
- orig_node = batadv_orig_hash_find(bat_priv, packet->target_orig);
- if (!orig_node)
- goto out;
-
- skb = skb_clone(info->skb_packet, GFP_ATOMIC);
- if (!skb)
- goto out;
-
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
- kfree_skb(skb);
-
-out:
- if (orig_node)
- batadv_orig_node_free_ref(orig_node);
-}
-
-/* only send one vis packet. called from batadv_send_vis_packets() */
-static void batadv_send_vis_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_info *info)
-{
- struct batadv_hard_iface *primary_if;
- struct batadv_vis_packet *packet;
-
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- goto out;
-
- packet = (struct batadv_vis_packet *)info->skb_packet->data;
- if (packet->header.ttl < 2) {
- pr_debug("Error - can't send vis packet: ttl exceeded\n");
- goto out;
- }
-
- memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
- packet->header.ttl--;
-
- if (is_broadcast_ether_addr(packet->target_orig))
- batadv_broadcast_vis_packet(bat_priv, info);
- else
- batadv_unicast_vis_packet(bat_priv, info);
- packet->header.ttl++; /* restore TTL */
-
-out:
- if (primary_if)
- batadv_hardif_free_ref(primary_if);
-}
-
-/* called from timer; send (and maybe generate) vis packet. */
-static void batadv_send_vis_packets(struct work_struct *work)
-{
- struct delayed_work *delayed_work;
- struct batadv_priv *bat_priv;
- struct batadv_priv_vis *priv_vis;
- struct batadv_vis_info *info;
-
- delayed_work = container_of(work, struct delayed_work, work);
- priv_vis = container_of(delayed_work, struct batadv_priv_vis, work);
- bat_priv = container_of(priv_vis, struct batadv_priv, vis);
- spin_lock_bh(&bat_priv->vis.hash_lock);
- batadv_purge_vis_packets(bat_priv);
-
- if (batadv_generate_vis_packet(bat_priv) == 0) {
- /* schedule if generation was successful */
- batadv_send_list_add(bat_priv, bat_priv->vis.my_info);
- }
-
- while (!list_empty(&bat_priv->vis.send_list)) {
- info = list_first_entry(&bat_priv->vis.send_list,
- typeof(*info), send_list);
-
- kref_get(&info->refcount);
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-
- batadv_send_vis_packet(bat_priv, info);
-
- spin_lock_bh(&bat_priv->vis.hash_lock);
- batadv_send_list_del(info);
- kref_put(&info->refcount, batadv_free_info);
- }
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-
- queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work,
- msecs_to_jiffies(BATADV_VIS_INTERVAL));
-}
-
-/* init the vis server. this may only be called when if_list is already
- * initialized (e.g. bat0 is initialized, interfaces have been added)
- */
-int batadv_vis_init(struct batadv_priv *bat_priv)
-{
- struct batadv_vis_packet *packet;
- int hash_added;
- unsigned int len;
- unsigned long first_seen;
- struct sk_buff *tmp_skb;
-
- if (bat_priv->vis.hash)
- return 0;
-
- spin_lock_bh(&bat_priv->vis.hash_lock);
-
- bat_priv->vis.hash = batadv_hash_new(256);
- if (!bat_priv->vis.hash) {
- pr_err("Can't initialize vis_hash\n");
- goto err;
- }
-
- batadv_hash_set_lock_class(bat_priv->vis.hash,
- &batadv_vis_hash_lock_class_key);
-
- bat_priv->vis.my_info = kmalloc(BATADV_MAX_VIS_PACKET_SIZE, GFP_ATOMIC);
- if (!bat_priv->vis.my_info)
- goto err;
-
- len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN;
- bat_priv->vis.my_info->skb_packet = netdev_alloc_skb_ip_align(NULL,
- len);
- if (!bat_priv->vis.my_info->skb_packet)
- goto free_info;
-
- bat_priv->vis.my_info->skb_packet->priority = TC_PRIO_CONTROL;
- skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN);
- tmp_skb = bat_priv->vis.my_info->skb_packet;
- packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet));
-
- /* prefill the vis info */
- first_seen = jiffies - msecs_to_jiffies(BATADV_VIS_INTERVAL);
- bat_priv->vis.my_info->first_seen = first_seen;
- INIT_LIST_HEAD(&bat_priv->vis.my_info->recv_list);
- INIT_LIST_HEAD(&bat_priv->vis.my_info->send_list);
- kref_init(&bat_priv->vis.my_info->refcount);
- bat_priv->vis.my_info->bat_priv = bat_priv;
- packet->header.version = BATADV_COMPAT_VERSION;
- packet->header.packet_type = BATADV_VIS;
- packet->header.ttl = BATADV_TTL;
- packet->seqno = 0;
- packet->reserved = 0;
- packet->entries = 0;
-
- INIT_LIST_HEAD(&bat_priv->vis.send_list);
-
- hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp,
- batadv_vis_info_choose,
- bat_priv->vis.my_info,
- &bat_priv->vis.my_info->hash_entry);
- if (hash_added != 0) {
- pr_err("Can't add own vis packet into hash\n");
- /* not in hash, need to remove it manually. */
- kref_put(&bat_priv->vis.my_info->refcount, batadv_free_info);
- goto err;
- }
-
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-
- INIT_DELAYED_WORK(&bat_priv->vis.work, batadv_send_vis_packets);
- queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work,
- msecs_to_jiffies(BATADV_VIS_INTERVAL));
-
- return 0;
-
-free_info:
- kfree(bat_priv->vis.my_info);
- bat_priv->vis.my_info = NULL;
-err:
- spin_unlock_bh(&bat_priv->vis.hash_lock);
- batadv_vis_quit(bat_priv);
- return -ENOMEM;
-}
-
-/* Decrease the reference count on a hash item info */
-static void batadv_free_info_ref(struct hlist_node *node, void *arg)
-{
- struct batadv_vis_info *info;
-
- info = container_of(node, struct batadv_vis_info, hash_entry);
- batadv_send_list_del(info);
- kref_put(&info->refcount, batadv_free_info);
-}
-
-/* shutdown vis-server */
-void batadv_vis_quit(struct batadv_priv *bat_priv)
-{
- if (!bat_priv->vis.hash)
- return;
-
- cancel_delayed_work_sync(&bat_priv->vis.work);
-
- spin_lock_bh(&bat_priv->vis.hash_lock);
- /* properly remove, kill timers ... */
- batadv_hash_delete(bat_priv->vis.hash, batadv_free_info_ref, NULL);
- bat_priv->vis.hash = NULL;
- bat_priv->vis.my_info = NULL;
- spin_unlock_bh(&bat_priv->vis.hash_lock);
-}
diff --git a/net/batman-adv/vis.h b/net/batman-adv/vis.h
deleted file mode 100644
index ad92b0e3c230..000000000000
--- a/net/batman-adv/vis.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors:
- *
- * Simon Wunderlich, Marek Lindner
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
-
-#ifndef _NET_BATMAN_ADV_VIS_H_
-#define _NET_BATMAN_ADV_VIS_H_
-
-/* timeout of vis packets in milliseconds */
-#define BATADV_VIS_TIMEOUT 200000
-
-int batadv_vis_seq_print_text(struct seq_file *seq, void *offset);
-void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_packet *vis_packet,
- int vis_info_len);
-void batadv_receive_client_update_packet(struct batadv_priv *bat_priv,
- struct batadv_vis_packet *vis_packet,
- int vis_info_len);
-int batadv_vis_init(struct batadv_priv *bat_priv);
-void batadv_vis_quit(struct batadv_priv *bat_priv);
-
-#endif /* _NET_BATMAN_ADV_VIS_H_ */