batman-adv: improved gateway tq-based selection
authorAntonio Quartulli <ordex@autistici.org>
Tue, 26 Apr 2011 19:31:45 +0000 (21:31 +0200)
committerSven Eckelmann <sven@narfation.org>
Mon, 20 Jun 2011 09:37:41 +0000 (11:37 +0200)
If a client issues a DHCPREQUEST for renewal, the packet is dropped
if the old destination (the old gateway for the client) TQ is smaller
than the current best gateway TQ less GW_THRESHOLD

Signed-off-by: Antonio Quartulli <ordex@autistici.org>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
net/batman-adv/gateway_client.c
net/batman-adv/gateway_client.h
net/batman-adv/main.h
net/batman-adv/soft-interface.c

index 6381864c1ca45ae6f46ca379f24a0c77c73244eb..8b25b52a4764da6f47916755788fd89c3c076923 100644 (file)
 #include "gateway_common.h"
 #include "hard-interface.h"
 #include "originator.h"
+#include "routing.h"
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/udp.h>
 #include <linux/if_vlan.h>
 
+/* This is the offset of the options field in a dhcp packet starting at
+ * the beginning of the dhcp header */
+#define DHCP_OPTIONS_OFFSET 240
+#define DHCP_REQUEST 3
+
 static void gw_node_free_ref(struct gw_node *gw_node)
 {
        if (atomic_dec_and_test(&gw_node->refcount))
@@ -509,14 +515,75 @@ out:
        return ret;
 }
 
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
+static bool is_type_dhcprequest(struct sk_buff *skb, int header_len)
+{
+       int ret = false;
+       unsigned char *p;
+       int pkt_len;
+
+       if (skb_linearize(skb) < 0)
+               goto out;
+
+       pkt_len = skb_headlen(skb);
+
+       if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1)
+               goto out;
+
+       p = skb->data + header_len + DHCP_OPTIONS_OFFSET;
+       pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1;
+
+       /* Access the dhcp option lists. Each entry is made up by:
+        * - octect 1: option type
+        * - octect 2: option data len (only if type != 255 and 0)
+        * - octect 3: option data */
+       while (*p != 255 && !ret) {
+               /* p now points to the first octect: option type */
+               if (*p == 53) {
+                       /* type 53 is the message type option.
+                        * Jump the len octect and go to the data octect */
+                       if (pkt_len < 2)
+                               goto out;
+                       p += 2;
+
+                       /* check if the message type is what we need */
+                       if (*p == DHCP_REQUEST)
+                               ret = true;
+                       break;
+               } else if (*p == 0) {
+                       /* option type 0 (padding), just go forward */
+                       if (pkt_len < 1)
+                               goto out;
+                       pkt_len--;
+                       p++;
+               } else {
+                       /* This is any other option. So we get the length... */
+                       if (pkt_len < 1)
+                               goto out;
+                       pkt_len--;
+                       p++;
+
+                       /* ...and then we jump over the data */
+                       if (pkt_len < *p)
+                               goto out;
+                       pkt_len -= *p;
+                       p += (*p);
+               }
+       }
+out:
+       return ret;
+}
+
+int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb,
+                struct orig_node *old_gw)
 {
        struct ethhdr *ethhdr;
        struct iphdr *iphdr;
        struct ipv6hdr *ipv6hdr;
        struct udphdr *udphdr;
        struct gw_node *curr_gw;
+       struct neigh_node *neigh_curr = NULL, *neigh_old = NULL;
        unsigned int header_len = 0;
+       int ret = 1;
 
        if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF)
                return 0;
@@ -584,7 +651,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
        if (!curr_gw)
                return 0;
 
+       /* If old_gw != NULL then this packet is unicast.
+        * So, at this point we have to check the message type: if it is a
+        * DHCPREQUEST we have to decide whether to drop it or not */
+       if (old_gw && curr_gw->orig_node != old_gw) {
+               if (is_type_dhcprequest(skb, header_len)) {
+                       /* If the dhcp packet has been sent to a different gw,
+                        * we have to evaluate whether the old gw is still
+                        * reliable enough */
+                       neigh_curr = find_router(bat_priv, curr_gw->orig_node,
+                                                NULL);
+                       neigh_old = find_router(bat_priv, old_gw, NULL);
+                       if (!neigh_curr || !neigh_old)
+                               goto free_neigh;
+                       if (neigh_curr->tq_avg - neigh_old->tq_avg <
+                                                               GW_THRESHOLD)
+                               ret = -1;
+               }
+       }
+free_neigh:
+       if (neigh_old)
+               neigh_node_free_ref(neigh_old);
+       if (neigh_curr)
+               neigh_node_free_ref(neigh_curr);
        if (curr_gw)
                gw_node_free_ref(curr_gw);
-       return 1;
+       return ret;
 }
index 1ce8c6066da1cb1ec46c096b73b80b06707abc74..b9b983c07feb41a61bff77b596f14c91690c3f4c 100644 (file)
@@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv,
 void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node);
 void gw_node_purge(struct bat_priv *bat_priv);
 int gw_client_seq_print_text(struct seq_file *seq, void *offset);
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb);
+int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb,
+                struct orig_node *old_gw);
 
 #endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */
index f9e0e174b3f6efc9ebadb631968f85971226d1ad..4f293b59447548fddaa31d1460f49266092262fa 100644 (file)
@@ -91,7 +91,6 @@ enum mesh_state {
 #define BCAST_QUEUE_LEN                256
 #define BATMAN_QUEUE_LEN       256
 
-
 enum uev_action {
        UEV_ADD = 0,
        UEV_DEL,
@@ -102,6 +101,8 @@ enum uev_type {
        UEV_GW = 0
 };
 
+#define GW_THRESHOLD   50
+
 /*
  * Debug Messages
  */
index 3371ece680a2071db9c00979be904f79b962f9fd..2dcdbb7a236c3bca0e00eeda9587f3ad6af122ca 100644 (file)
@@ -30,6 +30,7 @@
 #include "gateway_common.h"
 #include "gateway_client.h"
 #include "bat_sysfs.h"
+#include "originator.h"
 #include <linux/slab.h>
 #include <linux/ethtool.h>
 #include <linux/etherdevice.h>
@@ -561,6 +562,7 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
        struct bcast_packet *bcast_packet;
        struct vlan_ethhdr *vhdr;
        struct softif_neigh *curr_softif_neigh = NULL;
+       struct orig_node *orig_node = NULL;
        int data_len = skb->len, ret;
        short vid = -1;
        bool do_bcast = false;
@@ -595,8 +597,10 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
        /* Register the client MAC in the transtable */
        tt_local_add(soft_iface, ethhdr->h_source);
 
-       if (is_multicast_ether_addr(ethhdr->h_dest)) {
-               ret = gw_is_target(bat_priv, skb);
+       orig_node = transtable_search(bat_priv, ethhdr->h_dest);
+       if (is_multicast_ether_addr(ethhdr->h_dest) ||
+                               (orig_node && orig_node->gw_flags)) {
+               ret = gw_is_target(bat_priv, skb, orig_node);
 
                if (ret < 0)
                        goto dropped;
@@ -656,6 +660,8 @@ end:
                softif_neigh_free_ref(curr_softif_neigh);
        if (primary_if)
                hardif_free_ref(primary_if);
+       if (orig_node)
+               orig_node_free_ref(orig_node);
        return NETDEV_TX_OK;
 }