From: Simon Wunderlich simon@open-mesh.com
This patchset adds the network wide multi interface optimization as proposed in our wiki [1] for BATMAN IV. The main purpose is to do interface alternating and bonding by considering multi interface capabilities globally and not just for the (next) link, otherwise non-optimal links may be chosen.
This patchset needs to change a lot of core data structures and routing, please review it carefully. It is based on the current master plus the "batman-adv: forward late OGMs from best next hop" patch. A local development branch exists on the public git repo [2].
Things to discuss from my end are: * are there any metric proposals for the wifi half duplex penalty? I've just used "half" for now, but this makes the TQ degrade very quickly over the links. * Note also that this change will generally change the TQ propagation behaviour even for single interface networks, i.e. the TQ will degrade fast in any case in this networks after this change. This is inevitable if we want to mix single/multi interface devices * anything to consider for network coding? * this code was used in my virtual VMs only, I'd like to test it on multi-interface devices if the direction I go here can be confirmed. :)
As always, any comments are appreciated!
Thanks, Simon
[1] http://www.open-mesh.org/projects/batman-adv/wiki/network-wide-multi-link-op... [2] http://git.open-mesh.org/batman-adv.git/shortlog/refs/heads/simon/network-wi...
Simon Wunderlich (7): batman-adv: remove useless find_router look up batman-adv: remove bonding and interface alternating batman-adv: split tq information in neigh_node struct batman-adv: split out router from orig_node batman-adv: add WiFi penalty batman-adv: consider outgoing interface in OGM sending batman-adv: add bonding again
bat_iv_ogm.c | 654 +++++++++++++++++++++++++++++++---------------- distributed-arp-table.c | 3 +- gateway_client.c | 78 ++++-- hard-interface.c | 2 +- hard-interface.h | 1 + icmp_socket.c | 2 +- network-coding.c | 9 +- originator.c | 324 ++++++++++++++++++++--- originator.h | 14 +- routing.c | 413 ++++++++++-------------------- routing.h | 11 +- send.c | 42 ++- translation-table.c | 14 +- types.h | 79 ++++-- 14 files changed, 1037 insertions(+), 609 deletions(-)
From: Simon Wunderlich simon@open-mesh.com
This is not used anymore with the new fragmentation, and it might actually mess up the bonding code because find_router() assumes it is only called once per packet.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- send.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-)
diff --git a/send.c b/send.c index 32690c6..616364e 100644 --- a/send.c +++ b/send.c @@ -251,30 +251,19 @@ int batadv_send_skb_generic_unicast(struct batadv_priv *bat_priv, struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct batadv_unicast_packet *unicast_packet; struct batadv_orig_node *orig_node; - struct batadv_neigh_node *neigh_node; int ret = NET_RX_DROP;
/* get routing information */ - if (is_multicast_ether_addr(ethhdr->h_dest)) { + if (is_multicast_ether_addr(ethhdr->h_dest)) orig_node = batadv_gw_get_selected_orig(bat_priv); - if (orig_node) - goto find_router; - } + else + /* check for tt host - increases orig_node refcount. + * returns NULL in case of AP isolation + */ + orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, + ethhdr->h_dest);
- /* check for tt host - increases orig_node refcount. - * returns NULL in case of AP isolation - */ - orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, - ethhdr->h_dest); - -find_router: - /* find_router(): - * - if orig_node is NULL it returns NULL - * - increases neigh_nodes refcount if found. - */ - neigh_node = batadv_find_router(bat_priv, orig_node, NULL); - - if (!neigh_node) + if (!orig_node) goto out;
switch (packet_type) { @@ -306,8 +295,6 @@ find_router: ret = 0;
out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (orig_node) batadv_orig_node_free_ref(orig_node); if (ret == NET_RX_DROP)
From: Simon Wunderlich simon@open-mesh.com
Remove bonding and interface alternating code - it will be replaced by a new, network-wide multi interface optimization which enables both bonding and interface alternating in a better way.
Keep the sysfs and find router function though, this will be needed later.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- bat_iv_ogm.c | 7 -- originator.c | 13 +-- routing.c | 281 +++------------------------------------------------------- routing.h | 8 -- types.h | 10 +-- 5 files changed, 13 insertions(+), 306 deletions(-)
diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c index 2b94fdd..a340f9a 100644 --- a/bat_iv_ogm.c +++ b/bat_iv_ogm.c @@ -82,8 +82,6 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, if (!neigh_node) goto out;
- INIT_LIST_HEAD(&neigh_node->bonding_list); - neigh_node->orig_node = orig_neigh; neigh_node->if_incoming = hard_iface;
@@ -794,8 +792,6 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, neigh_node->last_ttl = batadv_ogm_packet->header.ttl; }
- batadv_bonding_candidate_add(orig_node, neigh_node); - /* if this neighbor already is our next hop there is nothing * to change */ @@ -1240,9 +1236,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node, batadv_ogm_packet, if_incoming);
- batadv_bonding_save_primary(orig_node, orig_neigh_node, - batadv_ogm_packet); - /* update ranking if it is not a duplicate or has the same * seqno and similar ttl as the non-duplicate */ diff --git a/originator.c b/originator.c index a591dc5..8e4bdec 100644 --- a/originator.c +++ b/originator.c @@ -120,20 +120,13 @@ out: static void batadv_orig_node_free_rcu(struct rcu_head *rcu) { struct hlist_node *node_tmp; - struct batadv_neigh_node *neigh_node, *tmp_neigh_node; + struct batadv_neigh_node *neigh_node; struct batadv_orig_node *orig_node;
orig_node = container_of(rcu, struct batadv_orig_node, rcu);
spin_lock_bh(&orig_node->neigh_list_lock);
- /* for all bonding members ... */ - list_for_each_entry_safe(neigh_node, tmp_neigh_node, - &orig_node->bond_list, bonding_list) { - list_del_rcu(&neigh_node->bonding_list); - batadv_neigh_node_free_ref(neigh_node); - } - /* for all neighbors towards this originator ... */ hlist_for_each_entry_safe(neigh_node, node_tmp, &orig_node->neigh_list, list) { @@ -234,7 +227,6 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, return NULL;
INIT_HLIST_HEAD(&orig_node->neigh_list); - INIT_LIST_HEAD(&orig_node->bond_list); spin_lock_init(&orig_node->ogm_cnt_lock); spin_lock_init(&orig_node->bcast_seqno_lock); spin_lock_init(&orig_node->neigh_list_lock); @@ -259,8 +251,6 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, 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); @@ -336,7 +326,6 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, neigh_purged = true;
hlist_del_rcu(&neigh_node->list); - batadv_bonding_candidate_del(orig_node, neigh_node); batadv_neigh_node_free_ref(neigh_node); } else { if ((!*best_neigh_node) || diff --git a/routing.c b/routing.c index 772eb0a..0f08cf8 100644 --- a/routing.c +++ b/routing.c @@ -96,104 +96,6 @@ out: batadv_neigh_node_free_ref(router); }
-/* caller must hold the neigh_list_lock */ -void batadv_bonding_candidate_del(struct batadv_orig_node *orig_node, - struct batadv_neigh_node *neigh_node) -{ - /* this neighbor is not part of our candidate list */ - if (list_empty(&neigh_node->bonding_list)) - goto out; - - list_del_rcu(&neigh_node->bonding_list); - INIT_LIST_HEAD(&neigh_node->bonding_list); - batadv_neigh_node_free_ref(neigh_node); - atomic_dec(&orig_node->bond_candidates); - -out: - return; -} - -void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node, - struct batadv_neigh_node *neigh_node) -{ - struct batadv_neigh_node *tmp_neigh_node, *router = NULL; - uint8_t interference_candidate = 0; - - spin_lock_bh(&orig_node->neigh_list_lock); - - /* only consider if it has the same primary address ... */ - if (!batadv_compare_eth(orig_node->orig, - neigh_node->orig_node->primary_addr)) - goto candidate_del; - - router = batadv_orig_node_get_router(orig_node); - if (!router) - goto candidate_del; - - /* ... and is good enough to be considered */ - if (neigh_node->tq_avg < router->tq_avg - BATADV_BONDING_TQ_THRESHOLD) - goto candidate_del; - - /* check if we have another candidate with the same mac address or - * interface. If we do, we won't select this candidate because of - * possible interference. - */ - hlist_for_each_entry_rcu(tmp_neigh_node, - &orig_node->neigh_list, list) { - if (tmp_neigh_node == neigh_node) - continue; - - /* we only care if the other candidate is even - * considered as candidate. - */ - if (list_empty(&tmp_neigh_node->bonding_list)) - continue; - - if ((neigh_node->if_incoming == tmp_neigh_node->if_incoming) || - (batadv_compare_eth(neigh_node->addr, - tmp_neigh_node->addr))) { - interference_candidate = 1; - break; - } - } - - /* don't care further if it is an interference candidate */ - if (interference_candidate) - goto candidate_del; - - /* this neighbor already is part of our candidate list */ - if (!list_empty(&neigh_node->bonding_list)) - goto out; - - if (!atomic_inc_not_zero(&neigh_node->refcount)) - goto out; - - list_add_rcu(&neigh_node->bonding_list, &orig_node->bond_list); - atomic_inc(&orig_node->bond_candidates); - goto out; - -candidate_del: - batadv_bonding_candidate_del(orig_node, neigh_node); - -out: - spin_unlock_bh(&orig_node->neigh_list_lock); - - if (router) - batadv_neigh_node_free_ref(router); -} - -/* copy primary address for bonding */ -void -batadv_bonding_save_primary(const struct batadv_orig_node *orig_node, - struct batadv_orig_node *orig_neigh_node, - const struct batadv_ogm_packet *batman_ogm_packet) -{ - if (!(batman_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP)) - return; - - memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN); -} - /* checks whether the host restarted and is in the protection time. * returns: * 0 if the packet is to be accepted @@ -420,107 +322,6 @@ out: return ret; }
-/* In the bonding case, send the packets in a round - * robin fashion over the remaining interfaces. - * - * This method rotates the bonding list and increases the - * returned router's refcount. - */ -static struct batadv_neigh_node * -batadv_find_bond_router(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; - - rcu_read_lock(); - list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, - bonding_list) { - if (!first_candidate) - first_candidate = tmp_neigh_node; - - /* recv_if == NULL on the first node. */ - if (tmp_neigh_node->if_incoming == recv_if) - continue; - - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; - - router = tmp_neigh_node; - break; - } - - /* use the first candidate if nothing was found. */ - if (!router && first_candidate && - atomic_inc_not_zero(&first_candidate->refcount)) - router = first_candidate; - - if (!router) - goto out; - - /* selected should point to the next element - * after the current router - */ - spin_lock_bh(&primary_orig->neigh_list_lock); - /* this is a list_move(), which unfortunately - * does not exist as rcu version - */ - list_del_rcu(&primary_orig->bond_list); - list_add_rcu(&primary_orig->bond_list, - &router->bonding_list); - spin_unlock_bh(&primary_orig->neigh_list_lock); - -out: - rcu_read_unlock(); - return router; -} - -/* Interface Alternating: Use the best of the - * remaining candidates which are not using - * this interface. - * - * Increases the returned router's refcount - */ -static struct batadv_neigh_node * -batadv_find_ifalter_router(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; - - rcu_read_lock(); - list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, - bonding_list) { - if (!first_candidate) - first_candidate = tmp_neigh_node; - - /* recv_if == NULL on the first node. */ - if (tmp_neigh_node->if_incoming == recv_if) - continue; - - if (router && tmp_neigh_node->tq_avg <= router->tq_avg) - continue; - - if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) - continue; - - /* decrement refcount of previously selected router */ - if (router) - batadv_neigh_node_free_ref(router); - - /* we found a better router (or at least one valid router) */ - router = tmp_neigh_node; - } - - /* use the first candidate if nothing was found. */ - if (!router && first_candidate && - atomic_inc_not_zero(&first_candidate->refcount)) - router = first_candidate; - - rcu_read_unlock(); - return router; -} - /** * batadv_check_unicast_packet - Check for malformed unicast packets * @bat_priv: the bat priv with all the soft interface information @@ -558,94 +359,32 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, return 0; }
-/* find a suitable router for this originator, and use - * bonding if possible. increases the found neighbors - * refcount. +/** + * batadv_find_router - find a suitable router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the destination node + * @recv_if: pointer to interface this packet was received on + * + * Returns the router which should be used for this orig_node on + * this interface, or NULL if not available. */ 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) { - struct batadv_orig_node *primary_orig_node; - struct batadv_orig_node *router_orig; struct batadv_neigh_node *router; - static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; - int bonding_enabled; - uint8_t *primary_addr;
if (!orig_node) return NULL;
router = batadv_orig_node_get_router(orig_node); if (!router) - goto err; + return NULL;
- /* without bonding, the first node should - * always choose the default router. - */ - bonding_enabled = atomic_read(&bat_priv->bonding); + /* TODO: fill this later with new bonding mechanism */
- rcu_read_lock(); - /* select default router to output */ - router_orig = router->orig_node; - if (!router_orig) - goto err_unlock; - - if ((!recv_if) && (!bonding_enabled)) - goto return_router; - - primary_addr = router_orig->primary_addr; - - /* if we have something in the primary_addr, we can search - * for a potential bonding candidate. - */ - if (batadv_compare_eth(primary_addr, zero_mac)) - goto return_router; - - /* find the orig_node which has the primary interface. might - * even be the same as our router_orig in many cases - */ - if (batadv_compare_eth(primary_addr, router_orig->orig)) { - primary_orig_node = router_orig; - } else { - primary_orig_node = batadv_orig_hash_find(bat_priv, - primary_addr); - if (!primary_orig_node) - goto return_router; - - batadv_orig_node_free_ref(primary_orig_node); - } - - /* with less than 2 candidates, we can't do any - * bonding and prefer the original router. - */ - if (atomic_read(&primary_orig_node->bond_candidates) < 2) - goto return_router; - - /* all nodes between should choose a candidate which - * is is not on the interface where the packet came - * in. - */ - batadv_neigh_node_free_ref(router); - - if (bonding_enabled) - router = batadv_find_bond_router(primary_orig_node, recv_if); - else - router = batadv_find_ifalter_router(primary_orig_node, recv_if); - -return_router: - if (router && router->if_incoming->if_status != BATADV_IF_ACTIVE) - goto err_unlock; - - rcu_read_unlock(); return router; -err_unlock: - rcu_read_unlock(); -err: - if (router) - batadv_neigh_node_free_ref(router); - return NULL; }
static int batadv_route_unicast_packet(struct sk_buff *skb, diff --git a/routing.h b/routing.h index 55d637a..b8fed80 100644 --- a/routing.h +++ b/routing.h @@ -46,14 +46,6 @@ 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, - 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, - const struct batadv_ogm_packet - *batman_ogm_packet); int batadv_window_protected(struct batadv_priv *bat_priv, int32_t seq_num_diff, unsigned long *last_reset);
diff --git a/types.h b/types.h index c388e47..059aa40 100644 --- a/types.h +++ b/types.h @@ -142,14 +142,12 @@ struct batadv_frag_list_entry { * 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 + * @neigh_list_lock: lock protecting neigh_list and router * @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 * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner * @in_coding_list: list of nodes this orig can hear @@ -183,7 +181,7 @@ struct batadv_orig_node { DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); uint32_t last_bcast_seqno; struct hlist_head neigh_list; - /* neigh_list_lock protects: neigh_list, router & bonding_list */ + /* neigh_list_lock protects: neigh_list and router */ spinlock_t neigh_list_lock; struct hlist_node hash_entry; struct batadv_priv *bat_priv; @@ -193,8 +191,6 @@ struct batadv_orig_node { spinlock_t ogm_cnt_lock; /* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */ spinlock_t bcast_seqno_lock; - atomic_t bond_candidates; - struct list_head bond_list; atomic_t refcount; struct rcu_head rcu; #ifdef CONFIG_BATMAN_ADV_NC @@ -244,7 +240,6 @@ struct batadv_gw_node { * @tq_index: ring buffer index * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv) * @last_ttl: last received ttl from this neigh node - * @bonding_list: list node for batadv_orig_node::bond_list * @last_seen: when last packet via this neighbor was received * @real_bits: bitfield containing the number of OGMs received from this neigh * node (relative to orig_node->last_real_seqno) @@ -262,7 +257,6 @@ struct batadv_neigh_node { uint8_t tq_index; uint8_t tq_avg; 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;
From: Simon Wunderlich simon@open-mesh.com
For the network wide multi interface optimization it is required to save metrics per outgoing interface in one neighbor. Therefore a new type is introduced to keep interface-specific information. This also requires some changes in access and list management.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- bat_iv_ogm.c | 133 +++++++++++++++++++++++++++++++++------------ gateway_client.c | 68 +++++++++++++++++++---- originator.c | 149 +++++++++++++++++++++++++++++++++++++++++++++------ originator.h | 3 ++ translation-table.c | 11 +++- types.h | 35 ++++++++---- 6 files changed, 325 insertions(+), 74 deletions(-)
diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c index a340f9a..e98386c 100644 --- a/bat_iv_ogm.c +++ b/bat_iv_ogm.c @@ -70,6 +70,7 @@ static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[])
return (uint8_t)(sum / count); } + static struct batadv_neigh_node * batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, const uint8_t *neigh_addr, @@ -82,6 +83,11 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, if (!neigh_node) goto out;
+ if (!atomic_inc_not_zero(&hard_iface->refcount)) { + kfree(neigh_node); + goto out; + } + neigh_node->orig_node = orig_neigh; neigh_node->if_incoming = hard_iface;
@@ -714,22 +720,39 @@ enum batadv_dup_status { BATADV_PROTECTED, };
+/** + * batadv_iv_ogm_orig_update - use OGM to update corresponding data in an + * originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the orig node who originally emitted the ogm packet + * @orig_node_ifinfo: ifinfo for the according outgoing interface + * @ethhdr: Ethernet header of the OGM + * @@batadv_ogm_packet: the ogm packet + * @if_incoming: interface where the packet was received + * @if_outgoing: interface for which the retransmission should be considered + * @tt_buff: pointer to the tt buffer + * @dup_status: the duplicate status of this ogm packet. + */ static void batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const struct ethhdr *ethhdr, const struct batadv_ogm_packet *batadv_ogm_packet, struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing, const unsigned char *tt_buff, enum batadv_dup_status dup_status) { struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; struct batadv_neigh_node *router = NULL; struct batadv_orig_node *orig_node_tmp; + struct batadv_neigh_node_ifinfo *neigh_ifinfo = NULL, + *tmp_neigh_ifinfo, + *router_ifinfo; int if_num; uint8_t sum_orig, sum_neigh; uint8_t *neigh_addr; - uint8_t tq_avg; + uint8_t tq_avg, *tq_recv;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "update_originator(): Searching and updating originator entry of received packet\n"); @@ -751,10 +774,19 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, 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; + /* only update the entry for this outgoing interface */ + hlist_for_each_entry_rcu(tmp_neigh_ifinfo, + &tmp_neigh_node->ifinfo_list, + list) { + if (tmp_neigh_ifinfo->if_outgoing != if_outgoing) + continue; + + tq_recv = tmp_neigh_ifinfo->tq_recv; + batadv_ring_buffer_set(tq_recv, + &tmp_neigh_ifinfo->tq_index, 0); + tq_avg = batadv_ring_buffer_avg(tq_recv); + tmp_neigh_ifinfo->tq_avg = tq_avg; + } spin_unlock_bh(&tmp_neigh_node->lq_update_lock); }
@@ -763,7 +795,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
orig_tmp = batadv_get_orig_node(bat_priv, ethhdr->h_source); if (!orig_tmp) - goto unlock; + goto out;
neigh_node = batadv_iv_ogm_neigh_new(if_incoming, ethhdr->h_source, @@ -771,25 +803,28 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
batadv_orig_node_free_ref(orig_tmp); if (!neigh_node) - goto unlock; + goto out; } else batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Updating existing last-hop neighbor of originator\n");
- rcu_read_unlock(); + neigh_ifinfo = batadv_neigh_node_get_ifinfo(neigh_node, + if_outgoing); + if (!neigh_ifinfo) + goto out;
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, + batadv_ring_buffer_set(neigh_ifinfo->tq_recv, + &neigh_ifinfo->tq_index, batadv_ogm_packet->tq); - neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv); + neigh_ifinfo->tq_avg = batadv_ring_buffer_avg(neigh_ifinfo->tq_recv); spin_unlock_bh(&neigh_node->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; + neigh_ifinfo->last_ttl = batadv_ogm_packet->header.ttl; }
/* if this neighbor already is our next hop there is nothing @@ -799,14 +834,15 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, if (router == neigh_node) goto out;
+ router_ifinfo = batadv_neigh_node_get_ifinfo(router, if_outgoing); /* if this neighbor does not offer a better TQ we won't consider it */ - if (router && (router->tq_avg > neigh_node->tq_avg)) + if (router_ifinfo && (router_ifinfo->tq_avg > neigh_ifinfo->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_ifinfo && (neigh_ifinfo->tq_avg == router_ifinfo->tq_avg)) { orig_node_tmp = router->orig_node; spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); if_num = router->if_incoming->if_num; @@ -823,25 +859,37 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, goto out; }
+ /* TODO: pass if_outgoing later */ batadv_update_route(bat_priv, orig_node, neigh_node); goto out;
-unlock: - rcu_read_unlock(); out: + rcu_read_unlock(); if (neigh_node) batadv_neigh_node_free_ref(neigh_node); if (router) batadv_neigh_node_free_ref(router); }
+/** + * batadv_iv_ogm_calc_tq - calculate tq for current received ogm packet + * @orig_node: the orig node who originally emitted the ogm packet + * @orig_neigh_node: the orig node struct of the neighbor who sent the packet + * @batadv_ogm_packet: the ogm packet + * @if_incoming: interface where the packet was received + * @if_outgoing: interface for which the retransmission should be considered + * + * Returns 1 if the link can be considered bidirectional, 0 otherwise + */ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, struct batadv_orig_node *orig_neigh_node, struct batadv_ogm_packet *batadv_ogm_packet, - struct batadv_hard_iface *if_incoming) + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node; + struct batadv_neigh_node_ifinfo *neigh_node_ifinfo; 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; @@ -885,7 +933,14 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, /* 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; + rcu_read_lock(); + neigh_node_ifinfo = batadv_neigh_node_get_ifinfo(neigh_node, + if_outgoing); + if (!neigh_node_ifinfo) + neigh_rq_count = 0; + else + neigh_rq_count = neigh_node_ifinfo->real_packet_count; + rcu_read_unlock(); spin_unlock_bh(&orig_node->ogm_cnt_lock);
/* pay attention to not get a value bigger than 100 % */ @@ -949,17 +1004,20 @@ out: * @ethhdr: Ethernet header of the packet * @batadv_ogm_packet: OGM packet to be considered * @if_incoming: interface where the OGM packet was received - * Returns: duplicate status as enum batadv_dup_status - * returns: + * @if_outgoing: interface for which the retransmission should be considered + * + * Returns duplicate status as enum batadv_dup_status */ enum batadv_dup_status batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, const struct batadv_ogm_packet *batadv_ogm_packet, - const struct batadv_hard_iface *if_incoming) + const struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_orig_node *orig_node; - struct batadv_neigh_node *tmp_neigh_node; + struct batadv_neigh_node *neigh_node; + struct batadv_neigh_node_ifinfo *neigh_ifinfo; int is_dup; int32_t seq_diff; int need_update = 0; @@ -985,15 +1043,20 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, }
rcu_read_lock(); - hlist_for_each_entry_rcu(tmp_neigh_node, + hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) { - neigh_addr = tmp_neigh_node->addr; - is_dup = batadv_test_bit(tmp_neigh_node->real_bits, + neigh_ifinfo = batadv_neigh_node_get_ifinfo(neigh_node, + if_outgoing); + if (WARN_ON(!neigh_ifinfo)) + continue; + + neigh_addr = neigh_node->addr; + is_dup = batadv_test_bit(neigh_ifinfo->real_bits, orig_node->last_real_seqno, seqno);
if (batadv_compare_eth(neigh_addr, ethhdr->h_source) && - tmp_neigh_node->if_incoming == if_incoming) { + neigh_node->if_incoming == if_incoming) { set_mark = 1; if (is_dup) ret = BATADV_NEIGH_DUP; @@ -1005,14 +1068,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
/* if the window moved, set the update flag. */ need_update |= batadv_bit_get_packet(bat_priv, - tmp_neigh_node->real_bits, + neigh_ifinfo->real_bits, seq_diff, set_mark);
- packet_count = bitmap_weight(tmp_neigh_node->real_bits, + packet_count = bitmap_weight(neigh_ifinfo->real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); - tmp_neigh_node->real_packet_count = packet_count; + neigh_ifinfo->real_packet_count = packet_count; } - rcu_read_unlock();
if (need_update) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, @@ -1020,6 +1082,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, orig_node->last_real_seqno, seqno); orig_node->last_real_seqno = seqno; } + rcu_read_unlock();
out: spin_unlock_bh(&orig_node->ogm_cnt_lock); @@ -1037,6 +1100,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, struct batadv_orig_node *orig_neigh_node, *orig_node; struct batadv_neigh_node *router = NULL, *router_router = NULL; struct batadv_neigh_node *orig_neigh_router = NULL; + struct batadv_neigh_node_ifinfo *router_ifinfo = NULL; int has_directlink_flag; int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; int is_bidirect; @@ -1169,7 +1233,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, return;
dup_status = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet, - if_incoming); + if_incoming, NULL);
if (dup_status == BATADV_PROTECTED) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, @@ -1188,7 +1252,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, if (router) router_router = batadv_orig_node_get_router(router->orig_node);
- if ((router && router->tq_avg != 0) && + if ((router && router_ifinfo->tq_avg != 0) && (batadv_compare_eth(router->addr, ethhdr->h_source))) is_from_best_next_hop = true;
@@ -1234,7 +1298,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, }
is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node, - batadv_ogm_packet, if_incoming); + batadv_ogm_packet, if_incoming, + NULL);
/* update ranking if it is not a duplicate or has the same * seqno and similar ttl as the non-duplicate @@ -1245,7 +1310,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, (sameseq && similar_ttl))) batadv_iv_ogm_orig_update(bat_priv, orig_node, ethhdr, batadv_ogm_packet, if_incoming, - tt_buff, dup_status); + NULL, tt_buff, dup_status);
/* is single hop (direct) neighbor */ if (is_single_hop_neigh) { diff --git a/gateway_client.c b/gateway_client.c index 3a27408..c134321 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -114,6 +114,7 @@ static struct batadv_gw_node * batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) { struct batadv_neigh_node *router; + struct batadv_neigh_node_ifinfo *router_ifinfo; struct batadv_gw_node *gw_node, *curr_gw = NULL; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; uint32_t gw_divisor; @@ -134,10 +135,14 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) if (!router) continue;
+ router_ifinfo = batadv_neigh_node_get_ifinfo(router, NULL); + if (!router_ifinfo) + goto next; + if (!atomic_inc_not_zero(&gw_node->refcount)) goto next;
- tq_avg = router->tq_avg; + tq_avg = router_ifinfo->tq_avg;
switch (atomic_read(&bat_priv->gw_sel_class)) { case 1: /* fast connection */ @@ -192,6 +197,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) { struct batadv_gw_node *curr_gw = NULL, *next_gw = NULL; struct batadv_neigh_node *router = NULL; + struct batadv_neigh_node_ifinfo *router_ifinfo = NULL; char gw_addr[18] = { '\0' };
/* The batman daemon checks here if we already passed a full originator @@ -199,6 +205,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) * hear about. This check is based on the daemon's uptime which we * don't have. */ + rcu_read_lock(); if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) goto out;
@@ -220,6 +227,12 @@ void batadv_gw_election(struct batadv_priv *bat_priv) batadv_gw_deselect(bat_priv); goto out; } + + router_ifinfo = batadv_neigh_node_get_ifinfo(router, NULL); + if (!router_ifinfo) { + batadv_gw_deselect(bat_priv); + goto out; + } }
if ((curr_gw) && (!next_gw)) { @@ -234,7 +247,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) next_gw->bandwidth_down / 10, next_gw->bandwidth_down % 10, next_gw->bandwidth_up / 10, - next_gw->bandwidth_up % 10, router->tq_avg); + next_gw->bandwidth_up % 10, router_ifinfo->tq_avg); batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD, gw_addr); } else { @@ -244,7 +257,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) next_gw->bandwidth_down / 10, next_gw->bandwidth_down % 10, next_gw->bandwidth_up / 10, - next_gw->bandwidth_up % 10, router->tq_avg); + next_gw->bandwidth_up % 10, router_ifinfo->tq_avg); batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE, gw_addr); } @@ -252,6 +265,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) batadv_gw_select(bat_priv, next_gw);
out: + rcu_read_unlock(); if (curr_gw) batadv_gw_node_free_ref(curr_gw); if (next_gw) @@ -265,8 +279,11 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, { struct batadv_orig_node *curr_gw_orig; struct batadv_neigh_node *router_gw = NULL, *router_orig = NULL; + struct batadv_neigh_node_ifinfo *router_gw_tq = NULL, + *router_orig_tq = NULL; uint8_t gw_tq_avg, orig_tq_avg;
+ rcu_read_lock(); curr_gw_orig = batadv_gw_get_selected_orig(bat_priv); if (!curr_gw_orig) goto deselect; @@ -275,6 +292,10 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, if (!router_gw) goto deselect;
+ router_gw_tq = batadv_neigh_node_get_ifinfo(router_gw, NULL); + if (!router_gw_tq) + goto deselect; + /* this node already is the gateway */ if (curr_gw_orig == orig_node) goto out; @@ -283,8 +304,15 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, if (!router_orig) goto out;
- gw_tq_avg = router_gw->tq_avg; - orig_tq_avg = router_orig->tq_avg; + router_orig_tq = batadv_neigh_node_get_ifinfo(router_orig, NULL); + if (!router_orig_tq) { + batadv_gw_deselect(bat_priv); + goto out; + } + + + gw_tq_avg = router_gw_tq->tq_avg; + orig_tq_avg = router_orig_tq->tq_avg;
/* the TQ value has to be better */ if (orig_tq_avg < gw_tq_avg) @@ -302,6 +330,7 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, gw_tq_avg, orig_tq_avg);
deselect: + rcu_read_unlock(); batadv_gw_deselect(bat_priv); out: if (curr_gw_orig) @@ -495,18 +524,24 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv, { struct batadv_gw_node *curr_gw; struct batadv_neigh_node *router; + struct batadv_neigh_node_ifinfo *router_ifinfo; int ret = -1;
+ rcu_read_lock(); router = batadv_orig_node_get_router(gw_node->orig_node); if (!router) goto out;
+ router_ifinfo = batadv_neigh_node_get_ifinfo(router, NULL); + if (!router_ifinfo) + goto out; + curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
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_ifinfo->tq_avg, router->addr, router->if_incoming->net_dev->name, gw_node->bandwidth_down / 10, gw_node->bandwidth_down % 10, @@ -517,6 +552,7 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv, if (curr_gw) batadv_gw_node_free_ref(curr_gw); out: + rcu_read_unlock(); return ret; }
@@ -697,10 +733,12 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL; struct batadv_orig_node *orig_dst_node = NULL; struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL; + struct batadv_neigh_node_ifinfo *neigh_curr_ifinfo, *neigh_old_ifinfo; bool ret, out_of_range = false; unsigned int header_len = 0; - uint8_t curr_tq_avg; + uint8_t curr_if_avg;
+ rcu_read_lock(); ret = batadv_gw_is_dhcp_target(skb, &header_len); if (!ret) goto out; @@ -723,7 +761,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, /* If we are a GW then we are our best GW. We can artificially * set the tq towards ourself as the maximum value */ - curr_tq_avg = BATADV_TQ_MAX_VALUE; + curr_if_avg = BATADV_TQ_MAX_VALUE; break; case BATADV_GW_MODE_CLIENT: curr_gw = batadv_gw_get_selected_gw_node(bat_priv); @@ -743,7 +781,12 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!neigh_curr) goto out;
- curr_tq_avg = neigh_curr->tq_avg; + neigh_curr_ifinfo = batadv_neigh_node_get_ifinfo(neigh_curr, + NULL); + if (!neigh_curr_ifinfo) + goto out; + + curr_if_avg = neigh_curr_ifinfo->tq_avg; break; case BATADV_GW_MODE_OFF: default: @@ -754,10 +797,15 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!neigh_old) goto out;
- if (curr_tq_avg - neigh_old->tq_avg > BATADV_GW_THRESHOLD) + neigh_old_ifinfo = batadv_neigh_node_get_ifinfo(neigh_old, NULL); + if (!neigh_old_ifinfo) + goto out; + + if (curr_if_avg - neigh_old_ifinfo->tq_avg > BATADV_GW_THRESHOLD) out_of_range = true;
out: + rcu_read_unlock(); if (orig_dst_node) batadv_orig_node_free_ref(orig_dst_node); if (curr_gw) diff --git a/originator.c b/originator.c index 8e4bdec..66af68d 100644 --- a/originator.c +++ b/originator.c @@ -68,10 +68,28 @@ err: return -ENOMEM; }
+static void batadv_neigh_node_free_rcu(struct rcu_head *rcu) +{ + struct hlist_node *node_tmp; + struct batadv_neigh_node *neigh_node; + struct batadv_neigh_node_ifinfo *neigh_node_ifinfo; + + neigh_node = container_of(rcu, struct batadv_neigh_node, rcu); + + hlist_for_each_entry_safe(neigh_node_ifinfo, node_tmp, + &neigh_node->ifinfo_list, list) { + if (neigh_node_ifinfo->if_outgoing) + batadv_hardif_free_ref(neigh_node_ifinfo->if_outgoing); + kfree(neigh_node_ifinfo); + } + + kfree(neigh_node); +} + void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node) { if (atomic_dec_and_test(&neigh_node->refcount)) - kfree_rcu(neigh_node, rcu); + call_rcu(&neigh_node->rcu, batadv_neigh_node_free_rcu); }
/* increases the refcounter of a found router */ @@ -90,6 +108,55 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node) return router; }
+/** + * batadv_neigh_node_get_ifinfo - gets the ifinfo from an neigh_node + * @neigh_node: the neigh node to be queried + * @if_outgoing: the interface for which the ifinfo should be acquired + * + * Note: this function must be called under rcu lock + * + * Returns the requested neigh_node_ifinfo or NULL if not found + */ +struct batadv_neigh_node_ifinfo * +batadv_neigh_node_get_ifinfo(struct batadv_neigh_node *neigh, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_node_ifinfo *neigh_ifinfo = NULL, + *tmp_neigh_ifinfo; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_ifinfo, &neigh->ifinfo_list, + list) { + if (tmp_neigh_ifinfo->if_outgoing != if_outgoing) + continue; + + neigh_ifinfo = tmp_neigh_ifinfo; + } + if (neigh_ifinfo) + goto out; + + neigh_ifinfo = kzalloc(sizeof(*neigh_ifinfo), GFP_ATOMIC); + if (!neigh_ifinfo) + goto out; + + if (if_outgoing && !atomic_inc_not_zero(&if_outgoing->refcount)) { + kfree(neigh_ifinfo); + neigh_ifinfo = NULL; + goto out; + } + + INIT_HLIST_NODE(&neigh_ifinfo->list); + neigh_ifinfo->if_outgoing = if_outgoing; + + spin_lock_bh(&neigh->lq_update_lock); + hlist_add_head_rcu(&neigh_ifinfo->list, &neigh->ifinfo_list); + spin_unlock_bh(&neigh->lq_update_lock); +out: + rcu_read_unlock(); + + return neigh_ifinfo; +} + struct batadv_neigh_node * batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, const uint8_t *neigh_addr) @@ -102,6 +169,7 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, goto out;
INIT_HLIST_NODE(&neigh_node->list); + INIT_HLIST_HEAD(&neigh_node->ifinfo_list);
memcpy(neigh_node->addr, neigh_addr, ETH_ALEN); spin_lock_init(&neigh_node->lq_update_lock); @@ -285,10 +353,16 @@ free_orig_node: return NULL; }
+/** + * batadv_purge_orig_neighbors - purges neighbors from originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be checked + * + * Returns true if any neighbor was purged, false otherwise + */ 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_orig_node *orig_node) { struct hlist_node *node_tmp; struct batadv_neigh_node *neigh_node; @@ -296,8 +370,6 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, unsigned long last_seen; struct batadv_hard_iface *if_incoming;
- *best_neigh_node = NULL; - spin_lock_bh(&orig_node->neigh_list_lock);
/* for all neighbors towards this originator ... */ @@ -327,10 +399,6 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
hlist_del_rcu(&neigh_node->list); 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; } }
@@ -338,6 +406,42 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, return neigh_purged; }
+/** + * batadv_find_best_neighbor - finds the best neighbor after purging + * @orig_node: orig node which is to be checked + * @if_outgoing: the interface for which the TQ should be compared + * + * Returns the current best neighbor + */ +static struct batadv_neigh_node * +batadv_find_best_neighbor(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_node *best = NULL, *neigh; + struct batadv_neigh_node_ifinfo *neigh_ifinfo, *best_ifinfo = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(neigh, &orig_node->neigh_list, list) { + neigh_ifinfo = batadv_neigh_node_get_ifinfo(neigh, + if_outgoing); + if (!neigh_ifinfo) + continue; + + if (!best) { + best = neigh; + best_ifinfo = neigh_ifinfo; + continue; + } + if (neigh_ifinfo->tq_avg > best_ifinfo->tq_avg) { + best = neigh; + best_ifinfo = neigh_ifinfo; + } + } + rcu_read_unlock(); + + return best; +} + static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node) { @@ -350,12 +454,12 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, orig_node->orig, jiffies_to_msecs(orig_node->last_seen)); return true; - } else { - if (batadv_purge_orig_neighbors(bat_priv, orig_node, - &best_neigh_node)) - batadv_update_route(bat_priv, orig_node, - best_neigh_node); } + if (!batadv_purge_orig_neighbors(bat_priv, orig_node)) + return false; + + best_neigh_node = batadv_find_best_neighbor(orig_node, NULL); + batadv_update_route(bat_priv, orig_node, best_neigh_node);
return false; } @@ -424,6 +528,7 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset) struct batadv_hard_iface *primary_if; struct batadv_orig_node *orig_node; struct batadv_neigh_node *neigh_node, *neigh_node_tmp; + struct batadv_neigh_node_ifinfo *neigh_ifinfo; int batman_count = 0; int last_seen_secs; int last_seen_msecs; @@ -450,7 +555,12 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset) if (!neigh_node) continue;
- if (neigh_node->tq_avg == 0) + neigh_ifinfo = batadv_neigh_node_get_ifinfo(neigh_node, + NULL); + if (!neigh_ifinfo) + goto next; + + if (neigh_ifinfo->tq_avg == 0) goto next;
last_seen_jiffies = jiffies - orig_node->last_seen; @@ -460,15 +570,20 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:", orig_node->orig, last_seen_secs, - last_seen_msecs, neigh_node->tq_avg, + last_seen_msecs, neigh_ifinfo->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) { + neigh_ifinfo = batadv_neigh_node_get_ifinfo( + neigh_node, NULL); + if (!neigh_ifinfo) + continue; + seq_printf(seq, " %pM (%3i)", neigh_node_tmp->addr, - neigh_node_tmp->tq_avg); + neigh_ifinfo->tq_avg); }
seq_puts(seq, "\n"); diff --git a/originator.h b/originator.h index 7887b84..a53b016 100644 --- a/originator.h +++ b/originator.h @@ -35,6 +35,9 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, 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); +struct batadv_neigh_node_ifinfo * +batadv_neigh_node_get_ifinfo(struct batadv_neigh_node *neigh, + struct batadv_hard_iface *if_outgoing); int batadv_orig_seq_print_text(struct seq_file *seq, void *offset); int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface, int max_if_num); diff --git a/translation-table.c b/translation-table.c index 44e7789..1e2adfe 100644 --- a/translation-table.c +++ b/translation-table.c @@ -942,6 +942,7 @@ static struct batadv_tt_orig_list_entry * batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry) { struct batadv_neigh_node *router = NULL; + struct batadv_neigh_node_ifinfo *router_ifinfo; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL; int best_tq = 0; @@ -952,9 +953,15 @@ batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry) if (!router) continue;
- if (router->tq_avg > best_tq) { + router_ifinfo = batadv_neigh_node_get_ifinfo(router, NULL); + if (!router_ifinfo) { + batadv_neigh_node_free_ref(router); + continue; + } + + if (router_ifinfo->tq_avg > best_tq) { best_entry = orig_entry; - best_tq = router->tq_avg; + best_tq = router_ifinfo->tq_avg; }
batadv_neigh_node_free_ref(router); diff --git a/types.h b/types.h index 059aa40..2bf7481 100644 --- a/types.h +++ b/types.h @@ -236,13 +236,8 @@ struct batadv_gw_node { * struct batadv_neigh_node - structure for single hop neighbors * @list: list node for batadv_orig_node::neigh_list * @addr: mac address of neigh node - * @tq_recv: ring buffer of received TQ values from this neigh node - * @tq_index: ring buffer index - * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv) - * @last_ttl: last received ttl from this neigh node + * @ifinfo_list: list for routing metrics per outgoing interface * @last_seen: when last packet via this neighbor was received - * @real_bits: bitfield containing the number of OGMs received from this neigh - * node (relative to orig_node->last_real_seqno) * @real_packet_count: counted result of real_bits * @orig_node: pointer to corresponding orig_node * @if_incoming: pointer to incoming hard interface @@ -253,17 +248,35 @@ struct batadv_gw_node { struct batadv_neigh_node { struct hlist_node list; uint8_t addr[ETH_ALEN]; + struct hlist_head ifinfo_list; + unsigned long last_seen; + 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_node_ifinfo - neighbor information per outgoing interface + * @list: list node for batadv_orig_node::neigh_list + * @if_outgoing: pointer to outgoing hard interface + * @tq_recv: ring buffer of received TQ values from this neigh node + * @tq_index: ring buffer index + * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv) + * @last_ttl: last received ttl from this neigh node + * @real_bits: bitfield containing the number of OGMs received from this neigh + * node (relative to orig_node->last_real_seqno) + * @rcu: struct used for freeing in an RCU-safe manner + */ +struct batadv_neigh_node_ifinfo { + struct hlist_node list; + struct batadv_hard_iface *if_outgoing; uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE]; uint8_t tq_index; uint8_t tq_avg; uint8_t last_ttl; - 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; };
From: Simon Wunderlich simon@open-mesh.com
For the network wide multi interface optimization there are different routers for each outgoing interface (outgoing from the OGM perspective, incoming for payload traffic). To reflect this, change the router and associated data to a list of routers.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- bat_iv_ogm.c | 385 +++++++++++++++++++++++++++++------------------ distributed-arp-table.c | 3 +- gateway_client.c | 10 +- icmp_socket.c | 2 +- network-coding.c | 9 +- originator.c | 166 +++++++++++++++++++- originator.h | 11 +- routing.c | 42 +++++- routing.h | 1 + translation-table.c | 3 +- types.h | 25 ++- 11 files changed, 483 insertions(+), 174 deletions(-)
diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c index e98386c..f0b16d6 100644 --- a/bat_iv_ogm.c +++ b/bat_iv_ogm.c @@ -736,6 +736,7 @@ enum batadv_dup_status { static void batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_orig_node_ifinfo *orig_node_ifinfo, const struct ethhdr *ethhdr, const struct batadv_ogm_packet *batadv_ogm_packet, struct batadv_hard_iface *if_incoming, @@ -823,21 +824,28 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, spin_unlock_bh(&neigh_node->lq_update_lock);
if (dup_status == BATADV_NO_DUP) { - orig_node->last_ttl = batadv_ogm_packet->header.ttl; + orig_node_ifinfo->last_ttl = batadv_ogm_packet->header.ttl; neigh_ifinfo->last_ttl = batadv_ogm_packet->header.ttl; }
/* if this neighbor already is our next hop there is nothing * to change */ - router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_node_get_router(orig_node, if_outgoing); if (router == neigh_node) goto out;
- router_ifinfo = batadv_neigh_node_get_ifinfo(router, if_outgoing); - /* if this neighbor does not offer a better TQ we won't consider it */ - if (router_ifinfo && (router_ifinfo->tq_avg > neigh_ifinfo->tq_avg)) - goto out; + if (router) { + router_ifinfo = batadv_neigh_node_get_ifinfo(router, + if_outgoing); + /* if this neighbor does not offer a better TQ we won't + * consider it + */ + if (router_ifinfo->tq_avg > neigh_ifinfo->tq_avg) + goto out; + } else { + router_ifinfo = NULL; + }
/* if the TQ is the same and the link not more symmetric we * won't consider it either @@ -859,8 +867,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, goto out; }
- /* TODO: pass if_outgoing later */ - batadv_update_route(bat_priv, orig_node, neigh_node); + batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node); goto out;
out: @@ -1016,6 +1023,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_orig_node *orig_node; + struct batadv_orig_node_ifinfo *orig_node_ifinfo; struct batadv_neigh_node *neigh_node; struct batadv_neigh_node_ifinfo *neigh_ifinfo; int is_dup; @@ -1031,8 +1039,14 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, if (!orig_node) return BATADV_NO_DUP;
+ orig_node_ifinfo = batadv_orig_node_get_ifinfo(orig_node, if_outgoing); + if (WARN_ON(!orig_node_ifinfo)) { + batadv_orig_node_free_ref(orig_node); + return 0; + } + spin_lock_bh(&orig_node->ogm_cnt_lock); - seq_diff = seqno - orig_node->last_real_seqno; + seq_diff = seqno - orig_node_ifinfo->last_real_seqno;
/* signalize caller that the packet is to be dropped. */ if (!hlist_empty(&orig_node->neigh_list) && @@ -1052,7 +1066,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
neigh_addr = neigh_node->addr; is_dup = batadv_test_bit(neigh_ifinfo->real_bits, - orig_node->last_real_seqno, + orig_node_ifinfo->last_real_seqno, seqno);
if (batadv_compare_eth(neigh_addr, ethhdr->h_source) && @@ -1078,9 +1092,10 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
if (need_update) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "updating last_seqno: old %u, new %u\n", - orig_node->last_real_seqno, seqno); - orig_node->last_real_seqno = seqno; + "%s updating last_seqno: old %u, new %u\n", + if_outgoing ? if_outgoing->net_dev->name : "NULL", + orig_node_ifinfo->last_real_seqno, seqno); + orig_node_ifinfo->last_real_seqno = seqno; } rcu_read_unlock();
@@ -1090,26 +1105,214 @@ out: return ret; }
-static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, - struct batadv_ogm_packet *batadv_ogm_packet, - const unsigned char *tt_buff, - struct batadv_hard_iface *if_incoming) + +/** + * batadv_iv_ogm_process_per_outif - process a batman iv OGM for an outgoing if + * @ethhdr: the original ethernet header of the sender + * @orig_node: the orig node for the originator of this packet + * @batadv_ogm_packet: pointer to the ogm packet + * @tt_buff: pointer to the tt buffer + * @if_incoming: the interface where this packet was receved + * @if_outgoing: the interface for which the packet should be considered + */ +static void +batadv_iv_ogm_process_per_outif(const struct ethhdr *ethhdr, + struct batadv_orig_node *orig_node, + struct batadv_ogm_packet *batadv_ogm_packet, + const unsigned char *tt_buff, + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) { 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_neigh_node *router = NULL, *router_router = NULL; struct batadv_neigh_node *orig_neigh_router = NULL; struct batadv_neigh_node_ifinfo *router_ifinfo = NULL; - int has_directlink_flag; - int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; - int is_bidirect; - bool is_single_hop_neigh = false; - bool is_from_best_next_hop = false; - int sameseq, similar_ttl; + struct batadv_orig_node *orig_neigh_node; + struct batadv_orig_node_ifinfo *orig_node_ifinfo; + int is_bidirect, sameseq, similar_ttl; enum batadv_dup_status dup_status; - uint32_t if_incoming_seqno; uint8_t *prev_sender; + struct batadv_ogm_packet ogm_packet_backup; + bool is_from_best_next_hop = false; + bool is_single_hop_neigh = false; + + /* some functions change tq value and/or flags. backup the ogm packet + * and restore it at the end to allow other interfaces to access the + * original data. + */ + + memcpy(&ogm_packet_backup, batadv_ogm_packet, + sizeof(ogm_packet_backup)); + + dup_status = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet, + if_incoming, if_outgoing); + if (batadv_compare_eth(ethhdr->h_source, batadv_ogm_packet->orig)) + is_single_hop_neigh = true; + + if (dup_status == BATADV_PROTECTED) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: packet within seqno protection time (sender: %pM)\n", + ethhdr->h_source); + goto out; + } + + if (batadv_ogm_packet->tq == 0) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: originator packet with tq equal 0\n"); + goto out; + } + + rcu_read_lock(); + router = batadv_orig_node_get_router(orig_node, if_outgoing); + if (router) + router_router = batadv_orig_node_get_router(router->orig_node, + if_outgoing); + + if (router) + router_ifinfo = batadv_neigh_node_get_ifinfo(router, + if_outgoing); + + if ((router_ifinfo && router_ifinfo->tq_avg != 0) && + (batadv_compare_eth(router->addr, ethhdr->h_source))) + is_from_best_next_hop = true; + rcu_read_unlock(); + + prev_sender = batadv_ogm_packet->prev_sender; + /* avoid temporary routing loops */ + if (router && router_router && + (batadv_compare_eth(router->addr, prev_sender)) && + !(batadv_compare_eth(batadv_ogm_packet->orig, prev_sender)) && + (batadv_compare_eth(router->addr, router_router->addr))) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %pM)\n", + ethhdr->h_source); + goto out; + } + + /* 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); + + if (!orig_neigh_node) + goto out; + + /* Update nc_nodes of the originator */ + batadv_nc_update_nc_node(bat_priv, orig_node, orig_neigh_node, + batadv_ogm_packet, is_single_hop_neigh); + + orig_neigh_router = batadv_orig_node_get_router(orig_neigh_node, + if_outgoing); + + /* drop packet if sender is not a direct neighbor and if we + * don't route towards it + */ + if (!is_single_hop_neigh && (!orig_neigh_router)) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: OGM via unknown neighbor!\n"); + goto out_neigh; + } + + is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node, + batadv_ogm_packet, if_incoming, + if_outgoing); + + /* update ranking if it is not a duplicate or has the same + * seqno and similar ttl as the non-duplicate + */ + rcu_read_lock(); + orig_node_ifinfo = batadv_orig_node_get_ifinfo(orig_node, if_outgoing); + if (!orig_node_ifinfo) { + rcu_read_unlock(); + goto out_neigh; + } + + sameseq = (orig_node_ifinfo->last_real_seqno == + ntohl(batadv_ogm_packet->seqno)); + similar_ttl = (orig_node_ifinfo->last_ttl - 3) <= + batadv_ogm_packet->header.ttl; + if (is_bidirect && ((dup_status == BATADV_NO_DUP) || + (sameseq && similar_ttl))) + batadv_iv_ogm_orig_update(bat_priv, orig_node, + orig_node_ifinfo, ethhdr, + batadv_ogm_packet, if_incoming, + if_outgoing, tt_buff, dup_status); + rcu_read_unlock(); + + /* is single hop (direct) neighbor */ + if (is_single_hop_neigh) { + if ((batadv_ogm_packet->header.ttl <= 2) && + (if_incoming != if_outgoing)) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: OGM from secondary interface and wrong outgoing interface\n"); + goto out_neigh; + } + /* mark direct link on incoming interface */ + batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, + is_single_hop_neigh, + is_from_best_next_hop, if_incoming); + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Forwarding packet: rebroadcast neighbor packet with direct link flag\n"); + goto out_neigh; + } + + /* multihop originator */ + if (!is_bidirect) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: not received via bidirectional link\n"); + goto out_neigh; + } + + if (dup_status == BATADV_NEIGH_DUP) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: duplicate packet received\n"); + goto out_neigh; + } + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Forwarding packet: rebroadcast originator packet\n"); + batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, + is_single_hop_neigh, is_from_best_next_hop, + if_incoming); + +out_neigh: + if ((orig_neigh_node) && (!is_single_hop_neigh)) + batadv_orig_node_free_ref(orig_neigh_node); +out: + if (router) + batadv_neigh_node_free_ref(router); + if (router_router) + batadv_neigh_node_free_ref(router_router); + if (orig_neigh_router) + batadv_neigh_node_free_ref(orig_neigh_router); + + memcpy(batadv_ogm_packet, &ogm_packet_backup, + sizeof(ogm_packet_backup)); +} + +/** + * batadv_iv_ogm_process - process an incoming batman iv OGM + * @ethhdr: the original ethernet header of the sender + * @batadv_ogm_packet: pointer to the ogm packet + * @tt_buff: pointer to the tt buffer + * @if_incoming: the interface where this packet was receved + */ +static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, + struct batadv_ogm_packet *batadv_ogm_packet, + const unsigned char *tt_buff, + struct batadv_hard_iface *if_incoming) +{ + 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; + uint32_t if_incoming_seqno; + int has_directlink_flag; + int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0;
/* Silently drop when the batman packet is actually not a * correct packet. @@ -1134,9 +1337,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, else has_directlink_flag = 0;
- if (batadv_compare_eth(ethhdr->h_source, batadv_ogm_packet->orig)) - 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, tq %d, TTL %d, V %d, IDF %d)\n", ethhdr->h_source, if_incoming->net_dev->name, @@ -1232,127 +1432,24 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, if (!orig_node) return;
- dup_status = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet, - if_incoming, NULL); - - if (dup_status == BATADV_PROTECTED) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: packet within seqno protection time (sender: %pM)\n", - ethhdr->h_source); - goto out; - } - - if (batadv_ogm_packet->tq == 0) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: originator packet with tq equal 0\n"); - goto out; - } - - router = batadv_orig_node_get_router(orig_node); - if (router) - router_router = batadv_orig_node_get_router(router->orig_node); - - if ((router && router_ifinfo->tq_avg != 0) && - (batadv_compare_eth(router->addr, ethhdr->h_source))) - is_from_best_next_hop = true; - - prev_sender = batadv_ogm_packet->prev_sender; - /* avoid temporary routing loops */ - if (router && router_router && - (batadv_compare_eth(router->addr, prev_sender)) && - !(batadv_compare_eth(batadv_ogm_packet->orig, prev_sender)) && - (batadv_compare_eth(router->addr, router_router->addr))) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %pM)\n", - ethhdr->h_source); - 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); + batadv_iv_ogm_process_per_outif(ethhdr, orig_node, batadv_ogm_packet, + tt_buff, if_incoming, NULL);
- if (!orig_neigh_node) - goto out; + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue;
- /* Update nc_nodes of the originator */ - batadv_nc_update_nc_node(bat_priv, orig_node, orig_neigh_node, - batadv_ogm_packet, is_single_hop_neigh); + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue;
- orig_neigh_router = batadv_orig_node_get_router(orig_neigh_node); - - /* drop packet if sender is not a direct neighbor and if we - * don't route towards it - */ - if (!is_single_hop_neigh && (!orig_neigh_router)) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: OGM via unknown neighbor!\n"); - goto out_neigh; - } - - is_bidirect = batadv_iv_ogm_calc_tq(orig_node, orig_neigh_node, - batadv_ogm_packet, if_incoming, - NULL); - - /* update ranking if it is not a duplicate or has the same - * seqno and similar ttl as the non-duplicate - */ - sameseq = orig_node->last_real_seqno == ntohl(batadv_ogm_packet->seqno); - similar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->header.ttl; - if (is_bidirect && ((dup_status == BATADV_NO_DUP) || - (sameseq && similar_ttl))) - batadv_iv_ogm_orig_update(bat_priv, orig_node, ethhdr, - batadv_ogm_packet, if_incoming, - NULL, tt_buff, dup_status); - - /* is single hop (direct) neighbor */ - if (is_single_hop_neigh) { - /* mark direct link on incoming interface */ - batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, - is_single_hop_neigh, - is_from_best_next_hop, if_incoming); - - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Forwarding packet: rebroadcast neighbor packet with direct link flag\n"); - goto out_neigh; + batadv_iv_ogm_process_per_outif(ethhdr, orig_node, + batadv_ogm_packet, tt_buff, + if_incoming, hard_iface); } - - /* multihop originator */ - if (!is_bidirect) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: not received via bidirectional link\n"); - goto out_neigh; - } - - if (dup_status == BATADV_NEIGH_DUP) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: duplicate packet received\n"); - goto out_neigh; - } - - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Forwarding packet: rebroadcast originator packet\n"); - batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, - is_single_hop_neigh, is_from_best_next_hop, - if_incoming); - -out_neigh: - if ((orig_neigh_node) && (!is_single_hop_neigh)) - batadv_orig_node_free_ref(orig_neigh_node); -out: - if (router) - batadv_neigh_node_free_ref(router); - if (router_router) - batadv_neigh_node_free_ref(router_router); - if (orig_neigh_router) - batadv_neigh_node_free_ref(orig_neigh_router); + rcu_read_unlock();
batadv_orig_node_free_ref(orig_node); } diff --git a/distributed-arp-table.c b/distributed-arp-table.c index 99da412..d43e486 100644 --- a/distributed-arp-table.c +++ b/distributed-arp-table.c @@ -586,7 +586,8 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv, if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND) continue;
- neigh_node = batadv_orig_node_get_router(cand[i].orig_node); + neigh_node = batadv_orig_node_get_router(cand[i].orig_node, + NULL); if (!neigh_node) goto free_orig;
diff --git a/gateway_client.c b/gateway_client.c index c134321..efe6238 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -131,7 +131,7 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) continue;
orig_node = gw_node->orig_node; - router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_node_get_router(orig_node, NULL); if (!router) continue;
@@ -222,7 +222,7 @@ void batadv_gw_election(struct batadv_priv *bat_priv) if (next_gw) { sprintf(gw_addr, "%pM", next_gw->orig_node->orig);
- router = batadv_orig_node_get_router(next_gw->orig_node); + router = batadv_orig_node_get_router(next_gw->orig_node, NULL); if (!router) { batadv_gw_deselect(bat_priv); goto out; @@ -288,7 +288,7 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, if (!curr_gw_orig) goto deselect;
- router_gw = batadv_orig_node_get_router(curr_gw_orig); + router_gw = batadv_orig_node_get_router(curr_gw_orig, NULL); if (!router_gw) goto deselect;
@@ -300,7 +300,7 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, if (curr_gw_orig == orig_node) goto out;
- router_orig = batadv_orig_node_get_router(orig_node); + router_orig = batadv_orig_node_get_router(orig_node, NULL); if (!router_orig) goto out;
@@ -528,7 +528,7 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv, int ret = -1;
rcu_read_lock(); - router = batadv_orig_node_get_router(gw_node->orig_node); + router = batadv_orig_node_get_router(gw_node->orig_node, NULL); if (!router) goto out;
diff --git a/icmp_socket.c b/icmp_socket.c index b27508b..fc5cd17 100644 --- a/icmp_socket.c +++ b/icmp_socket.c @@ -222,7 +222,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, if (!orig_node) goto dst_unreach;
- neigh_node = batadv_orig_node_get_router(orig_node); + neigh_node = batadv_orig_node_get_router(orig_node, NULL); if (!neigh_node) goto dst_unreach;
diff --git a/network-coding.c b/network-coding.c index ef88a1f..0e1cd7e 100644 --- a/network-coding.c +++ b/network-coding.c @@ -1010,12 +1010,17 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, int coded_size = sizeof(*coded_packet); int header_add = coded_size - unicast_size;
- router_neigh = batadv_orig_node_get_router(neigh_node->orig_node); + /* TODO: do we need to consider the outgoing interface for + * coded packets? + */ + router_neigh = batadv_orig_node_get_router(neigh_node->orig_node, + NULL); if (!router_neigh) goto out;
neigh_tmp = nc_packet->neigh_node; - router_coding = batadv_orig_node_get_router(neigh_tmp->orig_node); + router_coding = batadv_orig_node_get_router(neigh_tmp->orig_node, + NULL); if (!router_coding) goto out;
diff --git a/originator.c b/originator.c index 66af68d..16e277d 100644 --- a/originator.c +++ b/originator.c @@ -92,14 +92,28 @@ void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node) call_rcu(&neigh_node->rcu, batadv_neigh_node_free_rcu); }
-/* increases the refcounter of a found router */ +/** + * batadv_orig_node_get_router - router to the originator depending on iface + * @orig_node: the orig node for the router + * @if_received: the interface where the packet to be transmitted was received + * Returns: the neighbor which should be router for this orig_node/iface + */ struct batadv_neigh_node * -batadv_orig_node_get_router(struct batadv_orig_node *orig_node) +batadv_orig_node_get_router(struct batadv_orig_node *orig_node, + const struct batadv_hard_iface *if_received) { - struct batadv_neigh_node *router; + struct batadv_orig_node_ifinfo *orig_node_ifinfo; + struct batadv_neigh_node *router = NULL;
rcu_read_lock(); - router = rcu_dereference(orig_node->router); + hlist_for_each_entry_rcu(orig_node_ifinfo, + &orig_node->ifinfo_list, list) { + if (orig_node_ifinfo->if_outgoing != if_received) + continue; + + router = rcu_dereference(orig_node_ifinfo->router); + break; + }
if (router && !atomic_inc_not_zero(&router->refcount)) router = NULL; @@ -109,6 +123,53 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node) }
/** + * batadv_orig_node_get_ifinfo - gets the ifinfo from an orig_node + * @orig_node: the orig node to be queried + * @if_received: the interface for which the ifinfo should be acquired + * Returns: the requested orig_node_ifinfo or NULL if not found + * + * Note: this function must be called under rcu lock + */ +struct batadv_orig_node_ifinfo * +batadv_orig_node_get_ifinfo(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_received) +{ + struct batadv_orig_node_ifinfo *tmp_orig_node_ifinfo, + *orig_node_ifinfo = NULL; + + hlist_for_each_entry_rcu(tmp_orig_node_ifinfo, + &orig_node->ifinfo_list, list) { + if (tmp_orig_node_ifinfo->if_outgoing != if_received) + continue; + orig_node_ifinfo = tmp_orig_node_ifinfo; + break; + } + + spin_lock_bh(&orig_node->neigh_list_lock); + if (!orig_node_ifinfo) { + orig_node_ifinfo = kzalloc(sizeof(*orig_node_ifinfo), + GFP_ATOMIC); + if (!orig_node_ifinfo) + goto out; + + if (if_received && + !atomic_inc_not_zero(&if_received->refcount)) { + kfree(orig_node_ifinfo); + orig_node_ifinfo = NULL; + goto out; + } + + orig_node_ifinfo->if_outgoing = if_received; + INIT_HLIST_NODE(&orig_node_ifinfo->list); + hlist_add_head_rcu(&orig_node_ifinfo->list, + &orig_node->ifinfo_list); + } + spin_unlock_bh(&orig_node->neigh_list_lock); +out: + return orig_node_ifinfo; +} + +/** * batadv_neigh_node_get_ifinfo - gets the ifinfo from an neigh_node * @neigh_node: the neigh node to be queried * @if_outgoing: the interface for which the ifinfo should be acquired @@ -185,11 +246,24 @@ out: return neigh_node; }
+static void batadv_orig_node_ifinfo_free_rcu(struct rcu_head *rcu) +{ + struct batadv_orig_node_ifinfo *orig_node_ifinfo; + + orig_node_ifinfo = container_of(rcu, struct batadv_orig_node_ifinfo, + rcu); + if (orig_node_ifinfo->if_outgoing) + batadv_hardif_free_ref(orig_node_ifinfo->if_outgoing); + + kfree(orig_node_ifinfo); +} + static void batadv_orig_node_free_rcu(struct rcu_head *rcu) { struct hlist_node *node_tmp; struct batadv_neigh_node *neigh_node; struct batadv_orig_node *orig_node; + struct batadv_orig_node_ifinfo *orig_node_ifinfo;
orig_node = container_of(rcu, struct batadv_orig_node, rcu);
@@ -202,6 +276,12 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) batadv_neigh_node_free_ref(neigh_node); }
+ hlist_for_each_entry_safe(orig_node_ifinfo, node_tmp, + &orig_node->ifinfo_list, list) { + hlist_del_rcu(&orig_node_ifinfo->list); + call_rcu(&orig_node_ifinfo->rcu, + batadv_orig_node_ifinfo_free_rcu); + } spin_unlock_bh(&orig_node->neigh_list_lock);
/* Free nc_nodes */ @@ -295,6 +375,7 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, return NULL;
INIT_HLIST_HEAD(&orig_node->neigh_list); + INIT_HLIST_HEAD(&orig_node->ifinfo_list); spin_lock_init(&orig_node->ogm_cnt_lock); spin_lock_init(&orig_node->bcast_seqno_lock); spin_lock_init(&orig_node->neigh_list_lock); @@ -309,7 +390,6 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); batadv_dat_init_orig_node_addr(orig_node); - orig_node->router = NULL; orig_node->tt_crc = 0; atomic_set(&orig_node->last_ttvn, 0); orig_node->tt_buff = NULL; @@ -354,6 +434,53 @@ free_orig_node: }
/** + * batadv_purge_orig_ifinfo - purge ifinfo entries from originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be checked + * Returns: true if any ifinfo entry was purged, false otherwise + */ +static bool +batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node) +{ + struct hlist_node *node_tmp; + struct batadv_orig_node_ifinfo *orig_node_ifinfo; + bool ifinfo_purged = false; + struct batadv_hard_iface *if_outgoing; + + rcu_read_lock(); + spin_lock_bh(&orig_node->neigh_list_lock); + + /* for all neighbors towards this originator ... */ + hlist_for_each_entry_safe(orig_node_ifinfo, node_tmp, + &orig_node->ifinfo_list, list) { + if_outgoing = orig_node_ifinfo->if_outgoing; + if (!if_outgoing) + continue; + + if ((if_outgoing->if_status != BATADV_IF_INACTIVE) && + (if_outgoing->if_status != BATADV_IF_NOT_IN_USE) && + (if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)) + continue; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "router/ifinfo purge: originator %pM, iface: %s\n", + orig_node->orig, if_outgoing->net_dev->name); + + ifinfo_purged = true; + + hlist_del_rcu(&orig_node_ifinfo->list); + call_rcu(&orig_node_ifinfo->rcu, + batadv_orig_node_ifinfo_free_rcu); + } + + spin_unlock_bh(&orig_node->neigh_list_lock); + rcu_read_unlock(); + return ifinfo_purged; +} + + +/** * batadv_purge_orig_neighbors - purges neighbors from originator * @bat_priv: the bat priv with all the soft interface information * @orig_node: orig node which is to be checked @@ -446,6 +573,8 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node) { struct batadv_neigh_node *best_neigh_node; + struct batadv_hard_iface *hard_iface; + bool changed = false;
if (batadv_has_timed_out(orig_node->last_seen, 2 * BATADV_PURGE_TIMEOUT)) { @@ -455,11 +584,31 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, jiffies_to_msecs(orig_node->last_seen)); return true; } - if (!batadv_purge_orig_neighbors(bat_priv, orig_node)) + changed = changed || batadv_purge_orig_ifinfo(bat_priv, orig_node); + changed = changed || batadv_purge_orig_neighbors(bat_priv, orig_node); + + if (!changed) return false;
+ /* first for NULL ... */ best_neigh_node = batadv_find_best_neighbor(orig_node, NULL); - batadv_update_route(bat_priv, orig_node, best_neigh_node); + batadv_update_route(bat_priv, orig_node, NULL, best_neigh_node); + + /* ... then for all other interfaces. */ + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue; + + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + best_neigh_node = batadv_find_best_neighbor(orig_node, + hard_iface); + batadv_update_route(bat_priv, orig_node, hard_iface, + best_neigh_node); + } + rcu_read_unlock();
return false; } @@ -551,7 +700,8 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - neigh_node = batadv_orig_node_get_router(orig_node); + neigh_node = batadv_orig_node_get_router(orig_node, + NULL); if (!neigh_node) continue;
diff --git a/originator.h b/originator.h index a53b016..8eb049c 100644 --- a/originator.h +++ b/originator.h @@ -34,7 +34,16 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, const uint8_t *neigh_addr); 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); +batadv_orig_node_get_router(struct batadv_orig_node *orig_node, + const struct batadv_hard_iface *if_received); +void +batadv_orig_node_set_router(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_received, + struct batadv_neigh_node *router); +struct batadv_orig_node_ifinfo * +batadv_orig_node_get_ifinfo(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_received); + struct batadv_neigh_node_ifinfo * batadv_neigh_node_get_ifinfo(struct batadv_neigh_node *neigh, struct batadv_hard_iface *if_outgoing); diff --git a/routing.c b/routing.c index 0f08cf8..c1b6d85 100644 --- a/routing.c +++ b/routing.c @@ -33,13 +33,35 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if);
+/* _batadv_update_route - set the router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be configured + * @recv_if: the receive interface for which this route is set + * @neigh_node: neighbor which should be the next router + * + * This function does not perform any error checks + */ static void _batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, struct batadv_neigh_node *neigh_node) { + struct batadv_orig_node_ifinfo *orig_node_ifinfo; struct batadv_neigh_node *curr_router;
- curr_router = batadv_orig_node_get_router(orig_node); + /* use batadv_orig_node_get_ifinfo() instead of + * batadv_orig_node_get_router() to re-use the ifinfo below. + */ + rcu_read_lock(); + orig_node_ifinfo = batadv_orig_node_get_ifinfo(orig_node, recv_if); + if (orig_node_ifinfo) { + curr_router = rcu_dereference(orig_node_ifinfo->router); + if (!atomic_inc_not_zero(&curr_router->refcount)) + curr_router = NULL; + } else { + rcu_read_unlock(); + return; + }
/* route deleted */ if ((curr_router) && (!neigh_node)) { @@ -69,16 +91,26 @@ static void _batadv_update_route(struct batadv_priv *bat_priv, neigh_node = NULL;
spin_lock_bh(&orig_node->neigh_list_lock); - rcu_assign_pointer(orig_node->router, neigh_node); + rcu_assign_pointer(orig_node_ifinfo->router, neigh_node); spin_unlock_bh(&orig_node->neigh_list_lock);
+ rcu_read_unlock(); + /* decrease refcount of previous best neighbor */ if (curr_router) batadv_neigh_node_free_ref(curr_router); }
+/** + * batadv_update_route - set the router for this originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: orig node which is to be configured + * @recv_if: the receive interface for which this route is set + * @neigh_node: neighbor which should be the next router + */ void batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, struct batadv_neigh_node *neigh_node) { struct batadv_neigh_node *router = NULL; @@ -86,10 +118,10 @@ void batadv_update_route(struct batadv_priv *bat_priv, if (!orig_node) goto out;
- router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_node_get_router(orig_node, recv_if);
if (router != neigh_node) - _batadv_update_route(bat_priv, orig_node, neigh_node); + _batadv_update_route(bat_priv, orig_node, recv_if, neigh_node);
out: if (router) @@ -378,7 +410,7 @@ batadv_find_router(struct batadv_priv *bat_priv, if (!orig_node) return NULL;
- router = batadv_orig_node_get_router(orig_node); + router = batadv_orig_node_get_router(orig_node, recv_if); if (!router) return NULL;
diff --git a/routing.h b/routing.h index b8fed80..7a7c6e9 100644 --- a/routing.h +++ b/routing.h @@ -25,6 +25,7 @@ bool batadv_check_management_packet(struct sk_buff *skb, int header_len); void batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if, struct batadv_neigh_node *neigh_node); int batadv_recv_icmp_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); diff --git a/translation-table.c b/translation-table.c index 1e2adfe..c40b734 100644 --- a/translation-table.c +++ b/translation-table.c @@ -949,7 +949,8 @@ batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry)
head = &tt_global_entry->orig_list; hlist_for_each_entry_rcu(orig_entry, head, list) { - router = batadv_orig_node_get_router(orig_entry->orig_node); + router = batadv_orig_node_get_router(orig_entry->orig_node, + NULL); if (!router) continue;
diff --git a/types.h b/types.h index 2bf7481..7e49f18 100644 --- a/types.h +++ b/types.h @@ -85,6 +85,23 @@ struct batadv_hard_iface { struct work_struct cleanup_work; };
+/* struct batadv_orig_node_ifinfo - originator info per outgoing interface + * @list: list node for orig_node::ifinfo_list + * @if_outgoing: pointer to outgoing hard interface + * @router: router that should be used to reach this originator + * @last_real_seqno: last and best known sequence number + * @last_ttl: ttl of last received packet + * @rcu: struct used for freeing in an RCU-safe manner + */ +struct batadv_orig_node_ifinfo { + struct hlist_node list; + struct batadv_hard_iface *if_outgoing; + struct batadv_neigh_node __rcu *router; /* rcu protected pointer */ + uint32_t last_real_seqno; + uint8_t last_ttl; + struct rcu_head rcu; +}; + /** * struct batadv_frag_table_entry - head in the fragment buffer table * @head: head of list with fragments @@ -117,7 +134,7 @@ struct batadv_frag_list_entry { * 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 + * @ifinfo_list: list for routers per outgoing interface * @batadv_dat_addr_t: address of the orig node in the distributed hash * @bcast_own: bitfield containing the number of our OGMs this orig_node * rebroadcasted "back" to us (relative to last_real_seqno) @@ -135,8 +152,6 @@ struct batadv_frag_list_entry { * @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 - * @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 * from this orig node this host already has seen (relative to * last_bcast_seqno) @@ -159,7 +174,7 @@ struct batadv_frag_list_entry { struct batadv_orig_node { uint8_t orig[ETH_ALEN]; uint8_t primary_addr[ETH_ALEN]; - struct batadv_neigh_node __rcu *router; /* rcu protected pointer */ + struct hlist_head ifinfo_list; #ifdef CONFIG_BATMAN_ADV_DAT batadv_dat_addr_t dat_addr; #endif @@ -176,8 +191,6 @@ struct batadv_orig_node { spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */ atomic_t tt_size; bool tt_initialised; - 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;
From: Simon Wunderlich simon@open-mesh.com
If the same interface is used for sending and receiving, there might be throughput degradation on half-duplex interfaces such as WiFi. Add a penalty if the same interface is used to reflect this problem in the metric.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- bat_iv_ogm.c | 26 ++++++++++++++++++++++---- hard-interface.c | 2 +- hard-interface.h | 1 + 3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c index f0b16d6..324bf5f 100644 --- a/bat_iv_ogm.c +++ b/bat_iv_ogm.c @@ -901,6 +901,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, 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_iface_penalty; unsigned int combined_tq;
/* find corresponding one hop neighbor */ @@ -983,15 +984,32 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, inv_asym_penalty /= neigh_rq_max_cube; tq_asym_penalty = BATADV_TQ_MAX_VALUE - inv_asym_penalty;
- combined_tq = batadv_ogm_packet->tq * tq_own * tq_asym_penalty; - combined_tq /= BATADV_TQ_MAX_VALUE * BATADV_TQ_MAX_VALUE; + /* penalize if the OGM is forwarded on the same interface. WiFi + * interfaces and other half duplex devices suffer from throughput + * drops as they can't send and receive at the same time. + */ + tq_iface_penalty = BATADV_TQ_MAX_VALUE; + if (if_outgoing && (if_incoming == if_outgoing) && + batadv_is_wifi_netdev(if_outgoing->net_dev)) + tq_iface_penalty = BATADV_TQ_MAX_VALUE / 2; + + combined_tq = batadv_ogm_packet->tq * + tq_own * + tq_asym_penalty * + tq_iface_penalty; + combined_tq /= BATADV_TQ_MAX_VALUE * + BATADV_TQ_MAX_VALUE * + BATADV_TQ_MAX_VALUE; batadv_ogm_packet->tq = combined_tq;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "bidirectional: orig = %-15pM neigh = %-15pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, total tq: %3i\n", + "bidirectional: orig = %-15pM neigh = %-15pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, iface_penalty: %3i, total tq: %3i, if_incoming = %s, if_outgoing = %s\n", orig_node->orig, orig_neigh_node->orig, total_count, neigh_rq_count, tq_own, - tq_asym_penalty, batadv_ogm_packet->tq); + tq_asym_penalty, tq_iface_penalty, + batadv_ogm_packet->tq, + if_incoming ? if_incoming->net_dev->name : "NULL", + if_outgoing ? if_outgoing->net_dev->name : "NULL");
/* if link has the minimum required transmission quality * consider it bidirectional diff --git a/hard-interface.c b/hard-interface.c index c343aa8..dbcd446 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -124,7 +124,7 @@ static int batadv_is_valid_iface(const struct net_device *net_dev) * * Returns true if the net device is a 802.11 wireless device, false otherwise. */ -static bool batadv_is_wifi_netdev(struct net_device *net_device) +bool batadv_is_wifi_netdev(struct net_device *net_device) { #ifdef CONFIG_WIRELESS_EXT /* pre-cfg80211 drivers have to implement WEXT, so it is possible to diff --git a/hard-interface.h b/hard-interface.h index 4989288..1a90ea5 100644 --- a/hard-interface.h +++ b/hard-interface.h @@ -52,6 +52,7 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface); void batadv_update_min_mtu(struct net_device *soft_iface); void batadv_hardif_free_rcu(struct rcu_head *rcu); bool batadv_is_wifi_iface(int ifindex); +bool batadv_is_wifi_netdev(struct net_device *net_device);
static inline void batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface)
From: Simon Wunderlich simon@open-mesh.com
The current OGM sending an aggregation functionality decides on which interfaces a packet should be sent when it parses the forward packet struct. However, with the network wide multi interface optimization the outgoing interface is decided by the OGM processing function.
This is reflected by moving the decision in the OGM processing function and add the outgoing interface in the forwarding packet struct. This practically implies that an OGM may be added multiple times (once per outgoing interface), and this also affects aggregation which needs to consider the outgoing interface as well.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- bat_iv_ogm.c | 133 +++++++++++++++++++++++++++++++++++++++------------------- send.c | 13 ++++-- types.h | 7 +++- 3 files changed, 104 insertions(+), 49 deletions(-)
diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c index 324bf5f..3fc900c 100644 --- a/bat_iv_ogm.c +++ b/bat_iv_ogm.c @@ -272,7 +272,6 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, /* send a batman ogm packet */ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet) { - struct batadv_hard_iface *hard_iface; struct net_device *soft_iface; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL; @@ -292,6 +291,12 @@ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet) soft_iface = forw_packet->if_incoming->soft_iface; bat_priv = netdev_priv(soft_iface);
+ if (WARN_ON(!forw_packet->if_outgoing)) + goto out; + + if (WARN_ON(forw_packet->if_outgoing->soft_iface != soft_iface)) + goto out; + if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE) goto out;
@@ -299,52 +304,35 @@ static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet) if (!primary_if) goto out;
- /* multihomed peer assumed - * non-primary OGMs are only broadcasted on their interface - */ - if ((directlink && (batadv_ogm_packet->header.ttl == 1)) || - (forw_packet->own && (forw_packet->if_incoming != primary_if))) { - /* FIXME: what about aggregated packets ? */ - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "%s packet (originator %pM, seqno %u, TTL %d) on interface %s [%pM]\n", - (forw_packet->own ? "Sending own" : "Forwarding"), - batadv_ogm_packet->orig, - ntohl(batadv_ogm_packet->seqno), - batadv_ogm_packet->header.ttl, - forw_packet->if_incoming->net_dev->name, - forw_packet->if_incoming->net_dev->dev_addr); - - /* skb is only used once and than forw_packet is free'd */ - batadv_send_skb_packet(forw_packet->skb, - forw_packet->if_incoming, - batadv_broadcast_addr); - forw_packet->skb = NULL; - - goto out; - } - - /* broadcast on every interface */ - rcu_read_lock(); - list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { - if (hard_iface->soft_iface != soft_iface) - continue; - - batadv_iv_ogm_send_to_if(forw_packet, hard_iface); - } - rcu_read_unlock(); + /* only for one specific outgoing interface */ + batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing);
out: if (primary_if) batadv_hardif_free_ref(primary_if); }
-/* return true if new_packet can be aggregated with forw_packet */ +/** + * batadv_iv_ogm_can_aggregate - find out if an OGM can be aggregated on an + * existing forward packet + * @new_bat_ogm_packet: OGM packet to be aggregated + * @bat_priv: the bat priv with all the soft interface information + * @packet_len: (total) length of the OGM + * @send_time: timestamp (jiffies) when the packet is to be sent + * @direktlink: true if this is a direct link packet + * @if_incoming: interface where the packet was received + * @if_outgoing: interface for which the retransmission should be considered + * @forw_packet: the forwarded packet which should be checked + * + * Returns true if new_packet can be aggregated with forw_packet + */ static bool batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, struct batadv_priv *bat_priv, int packet_len, unsigned long send_time, bool directlink, const struct batadv_hard_iface *if_incoming, + const struct batadv_hard_iface *if_outgoing, const struct batadv_forw_packet *forw_packet) { struct batadv_ogm_packet *batadv_ogm_packet; @@ -378,6 +366,13 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, if (!primary_if) goto out;
+ /* packet is not leaving on the same interface. + * TODO: some other parts here could be reworked as the + * outgoing interface is specified now. + */ + if (forw_packet->if_outgoing != if_outgoing) + goto out; + /* packets without direct link flag and high TTL * are flooded through the net */ @@ -424,6 +419,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, int packet_len, unsigned long send_time, bool direct_link, struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing, int own_packet) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); @@ -434,6 +430,9 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, if (!atomic_inc_not_zero(&if_incoming->refcount)) return;
+ if (!atomic_inc_not_zero(&if_outgoing->refcount)) + goto out_free_incoming; + /* own packet should always be scheduled */ if (!own_packet) { if (!batadv_atomic_dec_not_zero(&bat_priv->batman_queue_left)) { @@ -473,6 +472,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
forw_packet_aggr->own = own_packet; forw_packet_aggr->if_incoming = if_incoming; + forw_packet_aggr->if_outgoing = if_outgoing; forw_packet_aggr->num_packets = 0; forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS; forw_packet_aggr->send_time = send_time; @@ -495,6 +495,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
return; out: + batadv_hardif_free_ref(if_outgoing); +out_free_incoming: batadv_hardif_free_ref(if_incoming); }
@@ -518,10 +520,21 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, } }
+/** + * batadv_iv_ogm_queue_add - queue up an OGM for transmission + * @bat_priv: the bat priv with all the soft interface information + * @packet_buff: pointer to the OGM + * @packet_len: (total) length of the OGM + * @if_incoming: interface where the packet was received + * @if_outgoing: interface for which the retransmission should be considered + * @own_packet: true if it is a self-generated ogm + * @send_time: timestamp (jiffies) when the packet is to be sent + */ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, unsigned char *packet_buff, int packet_len, struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing, int own_packet, unsigned long send_time) { /* _aggr -> pointer to the packet we want to aggregate with @@ -547,6 +560,7 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, bat_priv, packet_len, send_time, direct_link, if_incoming, + if_outgoing, forw_packet_pos)) { forw_packet_aggr = forw_packet_pos; break; @@ -570,7 +584,8 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
batadv_iv_ogm_aggregate_new(packet_buff, packet_len, send_time, direct_link, - if_incoming, own_packet); + if_incoming, if_outgoing, + own_packet); } else { batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff, packet_len, direct_link); @@ -583,7 +598,8 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, struct batadv_ogm_packet *batadv_ogm_packet, bool is_single_hop_neigh, bool is_from_best_next_hop, - struct batadv_hard_iface *if_incoming) + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); uint16_t tvlv_len; @@ -628,7 +644,8 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet, BATADV_OGM_HLEN + tvlv_len, - if_incoming, 0, batadv_iv_ogm_fwd_send_time()); + if_incoming, if_outgoing, 0, + batadv_iv_ogm_fwd_send_time()); }
/** @@ -671,10 +688,11 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff; struct batadv_ogm_packet *batadv_ogm_packet; - struct batadv_hard_iface *primary_if; + struct batadv_hard_iface *primary_if, *tmp_hard_iface; int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len; uint32_t seqno; uint16_t tvlv_len = 0; + unsigned long send_time;
primary_if = batadv_primary_if_get_selected(bat_priv);
@@ -697,9 +715,31 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) atomic_inc(&hard_iface->bat_iv.ogm_seqno);
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, - batadv_iv_ogm_emit_send_time(bat_priv)); + + send_time = batadv_iv_ogm_emit_send_time(bat_priv); + + if (hard_iface == primary_if) { + /* OGMs from primary interfaces are scheduled on all + * interfaces. + */ + rcu_read_lock(); + list_for_each_entry_rcu(tmp_hard_iface, &batadv_hardif_list, + list) { + if (tmp_hard_iface->soft_iface != + hard_iface->soft_iface) + continue; + batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, + *ogm_buff_len, hard_iface, + tmp_hard_iface, 1, send_time); + } + rcu_read_unlock(); + } else { + /* OGMs from secondary interfaces are only scheduled on their + * respective interfaces. + */ + batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, + hard_iface, hard_iface, 1, send_time); + }
if (primary_if) batadv_hardif_free_ref(primary_if); @@ -1261,6 +1301,10 @@ batadv_iv_ogm_process_per_outif(const struct ethhdr *ethhdr, if_outgoing, tt_buff, dup_status); rcu_read_unlock();
+ /* don't forward packet if no outgoing interface was specified */ + if (!if_outgoing) + goto out_neigh; + /* is single hop (direct) neighbor */ if (is_single_hop_neigh) { if ((batadv_ogm_packet->header.ttl <= 2) && @@ -1272,7 +1316,8 @@ batadv_iv_ogm_process_per_outif(const struct ethhdr *ethhdr, /* mark direct link on incoming interface */ batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, is_single_hop_neigh, - is_from_best_next_hop, if_incoming); + is_from_best_next_hop, if_incoming, + if_outgoing);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Forwarding packet: rebroadcast neighbor packet with direct link flag\n"); @@ -1296,7 +1341,7 @@ batadv_iv_ogm_process_per_outif(const struct ethhdr *ethhdr, "Forwarding packet: rebroadcast originator packet\n"); batadv_iv_ogm_forward(orig_node, ethhdr, batadv_ogm_packet, is_single_hop_neigh, is_from_best_next_hop, - if_incoming); + if_incoming, if_outgoing);
out_neigh: if ((orig_neigh_node) && (!is_single_hop_neigh)) diff --git a/send.c b/send.c index 616364e..485b703 100644 --- a/send.c +++ b/send.c @@ -328,6 +328,8 @@ static void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet) kfree_skb(forw_packet->skb); if (forw_packet->if_incoming) batadv_hardif_free_ref(forw_packet->if_incoming); + if (forw_packet->if_outgoing) + batadv_hardif_free_ref(forw_packet->if_outgoing); kfree(forw_packet); }
@@ -391,6 +393,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
forw_packet->skb = newskb; forw_packet->if_incoming = primary_if; + forw_packet->if_outgoing = NULL;
/* how often did we send the bcast packet ? */ forw_packet->num_packets = 0; @@ -488,9 +491,12 @@ void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work)
/* we have to have at least one packet in the queue * to determine the queues wake up time unless we are - * shutting down + * shutting down. + * + * only re-schedule if this is the "original" copy. */ - if (forw_packet->own) + if (forw_packet->own && + forw_packet->if_incoming == forw_packet->if_outgoing) batadv_schedule_bat_ogm(forw_packet->if_incoming);
out: @@ -551,7 +557,8 @@ batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, * we delete only packets belonging to the given interface */ if ((hard_iface) && - (forw_packet->if_incoming != hard_iface)) + (forw_packet->if_incoming != hard_iface) && + (forw_packet->if_outgoing != hard_iface)) continue;
spin_unlock_bh(&bat_priv->forw_bat_list_lock); diff --git a/types.h b/types.h index 7e49f18..37bf7db 100644 --- a/types.h +++ b/types.h @@ -915,8 +915,10 @@ struct batadv_skb_cb { * @direct_link_flags: direct link flags for aggregated OGM packets * @num_packets: counter for bcast packet retransmission * @delayed_work: work queue callback item for packet sending - * @if_incoming: pointer incoming hard-iface or primary iface if locally - * generated packet + * @if_incoming: pointer to incoming hard-iface or primary iface if + * locally generated packet + * @if_outgoing: packet where the packet should be sent to, or NULL if + * unspecified */ struct batadv_forw_packet { struct hlist_node list; @@ -928,6 +930,7 @@ struct batadv_forw_packet { uint8_t num_packets; struct delayed_work delayed_work; struct batadv_hard_iface *if_incoming; + struct batadv_hard_iface *if_outgoing; };
/**
From: Simon Wunderlich simon@open-mesh.com
With the new interface alternating, the first hop may send packets in a round robin fashion to it's neighbors because it has multiple valid routes built by the multi interface optimization. This patch enables the feature if bonding is selected. Note that unlike the bonding implemented before, this version is much simpler and may even enable multi path routing to a certain degree.
Signed-off-by: Simon Wunderlich simon@open-mesh.com --- routing.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- routing.h | 2 +- types.h | 2 ++ 3 files changed, 104 insertions(+), 4 deletions(-)
diff --git a/routing.c b/routing.c index c1b6d85..87591b6 100644 --- a/routing.c +++ b/routing.c @@ -403,9 +403,15 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, 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) + struct batadv_hard_iface *recv_if) { - struct batadv_neigh_node *router; + struct batadv_neigh_node *router, *cand_router, + *first_candidate_router = NULL, + *next_candidate_router; + struct batadv_neigh_node_ifinfo *router_ifinfo, *cand_ifinfo; + struct batadv_orig_node_ifinfo *cand, *first_candidate = NULL, + *next_candidate = NULL; + bool last_candidate_found = false;
if (!orig_node) return NULL; @@ -414,7 +420,99 @@ batadv_find_router(struct batadv_priv *bat_priv, if (!router) return NULL;
- /* TODO: fill this later with new bonding mechanism */ + /* only consider bonding for recv_if == NULL (first hop) and if + * activated. + */ + if (recv_if || !atomic_read(&bat_priv->bonding)) + return router; + + /* bonding: loop through the list of possible routers found + * for the various outgoing interfaces and find a candidate after + * the last chosen bonding candidate (next_candidate). If no such + * router is found, use the first candidate found (the last chosen + * bonding candidate might have been the last one in the list). + * If this can't be found either, return the previously choosen + * router - obviously there are no other candidates. + */ + rcu_read_lock(); + router_ifinfo = batadv_neigh_node_get_ifinfo(router, recv_if); + hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, + list) { + cand_router = rcu_dereference(cand->router); + if (!cand_router) + continue; + + if (!atomic_inc_not_zero(&cand_router->refcount)) + continue; + + cand_ifinfo = batadv_neigh_node_get_ifinfo(cand_router, + cand->if_outgoing); + if (!cand_ifinfo) + goto next; + + /* alternative candidate should be good enough to be + * considered + */ + if (cand_ifinfo->tq_avg < + (router_ifinfo->tq_avg - BATADV_BONDING_TQ_THRESHOLD)) + goto next; + + /* don't use the same router twice */ + if (orig_node->last_bonding_candidate && + (orig_node->last_bonding_candidate->router == + cand_router)) + goto next; + + /* mark the first possible candidate */ + if (!first_candidate) { + atomic_inc(&cand_router->refcount); + first_candidate = cand; + first_candidate_router = cand_router; + } + + /* check if already passed the next candidate ... this function + * should get the next candidate AFTER the last used bonding + * candidate. + */ + if (orig_node->last_bonding_candidate) { + if (orig_node->last_bonding_candidate == cand) { + last_candidate_found = true; + goto next; + } + + if (!last_candidate_found) + goto next; + } + + next_candidate = cand; + next_candidate_router = cand_router; + break; +next: + batadv_neigh_node_free_ref(cand_router); + } + + if (next_candidate) { + /* found a possible candidate after the last chosen bonding + * candidate, return it. + */ + batadv_neigh_node_free_ref(router); + if (first_candidate) + batadv_neigh_node_free_ref(first_candidate_router); + router = next_candidate_router; + orig_node->last_bonding_candidate = next_candidate; + } else if (first_candidate) { + /* found no possible candidate after the last candidate, return + * the first candidate if available, or the already selected + * router otherwise. + */ + batadv_neigh_node_free_ref(router); + router = first_candidate_router; + orig_node->last_bonding_candidate = first_candidate; + } else { + orig_node->last_bonding_candidate = NULL; + } + + rcu_read_unlock();
return router; } diff --git a/routing.h b/routing.h index 7a7c6e9..2c80b6a 100644 --- a/routing.h +++ b/routing.h @@ -46,7 +46,7 @@ int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, 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); + struct batadv_hard_iface *recv_if); int batadv_window_protected(struct batadv_priv *bat_priv, int32_t seq_num_diff, unsigned long *last_reset);
diff --git a/types.h b/types.h index 37bf7db..f043125 100644 --- a/types.h +++ b/types.h @@ -135,6 +135,7 @@ struct batadv_frag_list_entry { * @orig: originator ethernet address * @primary_addr: hosts primary interface address * @ifinfo_list: list for routers per outgoing interface + * @last_bonding_candidate: pointer to last ifinfo of last used router * @batadv_dat_addr_t: address of the orig node in the distributed hash * @bcast_own: bitfield containing the number of our OGMs this orig_node * rebroadcasted "back" to us (relative to last_real_seqno) @@ -175,6 +176,7 @@ struct batadv_orig_node { uint8_t orig[ETH_ALEN]; uint8_t primary_addr[ETH_ALEN]; struct hlist_head ifinfo_list; + struct batadv_orig_node_ifinfo *last_bonding_candidate; #ifdef CONFIG_BATMAN_ADV_DAT batadv_dat_addr_t dat_addr; #endif
b.a.t.m.a.n@lists.open-mesh.org