batman-adv: move bat_algo functions into a separate file
[linux-block.git] / net / batman-adv / bat_v_ogm.c
index d9bcbe6e7d65a8cbb646e842fdca560d3b4a104a..93e3d760bfe0370ab0125ff3487b2d696c7fd129 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/if_ether.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
+#include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/random.h>
@@ -38,6 +39,7 @@
 #include <linux/types.h>
 #include <linux/workqueue.h>
 
+#include "bat_algo.h"
 #include "hard-interface.h"
 #include "hash.h"
 #include "originator.h"
@@ -45,6 +47,7 @@
 #include "routing.h"
 #include "send.h"
 #include "translation-table.h"
+#include "tvlv.h"
 
 /**
  * batadv_v_ogm_orig_get - retrieve and possibly create an originator node
@@ -176,6 +179,9 @@ static void batadv_v_ogm_send(struct work_struct *work)
                if (hard_iface->soft_iface != bat_priv->soft_iface)
                        continue;
 
+               if (!kref_get_unless_zero(&hard_iface->refcount))
+                       continue;
+
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Sending own OGM2 packet (originator %pM, seqno %u, throughput %u, TTL %d) on interface %s [%pM]\n",
                           ogm_packet->orig, ntohl(ogm_packet->seqno),
@@ -185,10 +191,13 @@ static void batadv_v_ogm_send(struct work_struct *work)
 
                /* this skb gets consumed by batadv_v_ogm_send_to_if() */
                skb_tmp = skb_clone(skb, GFP_ATOMIC);
-               if (!skb_tmp)
+               if (!skb_tmp) {
+                       batadv_hardif_put(hard_iface);
                        break;
+               }
 
                batadv_v_ogm_send_to_if(skb_tmp, hard_iface);
+               batadv_hardif_put(hard_iface);
        }
        rcu_read_unlock();
 
@@ -233,73 +242,6 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface)
        ether_addr_copy(ogm_packet->orig, primary_iface->net_dev->dev_addr);
 }
 
-/**
- * batadv_v_ogm_orig_update - update the originator status based on the received
- *  OGM
- * @bat_priv: the bat priv with all the soft interface information
- * @orig_node: the originator to update
- * @neigh_node: the neighbour the OGM has been received from (to update)
- * @ogm2: the received OGM
- * @if_outgoing: the interface where this OGM is going to be forwarded through
- */
-static void
-batadv_v_ogm_orig_update(struct batadv_priv *bat_priv,
-                        struct batadv_orig_node *orig_node,
-                        struct batadv_neigh_node *neigh_node,
-                        const struct batadv_ogm2_packet *ogm2,
-                        struct batadv_hard_iface *if_outgoing)
-{
-       struct batadv_neigh_ifinfo *router_ifinfo = NULL, *neigh_ifinfo = NULL;
-       struct batadv_neigh_node *router = NULL;
-       s32 neigh_seq_diff;
-       u32 neigh_last_seqno;
-       u32 router_last_seqno;
-       u32 router_throughput, neigh_throughput;
-
-       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                  "Searching and updating originator entry of received packet\n");
-
-       /* if this neighbor already is our next hop there is nothing
-        * to change
-        */
-       router = batadv_orig_router_get(orig_node, if_outgoing);
-       if (router == neigh_node)
-               goto out;
-
-       /* don't consider neighbours with worse throughput.
-        * also switch route if this seqno is BATADV_V_MAX_ORIGDIFF newer than
-        * the last received seqno from our best next hop.
-        */
-       if (router) {
-               router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing);
-               neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
-
-               /* if these are not allocated, something is wrong. */
-               if (!router_ifinfo || !neigh_ifinfo)
-                       goto out;
-
-               neigh_last_seqno = neigh_ifinfo->bat_v.last_seqno;
-               router_last_seqno = router_ifinfo->bat_v.last_seqno;
-               neigh_seq_diff = neigh_last_seqno - router_last_seqno;
-               router_throughput = router_ifinfo->bat_v.throughput;
-               neigh_throughput = neigh_ifinfo->bat_v.throughput;
-
-               if ((neigh_seq_diff < BATADV_OGM_MAX_ORIGDIFF) &&
-                   (router_throughput >= neigh_throughput))
-                       goto out;
-       }
-
-       batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node);
-
-out:
-       if (router_ifinfo)
-               batadv_neigh_ifinfo_put(router_ifinfo);
-       if (neigh_ifinfo)
-               batadv_neigh_ifinfo_put(neigh_ifinfo);
-       if (router)
-               batadv_neigh_node_put(router);
-}
-
 /**
  * batadv_v_forward_penalty - apply a penalty to the throughput metric forwarded
  *  with B.A.T.M.A.N. V OGMs
@@ -347,10 +289,12 @@ static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv,
 }
 
 /**
- * batadv_v_ogm_forward - forward an OGM to the given outgoing interface
+ * batadv_v_ogm_forward - check conditions and forward an OGM to the given
+ *  outgoing interface
  * @bat_priv: the bat priv with all the soft interface information
  * @ogm_received: previously received OGM to be forwarded
- * @throughput: throughput to announce, may vary per outgoing interface
+ * @orig_node: the originator which has been updated
+ * @neigh_node: the neigh_node through with the OGM has been received
  * @if_incoming: the interface on which this OGM was received on
  * @if_outgoing: the interface to which the OGM has to be forwarded to
  *
@@ -359,28 +303,57 @@ static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv,
  */
 static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
                                 const struct batadv_ogm2_packet *ogm_received,
