The following commit has been merged in the merge/master branch: commit 93f9991da9bd6dd42c0581292fafbeade27b4926 Merge: 17f2aaffd8ab1fd535c105a568c7c187c6ce7de5 c72c2785cdee4378ab0f4e241e717a224bf00c1e Author: Antonio Quartulli antonio@meshcoding.com Date: Sat Sep 21 13:02:47 2013 +0200
Merge remote-tracking branch 'pkg/next' into merge/master
Conflicts: net/batman-adv/Makefile.kbuild net/batman-adv/README net/batman-adv/compat.c net/batman-adv/compat.h net/batman-adv/main.h net/batman-adv/sysfs-class-net-mesh
diff --combined Documentation/ABI/testing/sysfs-class-net-mesh index bdcd8b4,ee4a479..ee4a479 --- a/Documentation/ABI/testing/sysfs-class-net-mesh +++ b/Documentation/ABI/testing/sysfs-class-net-mesh @@@ -6,13 -6,14 +6,14 @@@ Description Indicates whether the batman protocol messages of the mesh <mesh_iface> shall be aggregated or not.
- What: /sys/class/net/<mesh_iface>/mesh/ap_isolation + What: /sys/class/net/<mesh_iface>/mesh/<vlan_subdir>/ap_isolation Date: May 2011 Contact: Antonio Quartulli ordex@autistici.org Description: Indicates whether the data traffic going from a wireless client to another wireless client will be - silently dropped. + silently dropped. <vlan_subdir> is empty when referring + to the untagged lan.
What: /sys/class/net/<mesh_iface>/mesh/bonding Date: June 2010 @@@ -88,14 -89,3 +89,3 @@@ Contact: Marek Lindner <lindner_ Description: Defines the routing procotol this mesh instance uses to find the optimal paths through the mesh. - - What: /sys/class/net/<mesh_iface>/mesh/vis_mode - Date: May 2010 - Contact: Marek Lindner lindner_marek@yahoo.de - Description: - Each batman node only maintains information about its - own local neighborhood, therefore generating graphs - showing the topology of the entire mesh is not easily - feasible without having a central instance to collect - the local topologies from all nodes. This file allows - to activate the collecting (server) mode. diff --combined Documentation/networking/batman-adv.txt index c1d8204,897d1f4..897d1f4 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@@ -69,8 -69,7 +69,7 @@@ folder # aggregated_ogms gw_bandwidth log_level # ap_isolation gw_mode orig_interval # bonding gw_sel_class routing_algo - # bridge_loop_avoidance hop_penalty vis_mode - # fragmentation + # bridge_loop_avoidance hop_penalty fragmentation
There is a special folder for debugging information: @@@ -78,7 -77,7 +77,7 @@@ # ls /sys/kernel/debug/batman_adv/bat0/ # bla_backbone_table log transtable_global # bla_claim_table originators transtable_local - # gateways socket vis_data + # gateways socket
Some of the files contain all sort of status information regard- ing the mesh network. For example, you can view the table of @@@ -127,51 -126,6 +126,6 @@@ ously assigned to interfaces now used b # ifconfig eth0 0.0.0.0
- VISUALIZATION - ------------- - - If you want topology visualization, at least one mesh node must - be configured as VIS-server: - - # echo "server" > /sys/class/net/bat0/mesh/vis_mode - - Each node is either configured as "server" or as "client" (de- - fault: "client"). Clients send their topology data to the server - next to them, and server synchronize with other servers. If there - is no server configured (default) within the mesh, no topology - information will be transmitted. With these "synchronizing - servers", there can be 1 or more vis servers sharing the same (or - at least very similar) data. - - When configured as server, you can get a topology snapshot of - your mesh: - - # cat /sys/kernel/debug/batman_adv/bat0/vis_data - - This raw output is intended to be easily parsable and convertable - with other tools. Have a look at the batctl README if you want a - vis output in dot or json format for instance and how those out- - puts could then be visualised in an image. - - The raw format consists of comma separated values per entry where - each entry is giving information about a certain source inter- - face. Each entry can/has to have the following values: - -> "mac" - mac address of an originator's source interface - (each line begins with it) - -> "TQ mac value" - src mac's link quality towards mac address - of a neighbor originator's interface which - is being used for routing - -> "TT mac" - TT announced by source mac - -> "PRIMARY" - this is a primary interface - -> "SEC mac" - secondary mac address of source - (requires preceding PRIMARY) - - The TQ value has a range from 4 to 255 with 255 being the best. - The TT entries are showing which hosts are connected to the mesh - via bat0 or being bridged into the mesh network. The PRIMARY/SEC - values are only applied on primary interfaces - - LOGGING/DEBUGGING -----------------
diff --combined net/batman-adv/Makefile index 489bb36,4f4aabb..4f4aabb --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@@ -24,6 -24,7 +24,7 @@@ batman-adv-y += bitarray. 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 +38,3 @@@ batman-adv-y += send. 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 --combined net/batman-adv/bat_iv_ogm.c index 0a8a80c,a2b480a..a2b480a --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@@ -87,22 -87,198 +87,198 @@@ static uint8_t batadv_ring_buffer_avg(c 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 +311,8 @@@ static int batadv_iv_ogm_iface_enable(s 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 +382,12 @@@ static uint8_t batadv_hop_penalty(uint8
/* 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 +415,7 @@@ static void batadv_iv_ogm_send_to_if(st
/* 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 +431,18 @@@ 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 +776,7 @@@ static void batadv_iv_ogm_forward(struc 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 +796,7 @@@ 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 +817,7 @@@ 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 +837,22 @@@ batadv_iv_ogm_slide_own_bcast_window(st 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 +865,29 @@@ static void batadv_iv_ogm_schedule(stru 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 +933,18 @@@ batadv_iv_ogm_orig_update(struct batadv 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 +961,55 @@@
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 +1031,7 @@@ static int batadv_iv_ogm_calc_tq(struc 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 +1069,11 @@@ 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 +1155,13 @@@ batadv_iv_ogm_update_seqnos(const struc 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 +1176,7 @@@ 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 +1192,13 @@@ }
/* 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 +1210,7 @@@ }
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 +1222,7 @@@ static void batadv_iv_ogm_process(cons { 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 +1262,11 @@@ 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 +1306,8 @@@ 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 +1321,15 @@@ 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 +1352,7 @@@ 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 +1373,12 @@@ }
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 +1394,16 @@@ 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 +1492,9 @@@ static int batadv_iv_ogm_receive(struc 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 +1517,14 @@@
/* 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 +1534,106 @@@ 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 +1642,12 @@@ .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 --combined net/batman-adv/bridge_loop_avoidance.c index 264de88,28eb5e6..28eb5e6 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@@ -411,10 -411,10 +411,10 @@@ batadv_bla_get_backbone_gw(struct batad 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 +858,28 @@@ static int batadv_bla_process_claim(str 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 +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 +1330,26 @@@ 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 +1366,8 @@@ 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 +1376,7 @@@ 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 --combined net/batman-adv/bridge_loop_avoidance.h index 4b102e7,da173e7..da173e7 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@@ -30,7 -30,8 +30,8 @@@ int batadv_bla_is_backbone_gw(struct sk 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 +75,11 @@@ static inline int batadv_bla_backbone_t 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 --combined net/batman-adv/debugfs.c index f186a55,049a7a2..049a7a2 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@@ -28,7 -28,6 +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 +299,6 @@@ static int batadv_transtable_local_open 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 +349,6 @@@ static BATADV_DEBUGINFO(dat_cache, S_IR #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 +365,6 @@@ static struct batadv_debuginfo *batadv_ &batadv_debuginfo_dat_cache, #endif &batadv_debuginfo_transtable_local, - &batadv_debuginfo_vis_data, #ifdef CONFIG_BATMAN_ADV_NC &batadv_debuginfo_nc_nodes, #endif diff --combined net/batman-adv/distributed-arp-table.c index 06345d4,6c8c393..6c8c393 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@@ -19,6 -19,7 +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 +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 +206,11 @@@ static __be32 batadv_arp_ip_dst(struct */ 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 +224,26 @@@ * 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 +267,24 @@@ * @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 +293,13 @@@ 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 +308,8 @@@ 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 +423,10 @@@ static bool batadv_is_orig_node_eligibl 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 +596,9 @@@ static bool batadv_dat_send_data(struc 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 +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 +718,10 @@@ int batadv_dat_init(struct batadv_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 +731,9 @@@ */ 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 +761,8 @@@ int batadv_dat_cache_seq_print_text(str 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 +775,9 @@@ 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 +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 +907,31 @@@ bool batadv_dat_snoop_outgoing_arp_requ 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 +941,8 @@@ * 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 +953,15 @@@ 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 +996,14 @@@ bool batadv_dat_snoop_incoming_arp_requ 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 +1015,9 @@@ 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 +1028,22 @@@ 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 +1066,28 @@@ void batadv_dat_snoop_outgoing_arp_repl 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 +1109,13 @@@ bool batadv_dat_snoop_incoming_arp_repl __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 +1131,13 @@@ /* 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 +1160,8 @@@ bool batadv_dat_drop_broadcast_packet(s __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 +1172,14 @@@ 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 --combined net/batman-adv/distributed-arp-table.h index 125c8c6,60d853b..60d853b --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@@ -29,6 -29,7 +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 +99,10 @@@ static inline void batadv_dat_inc_count
#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 --combined net/batman-adv/fragmentation.c index 0000000,271d321..271d321 mode 000000,100644..100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@@ -1,0 -1,491 +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 --combined net/batman-adv/fragmentation.h index 0000000,ca029e2..ca029e2 mode 000000,100644..100644 --- a/net/batman-adv/fragmentation.h +++ b/net/batman-adv/fragmentation.h @@@ -1,0 -1,50 +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 --combined net/batman-adv/gateway_client.c index 1ce4b87,7299936..7299936 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@@ -118,7 -118,6 +118,6 @@@ batadv_gw_get_best_gw_node(struct batad 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 +137,13 @@@ 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 +221,6 @@@ void batadv_gw_election(struct batadv_p 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 +251,22 @@@ 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 +305,8 @@@ void batadv_gw_check_election(struct ba 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 +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 +363,116 @@@ 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 +517,7 @@@ static int batadv_write_buffer_text(str { 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 +525,15 @@@
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 +555,7 @@@ int batadv_gw_client_seq_print_text(str 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 +650,29 @@@ bool batadv_gw_is_dhcp_target(struct sk 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 +683,7 @@@ 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 +710,44 @@@ *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 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 +755,12 @@@
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 +792,7 @@@ 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 +803,7 @@@ 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 +811,8 @@@ 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 --combined net/batman-adv/gateway_client.h index ceef4eb,d95c2d2..d95c2d2 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@@ -29,7 -29,7 +29,7 @@@ void batadv_gw_check_election(struct ba 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 --combined net/batman-adv/gateway_common.c index 84bb2b1,07fd877..07fd877 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@@ -21,62 -21,20 +21,20 @@@ #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; + int ret, bw_unit_type = BATADV_BW_UNIT_KBIT; char *slash_ptr, *tmp_ptr; long ldown, lup;
@@@ -88,10 -46,10 +46,10 @@@ 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 -61,28 +61,28 @@@ 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 -94,152 +94,152 @@@ 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 + * + * Return 0 on success or negative error number in case of failure. + */ + int 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); + return 0; + } + + /** + * 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 --combined net/batman-adv/gateway_common.h index 509b2bf,f18e8b7..f18e8b7 --- a/net/batman-adv/gateway_common.h +++ b/net/batman-adv/gateway_common.h @@@ -26,12 -26,24 +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_types { + 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); + int 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 --combined net/batman-adv/hard-interface.c index e884dc7,0000000..1ba8a55 mode 100644,000000..100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@@ -1,717 -1,0 +1,721 @@@ +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: + * + * Marek Lindner, 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 "distributed-arp-table.h" +#include "hard-interface.h" +#include "soft-interface.h" +#include "send.h" +#include "translation-table.h" +#include "routing.h" +#include "sysfs.h" +#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> + +void batadv_hardif_free_rcu(struct rcu_head *rcu) +{ + struct batadv_hard_iface *hard_iface; + + hard_iface = container_of(rcu, struct batadv_hard_iface, rcu); + dev_put(hard_iface->net_dev); + kfree(hard_iface); +} + +struct batadv_hard_iface * +batadv_hardif_get_by_netdev(const struct net_device *net_dev) +{ + struct batadv_hard_iface *hard_iface; + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->net_dev == net_dev && + atomic_inc_not_zero(&hard_iface->refcount)) + goto out; + } + + hard_iface = NULL; + +out: + rcu_read_unlock(); + return hard_iface; +} + +/** + * batadv_is_on_batman_iface - check if a device is a batman iface descendant + * @net_dev: the device to check + * + * If the user creates any virtual device on top of a batman-adv interface, it + * is important to prevent this new interface to be used to create a new mesh + * network (this behaviour would lead to a batman-over-batman configuration). + * This function recursively checks all the fathers of the device passed as + * argument looking for a batman-adv soft interface. + * + * Returns true if the device is descendant of a batman-adv mesh interface (or + * if it is a batman-adv interface itself), false otherwise + */ +static bool batadv_is_on_batman_iface(const struct net_device *net_dev) +{ + struct net_device *parent_dev; + bool ret; + + /* check if this is a batman-adv mesh interface */ + if (batadv_softif_is_valid(net_dev)) + return true; + + /* no more parents..stop recursion */ + if (net_dev->iflink == net_dev->ifindex) + return false; + + /* recurse over the parent device */ + parent_dev = dev_get_by_index(&init_net, net_dev->iflink); + /* if we got a NULL parent_dev there is something broken.. */ + if (WARN(!parent_dev, "Cannot find parent device")) + return false; + + ret = batadv_is_on_batman_iface(parent_dev); + + if (parent_dev) + dev_put(parent_dev); + return ret; +} + +static int batadv_is_valid_iface(const struct net_device *net_dev) +{ + if (net_dev->flags & IFF_LOOPBACK) + return 0; + + if (net_dev->type != ARPHRD_ETHER) + return 0; + + if (net_dev->addr_len != ETH_ALEN) + return 0; + + /* no batman over batman */ + if (batadv_is_on_batman_iface(net_dev)) + return 0; + + return 1; +} + +/** + * batadv_is_wifi_netdev - check if the given net_device struct is a wifi + * interface + * @net_device: the device to check + * + * 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) +{ +#ifdef CONFIG_WIRELESS_EXT + /* pre-cfg80211 drivers have to implement WEXT, so it is possible to + * check for wireless_handlers != NULL + */ + if (net_device->wireless_handlers) + return true; +#endif + + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + return true; + + 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) +{ + struct batadv_hard_iface *hard_iface; + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->soft_iface != soft_iface) + continue; + + if (hard_iface->if_status == BATADV_IF_ACTIVE && + atomic_inc_not_zero(&hard_iface->refcount)) + goto out; + } + + hard_iface = NULL; + +out: + rcu_read_unlock(); + return hard_iface; +} + +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) + batadv_hardif_free_ref(primary_if); +} + +static void batadv_primary_if_select(struct batadv_priv *bat_priv, + struct batadv_hard_iface *new_hard_iface) +{ + struct batadv_hard_iface *curr_hard_iface; + + ASSERT_RTNL(); + + if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount)) + new_hard_iface = NULL; + + curr_hard_iface = rcu_dereference_protected(bat_priv->primary_if, 1); + rcu_assign_pointer(bat_priv->primary_if, new_hard_iface); + + if (!new_hard_iface) + goto out; + + bat_priv->bat_algo_ops->bat_primary_iface_set(new_hard_iface); + batadv_primary_if_update_addr(bat_priv, curr_hard_iface); + +out: + if (curr_hard_iface) + batadv_hardif_free_ref(curr_hard_iface); +} + +static bool +batadv_hardif_is_iface_up(const struct batadv_hard_iface *hard_iface) +{ + if (hard_iface->net_dev->flags & IFF_UP) + return true; + + return false; +} + +static void batadv_check_known_mac_addr(const struct net_device *net_dev) +{ + const struct batadv_hard_iface *hard_iface; + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if ((hard_iface->if_status != BATADV_IF_ACTIVE) && + (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)) + continue; + + if (hard_iface->net_dev == net_dev) + continue; + + if (!batadv_compare_eth(hard_iface->net_dev->dev_addr, + net_dev->dev_addr)) + continue; + + pr_warn("The newly added mac address (%pM) already exists on: %s\n", + net_dev->dev_addr, hard_iface->net_dev->name); + pr_warn("It is strongly recommended to keep mac addresses unique to avoid problems!\n"); + } + rcu_read_unlock(); +} + +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) && + (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)) + continue; + + 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 +batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv; + struct batadv_hard_iface *primary_if = NULL; + + if (hard_iface->if_status != BATADV_IF_INACTIVE) + goto out; + + bat_priv = netdev_priv(hard_iface->soft_iface); + + bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface); + hard_iface->if_status = BATADV_IF_TO_BE_ACTIVATED; + + /* the first active interface becomes our primary interface or + * the next active interface after the old primary interface was removed + */ + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + batadv_primary_if_select(bat_priv, hard_iface); + + batadv_info(hard_iface->soft_iface, "Interface activated: %s\n", + hard_iface->net_dev->name); + + batadv_update_min_mtu(hard_iface->soft_iface); + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); +} + +static void +batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface) +{ + if ((hard_iface->if_status != BATADV_IF_ACTIVE) && + (hard_iface->if_status != BATADV_IF_TO_BE_ACTIVATED)) + return; + + hard_iface->if_status = BATADV_IF_INACTIVE; + + batadv_info(hard_iface->soft_iface, "Interface deactivated: %s\n", + hard_iface->net_dev->name); + + batadv_update_min_mtu(hard_iface->soft_iface); +} + +/** + * batadv_master_del_slave - remove hard_iface from the current master interface + * @slave: the interface enslaved in another master + * @master: the master from which slave has to be removed + * + * Invoke ndo_del_slave on master passing slave as argument. In this way slave + * is free'd and master can correctly change its internal state. + * Return 0 on success, a negative value representing the error otherwise + */ +static int batadv_master_del_slave(struct batadv_hard_iface *slave, + struct net_device *master) +{ + int ret; + + if (!master) + return 0; + + ret = -EBUSY; + if (master->netdev_ops->ndo_del_slave) + ret = master->netdev_ops->ndo_del_slave(master, slave->net_dev); + + return ret; +} + +int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, + const char *iface_name) +{ + 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) + goto out; + + if (!atomic_inc_not_zero(&hard_iface->refcount)) + goto out; + + soft_iface = dev_get_by_name(&init_net, iface_name); + + if (!soft_iface) { + soft_iface = batadv_softif_create(iface_name); + + if (!soft_iface) { + ret = -ENOMEM; + goto err; + } + + /* dev_get_by_name() increases the reference counter for us */ + dev_hold(soft_iface); + } + + if (!batadv_softif_is_valid(soft_iface)) { + pr_err("Can't create batman mesh interface %s: already exists as regular interface\n", + soft_iface->name); + ret = -EINVAL; + goto err_dev; + } + + /* check if the interface is enslaved in another virtual one and + * in that case unlink it first + */ + master = netdev_master_upper_dev_get(hard_iface->net_dev); + ret = batadv_master_del_slave(hard_iface, master); + if (ret) + goto err_dev; + + hard_iface->soft_iface = soft_iface; + bat_priv = netdev_priv(hard_iface->soft_iface); + + ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface); + if (ret) + goto err_dev; + + ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface); + if (ret < 0) + goto err_upper; + + hard_iface->if_num = bat_priv->num_ifaces; + bat_priv->num_ifaces++; + hard_iface->if_status = BATADV_IF_INACTIVE; + ret = batadv_orig_hash_add_if(hard_iface, bat_priv->num_ifaces); + if (ret < 0) { + bat_priv->bat_algo_ops->bat_iface_disable(hard_iface); + bat_priv->num_ifaces--; + hard_iface->if_status = BATADV_IF_NOT_IN_USE; + goto err_upper; + } + + hard_iface->batman_adv_ptype.type = ethertype; + hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv; + 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); + else + batadv_err(hard_iface->soft_iface, + "Not using interface %s (retrying later): interface not active\n", + hard_iface->net_dev->name); + + /* begin scheduling originator messages on that interface */ + batadv_schedule_bat_ogm(hard_iface); + +out: + return 0; + +err_upper: + netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface); +err_dev: + hard_iface->soft_iface = NULL; + dev_put(soft_iface); +err: + batadv_hardif_free_ref(hard_iface); + return ret; +} + +void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface, + enum batadv_hard_if_cleanup autodel) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct batadv_hard_iface *primary_if = NULL; + + if (hard_iface->if_status == BATADV_IF_ACTIVE) + batadv_hardif_deactivate_interface(hard_iface); + + if (hard_iface->if_status != BATADV_IF_INACTIVE) + goto out; + + batadv_info(hard_iface->soft_iface, "Removing interface: %s\n", + hard_iface->net_dev->name); + dev_remove_pack(&hard_iface->batman_adv_ptype); + + bat_priv->num_ifaces--; + batadv_orig_hash_del_if(hard_iface, bat_priv->num_ifaces); + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (hard_iface == primary_if) { + struct batadv_hard_iface *new_if; + + new_if = batadv_hardif_get_active(hard_iface->soft_iface); + batadv_primary_if_select(bat_priv, new_if); + + if (new_if) + batadv_hardif_free_ref(new_if); + } + + bat_priv->bat_algo_ops->bat_iface_disable(hard_iface); + hard_iface->if_status = BATADV_IF_NOT_IN_USE; + + /* delete all references to this hard_iface */ + batadv_purge_orig_ref(bat_priv); + batadv_purge_outstanding_packets(bat_priv, hard_iface); + dev_put(hard_iface->soft_iface); + + /* nobody uses this interface anymore */ + 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; + batadv_hardif_free_ref(hard_iface); + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); +} + +/** + * batadv_hardif_remove_interface_finish - cleans up the remains of a hardif + * @work: work queue item + * + * Free the parts of the hard interface which can not be removed under + * rtnl lock (to prevent deadlock situations). + */ +static void batadv_hardif_remove_interface_finish(struct work_struct *work) +{ + struct batadv_hard_iface *hard_iface; + + hard_iface = container_of(work, struct batadv_hard_iface, + cleanup_work); + + batadv_sysfs_del_hardif(&hard_iface->hardif_obj); + batadv_hardif_free_ref(hard_iface); +} + +static struct batadv_hard_iface * +batadv_hardif_add_interface(struct net_device *net_dev) +{ + struct batadv_hard_iface *hard_iface; + int ret; + + ASSERT_RTNL(); + + ret = batadv_is_valid_iface(net_dev); + if (ret != 1) + goto out; + + dev_hold(net_dev); + + hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC); + if (!hard_iface) + goto release_dev; + + ret = batadv_sysfs_add_hardif(&hard_iface->hardif_obj, net_dev); + if (ret) + goto free_if; + + hard_iface->if_num = -1; + hard_iface->net_dev = net_dev; + hard_iface->soft_iface = NULL; + hard_iface->if_status = BATADV_IF_NOT_IN_USE; + INIT_LIST_HEAD(&hard_iface->list); + INIT_WORK(&hard_iface->cleanup_work, + batadv_hardif_remove_interface_finish); + + hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; + if (batadv_is_wifi_netdev(net_dev)) + hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; + + /* extra reference for return */ + atomic_set(&hard_iface->refcount, 2); + + batadv_check_known_mac_addr(hard_iface->net_dev); + list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list); + + return hard_iface; + +free_if: + kfree(hard_iface); +release_dev: + dev_put(net_dev); +out: + return NULL; +} + +static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface) +{ + ASSERT_RTNL(); + + /* first deactivate interface */ + if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) + batadv_hardif_disable_interface(hard_iface, + BATADV_IF_CLEANUP_AUTO); + + if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) + return; + + hard_iface->if_status = BATADV_IF_TO_BE_REMOVED; + queue_work(batadv_event_workqueue, &hard_iface->cleanup_work); +} + +void batadv_hardif_remove_interfaces(void) +{ + struct batadv_hard_iface *hard_iface, *hard_iface_tmp; + + rtnl_lock(); + list_for_each_entry_safe(hard_iface, hard_iface_tmp, + &batadv_hardif_list, list) { + list_del_rcu(&hard_iface->list); + batadv_hardif_remove_interface(hard_iface); + } + rtnl_unlock(); +} + +static int batadv_hard_if_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); + struct batadv_hard_iface *hard_iface; + struct batadv_hard_iface *primary_if = NULL; + struct batadv_priv *bat_priv; + + 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; + } + + hard_iface = batadv_hardif_get_by_netdev(net_dev); + if (!hard_iface && event == NETDEV_REGISTER) + hard_iface = batadv_hardif_add_interface(net_dev); + + if (!hard_iface) + goto out; + + switch (event) { + case NETDEV_UP: + batadv_hardif_activate_interface(hard_iface); + break; + case NETDEV_GOING_DOWN: + case NETDEV_DOWN: + batadv_hardif_deactivate_interface(hard_iface); + break; + case NETDEV_UNREGISTER: + list_del_rcu(&hard_iface->list); + + batadv_hardif_remove_interface(hard_iface); + break; + case NETDEV_CHANGEMTU: + if (hard_iface->soft_iface) + batadv_update_min_mtu(hard_iface->soft_iface); + break; + case NETDEV_CHANGEADDR: + if (hard_iface->if_status == BATADV_IF_NOT_IN_USE) + goto hardif_put; + + batadv_check_known_mac_addr(hard_iface->net_dev); + + bat_priv = netdev_priv(hard_iface->soft_iface); + bat_priv->bat_algo_ops->bat_iface_update_mac(hard_iface); + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto hardif_put; + + if (hard_iface == primary_if) + batadv_primary_if_update_addr(bat_priv, NULL); + break; + default: + break; + } + +hardif_put: + batadv_hardif_free_ref(hard_iface); +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return NOTIFY_DONE; +} + +struct notifier_block batadv_hard_if_notifier = { + .notifier_call = batadv_hard_if_event, +}; diff --combined net/batman-adv/icmp_socket.c index 5a99bb4,82ac647..82ac647 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@@ -192,25 -192,25 +192,25 @@@ static ssize_t batadv_socket_write(stru goto free_skb; }
- if (icmp_packet->header.packet_type != BATADV_ICMP) { + if (icmp_packet->icmph.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) { + if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n"); len = -EINVAL; goto free_skb; }
- icmp_packet->uid = socket_client->index; + icmp_packet->icmph.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; + if (icmp_packet->icmph.header.version != BATADV_COMPAT_VERSION) { + icmp_packet->icmph.msg_type = BATADV_PARAMETER_PROBLEM; + icmp_packet->icmph.header.version = BATADV_COMPAT_VERSION; batadv_socket_add_packet(socket_client, icmp_packet, packet_len); goto free_skb; @@@ -219,7 -219,7 +219,7 @@@ if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dst_unreach;
- orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst); + orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.dst); if (!orig_node) goto dst_unreach;
@@@ -233,7 -233,7 +233,7 @@@ if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) goto dst_unreach;
- memcpy(icmp_packet->orig, + memcpy(icmp_packet->icmph.orig, primary_if->net_dev->dev_addr, ETH_ALEN);
if (packet_len == sizeof(struct batadv_icmp_packet_rr)) @@@ -244,7 -244,7 +244,7 @@@ goto out;
dst_unreach: - icmp_packet->msg_type = BATADV_DESTINATION_UNREACHABLE; + icmp_packet->icmph.msg_type = BATADV_DESTINATION_UNREACHABLE; batadv_socket_add_packet(socket_client, icmp_packet, packet_len); free_skb: kfree_skb(skb); @@@ -318,7 -318,7 +318,7 @@@ static void batadv_socket_add_packet(st /* 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[icmp_packet->icmph.uid]) { spin_unlock_bh(&socket_client->lock); kfree(socket_packet); return; @@@ -347,7 -347,7 +347,7 @@@ void batadv_socket_receive_packet(struc { struct batadv_socket_client *hash;
- hash = batadv_socket_client_hash[icmp_packet->uid]; + hash = batadv_socket_client_hash[icmp_packet->icmph.uid]; if (hash) batadv_socket_add_packet(hash, icmp_packet, icmp_len); } diff --combined net/batman-adv/main.c index c72d1bc,5c5e64b..5c5e64b --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@@ -36,10 -36,11 +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, @@@ -108,9 -109,11 +109,11 @@@ int batadv_mesh_init(struct net_device 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); @@@ -118,6 -121,9 +121,9 @@@ 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) @@@ -127,13 -133,6 +133,6 @@@ 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; @@@ -146,6 -145,10 +145,10 @@@ if (ret < 0) goto err;
+ ret = batadv_gw_init(bat_priv); + if (ret < 0) + goto err; + atomic_set(&bat_priv->gw.reselect, 0); atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
@@@ -164,8 -167,6 +167,6 @@@ void batadv_mesh_free(struct net_devic
batadv_purge_outstanding_packets(bat_priv, NULL);
- batadv_vis_quit(bat_priv); - batadv_gw_node_purge(bat_priv); batadv_nc_free(bat_priv); batadv_dat_free(bat_priv); @@@ -184,6 -185,8 +185,8 @@@ */ batadv_originator_free(bat_priv);
+ batadv_gw_free(bat_priv); + free_percpu(bat_priv->bat_counters); bat_priv->bat_counters = NULL;
@@@ -254,6 -257,31 +257,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 @@@ -391,22 -419,31 +419,31 @@@ static void batadv_recv_handler_init(vo 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 @@@ -414,7 -451,12 +451,12 @@@ batadv_recv_handler_register(uint8_t pa 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; @@@ -460,7 -502,9 +502,9 @@@ int batadv_algo_register(struct batadv_ !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; @@@ -535,6 -579,601 +579,601 @@@ __be32 batadv_skb_crc32(struct sk_buff 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 --combined net/batman-adv/main.h index 2467552,0000000..cfbc49b mode 100644,000000..100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@@ -1,329 -1,0 +1,373 @@@ +/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: + * + * Marek Lindner, 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 + */ + +#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_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" +#endif + +/* B.A.T.M.A.N. parameters */ + +#define BATADV_TQ_MAX_VALUE 255 +#define BATADV_JITTER 20 + +/* Time To Live of broadcast messages */ +#define BATADV_TTL 50 + +/* purge originators after time in seconds if no valid packet comes in + * -> TODO: check influence on BATADV_TQ_LOCAL_WINDOW_SIZE + */ +#define BATADV_PURGE_TIMEOUT 200000 /* 200 seconds */ +#define BATADV_TT_LOCAL_TIMEOUT 600000 /* in milliseconds */ +#define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */ +#define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */ +#define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */ +#define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */ +#define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ +/* sliding packet range of received originator messages in sequence numbers + * (should be a multiple of our word size) + */ +#define BATADV_TQ_LOCAL_WINDOW_SIZE 64 +/* milliseconds we have to keep pending tt_req */ +#define BATADV_TT_REQUEST_TIMEOUT 3000 + +#define BATADV_TQ_GLOBAL_WINDOW_SIZE 5 +#define BATADV_TQ_LOCAL_BIDRECT_SEND_MINIMUM 1 +#define BATADV_TQ_LOCAL_BIDRECT_RECV_MINIMUM 1 +#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1 + +/* number of OGMs sent with the last tt diff */ +#define BATADV_TT_OGM_APPEND_MAX 3 + +/* Time in which a client can roam at most ROAMING_MAX_COUNT times in + * milliseconds + */ +#define BATADV_ROAMING_MAX_TIME 20000 +#define BATADV_ROAMING_MAX_COUNT 5 + +#define BATADV_NO_FLAGS 0 + +#define BATADV_NULL_IFINDEX 0 /* dummy ifindex used to avoid iface checks */ + +#define BATADV_NUM_WORDS BITS_TO_LONGS(BATADV_TQ_LOCAL_WINDOW_SIZE) + +#define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */ + +/* number of packets to send for broadcasts on different interface types */ +#define BATADV_NUM_BCASTS_DEFAULT 1 +#define BATADV_NUM_BCASTS_WIRELESS 3 +#define BATADV_NUM_BCASTS_MAX 3 + +/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */ +#define ARP_REQ_DELAY 250 +/* 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 + */ +#define BATADV_BONDING_TQ_THRESHOLD 50 + +/* should not be bigger than 512 bytes or change the size of + * forw_packet->direct_link_flags + */ +#define BATADV_MAX_AGGREGATION_BYTES 512 +#define BATADV_MAX_AGGREGATION_MS 100 + +#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */ +#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3) +#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10) +#define BATADV_BLA_WAIT_PERIODS 3 + +#define BATADV_DUPLIST_SIZE 16 +#define BATADV_DUPLIST_TIMEOUT 500 /* 500 ms */ +/* don't reset again within 30 seconds */ +#define BATADV_RESET_PROTECTION_MS 30000 +#define BATADV_EXPECTED_SEQNO_RANGE 65536 + +#define BATADV_NC_NODE_TIMEOUT 10000 /* Milliseconds */ + +enum batadv_mesh_state { + BATADV_MESH_INACTIVE, + BATADV_MESH_ACTIVE, + BATADV_MESH_DEACTIVATING, +}; + +#define BATADV_BCAST_QUEUE_LEN 256 +#define BATADV_BATMAN_QUEUE_LEN 256 + +enum batadv_uev_action { + BATADV_UEV_ADD = 0, + BATADV_UEV_DEL, + BATADV_UEV_CHANGE, +}; + +enum batadv_uev_type { + BATADV_UEV_GW = 0, +}; + +#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 + +/* Debug Messages */ +#ifdef pr_fmt +#undef pr_fmt +#endif +/* Append 'batman-adv: ' before kernel messages */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* Kernel headers */ + +#include <linux/mutex.h> /* mutex */ +#include <linux/module.h> /* needed by all modules */ +#include <linux/netdevice.h> /* netdevice */ +#include <linux/etherdevice.h> /* ethernet address classification */ +#include <linux/if_ether.h> /* ethernet header */ +#include <linux/poll.h> /* poll_table */ +#include <linux/kthread.h> /* kernel threads */ +#include <linux/pkt_sched.h> /* schedule types */ +#include <linux/workqueue.h> /* workqueue */ +#include <linux/percpu.h> +#include <linux/slab.h> +#include <net/sock.h> /* struct sock */ +#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) + +extern char batadv_routing_algo[]; +extern struct list_head batadv_hardif_list; + +extern unsigned char batadv_broadcast_addr[]; +extern struct workqueue_struct *batadv_event_workqueue; + +int batadv_mesh_init(struct net_device *soft_iface); +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, + struct net_device *orig_dev); +int +batadv_recv_handler_register(uint8_t packet_type, + int (*recv_handler)(struct sk_buff *, + struct batadv_hard_iface *)); +void batadv_recv_handler_unregister(uint8_t packet_type); +int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops); +int batadv_algo_select(struct batadv_priv *bat_priv, char *name); +int batadv_algo_seq_print_text(struct seq_file *seq, void *offset); +__be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr); + +/** + * enum batadv_dbg_level - available log levels + * @BATADV_DBG_BATMAN: OGM and TQ computations related messages + * @BATADV_DBG_ROUTES: route added / changed / deleted + * @BATADV_DBG_TT: translation table messages + * @BATADV_DBG_BLA: bridge loop avoidance messages + * @BATADV_DBG_DAT: ARP snooping and DAT related messages + * @BATADV_DBG_NC: network coding related messages + * @BATADV_DBG_ALL: the union of all the above log levels + */ +enum batadv_dbg_level { + BATADV_DBG_BATMAN = BIT(0), + BATADV_DBG_ROUTES = BIT(1), + BATADV_DBG_TT = BIT(2), + BATADV_DBG_BLA = BIT(3), + BATADV_DBG_DAT = BIT(4), + BATADV_DBG_NC = BIT(5), + BATADV_DBG_ALL = 63, +}; + +#ifdef CONFIG_BATMAN_ADV_DEBUG +int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...) +__printf(2, 3); + +#define batadv_dbg(type, bat_priv, fmt, arg...) \ + do { \ + if (atomic_read(&bat_priv->log_level) & type) \ + batadv_debug_log(bat_priv, fmt, ## arg);\ + } \ + while (0) +#else /* !CONFIG_BATMAN_ADV_DEBUG */ +__printf(3, 4) +static inline void batadv_dbg(int type __always_unused, + struct batadv_priv *bat_priv __always_unused, + const char *fmt __always_unused, ...) +{ +} +#endif + +#define batadv_info(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct batadv_priv *_batpriv = netdev_priv(_netdev); \ + batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \ + pr_info("%s: " fmt, _netdev->name, ## arg); \ + } while (0) +#define batadv_err(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct batadv_priv *_batpriv = netdev_priv(_netdev); \ + batadv_dbg(BATADV_DBG_ALL, _batpriv, fmt, ## arg); \ + pr_err("%s: " fmt, _netdev->name, ## arg); \ + } while (0) + +/* returns 1 if they are the same ethernet addr + * + * note: can't use ether_addr_equal() as it requires aligned memory + */ +static inline int batadv_compare_eth(const void *data1, const void *data2) +{ + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + +/** + * has_timed_out - compares current time (jiffies) and timestamp + timeout + * @timestamp: base value to compare with (in jiffies) + * @timeout: added to base value before comparing (in milliseconds) + * + * Returns true if current time is after timestamp + timeout + */ +static inline bool batadv_has_timed_out(unsigned long timestamp, + unsigned int timeout) +{ + return time_is_before_jiffies(timestamp + msecs_to_jiffies(timeout)); +} + +#define batadv_atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) + +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define batadv_smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + * they handle overflows/underflows and can correctly check for a + * predecessor/successor unless the variable sequence number has grown by + * more then 2**(bitwidth(x)-1)-1. + * This means that for a uint8_t with the maximum value 255, it would think: + * - when adding nothing - it is neither a predecessor nor a successor + * - before adding more than 127 to the starting value - it is a predecessor, + * - when adding 128 - it is neither a predecessor nor a successor, + * - after adding more than 127 to the starting value - it is a successor + */ +#define batadv_seq_before(x, y) ({typeof(x) _d1 = (x); \ + typeof(y) _d2 = (y); \ + typeof(x) _dummy = (_d1 - _d2); \ + (void) (&_d1 == &_d2); \ + _dummy > batadv_smallest_signed_int(_dummy); }) +#define batadv_seq_after(x, y) batadv_seq_before(y, x) + +/* Stop preemption on local cpu while incrementing the counter */ +static inline void batadv_add_counter(struct batadv_priv *bat_priv, size_t idx, + size_t count) +{ + this_cpu_add(bat_priv->bat_counters[idx], count); +} + +#define batadv_inc_counter(b, i) batadv_add_counter(b, i, 1) + +/* Sum and return the cpu-local counters for index 'idx' */ +static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv, + size_t idx) +{ + uint64_t *counters, sum = 0; + int cpu; + + for_each_possible_cpu(cpu) { + counters = per_cpu_ptr(bat_priv->bat_counters, cpu); + sum += counters[idx]; + } + + return sum; +} + +/* Define a macro to reach the control buffer of the skb. The members of the + * control buffer are defined in struct batadv_skb_cb in types.h. + * The macro is inspired by the similar macro TCP_SKB_CB() in tcp.h. + */ +#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 --combined net/batman-adv/network-coding.c index a487d46,173a96e..173a96e --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@@ -45,6 -45,59 +45,59 @@@ static void batadv_nc_start_timer(struc }
/** + * 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_init - initialise coding hash table and start house keeping * @bat_priv: the bat priv with all the soft interface information */ @@@ -78,6 -131,10 +131,10 @@@ int batadv_nc_init(struct batadv_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: @@@ -793,6 -850,10 +850,10 @@@ void batadv_nc_update_nc_node(struct ba 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) @@@ -933,7 -994,7 +994,7 @@@ static bool batadv_nc_code_packets(stru 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; @@@ -958,8 -1019,10 +1019,10 @@@ 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. @@@ -1727,6 -1790,8 +1790,8 @@@ free_nc_packet void batadv_nc_free(struct batadv_priv *bat_priv) { batadv_recv_handler_unregister(BATADV_CODED); + 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 --combined net/batman-adv/network-coding.h index 85a4ec8,8a97b40..8a97b40 --- a/net/batman-adv/network-coding.h +++ b/net/batman-adv/network-coding.h @@@ -22,6 -22,7 +22,7 @@@
#ifdef CONFIG_BATMAN_ADV_NC
+ void batadv_nc_status_update(struct net_device *net_dev); int batadv_nc_init(struct batadv_priv *bat_priv); void batadv_nc_free(struct batadv_priv *bat_priv); void batadv_nc_update_nc_node(struct batadv_priv *bat_priv, @@@ -46,6 -47,11 +47,11 @@@ int batadv_nc_init_debugfs(struct batad
#else /* ifdef CONFIG_BATMAN_ADV_NC */
+ static inline void batadv_nc_status_update(struct net_device *net_dev) + { + return; + } + static inline int batadv_nc_init(struct batadv_priv *bat_priv) { return 0; diff --combined net/batman-adv/originator.c index f50553a,8ab1434..8ab1434 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@@ -25,10 -25,10 +25,10 @@@ #include "routing.h" #include "gateway_client.h" #include "hard-interface.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 +36,7 @@@ 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 +44,88 @@@ 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 +172,20 @@@ batadv_orig_node_get_router(struct bata 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 +195,14 @@@ 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 +236,15 @@@ static void batadv_orig_node_free_rcu(s /* 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 +302,22 @@@ void batadv_originator_free(struct bata 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 +328,12 @@@
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 +345,32 @@@ memcpy(orig_node->orig, addr, ETH_ALEN); batadv_dat_init_orig_node_addr(orig_node); orig_node->router = NULL; 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 +379,16 @@@ 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 +421,12 @@@ 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 +477,14 @@@ static void _batadv_purge_orig(struct b 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 +515,26 @@@ int batadv_orig_seq_print_text(struct s { 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;
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 +543,7 @@@ int batadv_orig_hash_add_if(struct bata 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 +558,10 @@@
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 +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 +583,7 @@@ 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 +595,11 @@@
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 --combined net/batman-adv/originator.h index 7887b84,6f77d80..6f77d80 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@@ -22,16 -22,18 +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 +42,13 @@@ int batadv_orig_hash_add_if(struct bata 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 --combined net/batman-adv/packet.h index a51ccfc,f4bbb05..f4bbb05 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@@ -20,17 -20,34 +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 +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,29 -91,21 +91,21 @@@ 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. @@@ -99,10 -116,18 +116,18 @@@ 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_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 -138,22 +138,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,44 -172,69 +172,69 @@@ 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 { + /** + * batadc_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]; };
@@@ -209,15 -275,32 +275,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 -314,6 +314,6 @@@
#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 -346,96 +346,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 --combined net/batman-adv/routing.c index 0439395,71fba14..71fba14 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@@ -25,11 -25,12 +25,12 @@@ #include "icmp_socket.h" #include "translation-table.h" #include "originator.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 +47,7 @@@ static void _batadv_update_route(struc 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 +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 +142,9 @@@ 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 @@@ -259,7 -271,7 +271,7 @@@ static int batadv_recv_my_icmp_packet(s icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
/* add data to device queue */ - if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { + if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) { batadv_socket_receive_packet(icmp_packet, icmp_len); goto out; } @@@ -270,7 -282,7 +282,7 @@@
/* answer echo request (ping) */ /* 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;
@@@ -280,10 -292,11 +292,11 @@@
icmp_packet = (struct batadv_icmp_packet_rr *)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(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_ECHO_REPLY; + icmp_packet->icmph.header.ttl = BATADV_TTL;
if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; @@@ -307,9 -320,9 +320,9 @@@ static int batadv_recv_icmp_ttl_exceede 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 -331,7 +331,7 @@@ 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 -341,11 +341,11 @@@
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; @@@ -380,7 -394,9 +394,9 @@@ int batadv_recv_icmp_packet(struct sk_b icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
/* add record route information if not full */ - if ((hdr_size == sizeof(struct batadv_icmp_packet_rr)) && + if ((icmp_packet->icmph.msg_type == BATADV_ECHO_REPLY || + icmp_packet->icmph.msg_type == BATADV_ECHO_REQUEST) && + (hdr_size == sizeof(struct batadv_icmp_packet_rr)) && (icmp_packet->rr_cur < BATADV_RR_LEN)) { memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), ethhdr->h_dest, ETH_ALEN); @@@ -388,15 -404,15 +404,15 @@@ }
/* packet for me */ - if (batadv_is_my_mac(bat_priv, icmp_packet->dst)) + if (batadv_is_my_mac(bat_priv, icmp_packet->icmph.dst)) return batadv_recv_my_icmp_packet(bat_priv, skb, hdr_size);
/* TTL exceeded */ - if (icmp_packet->header.ttl < 2) + if (icmp_packet->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, icmp_packet->icmph.dst); if (!orig_node) goto out;
@@@ -407,7 -423,7 +423,7 @@@ icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
/* decrement ttl */ - icmp_packet->header.ttl--; + icmp_packet->icmph.header.ttl--;
/* route it */ if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP) @@@ -474,18 -490,25 +490,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 -520,7 +520,7 @@@ 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 -580,6 +580,6 @@@ static int batadv_check_unicast_packet( 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 -654,8 +654,8 @@@ batadv_find_router(struct batadv_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 -676,9 +676,9 @@@ static int batadv_route_unicast_packet( { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_orig_node *orig_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 -695,12 +695,12 @@@ 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 -735,6 +735,6 @@@ }
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 -745,7 +745,7 @@@ * @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 -756,22 +756,22 @@@ 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 -798,12 +798,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 -815,7 +815,7 @@@ 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 -823,9 +823,9 @@@ * 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 -871,7 +871,7 @@@ * 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 -883,7 +883,7 @@@ * 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 -902,34 +902,34 @@@ 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 -992,112 +992,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 -1199,3 +1199,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 --combined net/batman-adv/routing.h index 72a29bd,19544dd..19544dd --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@@ -30,23 -30,26 +30,26 @@@ int batadv_recv_icmp_packet(struct sk_b 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 --combined net/batman-adv/send.c index 0266edd,c83be5e..c83be5e --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@@ -24,12 -24,11 +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 +63,10 @@@ int batadv_send_skb_packet(struct sk_bu 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 +108,19 @@@ int batadv_send_skb_to_orig(struct sk_b /* 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 +134,225 @@@ 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 --combined net/batman-adv/send.h index e7b1788,aa2e253..aa2e253 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@@ -34,5 -34,58 +34,58 @@@ void batadv_send_outstanding_bat_ogm_pa 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 --combined net/batman-adv/soft-interface.c index 813db4e,a1f00e8..a1f00e8 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@@ -34,8 -34,6 +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 +118,10 @@@ static int batadv_interface_set_mac_add
/* 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_NULL_IFINDEX, + BATADV_NO_FLAGS); }
return 0; @@@ -139,36 -138,48 +138,48 @@@ static int batadv_interface_change_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 +196,12 @@@ 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 +301,12 @@@
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 +328,12 @@@ void batadv_interface_rx(struct net_dev 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 +345,12 @@@ 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 +386,10 @@@
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 +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 +611,7 @@@ static void batadv_set_lockdep_class(st */ 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 +619,13 @@@ 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 +642,7 @@@ 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 +667,17 @@@ #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 +692,10 @@@ 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 +781,11 @@@ static const struct net_device_ops bata .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 +822,7 @@@ static void batadv_softif_init_early(st
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 +830,7 @@@ */ 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 +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 --combined net/batman-adv/soft-interface.h index 2f2472c,06fc91f..06fc91f --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@@ -28,5 -28,9 +28,9 @@@ struct net_device *batadv_softif_create 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 --combined net/batman-adv/sysfs.c index 4114b96,096b511..096b511 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@@ -21,11 -21,12 +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 +40,53 @@@ static struct batadv_priv *batadv_kobj_ 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 attributed 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 +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 +179,41 @@@ ssize_t batadv_show_##_name(struct 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 +322,6 @@@ __batadv_store_uint_attr(const char *bu 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 +414,7 @@@ static ssize_t batadv_store_gw_mode(str */ 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 +422,13 @@@ static ssize_t batadv_show_gw_bwidth(st 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 +449,10 @@@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO 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 +468,8 @@@ static BATADV_ATTR(gw_bandwidth, S_IRUG 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 +482,6 @@@ &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 +497,16 @@@ 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 +557,80 @@@ void batadv_sysfs_del_meshif(struct net 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 --combined net/batman-adv/sysfs.h index 479acf4,c7d725d..c7d725d --- a/net/batman-adv/sysfs.h +++ b/net/batman-adv/sysfs.h @@@ -22,6 -22,12 +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 +42,10 @@@ void batadv_sysfs_del_meshif(struct net 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 --combined net/batman-adv/translation-table.c index 34510f3,1198aea..1198aea --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@@ -27,13 -27,14 +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 +42,8 @@@ batadv_tt_global_del_orig_list(struct b 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 +54,93 @@@ 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 (tt->vid != vid) continue;
- if (!atomic_inc_not_zero(&tt_common_entry->refcount)) + if (!atomic_inc_not_zero(&tt->refcount)) continue;
- tt_common_entry_tmp = tt_common_entry; + 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 +148,24 @@@ 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 +180,17 @@@ batadv_tt_local_entry_free_ref(struct b 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 +208,107 @@@ static void batadv_tt_orig_list_entry_f 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 +329,13 @@@ static void batadv_tt_local_event(struc 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;
@@@ -229,9 -379,55 +379,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_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) { - return changes_num * sizeof(struct batadv_tt_change); + 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 -451,46 +451,46 @@@ static void batadv_tt_global_free(struc 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 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;
- 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 -502,8 +502,8 @@@
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 -515,24 +515,24 @@@ 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,6 -541,7 +541,7 @@@ * (consistency check) */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; + tt_local->common.vid = vid; if (batadv_is_wifi_iface(ifindex)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; atomic_set(&tt_local->common.refcount, 2); @@@ -331,7 -553,7 +553,7 @@@ 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 -575,7 +575,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 -592,202 +592,202 @@@ } }
+ ret = true; + out: 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; + + rcu_read_lock(); + list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) { + num_vlan++; + num_entries += atomic_read(&vlan->tt.num_entries); + } + + 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;
- new_buff = kmalloc(new_packet_len, GFP_ATOMIC); + *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);
- /* 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; + 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; + + 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); + }
- req_len = min_packet_len; - req_len += batadv_tt_len(atomic_read(&bat_priv->tt.local_changes)); + change_offset = sizeof(**tt_data); + change_offset += num_vlan * sizeof(*tt_vlan);
- /* 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; + /* 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; + }
- batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len, - min_packet_len, req_len); + (*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); + + 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; + + tt_diff_entries_num = atomic_read(&bat_priv->tt.local_changes); + tt_diff_len = batadv_tt_len(tt_diff_entries_num); + + /* 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;
- batadv_tt_prepare_packet_buff(bat_priv, packet_buff, - packet_buff_len, min_packet_len); + tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data, + &tt_change, &tt_diff_len); + if (!tvlv_len) + return;
- new_len = *packet_buff_len - min_packet_len; - tt_buff = *packet_buff + min_packet_len; + tt_data->flags = BATADV_TT_OGM_DIFF;
- if (new_len > 0) - tot_changes = new_len / batadv_tt_len(1); + 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 -799,25 +799,25 @@@ 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 -828,9 +828,9 @@@ 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 -843,10 +843,10 @@@ 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 -857,7 +857,7 @@@ 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 -865,17 +865,17 @@@
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 -886,10 +886,10 @@@ (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 -913,29 +913,29 @@@ batadv_tt_local_set_pending(struct bata 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 -971,16 +971,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 -998,7 +998,7 @@@ 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 -1006,14 +1006,14 @@@ } }
- 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 -1025,7 +1025,7 @@@ 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 -1166,7 +1166,7 @@@ batadv_tt_global_orig_entry_add(struct
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 -1185,7 +1185,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 -1196,28 +1196,28 @@@ * 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 -1234,7 +1234,7 @@@
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 -1252,7 +1252,7 @@@
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 -1311,15 +1311,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 -1339,20 +1339,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 -1360,104 +1360,104 @@@ 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 -1479,9 +1479,9 @@@ int batadv_tt_global_seq_print_text(str 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 -1492,7 +1492,7 @@@ 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 -1514,8 +1514,8 @@@ batadv_tt_global_del_orig_list(struct b 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 -1530,21 +1530,21 @@@ batadv_tt_global_del_orig_entry(struct 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 -1591,25 +1591,25 @@@ batadv_tt_global_del_roaming(struct bat 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 -1638,8 +1638,8 @@@ * 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 -1657,18 +1657,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 -1678,7 +1678,7 @@@ 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 -1690,10 +1690,10 @@@ 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 -1702,11 +1702,11 @@@ 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 -1764,10 +1764,10 @@@ static void batadv_tt_global_purge(stru 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 -1826,49 +1826,49 @@@ _batadv_is_ap_isolated(struct batadv_tt 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 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 && atomic_read(&bat_priv->ap_isolation)) { - tt_local_entry = batadv_tt_local_hash_find(bat_priv, src); + 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 -1880,7 +1880,7 @@@ 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 -1897,39 +1897,39 @@@ 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;
for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@@ -1422,6 -1939,12 +1939,12 @@@ 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 -1966,59 +1966,59 @@@ 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)); + 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) { 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;
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)); + 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 -2037,9 +2037,9 @@@
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 -2101,14 +2101,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 -2135,30 +2135,30 @@@ static int batadv_tt_global_valid(cons 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 it 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 -2166,7 +2166,7 @@@
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 -2175,123 +2175,123 @@@ 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); - - out: - return skb; }
- 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) + /** + * 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 sk_buff *skb = NULL; - struct batadv_tt_query_packet *tt_request; - struct batadv_hard_iface *primary_if; - 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_tmp; + struct batadv_orig_node_vlan *vlan; + int i; + + /* 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, + struct batadv_tvlv_tt_vlan_data *tt_vlan, + uint16_t num_vlan, bool full_table) + { + struct batadv_tvlv_tt_data *tvlv_tt_data = NULL; + struct batadv_tt_req_node *tt_req_node = NULL; + 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 -2304,171 +2304,171 @@@ 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 + * node 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); + } + + /* 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; }
- 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; + 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 -2479,43 +2479,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 -2526,104 +2526,104 @@@ /* 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 -2634,13 +2634,13 @@@ 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 -2655,22 +2655,22 @@@ }
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 -2678,7 +2678,7 @@@ 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 -2688,31 +2688,31 @@@ 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 -2728,68 +2728,68 @@@ out return ret; }
- void batadv_handle_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_response) + /** + * batadv_dat_status_update - 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' : '.')); + resp_src, tt_data->ttvn, num_entries, + (tt_data->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; - - 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 -2870,28 +2870,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 -2899,22 +2899,22 @@@ 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 -2927,7 +2927,7 @@@ 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 -2938,9 +2938,9 @@@
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 -2952,25 +2952,25 @@@ 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 -2988,15 +2988,15 @@@ 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 -3024,11 +3024,11 @@@ static void batadv_tt_local_purge_pendi 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 -3039,25 +3039,25 @@@ } }
- 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 -3067,38 +3067,38 @@@
/* 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 -3108,8 +3108,8 @@@ 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 -3117,29 +3117,29 @@@ 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 -3155,9 +3155,9 @@@ 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 -3165,9 +3165,9 @@@ * 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 -3178,46 +3178,46 @@@ * 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 -3230,20 +3230,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 -3255,265 +3255,265 @@@ 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; + + 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 --combined net/batman-adv/translation-table.h index 659a3bb,026b1ff..026b1ff --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@@ -20,49 -20,35 +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); 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 --combined net/batman-adv/types.h index b2c94e1,f323822..f323822 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@@ -24,13 -24,6 +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, @@@ -60,7 -53,6 +53,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 -68,6 +68,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 -79,97 +79,97 @@@ };
/** + * 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 * @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 -177,9 +177,9 @@@ * 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 * @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 -189,11 +189,11 @@@ * @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 -202,26 +202,26 @@@ #ifdef CONFIG_BATMAN_ADV_DAT batadv_dat_addr_t dat_addr; #endif 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; 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 -234,28 +234,28 @@@ 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(1), + BATADV_ORIG_CAPA_HAS_NC = BIT(2), };
/** * 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 -263,57 +263,57 @@@ 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) * @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 -343,12 +343,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 -386,12 +386,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 -433,14 +433,14 @@@ * @changes_list_lock: lock protecting changes_list * @req_list_lock: lock protecting req_list * @roam_list_lock: lock protecting roam_list * @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 -455,12 +455,12 @@@ 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 -513,31 +513,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 -584,26 +584,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 -612,15 +612,15 @@@ * @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 -640,14 +640,14 @@@ * @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 -660,16 +660,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 -690,8 +690,8 @@@ 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 -700,7 +700,7 @@@ #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 @@@ -677,6 -794,7 +794,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 -804,7 +804,7 @@@ */ 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 -859,7 +859,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 -985,6 +985,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 -995,16 +995,16 @@@ * @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 -1015,17 +1015,17 @@@ 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 -1033,7 +1033,7 @@@ * 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 -1042,7 +1042,7 @@@ 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 -1062,60 +1062,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_ */