Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43676ab5 authored by Antonio Quartulli's avatar Antonio Quartulli Committed by Sven Eckelmann
Browse files

batman-adv: improved gateway tq-based selection



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: default avatarAntonio Quartulli <ordex@autistici.org>
Signed-off-by: default avatarMarek Lindner <lindner_marek@yahoo.de>
Signed-off-by: default avatarSven Eckelmann <sven@narfation.org>
parent 19595e05
Loading
Loading
Loading
Loading
+92 −2
Original line number Diff line number Diff line
@@ -25,11 +25,17 @@
#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 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
	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;
}
+2 −1
Original line number Diff line number Diff line
@@ -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_ */
+2 −1
Original line number Diff line number Diff line
@@ -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
 */
+8 −2
Original line number Diff line number Diff line
@@ -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 @@ static int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
		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;
}