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_algo_ops *bao = bat_priv->bat_algo_ops;
+       struct batadv_neigh_node *first_candidate_router = NULL;
+       struct batadv_neigh_node *next_candidate_router = NULL;
+       struct batadv_neigh_node *router, *cand_router = NULL;
+       struct batadv_neigh_node *last_cand_router = NULL;
+       struct batadv_orig_ifinfo *cand, *first_candidate = NULL;
+       struct batadv_orig_ifinfo *next_candidate = NULL;
+       struct batadv_orig_ifinfo *last_candidate;
+       bool last_candidate_found = false;
 
        if (!orig_node)
                return NULL;
 
        router = batadv_orig_router_get(orig_node, recv_if);
 
-       /* TODO: fill this later with new bonding mechanism */
+       /* only consider bonding for recv_if == BATADV_IF_DEFAULT (first hop)
+        * and if activated.
+        */
+       if (recv_if == BATADV_IF_DEFAULT || !atomic_read(&bat_priv->bonding) ||
+           !router)
+               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 previously
+        * 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();
+       last_candidate = orig_node->last_bonding_candidate;
+       if (last_candidate)
+               last_cand_router = rcu_dereference(last_candidate->router);
+
+       hlist_for_each_entry_rcu(cand, &orig_node->ifinfo_list, list) {
+               /* acquire some structures and references ... */
+               if (!atomic_inc_not_zero(&cand->refcount))
+                       continue;
+
+               cand_router = rcu_dereference(cand->router);
+               if (!cand_router)
+                       goto next;
+
+               if (!atomic_inc_not_zero(&cand_router->refcount)) {
+                       cand_router = NULL;
+                       goto next;
+               }
+
+               /* alternative candidate should be good enough to be
+                * considered
+                */
+               if (!bao->bat_neigh_is_equiv_or_better(cand_router,
+                                                      cand->if_outgoing,
+                                                      router, recv_if))
+                       goto next;
+
+               /* don't use the same router twice */
+               if (last_cand_router == cand_router)
+                       goto next;
+
+               /* mark the first possible candidate */
+               if (!first_candidate) {
+                       atomic_inc(&cand_router->refcount);
+                       atomic_inc(&cand->refcount);
+                       first_candidate = cand;
+                       first_candidate_router = cand_router;
+               }
+
+               /* check if the loop has already passed the previously selected
+                * candidate ... this function should select the next candidate
+                * AFTER the previously used bonding candidate.
+                */
+               if (!last_candidate || last_candidate_found) {
+                       next_candidate = cand;
+                       next_candidate_router = cand_router;
+                       break;
+               }
+
+               if (last_candidate == cand)
+                       last_candidate_found = true;
+next:
+               /* free references */
+               if (cand_router) {
+                       batadv_neigh_node_free_ref(cand_router);
+                       cand_router = NULL;
+               }
+               batadv_orig_ifinfo_free_ref(cand);
+       }
+       rcu_read_unlock();
+
+       /* last_bonding_candidate is reset below, remove the old reference. */
+       if (orig_node->last_bonding_candidate)
+               batadv_orig_ifinfo_free_ref(orig_node->last_bonding_candidate);
+
+       /* After finding candidates, handle the three cases:
+        * 1) there is a next candidate, use that
+        * 2) there is no next candidate, use the first of the list
+        * 3) there is no candidate at all, return the default router
+        */
+       if (next_candidate) {
+               batadv_neigh_node_free_ref(router);
+
+               /* remove references to first candidate, we don't need it. */
+               if (first_candidate) {
+                       batadv_neigh_node_free_ref(first_candidate_router);
+                       batadv_orig_ifinfo_free_ref(first_candidate);
+               }
+               router = next_candidate_router;
+               orig_node->last_bonding_candidate = next_candidate;
+       } else if (first_candidate) {
+               batadv_neigh_node_free_ref(router);
+
+               /* refcounting has already been done in the loop above. */
+               router = first_candidate_router;
+               orig_node->last_bonding_candidate = first_candidate;
+       } else {
+               orig_node->last_bonding_candidate = NULL;
+       }
 
        return router;
 }