[1/3] batman-adv: adding gateway functionality

Message ID 1262619976-5632-1-git-send-email-lindner_marek@yahoo.de (mailing list archive)
State Accepted, archived
Headers

Commit Message

Marek Lindner Jan. 4, 2010, 3:46 p.m. UTC
  Via the /proc filesystem you can change the gateway mode of a node
to server or client (default is: off). Servers will announce there
bandwidth, so that clients can choose their best gateway.

Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
---
 batman-adv-kernelland/Makefile.kbuild  |    2 +-
 batman-adv-kernelland/gateway_client.c |  335 ++++++++++++++++++++++++++++++++
 batman-adv-kernelland/gateway_client.h |   29 +++
 batman-adv-kernelland/gateway_common.c |  276 ++++++++++++++++++++++++++
 batman-adv-kernelland/gateway_common.h |   34 ++++
 batman-adv-kernelland/main.c           |    6 +
 batman-adv-kernelland/originator.c     |    6 +-
 batman-adv-kernelland/packet.h         |    4 +-
 batman-adv-kernelland/proc.c           |  144 ++++++++++++++
 batman-adv-kernelland/proc.h           |    2 +
 batman-adv-kernelland/routing.c        |   13 ++-
 batman-adv-kernelland/send.c           |    3 +
 batman-adv-kernelland/types.h          |   14 +-
 13 files changed, 860 insertions(+), 8 deletions(-)
 create mode 100644 batman-adv-kernelland/gateway_client.c
 create mode 100644 batman-adv-kernelland/gateway_client.h
 create mode 100644 batman-adv-kernelland/gateway_common.c
 create mode 100644 batman-adv-kernelland/gateway_common.h
  

Patch

diff --git a/batman-adv-kernelland/Makefile.kbuild b/batman-adv-kernelland/Makefile.kbuild
index f75d4af..dc357e7 100644
--- a/batman-adv-kernelland/Makefile.kbuild
+++ b/batman-adv-kernelland/Makefile.kbuild
@@ -32,4 +32,4 @@  EXTRA_CFLAGS += -DREVISION_VERSION=\"r$(REVISION)\"
 endif
 
 obj-m += batman-adv.o
