Merge tag 'cris-for-3.14' of git://jni.nu/cris
[cascardo/linux.git] / net / batman-adv / originator.c
index 8ab1434..6df12a2 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009-2013 B.A.T.M.A.N. contributors:
+/* Copyright (C) 2009-2014 B.A.T.M.A.N. contributors:
  *
  * Marek Lindner, Simon Wunderlich
  *
@@ -12,9 +12,7 @@
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "main.h"
@@ -41,7 +39,7 @@ int batadv_compare_orig(const struct hlist_node *node, const void *data2)
        const void *data1 = container_of(node, struct batadv_orig_node,
                                         hash_entry);
 
-       return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
+       return batadv_compare_eth(data1, data2);
 }
 
 /**
@@ -150,20 +148,114 @@ err:
        return -ENOMEM;
 }
 
+/**
+ * batadv_neigh_ifinfo_free_rcu - free the neigh_ifinfo object
+ * @rcu: rcu pointer of the neigh_ifinfo object
+ */
+static void batadv_neigh_ifinfo_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_neigh_ifinfo *neigh_ifinfo;
+
+       neigh_ifinfo = container_of(rcu, struct batadv_neigh_ifinfo, rcu);
+
+       if (neigh_ifinfo->if_outgoing != BATADV_IF_DEFAULT)
+               batadv_hardif_free_ref_now(neigh_ifinfo->if_outgoing);
+
+       kfree(neigh_ifinfo);
+}
+
+/**
+ * batadv_neigh_ifinfo_free_now - decrement the refcounter and possibly free
+ *  the neigh_ifinfo (without rcu callback)
+ * @neigh_ifinfo: the neigh_ifinfo object to release
+ */
+static void
+batadv_neigh_ifinfo_free_ref_now(struct batadv_neigh_ifinfo *neigh_ifinfo)
+{
+       if (atomic_dec_and_test(&neigh_ifinfo->refcount))
+               batadv_neigh_ifinfo_free_rcu(&neigh_ifinfo->rcu);
+}
+
+/**
+ * batadv_neigh_ifinfo_free_ref - decrement the refcounter and possibly free
+ *  the neigh_ifinfo
+ * @neigh_ifinfo: the neigh_ifinfo object to release
+ */
+void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo)
+{
+       if (atomic_dec_and_test(&neigh_ifinfo->refcount))
+               call_rcu(&neigh_ifinfo->rcu, batadv_neigh_ifinfo_free_rcu);
+}
+
+/**
+ * batadv_neigh_node_free_rcu - free the neigh_node
+ * @rcu: rcu pointer of the neigh_node
+ */
+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_ifinfo *neigh_ifinfo;
+
+       neigh_node = container_of(rcu, struct batadv_neigh_node, rcu);
+
+       hlist_for_each_entry_safe(neigh_ifinfo, node_tmp,
+                                 &neigh_node->ifinfo_list, list) {
+               batadv_neigh_ifinfo_free_ref_now(neigh_ifinfo);
+       }
+       batadv_hardif_free_ref_now(neigh_node->if_incoming);
+
+       kfree(neigh_node);
+}
+
+/**
+ * batadv_neigh_node_free_ref_now - decrement the neighbors refcounter
+ *  and possibly free it (without rcu callback)
+ * @neigh_node: neigh neighbor to free
+ */
+static void
+batadv_neigh_node_free_ref_now(struct batadv_neigh_node *neigh_node)
+{
+       if (atomic_dec_and_test(&neigh_node->refcount))
+               batadv_neigh_node_free_rcu(&neigh_node->rcu);
+}
+
+/**
+ * batadv_neigh_node_free_ref - decrement the neighbors refcounter
+ *  and possibly free it
+ * @neigh_node: neigh neighbor to free
+ */
 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 */
+/**
+ * batadv_orig_node_get_router - router to the originator depending on iface
+ * @orig_node: the orig node for the router
+ * @if_outgoing: the interface where the payload packet has been received or
+ *  the OGM should be sent to
+ *
+ * Returns the neighbor which should be router for this orig_node/iface.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
 struct batadv_neigh_node *
-batadv_orig_node_get_router(struct batadv_orig_node *orig_node)
+batadv_orig_router_get(struct batadv_orig_node *orig_node,
+                      const struct batadv_hard_iface *if_outgoing)
 {
-       struct batadv_neigh_node *router;
+       struct batadv_orig_ifinfo *orig_ifinfo;
+       struct batadv_neigh_node *router = NULL;
 
        rcu_read_lock();
-       router = rcu_dereference(orig_node->router);
+       hlist_for_each_entry_rcu(orig_ifinfo, &orig_node->ifinfo_list, list) {
+               if (orig_ifinfo->if_outgoing != if_outgoing)
+                       continue;
+
+               router = rcu_dereference(orig_ifinfo->router);
+               break;
+       }
 
        if (router && !atomic_inc_not_zero(&router->refcount))
                router = NULL;
@@ -172,6 +264,164 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node)
        return router;
 }
 
+/**
+ * batadv_orig_ifinfo_get - find the ifinfo from an orig_node
+ * @orig_node: the orig node to be queried
+ * @if_outgoing: the interface for which the ifinfo should be acquired
+ *
+ * Returns the requested orig_ifinfo or NULL if not found.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
+struct batadv_orig_ifinfo *
+batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
+                      struct batadv_hard_iface *if_outgoing)
+{
+       struct batadv_orig_ifinfo *tmp, *orig_ifinfo = NULL;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tmp, &orig_node->ifinfo_list,
+                                list) {
+               if (tmp->if_outgoing != if_outgoing)
+                       continue;
+
+               if (!atomic_inc_not_zero(&tmp->refcount))
+                       continue;
+
+               orig_ifinfo = tmp;
+               break;
+       }
+       rcu_read_unlock();
+
+       return orig_ifinfo;
+}
+
+/**
+ * batadv_orig_ifinfo_new - search and possibly create an orig_ifinfo object
+ * @orig_node: the orig node to be queried
+ * @if_outgoing: the interface for which the ifinfo should be acquired
+ *
+ * Returns NULL in case of failure or the orig_ifinfo object for the if_outgoing
+ * interface otherwise. The object is created and added to the list
+ * if it does not exist.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
+struct batadv_orig_ifinfo *
+batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node,
+                      struct batadv_hard_iface *if_outgoing)
+{
+       struct batadv_orig_ifinfo *orig_ifinfo = NULL;
+       unsigned long reset_time;
+
+       spin_lock_bh(&orig_node->neigh_list_lock);
+
+       orig_ifinfo = batadv_orig_ifinfo_get(orig_node, if_outgoing);
+       if (orig_ifinfo)
+               goto out;
+
+       orig_ifinfo = kzalloc(sizeof(*orig_ifinfo), GFP_ATOMIC);
+       if (!orig_ifinfo)
+               goto out;
+
+       if (if_outgoing != BATADV_IF_DEFAULT &&
+           !atomic_inc_not_zero(&if_outgoing->refcount)) {
+               kfree(orig_ifinfo);
+               orig_ifinfo = NULL;
+               goto out;
+       }
+
+       reset_time = jiffies - 1;
+       reset_time -= msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
+       orig_ifinfo->batman_seqno_reset = reset_time;
+       orig_ifinfo->if_outgoing = if_outgoing;
+       INIT_HLIST_NODE(&orig_ifinfo->list);
+       atomic_set(&orig_ifinfo->refcount, 2);
+       hlist_add_head_rcu(&orig_ifinfo->list,
+                          &orig_node->ifinfo_list);
+out:
+       spin_unlock_bh(&orig_node->neigh_list_lock);
+       return orig_ifinfo;
+}
+
+/**
+ * batadv_neigh_ifinfo_get - find 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
+ *
+ * The object is returned with refcounter increased by 1.
+ *
+ * Returns the requested neigh_ifinfo or NULL if not found
+ */
+struct batadv_neigh_ifinfo *
+batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
+                       struct batadv_hard_iface *if_outgoing)
+{
+       struct batadv_neigh_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;
+
+               if (!atomic_inc_not_zero(&tmp_neigh_ifinfo->refcount))
+                       continue;
+
+               neigh_ifinfo = tmp_neigh_ifinfo;
+               break;
+       }
+       rcu_read_unlock();
+
+       return neigh_ifinfo;
+}
+
+/**
+ * batadv_neigh_ifinfo_new - search and possibly create an neigh_ifinfo object
+ * @neigh_node: the neigh node to be queried
+ * @if_outgoing: the interface for which the ifinfo should be acquired
+ *
+ * Returns NULL in case of failure or the neigh_ifinfo object for the
+ * if_outgoing interface otherwise. The object is created and added to the list
+ * if it does not exist.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
+struct batadv_neigh_ifinfo *
+batadv_neigh_ifinfo_new(struct batadv_neigh_node *neigh,
+                       struct batadv_hard_iface *if_outgoing)
+{
+       struct batadv_neigh_ifinfo *neigh_ifinfo;
+
+       spin_lock_bh(&neigh->ifinfo_lock);
+
+       neigh_ifinfo = batadv_neigh_ifinfo_get(neigh, if_outgoing);
+       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);
+       atomic_set(&neigh_ifinfo->refcount, 2);
+       neigh_ifinfo->if_outgoing = if_outgoing;
+
+       hlist_add_head_rcu(&neigh_ifinfo->list, &neigh->ifinfo_list);
+
+out:
+       spin_unlock_bh(&neigh->ifinfo_lock);
+
+       return neigh_ifinfo;
+}
+
 /**
  * batadv_neigh_node_new - create and init a new neigh_node object
  * @hard_iface: the interface where the neighbour is connected to
@@ -193,13 +443,13 @@ 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);
+       spin_lock_init(&neigh_node->ifinfo_lock);
 
        memcpy(neigh_node->addr, neigh_addr, ETH_ALEN);
        neigh_node->if_incoming = hard_iface;
        neigh_node->orig_node = orig_node;
 
-       INIT_LIST_HEAD(&neigh_node->bonding_list);
-
        /* extra reference for return */
        atomic_set(&neigh_node->refcount, 2);
 
@@ -207,30 +457,68 @@ out:
        return neigh_node;
 }
 
+/**
+ * batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object
+ * @rcu: rcu pointer of the orig_ifinfo object
+ */
+static void batadv_orig_ifinfo_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_orig_ifinfo *orig_ifinfo;
+
+       orig_ifinfo = container_of(rcu, struct batadv_orig_ifinfo, rcu);
+
+       if (orig_ifinfo->if_outgoing != BATADV_IF_DEFAULT)
+               batadv_hardif_free_ref_now(orig_ifinfo->if_outgoing);
+
+       kfree(orig_ifinfo);
+}
+
+/**
+ * batadv_orig_ifinfo_free_ref - decrement the refcounter and possibly free
+ *  the orig_ifinfo (without rcu callback)
+ * @orig_ifinfo: the orig_ifinfo object to release
+ */
+static void
+batadv_orig_ifinfo_free_ref_now(struct batadv_orig_ifinfo *orig_ifinfo)
+{
+       if (atomic_dec_and_test(&orig_ifinfo->refcount))
+               batadv_orig_ifinfo_free_rcu(&orig_ifinfo->rcu);
+}
+
+/**
+ * batadv_orig_ifinfo_free_ref - decrement the refcounter and possibly free
+ *  the orig_ifinfo
+ * @orig_ifinfo: the orig_ifinfo object to release
+ */
+void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo)
+{
+       if (atomic_dec_and_test(&orig_ifinfo->refcount))
+               call_rcu(&orig_ifinfo->rcu, batadv_orig_ifinfo_free_rcu);
+}
+
 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;
+       struct batadv_orig_ifinfo *orig_ifinfo;
 
        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) {
                hlist_del_rcu(&neigh_node->list);
-               batadv_neigh_node_free_ref(neigh_node);
+               batadv_neigh_node_free_ref_now(neigh_node);
        }
 
+       hlist_for_each_entry_safe(orig_ifinfo, node_tmp,
+                                 &orig_node->ifinfo_list, list) {
+               hlist_del_rcu(&orig_ifinfo->list);
+               batadv_orig_ifinfo_free_ref_now(orig_ifinfo);
+       }
        spin_unlock_bh(&orig_node->neigh_list_lock);
 
        /* Free nc_nodes */
@@ -327,8 +615,8 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
                return NULL;
 
        INIT_HLIST_HEAD(&orig_node->neigh_list);
-       INIT_LIST_HEAD(&orig_node->bond_list);
        INIT_LIST_HEAD(&orig_node->vlan_list);
+       INIT_HLIST_HEAD(&orig_node->ifinfo_list);
        spin_lock_init(&orig_node->bcast_seqno_lock);
        spin_lock_init(&orig_node->neigh_list_lock);
        spin_lock_init(&orig_node->tt_buff_lock);
@@ -344,15 +632,11 @@ struct batadv_orig_node *batadv_orig_node_new(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;
        atomic_set(&orig_node->last_ttvn, 0);
        orig_node->tt_buff = NULL;
        orig_node->tt_buff_len = 0;
        reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
        orig_node->bcast_seqno_reset = reset_time;
-       orig_node->batman_seqno_reset = reset_time;
-
-       atomic_set(&orig_node->bond_candidates, 0);
 
        /* create a vlan object for the "untagged" LAN */
        vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS);
@@ -376,20 +660,76 @@ free_orig_node:
        return NULL;
 }
 
+/**
+ * batadv_purge_orig_ifinfo - purge obsolete 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 batadv_orig_ifinfo *orig_ifinfo;
+       struct batadv_hard_iface *if_outgoing;
+       struct hlist_node *node_tmp;
+       bool ifinfo_purged = false;
+
+       spin_lock_bh(&orig_node->neigh_list_lock);
+
+       /* for all ifinfo objects for this originator */
+       hlist_for_each_entry_safe(orig_ifinfo, node_tmp,
+                                 &orig_node->ifinfo_list, list) {
+               if_outgoing = orig_ifinfo->if_outgoing;
+
+               /* always keep the default interface */
+               if (if_outgoing == BATADV_IF_DEFAULT)
+                       continue;
+
+               /* don't purge if the interface is not (going) down */
+               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_ifinfo->list);
+               batadv_orig_ifinfo_free_ref(orig_ifinfo);
+               if (orig_node->last_bonding_candidate == orig_ifinfo) {
+                       orig_node->last_bonding_candidate = NULL;
+                       batadv_orig_ifinfo_free_ref(orig_ifinfo);
+               }
+       }
+
+       spin_unlock_bh(&orig_node->neigh_list_lock);
+
+       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
+ *
+ * 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)
+                           struct batadv_orig_node *orig_node)
 {
-       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct hlist_node *node_tmp;
        struct batadv_neigh_node *neigh_node;
        bool neigh_purged = false;
        unsigned long last_seen;
        struct batadv_hard_iface *if_incoming;
 
-       *best_neigh = NULL;
-
        spin_lock_bh(&orig_node->neigh_list_lock);
 
        /* for all neighbors towards this originator ... */
@@ -418,15 +758,7 @@ 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 {
-                       /* store the best_neighbour if this is the first
-                        * iteration or if a better neighbor has been found
-                        */
-                       if (!*best_neigh ||
-                           bao->bat_neigh_cmp(neigh_node, *best_neigh) > 0)
-                               *best_neigh = neigh_node;
                }
        }
 