-                                u32 throughput,
+                                struct batadv_orig_node *orig_node,
+                                struct batadv_neigh_node *neigh_node,
                                 struct batadv_hard_iface *if_incoming,
                                 struct batadv_hard_iface *if_outgoing)
 {
+       struct batadv_neigh_ifinfo *neigh_ifinfo = NULL;
+       struct batadv_orig_ifinfo *orig_ifinfo = NULL;
+       struct batadv_neigh_node *router = NULL;
        struct batadv_ogm2_packet *ogm_forward;
        unsigned char *skb_buff;
        struct sk_buff *skb;
        size_t packet_len;
        u16 tvlv_len;
 
+       /* only forward for specific interfaces, not for the default one. */
+       if (if_outgoing == BATADV_IF_DEFAULT)
+               goto out;
+
+       orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
+       if (!orig_ifinfo)
+               goto out;
+
+       /* acquire possibly updated router */
+       router = batadv_orig_router_get(orig_node, if_outgoing);
+
+       /* strict rule: forward packets coming from the best next hop only */
+       if (neigh_node != router)
+               goto out;
+
+       /* don't forward the same seqno twice on one interface */
+       if (orig_ifinfo->last_seqno_forwarded == ntohl(ogm_received->seqno))
+               goto out;
+
+       orig_ifinfo->last_seqno_forwarded = ntohl(ogm_received->seqno);
+
        if (ogm_received->ttl <= 1) {
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n");
-               return;
+               goto out;
        }
 
+       neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
+       if (!neigh_ifinfo)
+               goto out;
+
        tvlv_len = ntohs(ogm_received->tvlv_len);
 
        packet_len = BATADV_OGM2_HLEN + tvlv_len;
        skb = netdev_alloc_skb_ip_align(if_outgoing->net_dev,
                                        ETH_HLEN + packet_len);
        if (!skb)
-               return;
+               goto out;
 
        skb_reserve(skb, ETH_HLEN);
        skb_buff = skb_put(skb, packet_len);
@@ -388,15 +361,23 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
 
        /* apply forward penalty */
        ogm_forward = (struct batadv_ogm2_packet *)skb_buff;
-       ogm_forward->throughput = htonl(throughput);
+       ogm_forward->throughput = htonl(neigh_ifinfo->bat_v.throughput);
        ogm_forward->ttl--;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                   "Forwarding OGM2 packet on %s: throughput %u, ttl %u, received via %s\n",
-                  if_outgoing->net_dev->name, throughput, ogm_forward->ttl,
-                  if_incoming->net_dev->name);
+                  if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
+                  ogm_forward->ttl, if_incoming->net_dev->name);
 
        batadv_v_ogm_send_to_if(skb, if_outgoing);
+
+out:
+       if (orig_ifinfo)
+               batadv_orig_ifinfo_put(orig_ifinfo);
+       if (router)
+               batadv_neigh_node_put(router);
+       if (neigh_ifinfo)
+               batadv_neigh_ifinfo_put(neigh_ifinfo);
 }
 
 /**
@@ -493,8 +474,10 @@ out:
  * @neigh_node: the neigh_node through with the OGM has been received
  * @if_incoming: the interface where this packet was received
  * @if_outgoing: the interface for which the packet should be considered
+ *
+ * Return: true if the packet should be forwarded, false otherwise
  */
-static void batadv_v_ogm_route_update(struct batadv_priv *bat_priv,
+static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv,
                                      const struct ethhdr *ethhdr,
                                      const struct batadv_ogm2_packet *ogm2,
                                      struct batadv_orig_node *orig_node,
@@ -503,14 +486,14 @@ static void batadv_v_ogm_route_update(struct batadv_priv *bat_priv,
                                      struct batadv_hard_iface *if_outgoing)
 {
        struct batadv_neigh_node *router = NULL;
-       struct batadv_neigh_ifinfo *neigh_ifinfo = NULL;
        struct batadv_orig_node *orig_neigh_node = NULL;
-       struct batadv_orig_ifinfo *orig_ifinfo = NULL;
        struct batadv_neigh_node *orig_neigh_router = NULL;
-
-       neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
-       if (!neigh_ifinfo)
-               goto out;
+       struct batadv_neigh_ifinfo *router_ifinfo = NULL, *neigh_ifinfo = NULL;
+       u32 router_throughput, neigh_throughput;
+       u32 router_last_seqno;
+       u32 neigh_last_seqno;
+       s32 neigh_seq_diff;
+       bool forward = false;
 
        orig_neigh_node = batadv_v_ogm_orig_get(bat_priv, ethhdr->h_source);
        if (!orig_neigh_node)
@@ -529,47 +512,57 @@ static void batadv_v_ogm_route_update(struct batadv_priv *bat_priv,
                goto out;
        }
 
-       if (router)
-               batadv_neigh_node_put(router);
+       /* Mark the OGM to be considered for forwarding, and update routes
+        * if needed.
+        */
+       forward = true;
 
-       /* Update routes, and check if the OGM is from the best next hop */
-       batadv_v_ogm_orig_update(bat_priv, orig_node, neigh_node, ogm2,
-                                if_outgoing);
+       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                  "Searching and updating originator entry of received packet\n");
 
-       orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing);
-       if (!orig_ifinfo)
+       /* if this neighbor already is our next hop there is nothing
+        * to change
+        */
+       if (router == neigh_node)
                goto out;
 