-batman-adv-objs := main.o proc.o send.o routing.o soft-interface.o device.o translation-table.o bitarray.o hash.o ring_buffer.o vis.o hard-interface.o aggregation.o originator.o $(shell [ "2" -eq "$(VERSION)" ] && [ "6" -eq "$(PATCHLEVEL)" ] && [ "$(SUBLEVEL)" -le "28" ] && echo bat_printk.o)
+batman-adv-objs := main.o proc.o send.o routing.o soft-interface.o device.o translation-table.o bitarray.o hash.o ring_buffer.o vis.o hard-interface.o aggregation.o originator.o gateway_common.o gateway_client.o $(shell [ "2" -eq "$(VERSION)" ] && [ "6" -eq "$(PATCHLEVEL)" ] && [ "$(SUBLEVEL)" -le "28" ] && echo bat_printk.o)
diff --git a/batman-adv-kernelland/gateway_client.c b/batman-adv-kernelland/gateway_client.c
new file mode 100644
index 0000000..55789ad
--- /dev/null
+++ b/batman-adv-kernelland/gateway_client.c
@@ -0,0 +1,335 @@ 
+/*
+ * Copyright (C) 2009 B.A.T.M.A.N. contributors:
+ * Marek Lindner
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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
+ *
+ */
+
+#include "main.h"
+#include "gateway_client.h"
+#include "gateway_common.h"
+
+LIST_HEAD(gw_list);
+DEFINE_SPINLOCK(curr_gw_lock);
+DEFINE_SPINLOCK(gw_list_lock);
+atomic_t gw_clnt_class;
+static struct gw_node *curr_gateway;
+
+void gw_deselect(void)
+{
+	spin_lock(&curr_gw_lock);
+	curr_gateway = NULL;
+	spin_unlock(&curr_gw_lock);
+}
+
+void gw_election(void)
+{
+	struct gw_node *gw_node, *curr_gw_tmp = NULL;
+	uint8_t max_tq = 0;
+	uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
+	int down, up;
+
+	/**
+	 * The batman daemon checks here if we already passed a full originator
+	 * cycle in order to make sure we don't choose the first gateway we
+	 * hear about. This check is based on the daemon's uptime which we
+	 * don't have.
+	 **/
+	if (atomic_read(&gw_clnt_class) == 0)
+		return;
+
+	if (curr_gateway)
+		return;
+
+	rcu_read_lock();
+	if (list_empty(&gw_list)) {
+		rcu_read_unlock();
+
+		if (curr_gateway) {
+			bat_dbg(DBG_BATMAN,
+				"Removing selected gateway - no gateway in range\n");
+			gw_deselect();
+		}
+
+		return;
+	}
+
+	list_for_each_entry_rcu(gw_node, &gw_list, list) {
+		if (!gw_node->orig_node->router)
+			continue;
+
+		if (gw_node->deleted)
+			continue;
+
+		switch (atomic_read(&gw_clnt_class)) {
+		case 1: /* fast connection */
+			gw_srv_class_to_kbit(gw_node->orig_node->gw_flags,
+					     &down, &up);
+
+			tmp_gw_factor = (gw_node->orig_node->router->tq_avg *
+					 gw_node->orig_node->router->tq_avg *
+					 down * 100 * 100) /
+					 (TQ_LOCAL_WINDOW_SIZE *
+					 TQ_LOCAL_WINDOW_SIZE * 64);
+
+			if ((tmp_gw_factor > max_gw_factor) ||
+			    ((tmp_gw_factor == max_gw_factor) &&
+			     (gw_node->orig_node->router->tq_avg > max_tq)))
+				curr_gw_tmp = gw_node;
+			break;
+
+		default: /**
+			  * 2:  stable connection (use best statistic)
+			  * 3:  fast-switch (use best statistic but change as
+			  *     soon as a better gateway appears)
+			  * XX: late-switch (use best statistic but change as
+			  *     soon as a better gateway appears which has
+			  *     $routing_class more tq points)
+			  **/
+			if (gw_node->orig_node->router->tq_avg > max_tq)
+				curr_gw_tmp = gw_node;
+			break;
+		}
+
+		if (gw_node->orig_node->router->tq_avg > max_tq)
+			max_tq = gw_node->orig_node->router->tq_avg;
+
+		if (tmp_gw_factor > max_gw_factor)
+			max_gw_factor = tmp_gw_factor;
+	}
+	rcu_read_unlock();
+
+	spin_lock(&curr_gw_lock);
+	if (curr_gateway != curr_gw_tmp) {
+		if ((curr_gateway) && (!curr_gw_tmp))
+			bat_dbg(DBG_BATMAN,
+				"Removing selected gateway - no gateway in range\n");
+		else if ((!curr_gateway) && (curr_gw_tmp))
+			bat_dbg(DBG_BATMAN,
+				"Adding route to gateway %pM (gw_flags: %i, tq: %i)\n",
+				curr_gw_tmp->orig_node->orig,
+				curr_gw_tmp->orig_node->gw_flags,
+				curr_gw_tmp->orig_node->router->tq_avg);
+		else
+			bat_dbg(DBG_BATMAN,
+				"Changing route to gateway %pM (gw_flags: %i, tq: %i)\n",
+				curr_gw_tmp->orig_node->orig,
+				curr_gw_tmp->orig_node->gw_flags,
+				curr_gw_tmp->orig_node->router->tq_avg);
+
+		curr_gateway = curr_gw_tmp;
+	}
+	spin_unlock(&curr_gw_lock);
+}
+
+void gw_check_election(struct orig_node *orig_node)
+{
+	struct gw_node *curr_gateway_tmp;
+	uint8_t gw_tq_avg, orig_tq_avg;
+
+	spin_lock(&curr_gw_lock);
+	curr_gateway_tmp = curr_gateway;
+	spin_unlock(&curr_gw_lock);
+
+	if (!curr_gateway_tmp)
+		return;
+
+	/* this node already is the gateway */
+	if (curr_gateway_tmp->orig_node == orig_node)
+		return;
+
+	if (!orig_node->router)
+		return;
+
+	gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg;
+	orig_tq_avg = orig_node->router->tq_avg;
+
+	/* the TQ value has to be better */
+	if (orig_tq_avg < gw_tq_avg)
+		return;
+
+	/**
+	 * if the routing class is greater than 3 the value tells us how much
+	 * greater the TQ value of the new gateway must be
+	 **/
+	if ((atomic_read(&gw_clnt_class) > 3) &&
+	    (orig_tq_avg - gw_tq_avg < atomic_read(&gw_clnt_class)))
+		return;
+
+	bat_dbg(DBG_BATMAN,
+		"Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i) \n",
+		gw_tq_avg, orig_tq_avg);
+
+	gw_deselect();
+}
+
+static void gw_node_add(struct orig_node *orig_node, uint8_t new_gwflags)
+{
+	struct gw_node *gw_node;
+	int down, up;
+
+	gw_node = kmalloc(sizeof(struct gw_node), GFP_ATOMIC);
+	if (!gw_node)
+		return;
+
+	memset(gw_node, 0, sizeof(struct gw_node));
+	INIT_LIST_HEAD(&gw_node->list);
+	gw_node->orig_node = orig_node;
+
+	list_add_tail_rcu(&gw_node->list, &gw_list);
+
+	gw_srv_class_to_kbit(new_gwflags, &down, &up);
+	bat_dbg(DBG_BATMAN,
+		"Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n",
+		orig_node->orig, new_gwflags,
+		(down > 2048 ? down / 1024 : down),
+		(down > 2048 ? "MBit" : "KBit"),
+		(up > 2048 ? up / 1024 : up),
+		(up > 2048 ? "MBit" : "KBit"));
+}
+
+void gw_node_update(struct orig_node *orig_node, uint8_t new_gwflags)
+{
+	struct gw_node *gw_node;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(gw_node, &gw_list, list) {
+		if (gw_node->orig_node != orig_node)
+			continue;
+
+		bat_dbg(DBG_BATMAN,
+			"Gateway class of originator %pM changed from %i to %i\n",
+			orig_node->orig, gw_node->orig_node->gw_flags,
+			new_gwflags);
+
+		gw_node->deleted = 0;
+
+		if (new_gwflags == 0) {
+			gw_node->deleted = jiffies;
+			bat_dbg(DBG_BATMAN,
+				"Gateway %pM removed from gateway list\n",
+				orig_node->orig);
+
+			if (gw_node == curr_gateway)
+				gw_deselect();
+		}
+
+		return;
+	}
+	rcu_read_unlock();
+
+	if (new_gwflags == 0)
+		return;
+
+	gw_node_add(orig_node, new_gwflags);
+}
+
+void gw_node_delete(struct orig_node *orig_node)
+{
+	return gw_node_update(orig_node, 0);
+}
+
+static void gw_node_free(struct rcu_head *rcu)
+{
+	struct gw_node *gw_node = container_of(rcu, struct gw_node, rcu);
+	kfree(gw_node);
+}
+
+void gw_node_purge_deleted(void)
+{
+	struct gw_node *gw_node, *gw_node_tmp;
+	unsigned long timeout = (2 * PURGE_TIMEOUT * HZ) / 1000;
+
+	spin_lock(&gw_list_lock);
+
+	list_for_each_entry_safe(gw_node, gw_node_tmp, &gw_list, list) {
+		if ((gw_node->deleted) &&
+		    (time_after(jiffies, gw_node->deleted + timeout))) {
+
+			list_del_rcu(&gw_node->list);
+			call_rcu(&gw_node->rcu, gw_node_free);
+		}
+	}
+
+	spin_unlock(&gw_list_lock);
+}
+
+void gw_node_list_free(void)
+{
+	struct gw_node *gw_node, *gw_node_tmp;
+
+	spin_lock(&gw_list_lock);
+
+	list_for_each_entry_safe(gw_node, gw_node_tmp, &gw_list, list) {
+		list_del_rcu(&gw_node->list);
+		call_rcu(&gw_node->rcu, gw_node_free);
+	}
+
+	gw_deselect();
+	spin_unlock(&gw_list_lock);
+}
+
+static int _write_buffer_text(unsigned char *buff, int bytes_written,
+			      struct gw_node *gw_node)
+{
+	int down, up;
+	char gw_str[ETH_STR_LEN], router_str[ETH_STR_LEN];
+
+	addr_to_string(gw_str, gw_node->orig_node->orig);
+	addr_to_string(router_str, gw_node->orig_node->router->addr);
+	gw_srv_class_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
+
+	return sprintf(buff + bytes_written,
+		       "%s %-17s (%3i) %17s [%10s]: %3i - %i%s/%i%s\n",
+		       (curr_gateway == gw_node ? "=>" : "  "),
+		       gw_str,
+		       gw_node->orig_node->router->tq_avg,
+		       router_str,
+		       gw_node->orig_node->router->if_incoming->dev,
+		       gw_node->orig_node->gw_flags,
+		       (down > 2048 ? down / 1024 : down),
+		       (down > 2048 ? "MBit" : "KBit"),
+		       (up > 2048 ? up / 1024 : up),
+		       (up > 2048 ? "MBit" : "KBit"));
+}
+
+int gw_client_fill_buffer_text(unsigned char *buff, int buff_len)
+{
+	struct gw_node *gw_node;
+	int bytes_written = 0, gw_count = 0;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(gw_node, &gw_list, list) {
+		if (gw_node->deleted)
+			continue;
+
+		if (!gw_node->orig_node->router)
+			continue;
+
+		if (buff_len < bytes_written + (2 * ETH_STR_LEN) + 30)
+			break;
+
+		bytes_written += _write_buffer_text(buff,
+						    bytes_written,
+						    gw_node);
+		gw_count++;
+	}
+	rcu_read_unlock();
+
+	if (gw_count == 0)
+		sprintf(buff, "No gateways in range ... \n");
+
+	return bytes_written;
+}
diff --git a/batman-adv-kernelland/gateway_client.h b/batman-adv-kernelland/gateway_client.h
new file mode 100644
index 0000000..5eb1e4c
--- /dev/null
+++ b/batman-adv-kernelland/gateway_client.h
@@ -0,0 +1,29 @@ 
+/*
+ * Copyright (C) 2009 B.A.T.M.A.N. contributors:
+ * Marek Lindner
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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
+ *
+ */
+
+extern atomic_t gw_clnt_class;
+
+void gw_deselect(void);
+void gw_election(void);
+void gw_check_election(struct orig_node *orig_node);
+void gw_node_update(struct orig_node *orig_node, uint8_t new_gwflags);
+void gw_node_delete(struct orig_node *orig_node);
+void gw_node_purge_deleted(void);
+void gw_node_list_free(void);
+int gw_client_fill_buffer_text(unsigned char *buff, int buff_len);
diff --git a/batman-adv-kernelland/gateway_common.c b/batman-adv-kernelland/gateway_common.c
new file mode 100644
index 0000000..1d7fd2c
--- /dev/null
+++ b/batman-adv-kernelland/gateway_common.c
@@ -0,0 +1,276 @@ 
+/*
+ * Copyright (C) 2009 B.A.T.M.A.N. contributors:
+ * Marek Lindner
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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
+ *
+ */
+
+#include "main.h"
+#include "gateway_common.h"
+#include "gateway_client.h"
+
+atomic_t gw_mode;
+atomic_t gw_srv_class;
+
+/* calculates the gateway class from kbit */
+void kbit_to_gw_srv_class(int down, int up, long *gw_srv_class)
+{
+	int mdown = 0, tdown, tup, difference;
+	uint8_t sbit, part;
+
+	*gw_srv_class = 0;
+	difference = 0x0FFFFFFF;
+
+	/* test all downspeeds */
+	for (sbit = 0; sbit < 2; sbit++) {
+		for (part = 0; part < 16; part++) {
+			tdown = 32 * (sbit + 2) * (1 << part);
+
+			if (abs(tdown - down) < difference) {
+				*gw_srv_class = (sbit << 7) + (part << 3);
+				difference = abs(tdown - down);
+				mdown = tdown;
+			}
+		}
+	}
+
+	/* test all upspeeds */
+	difference = 0x0FFFFFFF;
+
+	for (part = 0; part < 8; part++) {
+		tup = ((part + 1) * (mdown)) / 8;
+
+		if (abs(tup - up) < difference) {
+			*gw_srv_class = (*gw_srv_class & 0xF8) | part;
+			difference = abs(tup - up);
+		}
+	}
+}
+
+/* returns the up and downspeeds in kbit, calculated from the class */
+void gw_srv_class_to_kbit(uint8_t gw_srv_class, int *down, int *up)
+{
+	char sbit = (gw_srv_class & 0x80) >> 7;
+	char dpart = (gw_srv_class & 0x7C) >> 3;
+	char upart = (gw_srv_class & 0x07);
+
+	if (!gw_srv_class) {
+		*down = 0;
+		*up = 0;
+		return;
+	}
+
+	*down = 32 * (sbit + 2) * (1 << dpart);
+	*up = ((upart + 1) * (*down)) / 8;
+}
+
+static bool parse_gw_mode_tok(char *tokptr, long *gw_mode_tmp,
+			      char **gw_mode_tmp_str, long *gw_clnt_class_tmp,
+			      long *up, long *down)
+{
+	int ret;
+	char *slash_ptr, *tmp_ptr;
+
+	switch (*gw_mode_tmp) {
+	case GW_MODE_CLIENT:
+		ret = strict_strtol(tokptr, 10, gw_clnt_class_tmp);
+		if (ret) {
+			printk(KERN_ERR "Client class of gateway mode invalid: %s\n",
+			       tokptr);
+			return false;
+		}
+
+		if (*gw_clnt_class_tmp > TQ_MAX_VALUE) {
+			printk(KERN_ERR "Client class of gateway mode greater than %i: %ld\n",
+					TQ_MAX_VALUE, *gw_clnt_class_tmp);
+			return false;
+		}
+
+		break;
+	case GW_MODE_SERVER:
+		slash_ptr = strchr(tokptr, '/');
+		if (slash_ptr)
+			*slash_ptr = 0;
+
+		ret = strict_strtol(tokptr, 10, down);
+		if (ret) {
+			printk(KERN_ERR "Download speed of gateway mode invalid: %s\n",
+			       tokptr);
+			return false;
+		}
+
+		tmp_ptr = tokptr + strlen(tokptr) - 4;
+
+		if ((strlen(tokptr) > 4) &&
+			((strncmp(tmp_ptr, "MBit", 4) == 0) ||
+			(strncmp(tmp_ptr, "mbit", 4) == 0) ||
+			(strncmp(tmp_ptr, "Mbit", 4) == 0)))
+			*down *= 1024;
+
+		/* we also got some upload info */
+		if (slash_ptr) {
+			ret = strict_strtol(slash_ptr + 1, 10, up);
+			if (ret) {
+				printk(KERN_ERR "Upload speed of gateway mode invalid: %s\n",
+				       slash_ptr + 1);
+				return false;
+			}
+
+			tmp_ptr = slash_ptr + 1 + strlen(slash_ptr + 1) - 4;
+
+			if ((strlen(slash_ptr + 1) > 4) &&
+				((strncmp(tmp_ptr, "MBit", 4) == 0) ||
+				(strncmp(tmp_ptr, "mbit", 4) == 0) ||
+				(strncmp(tmp_ptr, "Mbit", 4) == 0)))
+				*up *= 1024;
+
+			*slash_ptr = '/';
+		}
+
+		break;
+	default:
+		if (strcmp(tokptr, GW_MODE_OFF_NAME) == 0) {
+			*gw_mode_tmp = GW_MODE_OFF;
+			*gw_mode_tmp_str = GW_MODE_OFF_NAME;
+		}
+
+		if (strcmp(tokptr, GW_MODE_CLIENT_NAME) == 0) {
+			*gw_mode_tmp = GW_MODE_CLIENT;
+			*gw_mode_tmp_str = GW_MODE_CLIENT_NAME;
+		}
+
+		if (strcmp(tokptr, GW_MODE_SERVER_NAME) == 0) {
+			*gw_mode_tmp = GW_MODE_SERVER;
+			*gw_mode_tmp_str = GW_MODE_SERVER_NAME;
+		}
+	}
+
+	return true;
+}
+
+ssize_t gw_mode_set(const char __user *userbuffer, size_t count)
+{
+	char *gw_mode_string, *tokptr, *cp;
+	char *gw_mode_curr_str, *gw_mode_tmp_str = NULL;
+	int finished, not_copied = 0;
+	long gw_mode_curr, gw_mode_tmp = GW_MODE_OFF;
+	long gw_srv_class_tmp = 0, gw_clnt_class_tmp = 0, up = 0, down = 0;
+	bool ret;
+
+	gw_mode_string = kmalloc(count, GFP_KERNEL);
+
+	if (!gw_mode_string)
+		return -ENOMEM;
+
+	not_copied = copy_from_user(gw_mode_string, userbuffer, count);
+	gw_mode_string[count - not_copied - 1] = 0;
+
+	tokptr = gw_mode_string;
+	gw_mode_curr = atomic_read(&gw_mode);
+
+	for (cp = gw_mode_string, finished = 0; !finished; cp++) {
+		switch (*cp) {
+		case 0:
+			finished = 1;
+		case ' ':
+		case '\n':
+		case '\t':
+			*cp = 0;
+			ret = parse_gw_mode_tok(tokptr, &gw_mode_tmp,
+						&gw_mode_tmp_str,
+						&gw_clnt_class_tmp,
+						&up, &down);
+
+			if (!ret)
+				goto end;
+
+			tokptr = cp + 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!gw_mode_tmp_str) {
+		printk(KERN_INFO "Gateway mode can only be set to: '%s', '%s' or '%s' - given value: %s\n",
+			GW_MODE_OFF_NAME, GW_MODE_CLIENT_NAME,
+			GW_MODE_SERVER_NAME, gw_mode_string);
+		goto end;
+	}
+
+	switch (gw_mode_curr) {
+	case GW_MODE_CLIENT:
+		gw_mode_curr_str = GW_MODE_CLIENT_NAME;
+		break;
+	case GW_MODE_SERVER:
+		gw_mode_curr_str = GW_MODE_SERVER_NAME;
+		break;
+	default:
+		gw_mode_curr_str = GW_MODE_OFF_NAME;
+		break;
+	}
+
+	switch (gw_mode_tmp) {
+	case GW_MODE_CLIENT:
+		if ((gw_mode_tmp == GW_MODE_CLIENT) && (!gw_clnt_class_tmp))
+			gw_clnt_class_tmp = 20;
+
+		printk(KERN_INFO "Changing gateway mode from: '%s' to: '%s' (gw_clnt_class: %ld)\n",
+			  gw_mode_curr_str, gw_mode_tmp_str,
+			  gw_clnt_class_tmp);
+		break;
+	case GW_MODE_SERVER:
+		if (!down)
+			down = 2000;
+
+		if (!up)
+			up = down / 5;
+
+		kbit_to_gw_srv_class(down, up, &gw_srv_class_tmp);
+
+		/**
+		 * the gw class we guessed above might not match the given
+		 * speeds, hence we need to calculate it back to show the
+		 * number that is going to be propagated
+		 **/
+		gw_srv_class_to_kbit((uint8_t)gw_srv_class_tmp,
+				     (int *)&down, (int *)&up);
+
+		printk(KERN_INFO
+			  "Changing gateway mode from: '%s' to: '%s' (gw_srv_class: %ld -> propagating: %ld%s/%ld%s)\n",
+			  gw_mode_curr_str, gw_mode_tmp_str,
+			  gw_srv_class_tmp,
+			  (down > 2048 ? down / 1024 : down),
+			  (down > 2048 ? "MBit" : "KBit"),
+			  (up > 2048 ? up / 1024 : up),
+			  (up > 2048 ? "MBit" : "KBit"));
+		break;
+	default:
+		printk(KERN_INFO "Changing gateway mode from: '%s' to: '%s'\n",
+			  gw_mode_curr_str, gw_mode_tmp_str);
+		break;
+	}
+
+	atomic_set(&gw_mode, gw_mode_tmp);
+	atomic_set(&gw_srv_class, gw_srv_class_tmp);
+	atomic_set(&gw_clnt_class, gw_clnt_class_tmp);
+
+	if (gw_clnt_class_tmp == 0)
+		gw_deselect();
+
+end:
+	kfree(gw_mode_string);
+	return count;
+}
diff --git a/batman-adv-kernelland/gateway_common.h b/batman-adv-kernelland/gateway_common.h
new file mode 100644
index 0000000..365cd00
--- /dev/null
+++ b/batman-adv-kernelland/gateway_common.h
@@ -0,0 +1,34 @@ 
+/*
+ * Copyright (C) 2009 B.A.T.M.A.N. contributors:
+ * Marek Lindner
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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
+ *
+ */
+
+enum gw_modes {
+	GW_MODE_OFF,
+	GW_MODE_CLIENT,
+	GW_MODE_SERVER,
+};
+
+#define GW_MODE_OFF_NAME	"off"
+#define GW_MODE_CLIENT_NAME	"client"
+#define GW_MODE_SERVER_NAME	"server"
+
+extern atomic_t gw_mode;
+extern atomic_t gw_srv_class;
+
+void gw_srv_class_to_kbit(uint8_t gw_class, int *down, int *up);
+ssize_t gw_mode_set(const char __user *userbuffer, size_t count);
diff --git a/batman-adv-kernelland/main.c b/batman-adv-kernelland/main.c
index 1d80ea3..a64f070 100644
--- a/batman-adv-kernelland/main.c
+++ b/batman-adv-kernelland/main.c
@@ -28,6 +28,8 @@ 
 #include "device.h"
 #include "translation-table.h"
 #include "hard-interface.h"
+#include "gateway_common.h"
+#include "gateway_client.h"
 #include "types.h"
 #include "vis.h"
 #include "hash.h"
@@ -85,6 +87,9 @@  int init_module(void)
 	atomic_set(&vis_interval, 1000);/* TODO: raise this later, this is only
 					 * for debugging now. */
 	atomic_set(&aggregation_enabled, 1);
+	atomic_set(&gw_mode, GW_MODE_OFF);
+	atomic_set(&gw_srv_class, 0);
+	atomic_set(&gw_clnt_class, 0);
 
 	/* the name should not be longer than 10 chars - see
 	 * http://lwn.net/Articles/23634/ */
@@ -191,6 +196,7 @@  void shutdown_module(void)
 
 	/* TODO: unregister BATMAN pack */
 
+	gw_node_list_free();
 	originator_free();
 
 	hna_local_free();
diff --git a/batman-adv-kernelland/originator.c b/batman-adv-kernelland/originator.c
index cff433e..a9ba24c 100644
--- a/batman-adv-kernelland/originator.c
+++ b/batman-adv-kernelland/originator.c
@@ -27,7 +27,7 @@ 
 #include "translation-table.h"
 #include "routing.h"
 #include "compat.h"
-
+#include "gateway_client.h"
 
 static DECLARE_DELAYED_WORK(purge_orig_wq, purge_orig);
 
@@ -241,6 +241,8 @@  void purge_orig(struct work_struct *work)
 	while (hash_iterate(orig_hash, &hashit)) {
 		orig_node = hashit.bucket->data;
 		if (purge_orig_node(orig_node)) {
+			if (orig_node->gw_flags)
+				gw_node_delete(orig_node);
 			hash_remove_bucket(orig_hash, &hashit);
 			free_orig_node(orig_node);
 		}
@@ -248,5 +250,7 @@  void purge_orig(struct work_struct *work)
 
 	spin_unlock_irqrestore(&orig_hash_lock, flags);
 
+	gw_node_purge_deleted();
+	gw_election();
 	start_purge_timer();
 }
diff --git a/batman-adv-kernelland/packet.h b/batman-adv-kernelland/packet.h
index ad006ce..b543b03 100644
--- a/batman-adv-kernelland/packet.h
+++ b/batman-adv-kernelland/packet.h
@@ -28,7 +28,7 @@ 
 #define BAT_VIS       0x05
 
 /* this file is included by batctl which needs these defines */
-#define COMPAT_VERSION 8
+#define COMPAT_VERSION 9
 #define DIRECTLINK 0x40
 #define VIS_SERVER 0x20
 
@@ -53,6 +53,8 @@  struct batman_packet {
 	uint8_t  prev_sender[6];
 	uint8_t  ttl;
 	uint8_t  num_hna;
+	uint8_t  gw_flags;  /* flags related to gateway class */
+	uint8_t  align;
 } __attribute__((packed));
 
 #define BAT_PACKET_LEN sizeof(struct batman_packet)
diff --git a/batman-adv-kernelland/proc.c b/batman-adv-kernelland/proc.c
index 1bf493f..747ed5f 100644
--- a/batman-adv-kernelland/proc.c
+++ b/batman-adv-kernelland/proc.c
@@ -28,6 +28,8 @@ 
 #include "hash.h"
 #include "vis.h"
 #include "compat.h"
+#include "gateway_common.h"
+#include "gateway_client.h"
 
 static struct proc_dir_entry *proc_batman_dir, *proc_interface_file;
 static struct proc_dir_entry *proc_orig_interval_file, *proc_originators_file;
@@ -35,6 +37,7 @@  static struct proc_dir_entry *proc_transt_local_file;
 static struct proc_dir_entry *proc_transt_global_file;
 static struct proc_dir_entry *proc_vis_srv_file, *proc_vis_data_file;
 static struct proc_dir_entry *proc_aggr_file;
+static struct proc_dir_entry *proc_gw_mode_file, *proc_gw_srv_list_file;
 
 static int proc_interfaces_read(struct seq_file *seq, void *offset)
 {
@@ -462,6 +465,99 @@  static int proc_aggr_open(struct inode *inode, struct file *file)
 	return single_open(file, proc_aggr_read, NULL);
 }
 
+static int proc_gw_mode_read(struct seq_file *seq, void *offset)
+{
+	int down, up;
+	long gw_mode_curr = atomic_read(&gw_mode);
+	uint8_t gw_srv_class_curr = (uint8_t)atomic_read(&gw_srv_class);
+
+	gw_srv_class_to_kbit(gw_srv_class_curr, &down, &up);
+
+	seq_printf(seq, "[%c] %s\n",
+		   (gw_mode_curr == GW_MODE_OFF) ? 'x' : ' ',
+		   GW_MODE_OFF_NAME);
+
+	if (gw_mode_curr == GW_MODE_CLIENT)
+		seq_printf(seq, "[x] %s (gw_clnt_class: %i)\n",
+			   GW_MODE_CLIENT_NAME,
+			   atomic_read(&gw_clnt_class));
+	else
+		seq_printf(seq, "[ ] %s\n", GW_MODE_CLIENT_NAME);
+
+	if (gw_mode_curr == GW_MODE_SERVER)
+		seq_printf(seq,
+			   "[x] %s (gw_srv_class: %i -> propagating: %i%s/%i%s)\n",
+			   GW_MODE_SERVER_NAME,
+			   gw_srv_class_curr,
+			   (down > 2048 ? down / 1024 : down),
+			   (down > 2048 ? "MBit" : "KBit"),
+			   (up > 2048 ? up / 1024 : up),
+			   (up > 2048 ? "MBit" : "KBit"));
+	else
+		seq_printf(seq, "[ ] %s\n", GW_MODE_SERVER_NAME);
+
+	return 0;
+}
+
+static int proc_gw_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, proc_gw_mode_read, NULL);
+}
+
+static ssize_t proc_gw_mode_write(struct file *instance,
+				    const char __user *userbuffer,
+				    size_t count, loff_t *data)
+{
+	return gw_mode_set(userbuffer, count);
+}
+
+static int proc_gw_srv_list_read(struct seq_file *seq, void *offset)
+{
+	char *buff;
+	int buffsize = 4096;
+
+	buff = kmalloc(buffsize, GFP_KERNEL);
+	if (!buff)
+		return 0;
+
+	rcu_read_lock();
+	if (list_empty(&if_list)) {
+		rcu_read_unlock();
+		seq_printf(seq,
+			   "BATMAN disabled - please specify interfaces to enable it\n");
+		goto end;
+	}
+
+	if (((struct batman_if *)if_list.next)->if_active != IF_ACTIVE) {
+		rcu_read_unlock();
+		seq_printf(seq,
+			   "BATMAN disabled - primary interface not active\n");
+		goto end;
+	}
+
+	seq_printf(seq,
+		   "      %-12s (%s/%i) %17s [%10s]: gw_srv_class ... [B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s] \n",
+		   "Gateway", "#", TQ_MAX_VALUE, "Nexthop",
+		   "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR,
+		   ((struct batman_if *)if_list.next)->dev,
+		   ((struct batman_if *)if_list.next)->addr_str);
+
+	rcu_read_unlock();
+
+	gw_client_fill_buffer_text(buff, buffsize);
+	seq_printf(seq, "%s", buff);
+
+end:
+	kfree(buff);
+	return 0;
+}
+
+static int proc_gw_srv_list_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, proc_gw_srv_list_read, NULL);
+}
+
+
 /* satisfying different prototypes ... */
 static ssize_t proc_dummy_write(struct file *file, const char __user *buffer,
 				size_t count, loff_t *ppos)
@@ -469,6 +565,24 @@  static ssize_t proc_dummy_write(struct file *file, const char __user *buffer,
 	return count;
 }
 
+static const struct file_operations proc_gw_srv_list_fops = {
+	.owner		= THIS_MODULE,
+	.open		= proc_gw_srv_list_open,
+	.read		= seq_read,
+	.write		= proc_dummy_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static const struct file_operations proc_gw_mode_fops = {
+	.owner		= THIS_MODULE,
+	.open		= proc_gw_mode_open,
+	.read		= seq_read,
+	.write		= proc_gw_mode_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
 static const struct file_operations proc_aggr_fops = {
 	.owner		= THIS_MODULE,
 	.open		= proc_aggr_open,
@@ -567,6 +681,12 @@  void cleanup_procfs(void)
 	if (proc_aggr_file)
 		remove_proc_entry(PROC_FILE_AGGR, proc_batman_dir);
 
+	if (proc_gw_mode_file)
+		remove_proc_entry(PROC_FILE_GW_MODE, proc_batman_dir);
+
+	if (proc_gw_srv_list_file)
+		remove_proc_entry(PROC_FILE_GW_SRV_LIST, proc_batman_dir);
+
 	if (proc_batman_dir)
 #ifdef __NET_NET_NAMESPACE_H
 		remove_proc_entry(PROC_ROOT_DIR, init_net.proc_net);
@@ -671,5 +791,29 @@  int setup_procfs(void)
 		return -EFAULT;
 	}
 
+	proc_gw_mode_file = create_proc_entry(PROC_FILE_GW_MODE,
+					   S_IWUSR | S_IRUGO,
+					   proc_batman_dir);
+	if (proc_gw_mode_file) {
+		proc_gw_mode_file->proc_fops = &proc_gw_mode_fops;
+	} else {
+		printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n",
+		       PROC_ROOT_DIR, PROC_FILE_GW_MODE);
+		cleanup_procfs();
+		return -EFAULT;
+	}
+
+	proc_gw_srv_list_file = create_proc_entry(PROC_FILE_GW_SRV_LIST,
+					   S_IWUSR | S_IRUGO,
+					   proc_batman_dir);
+	if (proc_gw_srv_list_file) {
+		proc_gw_srv_list_file->proc_fops = &proc_gw_srv_list_fops;
+	} else {
+		printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n",
+		       PROC_ROOT_DIR, PROC_FILE_GW_SRV_LIST);
+		cleanup_procfs();
+		return -EFAULT;
+	}
+
 	return 0;
 }
diff --git a/batman-adv-kernelland/proc.h b/batman-adv-kernelland/proc.h
index cd690e0..af38d40 100644
--- a/batman-adv-kernelland/proc.h
+++ b/batman-adv-kernelland/proc.h
@@ -34,6 +34,8 @@ 
 #define PROC_FILE_VIS_SRV "vis_server"
 #define PROC_FILE_VIS_DATA "vis_data"
 #define PROC_FILE_AGGR "aggregate_ogm"
+#define PROC_FILE_GW_MODE "gateway_mode"
+#define PROC_FILE_GW_SRV_LIST "gateway_srv_list"
 
 void cleanup_procfs(void);
 int setup_procfs(void);
diff --git a/batman-adv-kernelland/routing.c b/batman-adv-kernelland/routing.c
index 2eb932f..da6a779 100644
--- a/batman-adv-kernelland/routing.c
+++ b/batman-adv-kernelland/routing.c
@@ -33,6 +33,7 @@ 
 #include "vis.h"
 #include "aggregation.h"
 #include "compat.h"
+#include "gateway_client.h"
 
 DECLARE_WAIT_QUEUE_HEAD(thread_wait);
 
@@ -306,10 +307,20 @@  static void update_orig(struct orig_node *orig_node, struct ethhdr *ethhdr,
 		goto update_hna;
 
 	update_routes(orig_node, neigh_node, hna_buff, tmp_hna_buff_len);
-	return;
+	goto update_gw;
 
 update_hna:
 	update_routes(orig_node, orig_node->router, hna_buff, tmp_hna_buff_len);
+
+update_gw:
+	if (orig_node->gw_flags != batman_packet->gw_flags)
+		gw_node_update(orig_node, batman_packet->gw_flags);
+
+	orig_node->gw_flags = batman_packet->gw_flags;
+
+	/* restart gateway selection if fast or late switching was enabled */
+	if ((orig_node->gw_flags) && (atomic_read(&gw_clnt_class) > 2))
+		gw_check_election(orig_node);
 }
 
 static char count_real_packets(struct ethhdr *ethhdr,
diff --git a/batman-adv-kernelland/send.c b/batman-adv-kernelland/send.c
index edfdd5d..a40e8b8 100644
--- a/batman-adv-kernelland/send.c
+++ b/batman-adv-kernelland/send.c
@@ -28,6 +28,7 @@ 
 #include "types.h"
 #include "vis.h"
 #include "aggregation.h"
+#include "gateway_common.h"
 
 #include "compat.h"
 
@@ -279,6 +280,8 @@  void schedule_own_packet(struct batman_if *batman_if)
 	else
 		batman_packet->flags = 0;
 
+	batman_packet->gw_flags = (uint8_t)atomic_read(&gw_srv_class);
+
 	/* could be read by receive_bat_packet() */
 	atomic_inc(&batman_if->seqno);
 
diff --git a/batman-adv-kernelland/types.h b/batman-adv-kernelland/types.h
index dec1b54..495d94a 100644
--- a/batman-adv-kernelland/types.h
+++ b/batman-adv-kernelland/types.h
@@ -43,7 +43,6 @@  struct batman_if {
 	unsigned char *packet_buff;
 	int packet_len;
 	struct rcu_head rcu;
-
 };
 
 struct orig_node {               /* structure for orig_list maintaining nodes of mesh */
@@ -55,10 +54,10 @@  struct orig_node {               /* structure for orig_list maintaining nodes of
 	uint8_t tq_own;
 	int tq_asym_penalty;
 	unsigned long last_valid;        /* when last packet from this node was received */
-/*	uint8_t  gwflags;      * flags related to gateway functions: gateway class */
-	uint8_t  flags;    		/* for now only VIS_SERVER flag. */
+	uint8_t gw_flags;      /* flags related to gateway class */
+	uint8_t flags;    /* for now only VIS_SERVER flag. */
 	unsigned char *hna_buff;
-	int16_t  hna_buff_len;
+	int16_t hna_buff_len;
 	uint16_t last_real_seqno;   /* last and best known squence number */
 	uint8_t last_ttl;         /* ttl of last received packet */
 	TYPE_OF_WORD bcast_bits[NUM_WORDS];
@@ -66,6 +65,13 @@  struct orig_node {               /* structure for orig_list maintaining nodes of
 	struct list_head neigh_list;
 };
 
+struct gw_node {
+	struct list_head list;
+	struct orig_node *orig_node;
+	unsigned long deleted;
+	struct rcu_head rcu;
+};
+
 struct neigh_node {
 	struct list_head list;
 	uint8_t addr[ETH_ALEN];