@@ -434,10 +766,57 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
        return neigh_purged;
 }
 
+/**
+ * batadv_find_best_neighbor - finds the best neighbor after purging
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: orig node which is to be checked
+ * @if_outgoing: the interface for which the metric should be compared
+ *
+ * Returns the current best neighbor, with refcount increased.
+ */
+static struct batadv_neigh_node *
+batadv_find_best_neighbor(struct batadv_priv *bat_priv,
+                         struct batadv_orig_node *orig_node,
+                         struct batadv_hard_iface *if_outgoing)
+{
+       struct batadv_neigh_node *best = NULL, *neigh;
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(neigh, &orig_node->neigh_list, list) {
+               if (best && (bao->bat_neigh_cmp(neigh, if_outgoing,
+                                               best, if_outgoing) <= 0))
+                       continue;
+
+               if (!atomic_inc_not_zero(&neigh->refcount))
+                       continue;
+
+               if (best)
+                       batadv_neigh_node_free_ref(best);
+
+               best = neigh;
+       }
+       rcu_read_unlock();
+
+       return best;
+}
+
+/**
+ * batadv_purge_orig_node - purges obsolete information from an orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @orig_node: orig node which is to be checked
+ *
+ * This function checks if the orig_node or substructures of it have become
+ * obsolete, and purges this information if that's the case.
+ *
+ * Returns true if the orig_node is to be removed, false otherwise.
+ */
 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;
 
        if (batadv_has_timed_out(orig_node->last_seen,
                                 2 * BATADV_PURGE_TIMEOUT)) {
@@ -446,12 +825,39 @@ 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);
        }
+       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(bat_priv, orig_node,
+                                                   BATADV_IF_DEFAULT);
+       batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT,
+                           best_neigh_node);
+       if (best_neigh_node)
+               batadv_neigh_node_free_ref(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(bat_priv,
+                                                           orig_node,
+                                                           hard_iface);
+               batadv_update_route(bat_priv, orig_node, hard_iface,
+                                   best_neigh_node);
+               if (best_neigh_node)
+                       batadv_neigh_node_free_ref(best_neigh_node);
+       }
+       rcu_read_unlock();
 
        return false;
 }
@@ -534,8 +940,54 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
                return 0;
        }
 
-       bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq);
+       bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq,
+                                              BATADV_IF_DEFAULT);
+
+       return 0;
+}
+
+/**
+ * batadv_orig_hardif_seq_print_text - writes originator infos for a specific
+ *  outgoing interface
+ * @seq: debugfs table seq_file struct
+ * @offset: not used
+ *
+ * Returns 0
+ */
+int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset)
+{
+       struct net_device *net_dev = (struct net_device *)seq->private;
+       struct batadv_hard_iface *hard_iface;
+       struct batadv_priv *bat_priv;
+
+       hard_iface = batadv_hardif_get_by_netdev(net_dev);
+
+       if (!hard_iface || !hard_iface->soft_iface) {
+               seq_puts(seq, "Interface not known to B.A.T.M.A.N.\n");
+               goto out;
+       }
+
+       bat_priv = netdev_priv(hard_iface->soft_iface);
+       if (!bat_priv->bat_algo_ops->bat_orig_print) {
+               seq_puts(seq,
+                        "No printing function for this routing protocol\n");
+               goto out;
+       }
+
+       if (hard_iface->if_status != BATADV_IF_ACTIVE) {
+               seq_puts(seq, "Interface not active\n");
+               goto out;
+       }
 
+       seq_printf(seq, "[B.A.T.M.A.N. adv %s, IF/MAC: %s/%pM (%s %s)]\n",
+                  BATADV_SOURCE_VERSION, hard_iface->net_dev->name,
+                  hard_iface->net_dev->dev_addr,
+                  hard_iface->soft_iface->name, bat_priv->bat_algo_ops->name);
+
+       bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq, hard_iface);
+
+out:
+       batadv_hardif_free_ref(hard_iface);
        return 0;
 }