-       /* don't forward the same seqno twice on one interface */
-       if (orig_ifinfo->last_seqno_forwarded == ntohl(ogm2->seqno))
-               goto out;
+       /* don't consider neighbours with worse throughput.
+        * also switch route if this seqno is BATADV_V_MAX_ORIGDIFF newer than
+        * the last received seqno from our best next hop.
+        */
+       if (router) {
+               router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing);
+               neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
 
-       /* acquire possibly updated router */
-       router = batadv_orig_router_get(orig_node, if_outgoing);
+               /* if these are not allocated, something is wrong. */
+               if (!router_ifinfo || !neigh_ifinfo)
+                       goto out;
 
-       /* strict rule: forward packets coming from the best next hop only */
-       if (neigh_node != router)
-               goto out;
+               neigh_last_seqno = neigh_ifinfo->bat_v.last_seqno;
+               router_last_seqno = router_ifinfo->bat_v.last_seqno;
+               neigh_seq_diff = neigh_last_seqno - router_last_seqno;
+               router_throughput = router_ifinfo->bat_v.throughput;
+               neigh_throughput = neigh_ifinfo->bat_v.throughput;
 
-       /* only forward for specific interface, not for the default one. */
-       if (if_outgoing != BATADV_IF_DEFAULT) {
-               orig_ifinfo->last_seqno_forwarded = ntohl(ogm2->seqno);
-               batadv_v_ogm_forward(bat_priv, ogm2,
-                                    neigh_ifinfo->bat_v.throughput,
-                                    if_incoming, if_outgoing);
+               if ((neigh_seq_diff < BATADV_OGM_MAX_ORIGDIFF) &&
+                   (router_throughput >= neigh_throughput))
+                       goto out;
        }
 
+       batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node);
 out:
-       if (orig_ifinfo)
-               batadv_orig_ifinfo_put(orig_ifinfo);
        if (router)
                batadv_neigh_node_put(router);
        if (orig_neigh_router)
                batadv_neigh_node_put(orig_neigh_router);
        if (orig_neigh_node)
                batadv_orig_node_put(orig_neigh_node);
+       if (router_ifinfo)
+               batadv_neigh_ifinfo_put(router_ifinfo);
        if (neigh_ifinfo)
                batadv_neigh_ifinfo_put(neigh_ifinfo);
+
+       return forward;
 }
 
 /**
@@ -592,6 +585,7 @@ batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv,
                               struct batadv_hard_iface *if_outgoing)
 {
        int seqno_age;
+       bool forward;
 
        /* first, update the metric with according sanity checks */
        seqno_age = batadv_v_ogm_metric_update(bat_priv, ogm2, orig_node,
@@ -610,8 +604,14 @@ batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv,
                                               ntohs(ogm2->tvlv_len));
 
        /* if the metric update went through, update routes if needed */
-       batadv_v_ogm_route_update(bat_priv, ethhdr, ogm2, orig_node,
-                                 neigh_node, if_incoming, if_outgoing);
+       forward = batadv_v_ogm_route_update(bat_priv, ethhdr, ogm2, orig_node,
+                                           neigh_node, if_incoming,
+                                           if_outgoing);
+
+       /* if the routes have been processed correctly, check and forward */
+       if (forward)
+               batadv_v_ogm_forward(bat_priv, ogm2, orig_node, neigh_node,
+                                    if_incoming, if_outgoing);
 }
 
 /**
@@ -685,8 +685,8 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
        if (!orig_node)
                return;
 
-       neigh_node = batadv_neigh_node_new(orig_node, if_incoming,
-                                          ethhdr->h_source);
+       neigh_node = batadv_neigh_node_get_or_create(orig_node, if_incoming,
+                                                    ethhdr->h_source);
        if (!neigh_node)
                goto out;
 
@@ -713,9 +713,14 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
                if (hard_iface->soft_iface != bat_priv->soft_iface)
                        continue;
 
+               if (!kref_get_unless_zero(&hard_iface->refcount))
+                       continue;
+
                batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet,
                                               orig_node, neigh_node,
                                               if_incoming, hard_iface);
+
+               batadv_hardif_put(hard_iface);
        }
        rcu_read_unlock();
 out: