staging: batman-adv meshing protocol
authorAndrew Lunn <andrew@lunn.ch>
Mon, 9 Nov 2009 20:20:10 +0000 (21:20 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 11 Dec 2009 20:23:22 +0000 (12:23 -0800)
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
a routing protocol for multi-hop ad-hoc mesh networks. The
networks may be wired or wireless. See
http://www.open-mesh.org/ for more information and user space
tools.

This is the first submission for inclusion in staging.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
38 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/batman-adv/CHANGELOG [new file with mode: 0644]
drivers/staging/batman-adv/Kconfig [new file with mode: 0644]
drivers/staging/batman-adv/Makefile [new file with mode: 0644]
drivers/staging/batman-adv/README [new file with mode: 0644]
drivers/staging/batman-adv/TODO [new file with mode: 0644]
drivers/staging/batman-adv/aggregation.c [new file with mode: 0644]
drivers/staging/batman-adv/aggregation.h [new file with mode: 0644]
drivers/staging/batman-adv/bitarray.c [new file with mode: 0644]
drivers/staging/batman-adv/bitarray.h [new file with mode: 0644]
drivers/staging/batman-adv/compat.h [new file with mode: 0644]
drivers/staging/batman-adv/device.c [new file with mode: 0644]
drivers/staging/batman-adv/device.h [new file with mode: 0644]
drivers/staging/batman-adv/hard-interface.c [new file with mode: 0644]
drivers/staging/batman-adv/hard-interface.h [new file with mode: 0644]
drivers/staging/batman-adv/hash.c [new file with mode: 0644]
drivers/staging/batman-adv/hash.h [new file with mode: 0644]
drivers/staging/batman-adv/log.c [new file with mode: 0644]
drivers/staging/batman-adv/log.h [new file with mode: 0644]
drivers/staging/batman-adv/main.c [new file with mode: 0644]
drivers/staging/batman-adv/main.h [new file with mode: 0644]
drivers/staging/batman-adv/packet.h [new file with mode: 0644]
drivers/staging/batman-adv/proc.c [new file with mode: 0644]
drivers/staging/batman-adv/proc.h [new file with mode: 0644]
drivers/staging/batman-adv/ring_buffer.c [new file with mode: 0644]
drivers/staging/batman-adv/ring_buffer.h [new file with mode: 0644]
drivers/staging/batman-adv/routing.c [new file with mode: 0644]
drivers/staging/batman-adv/routing.h [new file with mode: 0644]
drivers/staging/batman-adv/send.c [new file with mode: 0644]
drivers/staging/batman-adv/send.h [new file with mode: 0644]
drivers/staging/batman-adv/soft-interface.c [new file with mode: 0644]
drivers/staging/batman-adv/soft-interface.h [new file with mode: 0644]
drivers/staging/batman-adv/translation-table.c [new file with mode: 0644]
drivers/staging/batman-adv/translation-table.h [new file with mode: 0644]
drivers/staging/batman-adv/types.h [new file with mode: 0644]
drivers/staging/batman-adv/vis.c [new file with mode: 0644]
drivers/staging/batman-adv/vis.h [new file with mode: 0644]

index 6fbbedbc7fe75d5e1b5f2d60984cea7e380585fa..d30fa451e6f3c9e97426cb53f344d6b9df4b51da 100644 (file)
@@ -129,6 +129,8 @@ source "drivers/staging/wlags49_h2/Kconfig"
 
 source "drivers/staging/wlags49_h25/Kconfig"
 
+source "drivers/staging/batman-adv/Kconfig"
+
 source "drivers/staging/strip/Kconfig"
 
 source "drivers/staging/arlan/Kconfig"
index 61a81c148c9fcdf42a9f378f3f8cf9fadaa2f619..910e55dd79146dbf2e5f68ba526ff418faf9bb40 100644 (file)
@@ -46,6 +46,7 @@ obj-$(CONFIG_IIO)             += iio/
 obj-$(CONFIG_RAMZSWAP)         += ramzswap/
 obj-$(CONFIG_WLAGS49_H2)       += wlags49_h2/
 obj-$(CONFIG_WLAGS49_H25)      += wlags49_h25/
+obj-$(CONFIG_BATMAN_ADV)       += batman-adv/
 obj-$(CONFIG_STRIP)            += strip/
 obj-$(CONFIG_ARLAN)            += arlan/
 obj-$(CONFIG_WAVELAN)          += wavelan/
diff --git a/drivers/staging/batman-adv/CHANGELOG b/drivers/staging/batman-adv/CHANGELOG
new file mode 100644 (file)
index 0000000..8a18163
--- /dev/null
@@ -0,0 +1,37 @@
+batman-adv 0.2:
+
+* support latest kernels (2.6.20 - 2.6.31)
+* temporary routing loops / TTL code bug / ghost entries in originator table fixed
+* internal packet queue for packet aggregation & transmission retry (ARQ)
+  for payload broadcasts added
+* interface detection converted to event based handling to avoid timers
+* major linux coding style adjustments applied
+* all kernel version compatibility functions has been moved to compat.h
+* use random ethernet address generator from the kernel
+* /sys/module/batman_adv/version to export kernel module version
+* vis: secondary interface export for dot draw format + JSON output format added
+* many bugs (alignment issues, race conditions, deadlocks, etc) squashed
+
+ -- Sat, 07 Nov 2009 15:44:31 +0100
+
+batman-adv 0.1:
+
+* support latest kernels (2.6.20 - 2.6.28)
+* LOTS of cleanup: locking, stack usage, memory leaks
+* Change Ethertype from 0x0842 to 0x4305
+  unregistered at IEEE, if you want to sponsor an official Ethertype ($2500)
+  please contact us
+
+ -- Sun, 28 Dec 2008 00:44:31 +0100
+
+batman-adv 0.1-beta:
+
+* layer 2 meshing based on BATMAN TQ algorithm in kernelland
+* operates on any ethernet like interface
+* supports IPv4, IPv6, DHCP, etc
+* is controlled via /proc/net/batman-adv/
+* bridging via brctl is supported
+* interface watchdog (interfaces can be (de)activated dynamically)
+* offers integrated vis server which meshes/syncs with other vis servers in range
+
+ -- Mon, 05 May 2008 14:10:04 +0200
diff --git a/drivers/staging/batman-adv/Kconfig b/drivers/staging/batman-adv/Kconfig
new file mode 100644 (file)
index 0000000..b9742e7
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# B.A.T.M.A.N meshing protocol
+#
+
+config BATMAN_ADV
+       tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
+        default n
+       ---help---
+
+        B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
+        a routing protocol for multi-hop ad-hoc mesh networks. The
+        networks may be wired or wireless. See
+        http://www.open-mesh.org/ for more information and user space
+        tools.
+
+config BATMAN_DEBUG
+       bool "B.A.T.M.A.N. debugging"
+       depends on BATMAN != n
+       help
+
+         This is an option for use by developers; most people should
+         say N here. This enables compilation of support for
+         outputting debugging information to the kernel log. The
+         output is controlled via the module parameter debug.
+
diff --git a/drivers/staging/batman-adv/Makefile b/drivers/staging/batman-adv/Makefile
new file mode 100644 (file)
index 0000000..02da871
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+#
+# Marek Lindner, Simon Wunderlich
+#
+# 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
+#
+
+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 log.o
diff --git a/drivers/staging/batman-adv/README b/drivers/staging/batman-adv/README
new file mode 100644 (file)
index 0000000..3aaf393
--- /dev/null
@@ -0,0 +1,125 @@
+[state: 07-11-2009]
+
+BATMAN-ADV
+----------
+
+Batman-advanced is a new approach to wireless networking which does no longer
+operate on the IP basis. Unlike B.A.T.M.A.N, which exchanges information
+using UDP packets and sets routing tables, batman-advanced operates on ISO/OSI
+Layer 2 only and uses and routes (or better: bridges) Ethernet Frames. It
+emulates a virtual network switch of all nodes participating. Therefore all
+nodes appear to be link local, thus all higher operating protocols won't be
+affected by any changes within the network. You can run almost any protocol
+above B.A.T.M.A.N. Advanced, prominent examples are: IPv4, IPv6, DHCP, IPX.
+
+This is batman-advanced implemented as Linux kernel driver. It does not depend
+on any network (other) driver, and can be used on wifi as well as ethernet,
+vpn, etc ... (anything with ethernet-style layer 2).
+It compiles against and should work with Linux 2.6.20 - 2.6.31. Supporting older
+versions is not planned, but it's probably easy to backport it. If you work on a
+backport, feel free to contact us.  :-)
+
+COMPILE
+-------
+To compile against your currently installed kernel, just type:
+
+# make
+
+if you want to compile against some other kernel, use:
+
+# make KERNELPATH=/path/to/kernel
+
+USAGE
+-----
+
+insmod the batman-adv.ko in your kernel:
+
+# insmod batman-adv.ko
+
+the module is now waiting for activation. You must add some interfaces
+on which batman can operate. Each interface must be added separately:
+
+# echo wlan0 > /proc/net/batman-adv/interfaces
+
+( # echo wlan1 > /proc/net/batman-adv/interfaces )
+( # echo eth0 > /proc/net/batman-adv/interfaces )
+( ... )
+
+Now batman starts broadcasting on this interface.
+You can now view the table of originators (mesh participants) with:
+
+# cat /proc/net/batman-adv/originators
+
+The module will create a new interface "bat0", which can be used as a
+regular interface:
+
+# ifconfig bat0 inet 192.168.0.1 up
+# ping 192.168.0.2
+...
+
+If you want topology visualization, your meshnode must be configured
+as VIS-server:
+
+# echo "server" > /proc/net/batman-adv/vis
+
+Each node is either configured as "server" or as "client" (default:
+"client"). Clients send their topology data to the server next to them,
+and server synchronize with other servers. If there is no server
+configured (default) within the mesh, no topology information will be
+transmitted. With these "synchronizing servers", there can be 1 or
+more vis servers sharing the same (or at least very similar) data.
+
+When configured as server, you can get a topology snapshot of your mesh:
+
+# cat /proc/net/batman-adv/vis
+
+This output format is a graphviz formatted text file which can be
+processed with graphviz-tools like dot.
+The labels are similar/compatible to the ETX metric, 1.0 means perfect
+connection (100%), 2.0 means 50%, 3.0 means 33% and so on.
+
+Alternatively, a JSON output format is available. The format can be set
+using by writing either "dot_draw" or "json" into the vis_format file.
+"dot_draw" is selected by default.
+
+echo "json" > /proc/net/batman-adv/vis_format
+
+In very mobile scenarios, you might want to adjust the originator
+interval to a lower value. This will make the mesh more responsive to
+topology changes, but will also increase the overhead. Please make sure
+that all nodes in your mesh use the same interval. The default value
+is 1000 ms (1 second).
+
+# echo 1000 > /proc/net/batman-adv/orig_interval
+
+To deactivate batman, do:
+
+# echo "" > /proc/net/batman-adv/interfaces
+
+BATCTL
+------
+
+B.A.T.M.A.N.  advanced  operates on layer 2 and thus all hosts partici-
+pating in the virtual switch are completely transparent for all  proto-
+cols above layer 2. Therefore the common diagnosis tools do not work as
+expected. To overcome these problems batctl was created. At the  moment
+the  batctl contains ping, traceroute, tcpdump and interfaces to the
+kernel module settings.
+
+For more information, please see the manpage (man batctl).
+
+batctl is available on http://www.open-mesh.net/
+
+CONTACT
+-------
+
+Please send us comments, experiences, questions, anything :)
+
+IRC:             #batman on irc.freenode.org
+Mailing-list:    b.a.t.m.a.n@open-mesh.net
+(subscription at https://list.open-mesh.net/mm/listinfo/b.a.t.m.a.n )
+
+You can also contact the Authors:
+
+Marek Lindner <lindner_marek@yahoo.de>
+Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
diff --git a/drivers/staging/batman-adv/TODO b/drivers/staging/batman-adv/TODO
new file mode 100644 (file)
index 0000000..ea6dcf9
--- /dev/null
@@ -0,0 +1,51 @@
+=> proc interface
+* implement new interface to add/delete interfaces and setting options
+* /proc/sys/net/batman-adv/ as main folder
+* in interfaces/ list every available interface of the host
+* each interfaces/$iface/ contains the following files:
+-> enable (def: 0) [add/remove this interface to batman-adv]
+-> ogm_interval (def: 1000) [ogm interval of that interface]
+-> context (def: bat0) [later we want to support multiple mesh instances via
+-> bat0/bat1/bat2/..]
+-> status (read-only) [outputs the interface status from batman's
+-> perspective]
+* in mesh/batX/ list every available mesh subnet
+-> vis_server (def: 0) [enable/disable vis server for that mesh]
+-> vis_data (read-only) [outputs the vis data in a raw format]
+-> aggregate_ogm (def: 1) [enable/disable ogm aggregation for that mesh]
+-> originators (read-only) [outputs the originator table]
+-> transtable_global (read-only) [outputs the global translation table]
+-> transtable_local (read-only) [outputs the local translation table]
+
+=> vis "raw" data output
+* the raw format shall replace dot draw / json to offer a neutral that can
+* be converted
+* the format (comma seperated entries):
+-> "mac" -> mac address of an originator (each line begins with it)
+-> "TQ mac value" -> src mac's link quality towards mac address
+-> "HNA mac" -> HNA announced by source mac
+-> "PRIMARY" -> this is a primary interface
+-> "SEC mac" -> secondary mac address of source (requires preceeding
+-> PRIMARY)
+
+=> logging
+* the log level LOG_TYPE_CRIT, LOG_TYPE_WARN & LOG_TYPE_NOTICE will be
+* unified to use printk
+* LOG_TYPE_BATMAN & LOG_TYPE_ROUTES will also use printk but only after the
+* internal debug level has been raised
+* the internal debug level can be modified using a module parameter (debug)
+* or at run time via /sys/module/batman-adv/parameters/debug
+* make use of printk %pM support instead of converting mac addresses
+* manually
+
+=> strip out all backward compatibility support to older kernels
+   (only found in compat.h)
+
+=> fix checkpatch.pl errors
+
+Please send all patches to:
+       Marek Lindner <lindner_marek@yahoo.de>
+       Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
+       Andrew Lunn <andrew@lunn.ch>
+       b.a.t.m.a.n@lists.open-mesh.net
+       Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/staging/batman-adv/aggregation.c b/drivers/staging/batman-adv/aggregation.c
new file mode 100644 (file)
index 0000000..9c6e681
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "aggregation.h"
+#include "send.h"
+#include "routing.h"
+
+/* calculate the size of the hna information for a given packet */
+static int hna_len(struct batman_packet *batman_packet)
+{
+       return batman_packet->num_hna * ETH_ALEN;
+}
+
+/* return true if new_packet can be aggregated with forw_packet */
+static bool can_aggregate_with(struct batman_packet *new_batman_packet,
+                              int packet_len,
+                              unsigned long send_time,
+                              bool directlink,
+                              struct batman_if *if_incoming,
+                              struct forw_packet *forw_packet)
+{
+       struct batman_packet *batman_packet =
+               (struct batman_packet *)forw_packet->packet_buff;
+       int aggregated_bytes = forw_packet->packet_len + packet_len;
+
+       /**
+        * we can aggregate the current packet to this aggregated packet
+        * if:
+        *
+        * - the send time is within our MAX_AGGREGATION_MS time
+        * - the resulting packet wont be bigger than
+        *   MAX_AGGREGATION_BYTES
+        */
+
+       if (time_before(send_time, forw_packet->send_time) &&
+           (aggregated_bytes <= MAX_AGGREGATION_BYTES)) {
+
+               /**
+                * check aggregation compatibility
+                * -> direct link packets are broadcasted on
+                *    their interface only
+                * -> aggregate packet if the current packet is
+                *    a "global" packet as well as the base
+                *    packet
+                */
+
+               /* packets without direct link flag and high TTL
+                * are flooded through the net  */
+               if ((!directlink) &&
+                   (!(batman_packet->flags & DIRECTLINK)) &&
+                   (batman_packet->ttl != 1) &&
+
+                   /* own packets originating non-primary
+                    * interfaces leave only that interface */
+                   ((!forw_packet->own) ||
+                    (forw_packet->if_incoming->if_num == 0)))
+                       return true;
+
+               /* if the incoming packet is sent via this one
+                * interface only - we still can aggregate */
+               if ((directlink) &&
+                   (new_batman_packet->ttl == 1) &&
+                   (forw_packet->if_incoming == if_incoming))
+                       return true;
+
+       }
+
+       return false;
+}
+
+/* create a new aggregated packet and add this packet to it */
+static void new_aggregated_packet(unsigned char *packet_buff,
+                          int packet_len,
+                          unsigned long send_time,
+                          bool direct_link,
+                          struct batman_if *if_incoming,
+                          int own_packet)
+{
+       struct forw_packet *forw_packet_aggr;
+
+       forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC);
+       if (!forw_packet_aggr)
+               return;
+
+       forw_packet_aggr->packet_buff = kmalloc(MAX_AGGREGATION_BYTES,
+                                               GFP_ATOMIC);
+       if (!forw_packet_aggr->packet_buff) {
+               kfree(forw_packet_aggr);
+               return;
+       }
+
+       INIT_HLIST_NODE(&forw_packet_aggr->list);
+
+       forw_packet_aggr->packet_len = packet_len;
+       memcpy(forw_packet_aggr->packet_buff,
+              packet_buff,
+              forw_packet_aggr->packet_len);
+
+       forw_packet_aggr->own = own_packet;
+       forw_packet_aggr->if_incoming = if_incoming;
+       forw_packet_aggr->num_packets = 0;
+       forw_packet_aggr->direct_link_flags = 0;
+       forw_packet_aggr->send_time = send_time;
+
+       /* save packet direct link flag status */
+       if (direct_link)
+               forw_packet_aggr->direct_link_flags |= 1;
+
+       /* add new packet to packet list */
+       spin_lock(&forw_bat_list_lock);
+       hlist_add_head(&forw_packet_aggr->list, &forw_bat_list);
+       spin_unlock(&forw_bat_list_lock);
+
+       /* start timer for this packet */
+       INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work,
+                         send_outstanding_bat_packet);
+       queue_delayed_work(bat_event_workqueue,
+                          &forw_packet_aggr->delayed_work,
+                          send_time - jiffies);
+}
+
+/* aggregate a new packet into the existing aggregation */
+static void aggregate(struct forw_packet *forw_packet_aggr,
+                     unsigned char *packet_buff,
+                     int packet_len,
+                     bool direct_link)
+{
+       memcpy((forw_packet_aggr->packet_buff + forw_packet_aggr->packet_len),
+              packet_buff, packet_len);
+       forw_packet_aggr->packet_len += packet_len;
+       forw_packet_aggr->num_packets++;
+
+       /* save packet direct link flag status */
+       if (direct_link)
+               forw_packet_aggr->direct_link_flags |=
+                       (1 << forw_packet_aggr->num_packets);
+}
+
+void add_bat_packet_to_list(unsigned char *packet_buff, int packet_len,
+                           struct batman_if *if_incoming, char own_packet,
+                           unsigned long send_time)
+{
+       /**
+        * _aggr -> pointer to the packet we want to aggregate with
+        * _pos -> pointer to the position in the queue
+        */
+       struct forw_packet *forw_packet_aggr = NULL, *forw_packet_pos = NULL;
+       struct hlist_node *tmp_node;
+       struct batman_packet *batman_packet =
+               (struct batman_packet *)packet_buff;
+       bool direct_link = batman_packet->flags & DIRECTLINK ? 1 : 0;
+
+       /* find position for the packet in the forward queue */
+       spin_lock(&forw_bat_list_lock);
+       /* own packets are not to be aggregated */
+       if ((atomic_read(&aggregation_enabled)) && (!own_packet)) {
+               hlist_for_each_entry(forw_packet_pos, tmp_node, &forw_bat_list,
+                                    list) {
+                       if (can_aggregate_with(batman_packet,
+                                              packet_len,
+                                              send_time,
+                                              direct_link,
+                                              if_incoming,
+                                              forw_packet_pos)) {
+                               forw_packet_aggr = forw_packet_pos;
+                               break;
+                       }
+               }
+       }
+
+       /* nothing to aggregate with - either aggregation disabled or no
+        * suitable aggregation packet found */
+       if (forw_packet_aggr == NULL) {
+               /* the following section can run without the lock */
+               spin_unlock(&forw_bat_list_lock);
+               new_aggregated_packet(packet_buff, packet_len,
+                                     send_time, direct_link,
+                                     if_incoming, own_packet);
+       } else {
+               aggregate(forw_packet_aggr,
+                         packet_buff, packet_len,
+                         direct_link);
+               spin_unlock(&forw_bat_list_lock);
+       }
+}
+
+/* unpack the aggregated packets and process them one by one */
+void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
+                            int packet_len, struct batman_if *if_incoming)
+{
+       struct batman_packet *batman_packet;
+       int buff_pos = 0;
+       unsigned char *hna_buff;
+
+       batman_packet = (struct batman_packet *)packet_buff;
+
+       while (aggregated_packet(buff_pos, packet_len,
+                                batman_packet->num_hna)) {
+
+               /* network to host order for our 16bit seqno, and the
+                  orig_interval. */
+               batman_packet->seqno = ntohs(batman_packet->seqno);
+
+               hna_buff = packet_buff + buff_pos + BAT_PACKET_LEN;
+               receive_bat_packet(ethhdr, batman_packet,
+                                  hna_buff, hna_len(batman_packet),
+                                  if_incoming);
+
+               buff_pos += BAT_PACKET_LEN + hna_len(batman_packet);
+               batman_packet = (struct batman_packet *)
+                       (packet_buff + buff_pos);
+       }
+}
diff --git a/drivers/staging/batman-adv/aggregation.h b/drivers/staging/batman-adv/aggregation.h
new file mode 100644 (file)
index 0000000..6da8df9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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"
+
+/* is there another aggregated packet here? */
+static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna)
+{
+       int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_hna * ETH_ALEN);
+
+       return (next_buff_pos <= packet_len) &&
+               (next_buff_pos <= MAX_AGGREGATION_BYTES);
+}
+
+void add_bat_packet_to_list(unsigned char *packet_buff, int packet_len,
+                           struct batman_if *if_outgoing, char own_packet,
+                           unsigned long send_time);
+void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
+                            int packet_len, struct batman_if *if_incoming);
diff --git a/drivers/staging/batman-adv/bitarray.c b/drivers/staging/batman-adv/bitarray.c
new file mode 100644 (file)
index 0000000..3c67f5f
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors:
+ *
+ * Simon Wunderlich, 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 "bitarray.h"
+#include "log.h"
+
+/* returns true if the corresponding bit in the given seq_bits indicates true
+ * and curr_seqno is within range of last_seqno */
+uint8_t get_bit_status(TYPE_OF_WORD *seq_bits, uint16_t last_seqno,
+                      uint16_t curr_seqno)
+{
+       int16_t diff, word_offset, word_num;
+
+       diff = last_seqno - curr_seqno;
+       if (diff < 0 || diff >= TQ_LOCAL_WINDOW_SIZE) {
+               return 0;
+       } else {
+               /* which word */
+               word_num = (last_seqno - curr_seqno) / WORD_BIT_SIZE;
+               /* which position in the selected word */
+               word_offset = (last_seqno - curr_seqno) % WORD_BIT_SIZE;
+
+               if (seq_bits[word_num] & 1 << word_offset)
+                       return 1;
+               else
+                       return 0;
+       }
+}
+
+/* turn corresponding bit on, so we can remember that we got the packet */
+void bit_mark(TYPE_OF_WORD *seq_bits, int32_t n)
+{
+       int32_t word_offset, word_num;
+
+       /* if too old, just drop it */
+       if (n < 0 || n >= TQ_LOCAL_WINDOW_SIZE)
+               return;
+
+       /* which word */
+       word_num = n / WORD_BIT_SIZE;
+       /* which position in the selected word */
+       word_offset = n % WORD_BIT_SIZE;
+
+       seq_bits[word_num] |= 1 << word_offset; /* turn the position on */
+}
+
+/* shift the packet array by n places. */
+void bit_shift(TYPE_OF_WORD *seq_bits, int32_t n)
+{
+       int32_t word_offset, word_num;
+       int32_t i;
+
+       if (n <= 0)
+               return;
+
+       word_offset = n % WORD_BIT_SIZE;/* shift how much inside each word */
+       word_num = n / WORD_BIT_SIZE;   /* shift over how much (full) words */
+
+       for (i = NUM_WORDS - 1; i > word_num; i--) {
+               /* going from old to new, so we don't overwrite the data we copy
+                * from.
+                *
+                * left is high, right is low: FEDC BA98 7654 3210
+                *                                        ^^ ^^
+                *                             vvvv
+                * ^^^^ = from, vvvvv =to, we'd have word_num==1 and
+                * word_offset==WORD_BIT_SIZE/2 ????? in this example.
+                * (=24 bits)
+                *
+                * our desired output would be: 9876 5432 1000 0000
+                * */
+
+               seq_bits[i] =
+                       (seq_bits[i - word_num] << word_offset) +
+                       /* take the lower port from the left half, shift it left
+                        * to its final position */
+                       (seq_bits[i - word_num - 1] >>
+                        (WORD_BIT_SIZE-word_offset));
+               /* and the upper part of the right half and shift it left to
+                * it's position */
+               /* for our example that would be: word[0] = 9800 + 0076 =
+                * 9876 */
+       }
+       /* now for our last word, i==word_num, we only have the it's "left"
+        * half. that's the 1000 word in our example.*/
+
+       seq_bits[i] = (seq_bits[i - word_num] << word_offset);
+
+       /* pad the rest with 0, if there is anything */
+       i--;
+
+       for (; i >= 0; i--)
+               seq_bits[i] = 0;
+}
+
+
+/* receive and process one packet, returns 1 if received seq_num is considered
+ * new, 0 if old  */
+char bit_get_packet(TYPE_OF_WORD *seq_bits, int16_t seq_num_diff,
+                   int8_t set_mark)
+{
+       int i;
+
+       /* we already got a sequence number higher than this one, so we just
+        * mark it. this should wrap around the integer just fine */
+       if ((seq_num_diff < 0) && (seq_num_diff >= -TQ_LOCAL_WINDOW_SIZE)) {
+               if (set_mark)
+                       bit_mark(seq_bits, -seq_num_diff);
+               return 0;
+       }
+
+       /* it seems we missed a lot of packets or the other host restarted */
+       if ((seq_num_diff > TQ_LOCAL_WINDOW_SIZE) ||
+           (seq_num_diff < -TQ_LOCAL_WINDOW_SIZE)) {
+
+               if (seq_num_diff > TQ_LOCAL_WINDOW_SIZE)
+                       debug_log(LOG_TYPE_BATMAN,
+                                 "We missed a lot of packets (%i) !\n",
+                                 seq_num_diff-1);
+
+               if (-seq_num_diff > TQ_LOCAL_WINDOW_SIZE)
+                       debug_log(LOG_TYPE_BATMAN,
+                                 "Other host probably restarted !\n");
+
+               for (i = 0; i < NUM_WORDS; i++)
+                       seq_bits[i] = 0;
+
+               if (set_mark)
+                       seq_bits[0] = 1;  /* we only have the latest packet */
+       } else {
+               bit_shift(seq_bits, seq_num_diff);
+
+               if (set_mark)
+                       bit_mark(seq_bits, 0);
+       }
+
+       return 1;
+}
+
+/* count the hamming weight, how many good packets did we receive? just count
+ * the 1's. The inner loop uses the Kernighan algorithm, see
+ * http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
+ */
+int bit_packet_count(TYPE_OF_WORD *seq_bits)
+{
+       int i, hamming = 0;
+       TYPE_OF_WORD word;
+
+       for (i = 0; i < NUM_WORDS; i++) {
+               word = seq_bits[i];
+
+               while (word) {
+                       word &= word-1;
+                       hamming++;
+               }
+       }
+       return hamming;
+}
diff --git a/drivers/staging/batman-adv/bitarray.h b/drivers/staging/batman-adv/bitarray.h
new file mode 100644 (file)
index 0000000..ec72dd7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors:
+ *
+ * Simon Wunderlich, 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
+ *
+ */
+
+
+/* you should choose something big, if you don't want to waste cpu */
+#define TYPE_OF_WORD unsigned long
+#define WORD_BIT_SIZE (sizeof(TYPE_OF_WORD) * 8)
+
+/* returns true if the corresponding bit in the given seq_bits indicates true
+ * and curr_seqno is within range of last_seqno */
+uint8_t get_bit_status(TYPE_OF_WORD *seq_bits, uint16_t last_seqno,
+                                          uint16_t curr_seqno);
+
+/* turn corresponding bit on, so we can remember that we got the packet */
+void bit_mark(TYPE_OF_WORD *seq_bits, int32_t n);
+
+/* shift the packet array by n places. */
+void bit_shift(TYPE_OF_WORD *seq_bits, int32_t n);
+
+
+/* receive and process one packet, returns 1 if received seq_num is considered
+ * new, 0 if old  */
+char bit_get_packet(TYPE_OF_WORD *seq_bits, int16_t seq_num_diff,
+                                       int8_t set_mark);
+
+/* count the hamming weight, how many good packets did we receive? */
+int  bit_packet_count(TYPE_OF_WORD *seq_bits);
diff --git a/drivers/staging/batman-adv/compat.h b/drivers/staging/batman-adv/compat.h
new file mode 100644 (file)
index 0000000..f4e0a45
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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
+ *
+ *
+ * This file contains macros for maintaining compatibility with older versions
+ * of the Linux kernel.
+ */
+
+#include <linux/version.h>     /* LINUX_VERSION_CODE */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+
+#define skb_set_network_header(_skb, _offset) \
+       do { (_skb)->nh.raw = (_skb)->data + (_offset); } while (0)
+
+#define skb_reset_mac_header(_skb) \
+       do { (_skb)->mac.raw = (_skb)->data; } while (0)
+
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
+
+#define device_create(_cls, _parent, _devt, _device, _fmt) \
+       class_device_create(_cls, _parent, _devt, _device, _fmt)
+
+#define device_destroy(_cls, _device) \
+       class_device_destroy(_cls, _device)
+
+#else
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
+
+#define device_create(_cls, _parent, _devt, _device, _fmt) \
+       device_create_drvdata(_cls, _parent, _devt, _device, _fmt)
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) */
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
+
+#define cancel_delayed_work_sync(wq) cancel_rearming_delayed_work(wq)
+
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+#define strict_strtoul(cp, base, res) \
+       ({ \
+       int ret = 0; \
+       char *endp; \
+       *res = simple_strtoul(cp, &endp, base); \
+       if (cp == endp) \
+               ret = -EINVAL; \
+       ret; \
+})
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */
diff --git a/drivers/staging/batman-adv/device.c b/drivers/staging/batman-adv/device.c
new file mode 100644 (file)
index 0000000..1e7d1f8
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2007-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 "device.h"
+#include "log.h"
+#include "send.h"
+#include "types.h"
+#include "hash.h"
+
+#include "compat.h"
+
+static struct class *batman_class;
+
+static int Major;      /* Major number assigned to our device driver */
+
+static const struct file_operations fops = {
+       .open = bat_device_open,
+       .release = bat_device_release,
+       .read = bat_device_read,
+       .write = bat_device_write,
+       .poll = bat_device_poll,
+};
+
+static struct device_client *device_client_hash[256];
+
+void bat_device_init(void)
+{
+       int i;
+
+       for (i = 0; i < 256; i++)
+               device_client_hash[i] = NULL;
+}
+
+int bat_device_setup(void)
+{
+       int tmp_major;
+
+       if (Major)
+               return 1;
+
+       /* register our device - kernel assigns a free major number */
+       tmp_major = register_chrdev(0, DRIVER_DEVICE, &fops);
+       if (tmp_major < 0) {
+               debug_log(LOG_TYPE_WARN, "Registering the character device failed with %d\n",
+                         tmp_major);
+               return 0;
+       }
+
+       batman_class = class_create(THIS_MODULE, "batman-adv");
+
+       if (IS_ERR(batman_class)) {
+               debug_log(LOG_TYPE_WARN, "Could not register class 'batman-adv' \n");
+               return 0;
+       }
+
+       device_create(batman_class, NULL, MKDEV(tmp_major, 0), NULL,
+                     "batman-adv");
+
+       Major = tmp_major;
+       return 1;
+}
+
+void bat_device_destroy(void)
+{
+       if (!Major)
+               return;
+
+       device_destroy(batman_class, MKDEV(Major, 0));
+       class_destroy(batman_class);
+
+       /* Unregister the device */
+       unregister_chrdev(Major, DRIVER_DEVICE);
+
+       Major = 0;
+}
+
+int bat_device_open(struct inode *inode, struct file *file)
+{
+       unsigned int i;
+       struct device_client *device_client;
+
+       device_client = kmalloc(sizeof(struct device_client), GFP_KERNEL);
+
+       if (!device_client)
+               return -ENOMEM;
+
+       for (i = 0; i < 256; i++) {
+               if (!device_client_hash[i]) {
+                       device_client_hash[i] = device_client;
+                       break;
+               }
+       }
+
+       if (device_client_hash[i] != device_client) {
+               debug_log(LOG_TYPE_WARN, "Error - can't add another packet client: maximum number of clients reached \n");
+               kfree(device_client);
+               return -EXFULL;
+       }
+
+       INIT_LIST_HEAD(&device_client->queue_list);
+       device_client->queue_len = 0;
+       device_client->index = i;
+       device_client->lock = __SPIN_LOCK_UNLOCKED(device_client->lock);
+       init_waitqueue_head(&device_client->queue_wait);
+
+       file->private_data = device_client;
+
+       inc_module_count();
+       return 0;
+}
+
+int bat_device_release(struct inode *inode, struct file *file)
+{
+       struct device_client *device_client =
+               (struct device_client *)file->private_data;
+       struct device_packet *device_packet;
+       struct list_head *list_pos, *list_pos_tmp;
+
+       spin_lock(&device_client->lock);
+
+       /* for all packets in the queue ... */
+       list_for_each_safe(list_pos, list_pos_tmp, &device_client->queue_list) {
+               device_packet = list_entry(list_pos,
+                                          struct device_packet, list);
+
+               list_del(list_pos);
+               kfree(device_packet);
+       }
+
+       device_client_hash[device_client->index] = NULL;
+       spin_unlock(&device_client->lock);
+
+       kfree(device_client);
+       dec_module_count();
+
+       return 0;
+}
+
+ssize_t bat_device_read(struct file *file, char __user *buf, size_t count,
+                       loff_t *ppos)
+{
+       struct device_client *device_client =
+               (struct device_client *)file->private_data;
+       struct device_packet *device_packet;
+       int error;
+
+       if ((file->f_flags & O_NONBLOCK) && (device_client->queue_len == 0))
+               return -EAGAIN;
+
+       if ((!buf) || (count < sizeof(struct icmp_packet)))
+               return -EINVAL;
+
+       if (!access_ok(VERIFY_WRITE, buf, count))
+               return -EFAULT;
+
+       error = wait_event_interruptible(device_client->queue_wait,
+                                        device_client->queue_len);
+
+       if (error)
+               return error;
+
+       spin_lock(&device_client->lock);
+
+       device_packet = list_first_entry(&device_client->queue_list,
+                                        struct device_packet, list);
+       list_del(&device_packet->list);
+       device_client->queue_len--;
+
+       spin_unlock(&device_client->lock);
+
+       error = __copy_to_user(buf, &device_packet->icmp_packet,
+                              sizeof(struct icmp_packet));
+
+       kfree(device_packet);
+
+       if (error)
+               return error;
+
+       return sizeof(struct icmp_packet);
+}
+
+ssize_t bat_device_write(struct file *file, const char __user *buff,
+                        size_t len, loff_t *off)
+{
+       struct device_client *device_client =
+               (struct device_client *)file->private_data;
+       struct icmp_packet icmp_packet;
+       struct orig_node *orig_node;
+       struct batman_if *batman_if;
+
+       if (len < sizeof(struct icmp_packet)) {
+               debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from char device: invalid packet size\n");
+               return -EINVAL;
+       }
+
+       if (!access_ok(VERIFY_READ, buff, sizeof(struct icmp_packet)))
+               return -EFAULT;
+
+       if (__copy_from_user(&icmp_packet, buff, sizeof(icmp_packet)))
+               return -EFAULT;
+
+       if (icmp_packet.packet_type != BAT_ICMP) {
+               debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n");
+               return -EINVAL;
+       }
+
+       if (icmp_packet.msg_type != ECHO_REQUEST) {
+               debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n");
+               return -EINVAL;
+       }
+
+       icmp_packet.uid = device_client->index;
+
+       if (icmp_packet.version != COMPAT_VERSION) {
+               icmp_packet.msg_type = PARAMETER_PROBLEM;
+               icmp_packet.ttl = COMPAT_VERSION;
+               bat_device_add_packet(device_client, &icmp_packet);
+               goto out;
+       }
+
+       if (atomic_read(&module_state) != MODULE_ACTIVE)
+               goto dst_unreach;
+
+       spin_lock(&orig_hash_lock);
+       orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet.dst));
+
+       if (!orig_node)
+               goto unlock;
+
+       if (!orig_node->router)
+               goto unlock;
+
+       batman_if = orig_node->batman_if;
+
+       if (!batman_if)
+               goto unlock;
+
+       memcpy(icmp_packet.orig,
+              batman_if->net_dev->dev_addr,
+              ETH_ALEN);
+
+       send_raw_packet((unsigned char *)&icmp_packet,
+                       sizeof(struct icmp_packet),
+                       batman_if, orig_node->router->addr);
+
+       spin_unlock(&orig_hash_lock);
+       goto out;
+
+unlock:
+       spin_unlock(&orig_hash_lock);
+dst_unreach:
+       icmp_packet.msg_type = DESTINATION_UNREACHABLE;
+       bat_device_add_packet(device_client, &icmp_packet);
+out:
+       return len;
+}
+
+unsigned int bat_device_poll(struct file *file, poll_table *wait)
+{
+       struct device_client *device_client =
+               (struct device_client *)file->private_data;
+
+       poll_wait(file, &device_client->queue_wait, wait);
+
+       if (device_client->queue_len > 0)
+               return POLLIN | POLLRDNORM;
+
+       return 0;
+}
+
+void bat_device_add_packet(struct device_client *device_client,
+                          struct icmp_packet *icmp_packet)
+{
+       struct device_packet *device_packet;
+
+       device_packet = kmalloc(sizeof(struct device_packet), GFP_KERNEL);
+
+       if (!device_packet)
+               return;
+
+       INIT_LIST_HEAD(&device_packet->list);
+       memcpy(&device_packet->icmp_packet, icmp_packet,
+              sizeof(struct icmp_packet));
+
+       spin_lock(&device_client->lock);
+
+       /* while waiting for the lock the device_client could have been
+        * deleted */
+       if (!device_client_hash[icmp_packet->uid]) {
+               spin_unlock(&device_client->lock);
+               kfree(device_packet);
+               return;
+       }
+
+       list_add_tail(&device_packet->list, &device_client->queue_list);
+       device_client->queue_len++;
+
+       if (device_client->queue_len > 100) {
+               device_packet = list_first_entry(&device_client->queue_list,
+                                                struct device_packet, list);
+
+               list_del(&device_packet->list);
+               kfree(device_packet);
+               device_client->queue_len--;
+       }
+
+       spin_unlock(&device_client->lock);
+
+       wake_up(&device_client->queue_wait);
+}
+
+void bat_device_receive_packet(struct icmp_packet *icmp_packet)
+{
+       struct device_client *hash = device_client_hash[icmp_packet->uid];
+
+       if (hash)
+               bat_device_add_packet(hash, icmp_packet);
+}
diff --git a/drivers/staging/batman-adv/device.h b/drivers/staging/batman-adv/device.h
new file mode 100644 (file)
index 0000000..46c0f44
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007-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 "types.h"
+
+void bat_device_init(void);
+int bat_device_setup(void);
+void bat_device_destroy(void);
+int bat_device_open(struct inode *inode, struct file *file);
+int bat_device_release(struct inode *inode, struct file *file);
+ssize_t bat_device_read(struct file *file, char __user *buf, size_t count,
+                       loff_t *ppos);
+ssize_t bat_device_write(struct file *file, const char __user *buff,
+                        size_t len, loff_t *off);
+unsigned int bat_device_poll(struct file *file, poll_table *wait);
+void bat_device_add_packet(struct device_client *device_client,
+                          struct icmp_packet *icmp_packet);
+void bat_device_receive_packet(struct icmp_packet *icmp_packet);
diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c
new file mode 100644 (file)
index 0000000..5ea35da
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "hard-interface.h"
+#include "log.h"
+#include "soft-interface.h"
+#include "send.h"
+#include "translation-table.h"
+#include "routing.h"
+#include "hash.h"
+#include "compat.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+static char avail_ifs;
+static char active_ifs;
+
+static void hardif_free_interface(struct rcu_head *rcu);
+
+static struct batman_if *get_batman_if_by_name(char *name)
+{
+       struct batman_if *batman_if;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               if (strncmp(batman_if->dev, name, IFNAMSIZ) == 0)
+                       goto out;
+       }
+
+       batman_if = NULL;
+
+out:
+       rcu_read_unlock();
+       return batman_if;
+}
+
+int hardif_min_mtu(void)
+{
+       struct batman_if *batman_if;
+       /* allow big frames if all devices are capable to do so
+        * (have MTU > 1500 + BAT_HEADER_LEN) */
+       int min_mtu = ETH_DATA_LEN;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               if ((batman_if->if_active == IF_ACTIVE) ||
+                   (batman_if->if_active == IF_TO_BE_ACTIVATED))
+                       min_mtu = MIN(batman_if->net_dev->mtu - BAT_HEADER_LEN,
+                                     min_mtu);
+       }
+       rcu_read_unlock();
+
+       return min_mtu;
+}
+
+static void check_known_mac_addr(uint8_t *addr)
+{
+       struct batman_if *batman_if;
+       char mac_string[ETH_STR_LEN];
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               if ((batman_if->if_active != IF_ACTIVE) &&
+                   (batman_if->if_active != IF_TO_BE_ACTIVATED))
+                       continue;
+
+               if (!compare_orig(batman_if->net_dev->dev_addr, addr))
+                       continue;
+
+               addr_to_string(mac_string, addr);
+               debug_log(LOG_TYPE_WARN, "The newly added mac address (%s) already exists on: %s\n",
+                         mac_string, batman_if->dev);
+               debug_log(LOG_TYPE_WARN, "It is strongly recommended to keep mac addresses unique to avoid problems!\n");
+       }
+       rcu_read_unlock();
+}
+
+/* adjusts the MTU if a new interface with a smaller MTU appeared. */
+void update_min_mtu(void)
+{
+       int min_mtu;
+
+       min_mtu = hardif_min_mtu();
+       if (soft_device->mtu != min_mtu)
+               soft_device->mtu = min_mtu;
+}
+
+/* checks if the interface is up. (returns 1 if it is) */
+static int hardif_is_interface_up(char *dev)
+{
+       struct net_device *net_dev;
+
+       /**
+        * if we already have an interface in our interface list and
+        * the current interface is not the primary interface and
+        * the primary interface is not up and
+        * the primary interface has never been up - don't activate any
+        * secondary interface !
+        */
+
+       rcu_read_lock();
+       if ((!list_empty(&if_list)) &&
+           strncmp(((struct batman_if *)if_list.next)->dev, dev, IFNAMSIZ) &&
+           !(((struct batman_if *)if_list.next)->if_active == IF_ACTIVE) &&
+           !(((struct batman_if *)if_list.next)->if_active == IF_TO_BE_ACTIVATED) &&
+           (!main_if_was_up())) {
+               rcu_read_unlock();
+               goto end;
+       }
+       rcu_read_unlock();
+
+#ifdef __NET_NET_NAMESPACE_H
+       net_dev = dev_get_by_name(&init_net, dev);
+#else
+       net_dev = dev_get_by_name(dev);
+#endif
+       if (!net_dev)
+               goto end;
+
+       if (!(net_dev->flags & IFF_UP))
+               goto failure;
+
+       dev_put(net_dev);
+       return 1;
+
+failure:
+       dev_put(net_dev);
+end:
+       return 0;
+}
+
+/* deactivates the interface. */
+void hardif_deactivate_interface(struct batman_if *batman_if)
+{
+       if (batman_if->if_active != IF_ACTIVE)
+               return;
+
+       if (batman_if->raw_sock)
+               sock_release(batman_if->raw_sock);
+
+       /**
+        * batman_if->net_dev has been acquired by dev_get_by_name() in
+        * proc_interfaces_write() and has to be unreferenced.
+        */
+
+       if (batman_if->net_dev)
+               dev_put(batman_if->net_dev);
+
+       batman_if->raw_sock = NULL;
+       batman_if->net_dev = NULL;
+
+       batman_if->if_active = IF_INACTIVE;
+       active_ifs--;
+
+       debug_log(LOG_TYPE_NOTICE, "Interface deactivated: %s\n",
+                 batman_if->dev);
+}
+
+/* (re)activate given interface. */
+static void hardif_activate_interface(struct batman_if *batman_if)
+{
+       struct sockaddr_ll bind_addr;
+       int retval;
+
+       if (batman_if->if_active != IF_INACTIVE)
+               return;
+
+#ifdef __NET_NET_NAMESPACE_H
+       batman_if->net_dev = dev_get_by_name(&init_net, batman_if->dev);
+#else
+       batman_if->net_dev = dev_get_by_name(batman_if->dev);
+#endif
+       if (!batman_if->net_dev)
+               goto dev_err;
+
+       retval = sock_create_kern(PF_PACKET, SOCK_RAW,
+                                 __constant_htons(ETH_P_BATMAN),
+                                 &batman_if->raw_sock);
+
+       if (retval < 0) {
+               debug_log(LOG_TYPE_WARN, "Can't create raw socket: %i\n",
+                         retval);
+               goto sock_err;
+       }
+
+       bind_addr.sll_family = AF_PACKET;
+       bind_addr.sll_ifindex = batman_if->net_dev->ifindex;
+       bind_addr.sll_protocol = 0;     /* is set by the kernel */
+
+       retval = kernel_bind(batman_if->raw_sock,
+                            (struct sockaddr *)&bind_addr, sizeof(bind_addr));
+
+       if (retval < 0) {
+               debug_log(LOG_TYPE_WARN, "Can't create bind raw socket: %i\n",
+                         retval);
+               goto bind_err;
+       }
+
+       check_known_mac_addr(batman_if->net_dev->dev_addr);
+
+       batman_if->raw_sock->sk->sk_user_data =
+               batman_if->raw_sock->sk->sk_data_ready;
+       batman_if->raw_sock->sk->sk_data_ready = batman_data_ready;
+
+       addr_to_string(batman_if->addr_str, batman_if->net_dev->dev_addr);
+
+       memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig,
+              batman_if->net_dev->dev_addr, ETH_ALEN);
+       memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender,
+              batman_if->net_dev->dev_addr, ETH_ALEN);
+
+       batman_if->if_active = IF_TO_BE_ACTIVATED;
+       active_ifs++;
+
+       /* save the mac address if it is our primary interface */
+       if (batman_if->if_num == 0)
+               set_main_if_addr(batman_if->net_dev->dev_addr);
+
+       debug_log(LOG_TYPE_NOTICE, "Interface activated: %s\n",
+                 batman_if->dev);
+
+       return;
+
+bind_err:
+       sock_release(batman_if->raw_sock);
+sock_err:
+       dev_put(batman_if->net_dev);
+dev_err:
+       batman_if->raw_sock = NULL;
+       batman_if->net_dev = NULL;
+}
+
+static void hardif_free_interface(struct rcu_head *rcu)
+{
+       struct batman_if *batman_if = container_of(rcu, struct batman_if, rcu);
+
+       kfree(batman_if->packet_buff);
+       kfree(batman_if->dev);
+       kfree(batman_if);
+}
+
+/**
+ * called by
+ *  - echo '' > /proc/.../interfaces
+ *  - modprobe -r batman-adv-core
+ */
+/* removes and frees all interfaces */
+void hardif_remove_interfaces(void)
+{
+       struct batman_if *batman_if = NULL;
+
+       avail_ifs = 0;
+
+       /* no lock needed - we don't delete somewhere else */
+       list_for_each_entry(batman_if, &if_list, list) {
+
+               list_del_rcu(&batman_if->list);
+
+               /* first deactivate interface */
+               if (batman_if->if_active != IF_INACTIVE)
+                       hardif_deactivate_interface(batman_if);
+
+               call_rcu(&batman_if->rcu, hardif_free_interface);
+       }
+}
+
+static int resize_orig(struct orig_node *orig_node, int if_num)
+{
+       void *data_ptr;
+
+       data_ptr = kmalloc((if_num + 1) * sizeof(TYPE_OF_WORD) * NUM_WORDS,
+                          GFP_ATOMIC);
+       if (!data_ptr) {
+               debug_log(LOG_TYPE_WARN, "Can't resize orig: out of memory\n");
+               return -1;
+       }
+
+       memcpy(data_ptr, orig_node->bcast_own,
+              if_num * sizeof(TYPE_OF_WORD) * NUM_WORDS);
+       kfree(orig_node->bcast_own);
+       orig_node->bcast_own = data_ptr;
+
+       data_ptr = kmalloc((if_num + 1) * sizeof(uint8_t), GFP_ATOMIC);
+       if (!data_ptr) {
+               debug_log(LOG_TYPE_WARN, "Can't resize orig: out of memory\n");
+               return -1;
+       }
+
+       memcpy(data_ptr, orig_node->bcast_own_sum, if_num * sizeof(uint8_t));
+       kfree(orig_node->bcast_own_sum);
+       orig_node->bcast_own_sum = data_ptr;
+
+       return 0;
+}
+
+
+/* adds an interface the interface list and activate it, if possible */
+int hardif_add_interface(char *dev, int if_num)
+{
+       struct batman_if *batman_if;
+       struct batman_packet *batman_packet;
+       struct orig_node *orig_node;
+       struct hash_it_t *hashit = NULL;
+
+       batman_if = kmalloc(sizeof(struct batman_if), GFP_KERNEL);
+
+       if (!batman_if) {
+               debug_log(LOG_TYPE_WARN, "Can't add interface (%s): out of memory\n", dev);
+               return -1;
+       }
+
+       batman_if->raw_sock = NULL;
+       batman_if->net_dev = NULL;
+
+       if ((if_num == 0) && (num_hna > 0))
+               batman_if->packet_len = BAT_PACKET_LEN + num_hna * ETH_ALEN;
+       else
+               batman_if->packet_len = BAT_PACKET_LEN;
+
+       batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_KERNEL);
+
+       if (!batman_if->packet_buff) {
+               debug_log(LOG_TYPE_WARN, "Can't add interface packet (%s): out of memory\n", dev);
+               goto out;
+       }
+
+       batman_if->if_num = if_num;
+       batman_if->dev = dev;
+       batman_if->if_active = IF_INACTIVE;
+       INIT_RCU_HEAD(&batman_if->rcu);
+
+       debug_log(LOG_TYPE_NOTICE, "Adding interface: %s\n", dev);
+       avail_ifs++;
+
+       INIT_LIST_HEAD(&batman_if->list);
+
+       batman_packet = (struct batman_packet *)(batman_if->packet_buff);
+       batman_packet->packet_type = BAT_PACKET;
+       batman_packet->version = COMPAT_VERSION;
+       batman_packet->flags = 0x00;
+       batman_packet->ttl = (batman_if->if_num > 0 ? 2 : TTL);
+       batman_packet->flags = 0;
+       batman_packet->tq = TQ_MAX_VALUE;
+       batman_packet->num_hna = 0;
+
+       if (batman_if->packet_len != BAT_PACKET_LEN) {
+               unsigned char *hna_buff;
+               int hna_len;
+
+               hna_buff = batman_if->packet_buff + BAT_PACKET_LEN;
+               hna_len = batman_if->packet_len - BAT_PACKET_LEN;
+               batman_packet->num_hna = hna_local_fill_buffer(hna_buff,
+                                                              hna_len);
+       }
+
+       atomic_set(&batman_if->seqno, 1);
+
+       /* resize all orig nodes because orig_node->bcast_own(_sum) depend on
+        * if_num */
+       spin_lock(&orig_hash_lock);
+
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+               orig_node = hashit->bucket->data;
+               if (resize_orig(orig_node, if_num) == -1) {
+                       spin_unlock(&orig_hash_lock);
+                       goto out;
+               }
+       }
+
+       spin_unlock(&orig_hash_lock);
+
+       if (!hardif_is_interface_up(batman_if->dev))
+               debug_log(LOG_TYPE_WARN, "Not using interface %s (retrying later): interface not active\n", batman_if->dev);
+       else
+               hardif_activate_interface(batman_if);
+
+       list_add_tail_rcu(&batman_if->list, &if_list);
+
+       /* begin sending originator messages on that interface */
+       schedule_own_packet(batman_if);
+       return 1;
+
+out:
+       if (batman_if->packet_buff)
+               kfree(batman_if->packet_buff);
+       kfree(batman_if);
+       kfree(dev);
+       return -1;
+}
+
+char hardif_get_active_if_num(void)
+{
+       return active_ifs;
+}
+
+static int hard_if_event(struct notifier_block *this,
+                            unsigned long event, void *ptr)
+{
+       struct net_device *dev = (struct net_device *)ptr;
+       struct batman_if *batman_if = get_batman_if_by_name(dev->name);
+
+       if (!batman_if)
+               goto out;
+
+       switch (event) {
+       case NETDEV_GOING_DOWN:
+       case NETDEV_DOWN:
+       case NETDEV_UNREGISTER:
+               hardif_deactivate_interface(batman_if);
+               break;
+       case NETDEV_UP:
+               hardif_activate_interface(batman_if);
+               if ((atomic_read(&module_state) == MODULE_INACTIVE) &&
+                   (hardif_get_active_if_num() > 0)) {
+                       activate_module();
+               }
+               break;
+       /* NETDEV_CHANGEADDR - mac address change - what are we doing here ? */
+       default:
+               /* debug_log(LOG_TYPE_CRIT, "hard_if_event: %s %i\n", dev->name, event); */
+               break;
+       };
+
+       update_min_mtu();
+
+out:
+       return NOTIFY_DONE;
+}
+
+struct notifier_block hard_if_notifier = {
+        .notifier_call = hard_if_event,
+};
diff --git a/drivers/staging/batman-adv/hard-interface.h b/drivers/staging/batman-adv/hard-interface.h
new file mode 100644 (file)
index 0000000..742358c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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
+ *
+ */
+
+#define IF_INACTIVE 0
+#define IF_ACTIVE 1
+/* #define IF_TO_BE_DEACTIVATED 2 - not needed anymore */
+#define IF_TO_BE_ACTIVATED 3
+
+extern struct notifier_block hard_if_notifier;
+
+void hardif_remove_interfaces(void);
+int hardif_add_interface(char *dev, int if_num);
+void hardif_deactivate_interface(struct batman_if *batman_if);
+char hardif_get_active_if_num(void);
+void hardif_check_interfaces_status(void);
+void hardif_check_interfaces_status_wq(struct work_struct *work);
+int hardif_min_mtu(void);
+void update_min_mtu(void);
diff --git a/drivers/staging/batman-adv/hash.c b/drivers/staging/batman-adv/hash.c
new file mode 100644 (file)
index 0000000..61cb4a2
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors:
+ *
+ * Simon Wunderlich, 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 "hash.h"
+
+/* clears the hash */
+void hash_init(struct hashtable_t *hash)
+{
+       int i;
+
+       hash->elements = 0;
+
+       for (i = 0 ; i < hash->size; i++)
+               hash->table[i] = NULL;
+}
+
+/* remove the hash structure. if hashdata_free_cb != NULL, this function will be
+ * called to remove the elements inside of the hash.  if you don't remove the
+ * elements, memory might be leaked. */
+void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb)
+{
+       struct element_t *bucket, *last_bucket;
+       int i;
+
+       for (i = 0; i < hash->size; i++) {
+               bucket = hash->table[i];
+
+               while (bucket != NULL) {
+                       if (free_cb != NULL)
+                               free_cb(bucket->data);
+
+                       last_bucket = bucket;
+                       bucket = bucket->next;
+                       kfree(last_bucket);
+               }
+       }
+
+       hash_destroy(hash);
+}
+
+/* free only the hashtable and the hash itself. */
+void hash_destroy(struct hashtable_t *hash)
+{
+       kfree(hash->table);
+       kfree(hash);
+}
+
+/* iterate though the hash. first element is selected with iter_in NULL.  use
+ * the returned iterator to access the elements until hash_it_t returns NULL. */
+struct hash_it_t *hash_iterate(struct hashtable_t *hash,
+                              struct hash_it_t *iter_in)
+{
+       struct hash_it_t *iter;
+
+       if (!hash)
+               return NULL;
+
+       if (iter_in == NULL) {
+               iter = kmalloc(sizeof(struct hash_it_t), GFP_ATOMIC);
+               iter->index = -1;
+               iter->bucket = NULL;
+               iter->prev_bucket = NULL;
+       } else {
+               iter = iter_in;
+       }
+
+       /* sanity checks first (if our bucket got deleted in the last
+        * iteration): */
+       if (iter->bucket != NULL) {
+               if (iter->first_bucket != NULL) {
+                       /* we're on the first element and it got removed after
+                        * the last iteration. */
+                       if ((*iter->first_bucket) != iter->bucket) {
+                               /* there are still other elements in the list */
+                               if ((*iter->first_bucket) != NULL) {
+                                       iter->prev_bucket = NULL;
+                                       iter->bucket = (*iter->first_bucket);
+                                       iter->first_bucket =
+                                               &hash->table[iter->index];
+                                       return iter;
+                               } else {
+                                       iter->bucket = NULL;
+                               }
+                       }
+               } else if (iter->prev_bucket != NULL) {
+                       /*
+                       * we're not on the first element, and the bucket got
+                       * removed after the last iteration.  the last bucket's
+                       * next pointer is not pointing to our actual bucket
+                       * anymore.  select the next.
+                       */
+                       if (iter->prev_bucket->next != iter->bucket)
+                               iter->bucket = iter->prev_bucket;
+               }
+       }
+
+       /* now as we are sane, select the next one if there is some */
+       if (iter->bucket != NULL) {
+               if (iter->bucket->next != NULL) {
+                       iter->prev_bucket = iter->bucket;
+                       iter->bucket = iter->bucket->next;
+                       iter->first_bucket = NULL;
+                       return iter;
+               }
+       }
+
+       /* if not returned yet, we've reached the last one on the index and have
+        * to search forward */
+       iter->index++;
+       /* go through the entries of the hash table */
+       while (iter->index < hash->size) {
+               if ((hash->table[iter->index]) != NULL) {
+                       iter->prev_bucket = NULL;
+                       iter->bucket = hash->table[iter->index];
+                       iter->first_bucket = &hash->table[iter->index];
+                       return iter;
+               } else {
+                       iter->index++;
+               }
+       }
+
+       /* nothing to iterate over anymore */
+       kfree(iter);
+       return NULL;
+}
+
+/* allocates and clears the hash */
+struct hashtable_t *hash_new(int size, hashdata_compare_cb compare,
+                            hashdata_choose_cb choose)
+{
+       struct hashtable_t *hash;
+
+       hash = kmalloc(sizeof(struct hashtable_t) , GFP_ATOMIC);
+
+       if (hash == NULL)
+               return NULL;
+
+       hash->size = size;
+       hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC);
+
+       if (hash->table == NULL) {
+               kfree(hash);
+               return NULL;
+       }
+
+       hash_init(hash);
+
+       hash->compare = compare;
+       hash->choose = choose;
+
+       return hash;
+}
+
+/* adds data to the hashtable. returns 0 on success, -1 on error */
+int hash_add(struct hashtable_t *hash, void *data)
+{
+       int index;
+       struct element_t *bucket, *prev_bucket = NULL;
+
+       if (!hash)
+               return -1;
+
+       index = hash->choose(data, hash->size);
+       bucket = hash->table[index];
+
+       while (bucket != NULL) {
+               if (hash->compare(bucket->data, data))
+                       return -1;
+
+               prev_bucket = bucket;
+               bucket = bucket->next;
+       }
+
+       /* found the tail of the list, add new element */
+       bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC);
+
+       if (bucket == NULL)
+               return -1;
+
+       bucket->data = data;
+       bucket->next = NULL;
+
+       /* and link it */
+       if (prev_bucket == NULL)
+               hash->table[index] = bucket;
+       else
+               prev_bucket->next = bucket;
+
+       hash->elements++;
+       return 0;
+}
+
+/* finds data, based on the key in keydata. returns the found data on success,
+ * or NULL on error */
+void *hash_find(struct hashtable_t *hash, void *keydata)
+{
+       int index;
+       struct element_t *bucket;
+
+       if (!hash)
+               return NULL;
+
+       index = hash->choose(keydata , hash->size);
+       bucket = hash->table[index];
+
+       while (bucket != NULL) {
+               if (hash->compare(bucket->data, keydata))
+                       return bucket->data;
+
+               bucket = bucket->next;
+       }
+
+       return NULL;
+}
+
+/* remove bucket (this might be used in hash_iterate() if you already found the
+ * bucket you want to delete and don't need the overhead to find it again with
+ * hash_remove(). But usually, you don't want to use this function, as it
+ * fiddles with hash-internals. */
+void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t)
+{
+       void *data_save;
+
+       data_save = hash_it_t->bucket->data;
+
+       if (hash_it_t->prev_bucket != NULL)
+               hash_it_t->prev_bucket->next = hash_it_t->bucket->next;
+       else if (hash_it_t->first_bucket != NULL)
+               (*hash_it_t->first_bucket) = hash_it_t->bucket->next;
+
+       kfree(hash_it_t->bucket);
+       hash->elements--;
+
+       return data_save;
+}
+
+/* removes data from hash, if found. returns pointer do data on success, so you
+ * can remove the used structure yourself, or NULL on error .  data could be the
+ * structure you use with just the key filled, we just need the key for
+ * comparing. */
+void *hash_remove(struct hashtable_t *hash, void *data)
+{
+       struct hash_it_t hash_it_t;
+
+       hash_it_t.index = hash->choose(data, hash->size);
+       hash_it_t.bucket = hash->table[hash_it_t.index];
+       hash_it_t.prev_bucket = NULL;
+
+       while (hash_it_t.bucket != NULL) {
+               if (hash->compare(hash_it_t.bucket->data, data)) {
+                       hash_it_t.first_bucket =
+                               (hash_it_t.bucket ==
+                                hash->table[hash_it_t.index] ?
+                                &hash->table[hash_it_t.index] : NULL);
+                       return hash_remove_bucket(hash, &hash_it_t);
+               }
+
+               hash_it_t.prev_bucket = hash_it_t.bucket;
+               hash_it_t.bucket = hash_it_t.bucket->next;
+       }
+
+       return NULL;
+}
+
+/* resize the hash, returns the pointer to the new hash or NULL on
+ * error. removes the old hash on success. */
+struct hashtable_t *hash_resize(struct hashtable_t *hash, int size)
+{
+       struct hashtable_t *new_hash;
+       struct element_t *bucket;
+       int i;
+
+       /* initialize a new hash with the new size */
+       new_hash = hash_new(size, hash->compare, hash->choose);
+
+       if (new_hash == NULL)
+               return NULL;
+
+       /* copy the elements */
+       for (i = 0; i < hash->size; i++) {
+               bucket = hash->table[i];
+
+               while (bucket != NULL) {
+                       hash_add(new_hash, bucket->data);
+                       bucket = bucket->next;
+               }
+       }
+
+       /* remove hash and eventual overflow buckets but not the content
+        * itself. */
+       hash_delete(hash, NULL);
+
+       return new_hash;
+}
diff --git a/drivers/staging/batman-adv/hash.h b/drivers/staging/batman-adv/hash.h
new file mode 100644 (file)
index 0000000..bb60f08
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors:
+ *
+ * Simon Wunderlich, 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
+ *
+ */
+
+#ifndef _BATMAN_HASH_H
+#define _BATMAN_HASH_H
+
+typedef int (*hashdata_compare_cb)(void *, void *);
+typedef int (*hashdata_choose_cb)(void *, int);
+typedef void (*hashdata_free_cb)(void *);
+
+struct element_t {
+       void *data;             /* pointer to the data */
+       struct element_t *next; /* overflow bucket pointer */
+};
+
+struct hash_it_t {
+       int index;
+       struct element_t *bucket;
+       struct element_t *prev_bucket;
+       struct element_t **first_bucket;
+};
+
+struct hashtable_t {
+       struct element_t **table;   /* the hashtable itself, with the buckets */
+       int elements;               /* number of elements registered */
+       int size;                   /* size of hashtable */
+       hashdata_compare_cb compare;/* callback to a compare function.  should
+                                    * compare 2 element datas for their keys,
+                                    * return 0 if same and not 0 if not
+                                    * same */
+       hashdata_choose_cb choose;  /* the hashfunction, should return an index
+                                    * based on the key in the data of the first
+                                    * argument and the size the second */
+};
+
+/* clears the hash */
+void hash_init(struct hashtable_t *hash);
+
+/* allocates and clears the hash */
+struct hashtable_t *hash_new(int size, hashdata_compare_cb compare,
+                            hashdata_choose_cb choose);
+
+/* remove bucket (this might be used in hash_iterate() if you already found the
+ * bucket you want to delete and don't need the overhead to find it again with
+ * hash_remove().  But usually, you don't want to use this function, as it
+ * fiddles with hash-internals. */
+void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t);
+
+/* remove the hash structure. if hashdata_free_cb != NULL, this function will be
+ * called to remove the elements inside of the hash.  if you don't remove the
+ * elements, memory might be leaked. */
+void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb);
+
+/* free only the hashtable and the hash itself. */
+void hash_destroy(struct hashtable_t *hash);
+
+/* adds data to the hashtable. returns 0 on success, -1 on error */
+int hash_add(struct hashtable_t *hash, void *data);
+
+/* removes data from hash, if found. returns pointer do data on success, so you
+ * can remove the used structure yourself, or NULL on error .  data could be the
+ * structure you use with just the key filled, we just need the key for
+ * comparing. */
+void *hash_remove(struct hashtable_t *hash, void *data);
+
+/* finds data, based on the key in keydata. returns the found data on success,
+ * or NULL on error */
+void *hash_find(struct hashtable_t *hash, void *keydata);
+
+/* resize the hash, returns the pointer to the new hash or NULL on
+ * error. removes the old hash on success */
+struct hashtable_t *hash_resize(struct hashtable_t *hash, int size);
+
+/* iterate though the hash. first element is selected with iter_in NULL.  use
+ * the returned iterator to access the elements until hash_it_t returns NULL. */
+struct hash_it_t *hash_iterate(struct hashtable_t *hash,
+                              struct hash_it_t *iter_in);
+
+/* print the hash table for debugging */
+void hash_debug(struct hashtable_t *hash);
+#endif
diff --git a/drivers/staging/batman-adv/log.c b/drivers/staging/batman-adv/log.c
new file mode 100644 (file)
index 0000000..f37c7f0
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "log.h"
+
+#define LOG_BUF_MASK (log_buf_len-1)
+#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
+
+static char log_buf[LOG_BUF_LEN];
+static int log_buf_len = LOG_BUF_LEN;
+static unsigned long log_start;
+static unsigned long log_end;
+uint8_t log_level;
+
+static DEFINE_SPINLOCK(logbuf_lock);
+
+const struct file_operations proc_log_operations = {
+       .open           = log_open,
+       .release        = log_release,
+       .read           = log_read,
+       .write          = log_write,
+       .poll           = log_poll,
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(log_wait);
+
+static void emit_log_char(char c)
+{
+       LOG_BUF(log_end) = c;
+       log_end++;
+
+       if (log_end - log_start > log_buf_len)
+               log_start = log_end - log_buf_len;
+}
+
+static int fdebug_log(char *fmt, ...)
+{
+       int printed_len;
+       char *p;
+       va_list args;
+       static char debug_log_buf[256];
+       unsigned long flags;
+
+       spin_lock_irqsave(&logbuf_lock, flags);
+       va_start(args, fmt);
+       printed_len = vscnprintf(debug_log_buf, sizeof(debug_log_buf), fmt,
+                                args);
+       va_end(args);
+
+       for (p = debug_log_buf; *p != 0; p++)
+               emit_log_char(*p);
+
+       spin_unlock_irqrestore(&logbuf_lock, flags);
+
+       wake_up(&log_wait);
+
+       return 0;
+}
+
+int debug_log(int type, char *fmt, ...)
+{
+       va_list args;
+       int retval = 0;
+       char tmp_log_buf[256];
+
+       /* only critical information get into the official kernel log */
+       if (type == LOG_TYPE_CRIT) {
+               va_start(args, fmt);
+               vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args);
+               printk(KERN_ERR "batman-adv: %s", tmp_log_buf);
+               va_end(args);
+       }
+
+       if ((type == LOG_TYPE_CRIT) || (log_level & type)) {
+               va_start(args, fmt);
+               vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args);
+               fdebug_log("[%10u] %s", (jiffies / HZ), tmp_log_buf);
+               va_end(args);
+       }
+
+       return retval;
+}
+
+int log_open(struct inode *inode, struct file *file)
+{
+       inc_module_count();
+       return 0;
+}
+
+int log_release(struct inode *inode, struct file *file)
+{
+       dec_module_count();
+       return 0;
+}
+
+ssize_t log_read(struct file *file, char __user *buf, size_t count,
+                loff_t *ppos)
+{
+       int error, i = 0;
+       char c;
+       unsigned long flags;
+
+       if ((file->f_flags & O_NONBLOCK) && !(log_end - log_start))
+               return -EAGAIN;
+
+       if ((!buf) || (count < 0))
+               return -EINVAL;
+
+       if (count == 0)
+               return 0;
+
+       if (!access_ok(VERIFY_WRITE, buf, count))
+               return -EFAULT;
+
+       error = wait_event_interruptible(log_wait, (log_start - log_end));
+
+       if (error)
+               return error;
+
+       spin_lock_irqsave(&logbuf_lock, flags);
+
+       while ((!error) && (log_start != log_end) && (i < count)) {
+               c = LOG_BUF(log_start);
+
+               log_start++;
+
+               spin_unlock_irqrestore(&logbuf_lock, flags);
+
+               error = __put_user(c, buf);
+
+               spin_lock_irqsave(&logbuf_lock, flags);
+
+               buf++;
+               i++;
+
+       }
+
+       spin_unlock_irqrestore(&logbuf_lock, flags);
+
+       if (!error)
+               return i;
+
+       return error;
+}
+
+ssize_t log_write(struct file *file, const char __user *buf, size_t count,
+                 loff_t *ppos)
+{
+       return count;
+}
+
+unsigned int log_poll(struct file *file, poll_table *wait)
+{
+       poll_wait(file, &log_wait, wait);
+
+       if (log_end - log_start)
+               return POLLIN | POLLRDNORM;
+
+       return 0;
+}
diff --git a/drivers/staging/batman-adv/log.h b/drivers/staging/batman-adv/log.h
new file mode 100644 (file)
index 0000000..780e3ab
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 const struct file_operations proc_log_operations;
+extern uint8_t log_level;
+
+int debug_log(int type, char *fmt, ...);
+int log_open(struct inode *inode, struct file *file);
+int log_release(struct inode *inode, struct file *file);
+ssize_t log_read(struct file *file, char __user *buf, size_t count,
+                loff_t *ppos);
+ssize_t log_write(struct file *file, const char __user *buf, size_t count,
+                 loff_t *ppos);
+unsigned int log_poll(struct file *file, poll_table *wait);
diff --git a/drivers/staging/batman-adv/main.c b/drivers/staging/batman-adv/main.c
new file mode 100644 (file)
index 0000000..bb89bfc
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "proc.h"
+#include "log.h"
+#include "routing.h"
+#include "send.h"
+#include "soft-interface.h"
+#include "device.h"
+#include "translation-table.h"
+#include "hard-interface.h"
+#include "types.h"
+#include "vis.h"
+#include "hash.h"
+#include "compat.h"
+
+struct list_head if_list;
+struct hlist_head forw_bat_list;
+struct hlist_head forw_bcast_list;
+struct hashtable_t *orig_hash;
+
+DEFINE_SPINLOCK(orig_hash_lock);
+DEFINE_SPINLOCK(forw_bat_list_lock);
+DEFINE_SPINLOCK(forw_bcast_list_lock);
+
+atomic_t originator_interval;
+atomic_t vis_interval;
+atomic_t aggregation_enabled;
+int16_t num_hna;
+int16_t num_ifs;
+
+struct net_device *soft_device;
+
+static struct task_struct *kthread_task;
+
+unsigned char broadcastAddr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+atomic_t module_state;
+
+struct workqueue_struct *bat_event_workqueue;
+
+int init_module(void)
+{
+       int retval;
+
+       INIT_LIST_HEAD(&if_list);
+       INIT_HLIST_HEAD(&forw_bat_list);
+       INIT_HLIST_HEAD(&forw_bcast_list);
+
+       atomic_set(&module_state, MODULE_INACTIVE);
+
+       atomic_set(&originator_interval, 1000);
+       atomic_set(&vis_interval, 1000);/* TODO: raise this later, this is only
+                                        * for debugging now. */
+       atomic_set(&aggregation_enabled, 1);
+
+       /* the name should not be longer than 10 chars - see
+        * http://lwn.net/Articles/23634/ */
+       bat_event_workqueue = create_singlethread_workqueue("bat_events");
+
+       if (!bat_event_workqueue)
+               return -ENOMEM;
+
+       retval = setup_procfs();
+       if (retval < 0)
+               return retval;
+
+       bat_device_init();
+
+       /* initialize layer 2 interface */
+       soft_device = alloc_netdev(sizeof(struct bat_priv) , "bat%d",
+                                  interface_setup);
+
+       if (!soft_device) {
+               debug_log(LOG_TYPE_CRIT, "Unable to allocate the batman interface\n");
+               goto end;
+       }
+
+       retval = register_netdev(soft_device);
+
+       if (retval < 0) {
+               debug_log(LOG_TYPE_CRIT, "Unable to register the batman interface: %i\n", retval);
+               goto free_soft_device;
+       }
+
+       register_netdevice_notifier(&hard_if_notifier);
+
+       debug_log(LOG_TYPE_CRIT, "B.A.T.M.A.N. advanced %s%s (compatibility version %i) loaded \n",
+                 SOURCE_VERSION, REVISION_VERSION_STR, COMPAT_VERSION);
+
+       return 0;
+
+free_soft_device:
+       free_netdev(soft_device);
+       soft_device = NULL;
+end:
+       return -ENOMEM;
+}
+
+void cleanup_module(void)
+{
+       shutdown_module();
+
+       if (soft_device) {
+               unregister_netdev(soft_device);
+               soft_device = NULL;
+       }
+
+       unregister_netdevice_notifier(&hard_if_notifier);
+       cleanup_procfs();
+
+       destroy_workqueue(bat_event_workqueue);
+       bat_event_workqueue = NULL;
+}
+
+/* activates the module, creates bat device, starts timer ... */
+void activate_module(void)
+{
+       if (originator_init() < 1)
+               goto err;
+
+       if (hna_local_init() < 1)
+               goto err;
+
+       if (hna_global_init() < 1)
+               goto err;
+
+       hna_local_add(soft_device->dev_addr);
+
+       if (bat_device_setup() < 1)
+               goto end;
+
+       if (vis_init() < 1)
+               goto err;
+
+       /* (re)start kernel thread for packet processing */
+       if (!kthread_task) {
+               kthread_task = kthread_run(packet_recv_thread, NULL, "batman-adv");
+
+               if (IS_ERR(kthread_task)) {
+                       debug_log(LOG_TYPE_CRIT, "Unable to start packet receive thread\n");
+                       kthread_task = NULL;
+               }
+       }
+
+       update_min_mtu();
+       atomic_set(&module_state, MODULE_ACTIVE);
+       goto end;
+
+err:
+       debug_log(LOG_TYPE_CRIT, "Unable to allocate memory for mesh information structures: out of mem ?\n");
+       shutdown_module();
+end:
+       return;
+}
+
+/* shuts down the whole module.*/
+void shutdown_module(void)
+{
+       atomic_set(&module_state, MODULE_DEACTIVATING);
+
+       purge_outstanding_packets();
+       flush_workqueue(bat_event_workqueue);
+
+       vis_quit();
+
+       /* deactivate kernel thread for packet processing (if running) */
+       if (kthread_task) {
+               atomic_set(&exit_cond, 1);
+               wake_up_interruptible(&thread_wait);
+               kthread_stop(kthread_task);
+
+               kthread_task = NULL;
+       }
+
+       originator_free();
+
+       hna_local_free();
+       hna_global_free();
+
+       synchronize_net();
+       bat_device_destroy();
+
+       hardif_remove_interfaces();
+       synchronize_rcu();
+       atomic_set(&module_state, MODULE_INACTIVE);
+}
+
+void inc_module_count(void)
+{
+       try_module_get(THIS_MODULE);
+}
+
+void dec_module_count(void)
+{
+       module_put(THIS_MODULE);
+}
+
+int addr_to_string(char *buff, uint8_t *addr)
+{
+       return sprintf(buff, "%02x:%02x:%02x:%02x:%02x:%02x",
+                      addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+/* returns 1 if they are the same originator */
+
+int compare_orig(void *data1, void *data2)
+{
+       return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
+}
+
+/* hashfunction to choose an entry in a hash table of given size */
+/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */
+int choose_orig(void *data, int32_t size)
+{
+       unsigned char *key = data;
+       uint32_t hash = 0;
+       size_t i;
+
+       for (i = 0; i < 6; i++) {
+               hash += key[i];
+               hash += (hash << 10);
+               hash ^= (hash >> 6);
+       }
+
+       hash += (hash << 3);
+       hash ^= (hash >> 11);
+       hash += (hash << 15);
+
+       return hash % size;
+}
+
+int is_my_mac(uint8_t *addr)
+{
+       struct batman_if *batman_if;
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               if ((batman_if->net_dev) &&
+                   (compare_orig(batman_if->net_dev->dev_addr, addr))) {
+                       rcu_read_unlock();
+                       return 1;
+               }
+       }
+       rcu_read_unlock();
+       return 0;
+
+}
+
+int is_bcast(uint8_t *addr)
+{
+       return (addr[0] == (uint8_t)0xff) && (addr[1] == (uint8_t)0xff);
+}
+
+int is_mcast(uint8_t *addr)
+{
+       return *addr & 0x01;
+}
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE);
+#ifdef REVISION_VERSION
+MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION);
+#else
+MODULE_VERSION(SOURCE_VERSION);
+#endif
diff --git a/drivers/staging/batman-adv/main.h b/drivers/staging/batman-adv/main.h
new file mode 100644 (file)
index 0000000..facb6b7
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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
+ *
+ */
+
+/* Kernel Programming */
+#define LINUX
+
+#define DRIVER_AUTHOR "Marek Lindner <lindner_marek@yahoo.de>, Simon Wunderlich <siwu@hrz.tu-chemnitz.de>"
+#define DRIVER_DESC   "B.A.T.M.A.N. advanced"
+#define DRIVER_DEVICE "batman-adv"
+
+#define SOURCE_VERSION "0.2.1-beta"
+
+
+/* B.A.T.M.A.N. parameters */
+
+#define TQ_MAX_VALUE 255
+#define JITTER 20
+#define TTL 50                   /* Time To Live of broadcast messages */
+#define MAX_ADDR 16              /* number of interfaces which can be added to
+                                  * batman. */
+
+#define PURGE_TIMEOUT 200000      /* purge originators after time in ms if no
+                                  * valid packet comes in -> TODO: check
+                                  * influence on TQ_LOCAL_WINDOW_SIZE */
+#define LOCAL_HNA_TIMEOUT 3600000
+
+#define TQ_LOCAL_WINDOW_SIZE 64   /* sliding packet range of received originator
+                                  * messages in squence numbers (should be a
+                                  * multiple of our word size) */
+#define TQ_GLOBAL_WINDOW_SIZE 5
+#define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1
+#define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1
+#define TQ_TOTAL_BIDRECT_LIMIT 1
+
+#define TQ_HOP_PENALTY 10
+
+#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
+
+#define PACKBUFF_SIZE 2000
+#define LOG_BUF_LEN 8192         /* has to be a power of 2 */
+#define ETH_STR_LEN 20
+
+#define MAX_AGGREGATION_BYTES 512 /* should not be bigger than 512 bytes or
+                                  * change the size of
+                                  * forw_packet->direct_link_flags */
+#define MAX_AGGREGATION_MS 100
+
+#define MODULE_INACTIVE 0
+#define MODULE_ACTIVE 1
+#define MODULE_DEACTIVATING 2
+
+
+/*
+ * Logging
+ */
+
+#define LOG_TYPE_CRIT 0                /* highest priority for fatal errors such as
+                                * blocked sockets / failed packet delivery /
+                                * programming errors */
+#define LOG_TYPE_WARN 1                /* warnings for small errors like wrong user
+                                * input / damaged packets / etc */
+#define LOG_TYPE_NOTICE 2      /* notice information for new interfaces /
+                                * changed settings / new originators / etc */
+#define LOG_TYPE_BATMAN 4      /* all messages related to routing / flooding /
+                                * broadcasting / etc */
+#define LOG_TYPE_ROUTES 8      /* route or hna added / changed / deleted */
+#define LOG_TYPE_CRIT_NAME     "critical"
+#define LOG_TYPE_WARN_NAME     "warnings"
+#define LOG_TYPE_NOTICE_NAME   "notices"
+#define LOG_TYPE_BATMAN_NAME   "batman"
+#define LOG_TYPE_ROUTES_NAME   "routes"
+
+/*
+ *  Vis
+ */
+
+/* #define VIS_SUBCLUSTERS_DISABLED */
+
+/*
+ * Kernel headers
+ */
+
+#include <linux/mutex.h>       /* mutex */
+#include <linux/module.h>      /* needed by all modules */
+#include <linux/netdevice.h>   /* netdevice */
+#include <linux/if_ether.h>    /* ethernet header */
+#include <linux/poll.h>                /* poll_table */
+#include <linux/kthread.h>     /* kernel threads */
+#include <linux/pkt_sched.h>   /* schedule types */
+#include <linux/workqueue.h>   /* workqueue */
+#include <net/sock.h>          /* struct sock */
+#include <linux/jiffies.h>
+#include "types.h"
+
+#ifndef REVISION_VERSION
+#define REVISION_VERSION_STR ""
+#else
+#define REVISION_VERSION_STR " "REVISION_VERSION
+#endif
+
+extern struct list_head if_list;
+extern struct hlist_head forw_bat_list;
+extern struct hlist_head forw_bcast_list;
+extern struct hashtable_t *orig_hash;
+
+extern spinlock_t orig_hash_lock;
+extern spinlock_t forw_bat_list_lock;
+extern spinlock_t forw_bcast_list_lock;
+
+extern atomic_t originator_interval;
+extern atomic_t vis_interval;
+extern atomic_t aggregation_enabled;
+extern int16_t num_hna;
+extern int16_t num_ifs;
+
+extern struct net_device *soft_device;
+
+extern unsigned char broadcastAddr[];
+extern atomic_t module_state;
+extern struct workqueue_struct *bat_event_workqueue;
+
+void activate_module(void);
+void shutdown_module(void);
+void inc_module_count(void);
+void dec_module_count(void);
+int addr_to_string(char *buff, uint8_t *addr);
+int compare_orig(void *data1, void *data2);
+int choose_orig(void *data, int32_t size);
+int is_my_mac(uint8_t *addr);
+int is_bcast(uint8_t *addr);
+int is_mcast(uint8_t *addr);
+
+
diff --git a/drivers/staging/batman-adv/packet.h b/drivers/staging/batman-adv/packet.h
new file mode 100644 (file)
index 0000000..5627ca3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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
+ *
+ */
+
+#define ETH_P_BATMAN  0x4305   /* unofficial/not registered Ethertype */
+
+#define BAT_PACKET    0x01
+#define BAT_ICMP      0x02
+#define BAT_UNICAST   0x03
+#define BAT_BCAST     0x04
+#define BAT_VIS       0x05
+
+/* this file is included by batctl which needs these defines */
+#define COMPAT_VERSION 8
+#define DIRECTLINK 0x40
+#define VIS_SERVER 0x20
+
+/* ICMP message types */
+#define ECHO_REPLY 0
+#define DESTINATION_UNREACHABLE 3
+#define ECHO_REQUEST 8
+#define TTL_EXCEEDED 11
+#define PARAMETER_PROBLEM 12
+
+/* vis defines */
+#define VIS_TYPE_SERVER_SYNC           0
+#define VIS_TYPE_CLIENT_UPDATE         1
+
+struct batman_packet {
+       uint8_t  packet_type;
+       uint8_t  version;  /* batman version field */
+       uint8_t  flags;    /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */
+       uint8_t  tq;
+       uint16_t seqno;
+       uint8_t  orig[6];
+       uint8_t  prev_sender[6];
+       uint8_t  ttl;
+       uint8_t  num_hna;
+} __attribute__((packed));
+
+#define BAT_PACKET_LEN sizeof(struct batman_packet)
+
+struct icmp_packet {
+       uint8_t  packet_type;
+       uint8_t  version;  /* batman version field */
+       uint8_t  msg_type; /* see ICMP message types above */
+       uint8_t  ttl;
+       uint8_t  dst[6];
+       uint8_t  orig[6];
+       uint16_t seqno;
+       uint8_t  uid;
+} __attribute__((packed));
+
+struct unicast_packet {
+       uint8_t  packet_type;
+       uint8_t  version;  /* batman version field */
+       uint8_t  dest[6];
+       uint8_t  ttl;
+} __attribute__((packed));
+
+struct bcast_packet {
+       uint8_t  packet_type;
+       uint8_t  version;  /* batman version field */
+       uint8_t  orig[6];
+       uint16_t seqno;
+} __attribute__((packed));
+
+struct vis_packet {
+       uint8_t  packet_type;
+       uint8_t  version;        /* batman version field */
+       uint8_t  vis_type;       /* which type of vis-participant sent this? */
+       uint8_t  seqno;          /* sequence number */
+       uint8_t  entries;        /* number of entries behind this struct */
+       uint8_t  ttl;            /* TTL */
+       uint8_t  vis_orig[6];    /* originator that informs about its
+                                 * neighbours */
+       uint8_t  target_orig[6]; /* who should receive this packet */
+       uint8_t  sender_orig[6]; /* who sent or rebroadcasted this packet */
+} __attribute__((packed));
diff --git a/drivers/staging/batman-adv/proc.c b/drivers/staging/batman-adv/proc.c
new file mode 100644 (file)
index 0000000..aac3df7
--- /dev/null
@@ -0,0 +1,950 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "proc.h"
+#include "log.h"
+#include "routing.h"
+#include "translation-table.h"
+#include "hard-interface.h"
+#include "types.h"
+#include "hash.h"
+#include "vis.h"
+#include "compat.h"
+
+static uint8_t vis_format = DOT_DRAW;
+
+static struct proc_dir_entry *proc_batman_dir, *proc_interface_file;
+static struct proc_dir_entry *proc_orig_interval_file, *proc_originators_file;
+static struct proc_dir_entry *proc_log_file, *proc_log_level_file;
+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_file, *proc_vis_format_file;
+static struct proc_dir_entry *proc_aggr_file;
+
+static int proc_interfaces_read(struct seq_file *seq, void *offset)
+{
+       struct batman_if *batman_if;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               seq_printf(seq, "[%8s] %s %s \n",
+                          (batman_if->if_active == IF_ACTIVE ?
+                           "active" : "inactive"),
+                          batman_if->dev,
+                          (batman_if->if_active == IF_ACTIVE ?
+                           batman_if->addr_str : " "));
+       }
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int proc_interfaces_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_interfaces_read, NULL);
+}
+
+static ssize_t proc_interfaces_write(struct file *instance,
+                                    const char __user *userbuffer,
+                                    size_t count, loff_t *data)
+{
+       char *if_string, *colon_ptr = NULL, *cr_ptr = NULL;
+       int not_copied = 0, if_num = 0;
+       struct batman_if *batman_if = NULL;
+
+       if_string = kmalloc(count, GFP_KERNEL);
+
+       if (!if_string)
+               return -ENOMEM;
+
+       if (count > IFNAMSIZ - 1) {
+               debug_log(LOG_TYPE_WARN,
+                         "Can't add interface: device name is too long\n");
+               goto end;
+       }
+
+       not_copied = copy_from_user(if_string, userbuffer, count);
+       if_string[count - not_copied - 1] = 0;
+
+       colon_ptr = strchr(if_string, ':');
+       if (colon_ptr)
+               *colon_ptr = 0;
+
+       if (!colon_ptr) {
+               cr_ptr = strchr(if_string, '\n');
+               if (cr_ptr)
+                       *cr_ptr = 0;
+       }
+
+       if (strlen(if_string) == 0) {
+               shutdown_module();
+               num_ifs = 0;
+               goto end;
+       }
+
+       /* add interface */
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               if (strncmp(batman_if->dev, if_string, count) == 0) {
+                       debug_log(LOG_TYPE_WARN, "Given interface is already active: %s\n", if_string);
+                       rcu_read_unlock();
+                       goto end;
+
+               }
+
+               if_num++;
+       }
+       rcu_read_unlock();
+
+       hardif_add_interface(if_string, if_num);
+
+       if ((atomic_read(&module_state) == MODULE_INACTIVE) &&
+           (hardif_get_active_if_num() > 0))
+               activate_module();
+
+       rcu_read_lock();
+       if (list_empty(&if_list)) {
+               rcu_read_unlock();
+               goto end;
+       }
+       rcu_read_unlock();
+
+       num_ifs = if_num + 1;
+       return count;
+
+end:
+       kfree(if_string);
+       return count;
+}
+
+static int proc_orig_interval_read(struct seq_file *seq, void *offset)
+{
+       seq_printf(seq, "%i\n", atomic_read(&originator_interval));
+
+       return 0;
+}
+
+static ssize_t proc_orig_interval_write(struct file *file,
+                                       const char __user *buffer,
+                                       size_t count, loff_t *ppos)
+{
+       char *interval_string;
+       int not_copied = 0;
+       unsigned long originator_interval_tmp;
+       int retval;
+
+       interval_string = kmalloc(count, GFP_KERNEL);
+
+       if (!interval_string)
+               return -ENOMEM;
+
+       not_copied = copy_from_user(interval_string, buffer, count);
+       interval_string[count - not_copied - 1] = 0;
+
+       retval = strict_strtoul(interval_string, 10, &originator_interval_tmp);
+       if (retval) {
+               debug_log(LOG_TYPE_WARN, "New originator interval invalid\n");
+               goto end;
+       }
+
+       if (originator_interval_tmp <= JITTER * 2) {
+               debug_log(LOG_TYPE_WARN,
+                         "New originator interval too small: %i (min: %i)\n",
+                         originator_interval_tmp, JITTER * 2);
+               goto end;
+       }
+
+       debug_log(LOG_TYPE_NOTICE,
+                 "Changing originator interval from: %i to: %i\n",
+                 atomic_read(&originator_interval), originator_interval_tmp);
+
+       atomic_set(&originator_interval, originator_interval_tmp);
+
+end:
+       kfree(interval_string);
+       return count;
+}
+
+static int proc_orig_interval_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_orig_interval_read, NULL);
+}
+
+static int proc_originators_read(struct seq_file *seq, void *offset)
+{
+       struct hash_it_t *hashit = NULL;
+       struct orig_node *orig_node;
+       struct neigh_node *neigh_node;
+       int batman_count = 0;
+       char orig_str[ETH_STR_LEN], router_str[ETH_STR_LEN];
+
+       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,
+                  "  %-14s (%s/%i) %17s [%10s]: %20s ... [B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s] \n",
+                  "Originator", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF",
+                  "Potential nexthops", SOURCE_VERSION, REVISION_VERSION_STR,
+                  ((struct batman_if *)if_list.next)->dev,
+                  ((struct batman_if *)if_list.next)->addr_str);
+
+       rcu_read_unlock();
+       spin_lock(&orig_hash_lock);
+
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+
+               orig_node = hashit->bucket->data;
+
+               if (!orig_node->router)
+                       continue;
+
+               if (orig_node->router->tq_avg == 0)
+                       continue;
+
+               batman_count++;
+
+               addr_to_string(orig_str, orig_node->orig);
+               addr_to_string(router_str, orig_node->router->addr);
+
+               seq_printf(seq, "%-17s  (%3i) %17s [%10s]:",
+                          orig_str, orig_node->router->tq_avg,
+                          router_str, orig_node->router->if_incoming->dev);
+
+               list_for_each_entry(neigh_node, &orig_node->neigh_list, list) {
+                       addr_to_string(orig_str, neigh_node->addr);
+                       seq_printf(seq, " %17s (%3i)",
+                                  orig_str, neigh_node->tq_avg);
+               }
+
+               seq_printf(seq, "\n");
+
+       }
+
+       spin_unlock(&orig_hash_lock);
+
+       if (batman_count == 0)
+               seq_printf(seq, "No batman nodes in range ... \n");
+
+end:
+       return 0;
+}
+
+static int proc_originators_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_originators_read, NULL);
+}
+
+static int proc_log_level_read(struct seq_file *seq, void *offset)
+{
+
+       seq_printf(seq, "[x] %s (%d)\n", LOG_TYPE_CRIT_NAME, LOG_TYPE_CRIT);
+       seq_printf(seq, "[%c] %s (%d)\n",
+                  (LOG_TYPE_WARN & log_level) ? 'x' : ' ',
+                  LOG_TYPE_WARN_NAME, LOG_TYPE_WARN);
+       seq_printf(seq, "[%c] %s (%d)\n",
+                  (LOG_TYPE_NOTICE & log_level) ? 'x' : ' ',
+                  LOG_TYPE_NOTICE_NAME, LOG_TYPE_NOTICE);
+       seq_printf(seq, "[%c] %s (%d)\n",
+                  (LOG_TYPE_BATMAN & log_level) ? 'x' : ' ',
+                  LOG_TYPE_BATMAN_NAME, LOG_TYPE_BATMAN);
+       seq_printf(seq, "[%c] %s (%d)\n",
+                  (LOG_TYPE_ROUTES & log_level) ? 'x' : ' ',
+                  LOG_TYPE_ROUTES_NAME, LOG_TYPE_ROUTES);
+       return 0;
+}
+
+static int proc_log_level_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_log_level_read, NULL);
+}
+
+static ssize_t proc_log_level_write(struct file *instance,
+                                   const char __user *userbuffer,
+                                   size_t count, loff_t *data)
+{
+       char *log_level_string, *tokptr, *cp;
+       int finished, not_copied = 0;
+       unsigned long log_level_tmp = 0;
+
+       log_level_string = kmalloc(count, GFP_KERNEL);
+
+       if (!log_level_string)
+               return -ENOMEM;
+
+       not_copied = copy_from_user(log_level_string, userbuffer, count);
+       log_level_string[count - not_copied - 1] = 0;
+
+       if (strict_strtoul(log_level_string, 10, &log_level_tmp) < 0) {
+               /* was not a number, doing textual parsing */
+               log_level_tmp = 0;
+               tokptr = log_level_string;
+
+               for (cp = log_level_string, finished = 0; !finished; cp++) {
+                       switch (*cp) {
+                       case 0:
+                               finished = 1;
+                       case ' ':
+                       case '\n':
+                       case '\t':
+                               *cp = 0;
+                               /* compare */
+                               if (strcmp(tokptr, LOG_TYPE_WARN_NAME) == 0)
+                                       log_level_tmp |= LOG_TYPE_WARN;
+                               if (strcmp(tokptr, LOG_TYPE_NOTICE_NAME) == 0)
+                                       log_level_tmp |= LOG_TYPE_NOTICE;
+                               if (strcmp(tokptr, LOG_TYPE_BATMAN_NAME) == 0)
+                                       log_level_tmp |= LOG_TYPE_BATMAN;
+                               if (strcmp(tokptr, LOG_TYPE_ROUTES_NAME) == 0)
+                                       log_level_tmp |= LOG_TYPE_ROUTES;
+                               tokptr = cp + 1;
+                               break;
+                       default:
+                               ;
+                       }
+               }
+       }
+
+       debug_log(LOG_TYPE_CRIT, "Changing log_level from: %i to: %i\n",
+                 log_level, log_level_tmp);
+       log_level = log_level_tmp;
+
+       kfree(log_level_string);
+       return count;
+}
+
+static int proc_transt_local_read(struct seq_file *seq, void *offset)
+{
+       char *buf;
+
+       buf = kmalloc(4096, GFP_KERNEL);
+       if (!buf)
+               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;
+       }
+
+       rcu_read_unlock();
+
+       seq_printf(seq, "Locally retrieved addresses (from %s) announced via HNA:\n", soft_device->name);
+
+       hna_local_fill_buffer_text(buf, 4096);
+       seq_printf(seq, "%s", buf);
+
+end:
+       kfree(buf);
+       return 0;
+}
+
+static int proc_transt_local_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_transt_local_read, NULL);
+}
+
+static int proc_transt_global_read(struct seq_file *seq, void *offset)
+{
+       char *buf;
+
+       buf = kmalloc(4096, GFP_KERNEL);
+       if (!buf)
+               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;
+       }
+       rcu_read_unlock();
+
+
+       seq_printf(seq, "Globally announced HNAs received via the mesh (translation table):\n");
+
+       hna_global_fill_buffer_text(buf, 4096);
+       seq_printf(seq, "%s", buf);
+
+end:
+       kfree(buf);
+       return 0;
+}
+
+static int proc_transt_global_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_transt_global_read, NULL);
+}
+
+/* insert interface to the list of interfaces of one originator */
+
+static void proc_vis_insert_interface(const uint8_t *interface,
+                                     struct vis_if_list **if_entry,
+                                     bool primary)
+{
+       /* Did we get an empty list? (then insert imediately) */
+       if(*if_entry == NULL) {
+               *if_entry = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL);
+               if (*if_entry == NULL)
+                       return;
+
+               (*if_entry)->primary = primary;
+               (*if_entry)->next = NULL;
+               memcpy((*if_entry)->addr, interface, ETH_ALEN);
+       } else {
+               struct vis_if_list *head_if_entry = *if_entry;
+               /* Do we already have this interface in our list? */
+               while (!compare_orig((*if_entry)->addr, (void *)interface)) {
+
+                       /* Or did we reach the end (then append the interface) */
+                       if ((*if_entry)->next == NULL) {
+                               (*if_entry)->next = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL);
+                               if ((*if_entry)->next == NULL)
+                                       return;
+
+                               memcpy((*if_entry)->next->addr, interface, ETH_ALEN);
+                               (*if_entry)->next->primary = primary;
+                               (*if_entry)->next->next = NULL;
+                               break;
+                       }
+                       *if_entry = (*if_entry)->next;
+               }
+               /* Rewind the list to its head */
+               *if_entry = head_if_entry;
+       }
+}
+/* read an entry  */
+
+static void proc_vis_read_entry(struct seq_file *seq,
+                               struct vis_info_entry *entry,
+                               struct vis_if_list **if_entry,
+                               uint8_t *vis_orig,
+                               uint8_t current_format,
+                               uint8_t first_line)
+{
+       char from[40];
+       char to[40];
+       int int_part, frac_part;
+
+       addr_to_string(to, entry->dest);
+       if (entry->quality == 0) {
+#ifndef VIS_SUBCLUSTERS_DISABLED
+               proc_vis_insert_interface(vis_orig, if_entry, true);
+#endif /* VIS_SUBCLUSTERS_DISABLED */
+               addr_to_string(from, vis_orig);
+               if (current_format == DOT_DRAW) {
+                       seq_printf(seq, "\t\"%s\" -> \"%s\" [label=\"HNA\"]\n",
+                                  from, to);
+               } else {
+                       seq_printf(seq,
+                                  "%s\t{ router : \"%s\", gateway   : \"%s\", label : \"HNA\" }",
+                                  (first_line ? "" : ",\n"), from, to);
+               }
+       } else {
+#ifndef VIS_SUBCLUSTERS_DISABLED
+               proc_vis_insert_interface(entry->src, if_entry, compare_orig(entry->src, vis_orig));
+#endif /* VIS_SUBCLUSTERS_DISABLED */
+               addr_to_string(from, entry->src);
+
+               /* kernel has no printf-support for %f? it'd be better to return
+                * this in float. */
+
+               int_part = TQ_MAX_VALUE / entry->quality;
+               frac_part = 1000 * TQ_MAX_VALUE / entry->quality - int_part * 1000;
+
+               if (current_format == DOT_DRAW) {
+                       seq_printf(seq,
+                                  "\t\"%s\" -> \"%s\" [label=\"%d.%d\"]\n",
+                                  from, to, int_part, frac_part);
+               } else {
+                       seq_printf(seq,
+                                  "%s\t{ router : \"%s\", neighbour : \"%s\", label : %d.%d }",
+                                  (first_line ? "" : ",\n"), from, to, int_part, frac_part);
+               }
+       }
+}
+
+
+static int proc_vis_read(struct seq_file *seq, void *offset)
+{
+       struct hash_it_t *hashit = NULL;
+       struct vis_info *info;
+       struct vis_info_entry *entries;
+       struct vis_if_list *if_entries = NULL;
+       int i;
+       uint8_t current_format, first_line = 1;
+#ifndef VIS_SUBCLUSTERS_DISABLED
+       char tmp_addr_str[ETH_STR_LEN];
+       struct vis_if_list *tmp_if_next;
+#endif /* VIS_SUBCLUSTERS_DISABLED */
+
+       current_format = vis_format;
+
+       rcu_read_lock();
+       if (list_empty(&if_list) || (!is_vis_server())) {
+               rcu_read_unlock();
+               if (current_format == DOT_DRAW)
+                       seq_printf(seq, "digraph {\n}\n");
+               goto end;
+       }
+
+       rcu_read_unlock();
+
+       if (current_format == DOT_DRAW)
+               seq_printf(seq, "digraph {\n");
+
+       spin_lock(&vis_hash_lock);
+       while (NULL != (hashit = hash_iterate(vis_hash, hashit))) {
+               info = hashit->bucket->data;
+               entries = (struct vis_info_entry *)
+                       ((char *)info + sizeof(struct vis_info));
+
+               for (i = 0; i < info->packet.entries; i++) {
+                       proc_vis_read_entry(seq, &entries[i], &if_entries,
+                                           info->packet.vis_orig,
+                                           current_format, first_line);
+                       if (first_line)
+                               first_line = 0;
+               }
+
+#ifndef VIS_SUBCLUSTERS_DISABLED
+               /* Generate subgraphs from the collected items */
+               if (current_format == DOT_DRAW) {
+
+                       addr_to_string(tmp_addr_str, info->packet.vis_orig);
+                       seq_printf(seq, "\tsubgraph \"cluster_%s\" {\n", tmp_addr_str);
+                       while (if_entries != NULL) {
+
+                               addr_to_string(tmp_addr_str, if_entries->addr);
+                               if (if_entries->primary)
+                                       seq_printf(seq, "\t\t\"%s\" [peripheries=2]\n", tmp_addr_str);
+                               else
+                                       seq_printf(seq, "\t\t\"%s\"\n", tmp_addr_str);
+
+                               /* ... and empty the list while doing this */
+                               tmp_if_next = if_entries->next;
+                               kfree(if_entries);
+                               if_entries = tmp_if_next;
+                       }
+                       seq_printf(seq, "\t}\n");
+               }
+#endif /* VIS_SUBCLUSTERS_DISABLED */
+       }
+       spin_unlock(&vis_hash_lock);
+
+       if (current_format == DOT_DRAW)
+               seq_printf(seq, "}\n");
+       else
+               seq_printf(seq, "\n");
+end:
+       return 0;
+}
+
+/* setting the mode of the vis server by the user */
+static ssize_t proc_vis_write(struct file *file, const char __user * buffer,
+                             size_t count, loff_t *ppos)
+{
+       char *vis_mode_string;
+       int not_copied = 0;
+
+       vis_mode_string = kmalloc(count, GFP_KERNEL);
+
+       if (!vis_mode_string)
+               return -ENOMEM;
+
+       not_copied = copy_from_user(vis_mode_string, buffer, count);
+       vis_mode_string[count - not_copied - 1] = 0;
+
+       if (strcmp(vis_mode_string, "client") == 0) {
+               debug_log(LOG_TYPE_NOTICE, "Setting VIS mode to client\n");
+               vis_set_mode(VIS_TYPE_CLIENT_UPDATE);
+       } else if (strcmp(vis_mode_string, "server") == 0) {
+               debug_log(LOG_TYPE_NOTICE, "Setting VIS mode to server\n");
+               vis_set_mode(VIS_TYPE_SERVER_SYNC);
+       } else
+               debug_log(LOG_TYPE_WARN, "Unknown VIS mode: %s\n",
+                         vis_mode_string);
+
+       kfree(vis_mode_string);
+       return count;
+}
+
+static int proc_vis_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_vis_read, NULL);
+}
+
+static int proc_vis_format_read(struct seq_file *seq, void *offset)
+{
+       uint8_t current_format = vis_format;
+
+       seq_printf(seq, "[%c] %s\n",
+                  (current_format == DOT_DRAW) ? 'x' : ' ',
+                  VIS_FORMAT_DD_NAME);
+       seq_printf(seq, "[%c] %s\n",
+                  (current_format == JSON) ? 'x' : ' ',
+                  VIS_FORMAT_JSON_NAME);
+       return 0;
+}
+
+static int proc_vis_format_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_vis_format_read, NULL);
+}
+
+static ssize_t proc_vis_format_write(struct file *file,
+                                    const char __user *buffer,
+                                    size_t count, loff_t *ppos)
+{
+       char *vis_format_string;
+       int not_copied = 0;
+
+       vis_format_string = kmalloc(count, GFP_KERNEL);
+
+       if (!vis_format_string)
+               return -ENOMEM;
+
+       not_copied = copy_from_user(vis_format_string, buffer, count);
+       vis_format_string[count - not_copied - 1] = 0;
+
+       if (strcmp(vis_format_string, VIS_FORMAT_DD_NAME) == 0) {
+               debug_log(LOG_TYPE_NOTICE, "Setting VIS output format to: %s\n",
+                         VIS_FORMAT_DD_NAME);
+               vis_format = DOT_DRAW;
+       } else if (strcmp(vis_format_string, VIS_FORMAT_JSON_NAME) == 0) {
+               debug_log(LOG_TYPE_NOTICE, "Setting VIS output format to: %s\n",
+                         VIS_FORMAT_JSON_NAME);
+               vis_format = JSON;
+       } else
+               debug_log(LOG_TYPE_WARN, "Unknown VIS output format: %s\n",
+                         vis_format_string);
+
+       kfree(vis_format_string);
+       return count;
+}
+
+static int proc_aggr_read(struct seq_file *seq, void *offset)
+{
+       seq_printf(seq, "%i\n", atomic_read(&aggregation_enabled));
+
+       return 0;
+}
+
+static ssize_t proc_aggr_write(struct file *file, const char __user *buffer,
+                              size_t count, loff_t *ppos)
+{
+       char *aggr_string;
+       int not_copied = 0;
+       unsigned long aggregation_enabled_tmp;
+
+       aggr_string = kmalloc(count, GFP_KERNEL);
+
+       if (!aggr_string)
+               return -ENOMEM;
+
+       not_copied = copy_from_user(aggr_string, buffer, count);
+       aggr_string[count - not_copied - 1] = 0;
+
+       strict_strtoul(aggr_string, 10, &aggregation_enabled_tmp);
+
+       if ((aggregation_enabled_tmp != 0) && (aggregation_enabled_tmp != 1)) {
+               debug_log(LOG_TYPE_WARN, "Aggregation can only be enabled (1) or disabled (0), given value: %li\n", aggregation_enabled_tmp);
+               goto end;
+       }
+
+       debug_log(LOG_TYPE_NOTICE, "Changing aggregation from: %s (%i) to: %s (%li)\n",
+                 (atomic_read(&aggregation_enabled) == 1 ?
+                  "enabled" : "disabled"),
+                 atomic_read(&aggregation_enabled),
+                 (aggregation_enabled_tmp == 1 ? "enabled" : "disabled"),
+                 aggregation_enabled_tmp);
+
+       atomic_set(&aggregation_enabled, (unsigned)aggregation_enabled_tmp);
+end:
+       kfree(aggr_string);
+       return count;
+}
+
+static int proc_aggr_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_aggr_read, NULL);
+}
+
+/* satisfying different prototypes ... */
+static ssize_t proc_dummy_write(struct file *file, const char __user *buffer,
+                               size_t count, loff_t *ppos)
+{
+       return count;
+}
+
+static const struct file_operations proc_aggr_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_aggr_open,
+       .read           = seq_read,
+       .write          = proc_aggr_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_vis_format_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_vis_format_open,
+       .read           = seq_read,
+       .write          = proc_vis_format_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_vis_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_vis_open,
+       .read           = seq_read,
+       .write          = proc_vis_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_originators_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_originators_open,
+       .read           = seq_read,
+       .write          = proc_dummy_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_transt_local_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_transt_local_open,
+       .read           = seq_read,
+       .write          = proc_dummy_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_transt_global_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_transt_global_open,
+       .read           = seq_read,
+       .write          = proc_dummy_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_log_level_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_log_level_open,
+       .read           = seq_read,
+       .write          = proc_log_level_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_interfaces_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_interfaces_open,
+       .read           = seq_read,
+       .write          = proc_interfaces_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static const struct file_operations proc_orig_interval_fops = {
+       .owner          = THIS_MODULE,
+       .open           = proc_orig_interval_open,
+       .read           = seq_read,
+       .write          = proc_orig_interval_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+void cleanup_procfs(void)
+{
+       if (proc_transt_global_file)
+               remove_proc_entry(PROC_FILE_TRANST_GLOBAL, proc_batman_dir);
+
+       if (proc_transt_local_file)
+               remove_proc_entry(PROC_FILE_TRANST_LOCAL, proc_batman_dir);
+
+       if (proc_log_file)
+               remove_proc_entry(PROC_FILE_LOG, proc_batman_dir);
+
+       if (proc_log_level_file)
+               remove_proc_entry(PROC_FILE_LOG_LEVEL, proc_batman_dir);
+
+       if (proc_originators_file)
+               remove_proc_entry(PROC_FILE_ORIGINATORS, proc_batman_dir);
+
+       if (proc_orig_interval_file)
+               remove_proc_entry(PROC_FILE_ORIG_INTERVAL, proc_batman_dir);
+
+       if (proc_interface_file)
+               remove_proc_entry(PROC_FILE_INTERFACES, proc_batman_dir);
+
+       if (proc_vis_file)
+               remove_proc_entry(PROC_FILE_VIS, proc_batman_dir);
+
+       if (proc_vis_format_file)
+               remove_proc_entry(PROC_FILE_VIS_FORMAT, proc_batman_dir);
+
+       if (proc_aggr_file)
+               remove_proc_entry(PROC_FILE_AGGR, proc_batman_dir);
+
+       if (proc_batman_dir)
+#ifdef __NET_NET_NAMESPACE_H
+               remove_proc_entry(PROC_ROOT_DIR, init_net.proc_net);
+#else
+               remove_proc_entry(PROC_ROOT_DIR, proc_net);
+#endif
+}
+
+int setup_procfs(void)
+{
+#ifdef __NET_NET_NAMESPACE_H
+       proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, init_net.proc_net);
+#else
+       proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, proc_net);
+#endif
+
+       if (!proc_batman_dir) {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s' folder failed\n", PROC_ROOT_DIR);
+               return -EFAULT;
+       }
+
+       proc_interface_file = create_proc_entry(PROC_FILE_INTERFACES,
+                                               S_IWUSR | S_IRUGO,
+                                               proc_batman_dir);
+       if (proc_interface_file) {
+               proc_interface_file->proc_fops = &proc_interfaces_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_INTERFACES);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_orig_interval_file = create_proc_entry(PROC_FILE_ORIG_INTERVAL,
+                                                   S_IWUSR | S_IRUGO,
+                                                   proc_batman_dir);
+       if (proc_orig_interval_file) {
+               proc_orig_interval_file->proc_fops = &proc_orig_interval_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIG_INTERVAL);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_log_level_file = create_proc_entry(PROC_FILE_LOG_LEVEL,
+                                               S_IWUSR | S_IRUGO,
+                                               proc_batman_dir);
+       if (proc_log_level_file) {
+               proc_log_level_file->proc_fops = &proc_log_level_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_LOG_LEVEL);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_originators_file = create_proc_entry(PROC_FILE_ORIGINATORS,
+                                                 S_IRUGO, proc_batman_dir);
+       if (proc_originators_file) {
+               proc_originators_file->proc_fops = &proc_originators_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIGINATORS);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_log_file = create_proc_entry(PROC_FILE_LOG,
+                                         S_IRUGO, proc_batman_dir);
+       if (proc_log_file) {
+               proc_log_file->proc_fops = &proc_log_operations;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_FILE_LOG, PROC_FILE_GATEWAYS);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_transt_local_file = create_proc_entry(PROC_FILE_TRANST_LOCAL,
+                                                  S_IRUGO, proc_batman_dir);
+       if (proc_transt_local_file) {
+               proc_transt_local_file->proc_fops = &proc_transt_local_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_TRANST_LOCAL);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_transt_global_file = create_proc_entry(PROC_FILE_TRANST_GLOBAL,
+                                                   S_IRUGO, proc_batman_dir);
+       if (proc_transt_global_file) {
+               proc_transt_global_file->proc_fops = &proc_transt_global_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_TRANST_GLOBAL);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_vis_file = create_proc_entry(PROC_FILE_VIS, S_IWUSR | S_IRUGO,
+                                         proc_batman_dir);
+       if (proc_vis_file) {
+               proc_vis_file->proc_fops = &proc_vis_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_vis_format_file = create_proc_entry(PROC_FILE_VIS_FORMAT,
+                                                S_IWUSR | S_IRUGO,
+                                                proc_batman_dir);
+       if (proc_vis_format_file) {
+               proc_vis_format_file->proc_fops = &proc_vis_format_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_FORMAT);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       proc_aggr_file = create_proc_entry(PROC_FILE_AGGR, S_IWUSR | S_IRUGO,
+                                          proc_batman_dir);
+       if (proc_aggr_file) {
+               proc_aggr_file->proc_fops = &proc_aggr_fops;
+       } else {
+               printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_AGGR);
+               cleanup_procfs();
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+
diff --git a/drivers/staging/batman-adv/proc.h b/drivers/staging/batman-adv/proc.h
new file mode 100644 (file)
index 0000000..16d3efd
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#define PROC_ROOT_DIR "batman-adv"
+#define PROC_FILE_INTERFACES "interfaces"
+#define PROC_FILE_ORIG_INTERVAL "orig_interval"
+#define PROC_FILE_ORIGINATORS "originators"
+#define PROC_FILE_GATEWAYS "gateways"
+#define PROC_FILE_LOG "log"
+#define PROC_FILE_LOG_LEVEL "log_level"
+#define PROC_FILE_TRANST_LOCAL "transtable_local"
+#define PROC_FILE_TRANST_GLOBAL "transtable_global"
+#define PROC_FILE_VIS "vis"
+#define PROC_FILE_VIS_FORMAT "vis_format"
+#define PROC_FILE_AGGR "aggregate_ogm"
+
+void cleanup_procfs(void);
+int setup_procfs(void);
+
+/* While scanning for vis-entries of a particular vis-originator
+ * this list collects its interfaces to create a subgraph/cluster
+ * out of them later
+ */
+struct vis_if_list {
+       uint8_t addr[ETH_ALEN];
+       bool primary;
+       struct vis_if_list *next;
+};
diff --git a/drivers/staging/batman-adv/ring_buffer.c b/drivers/staging/batman-adv/ring_buffer.c
new file mode 100644 (file)
index 0000000..751c899
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007-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 "ring_buffer.h"
+
+void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value)
+{
+       lq_recv[*lq_index] = value;
+       *lq_index = (*lq_index + 1) % TQ_GLOBAL_WINDOW_SIZE;
+}
+
+uint8_t ring_buffer_avg(uint8_t lq_recv[])
+{
+       uint8_t *ptr;
+       uint16_t count = 0, i = 0, sum = 0;
+
+       ptr = lq_recv;
+
+       while (i < TQ_GLOBAL_WINDOW_SIZE) {
+               if (*ptr != 0) {
+                       count++;
+                       sum += *ptr;
+               }
+
+               i++;
+               ptr++;
+       }
+
+       if (count == 0)
+               return 0;
+
+       return (uint8_t)(sum / count);
+}
diff --git a/drivers/staging/batman-adv/ring_buffer.h b/drivers/staging/batman-adv/ring_buffer.h
new file mode 100644 (file)
index 0000000..6839ba9
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007-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
+ *
+ */
+
+void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value);
+uint8_t ring_buffer_avg(uint8_t lq_recv[]);
diff --git a/drivers/staging/batman-adv/routing.c b/drivers/staging/batman-adv/routing.c
new file mode 100644 (file)
index 0000000..4a14c36
--- /dev/null
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "routing.h"
+#include "log.h"
+#include "send.h"
+#include "soft-interface.h"
+#include "hard-interface.h"
+#include "device.h"
+#include "translation-table.h"
+#include "types.h"
+#include "hash.h"
+#include "ring_buffer.h"
+#include "vis.h"
+#include "aggregation.h"
+#include "compat.h"
+
+
+
+DECLARE_WAIT_QUEUE_HEAD(thread_wait);
+static DECLARE_DELAYED_WORK(purge_orig_wq, purge_orig);
+
+static atomic_t data_ready_cond;
+atomic_t exit_cond;
+
+static void start_purge_timer(void)
+{
+       queue_delayed_work(bat_event_workqueue, &purge_orig_wq, 1 * HZ);
+}
+
+int originator_init(void)
+{
+       if (orig_hash)
+               return 1;
+
+       spin_lock(&orig_hash_lock);
+       orig_hash = hash_new(128, compare_orig, choose_orig);
+
+       if (!orig_hash)
+               goto err;
+
+       spin_unlock(&orig_hash_lock);
+       start_purge_timer();
+       return 1;
+
+err:
+       spin_unlock(&orig_hash_lock);
+       return 0;
+}
+
+void originator_free(void)
+{
+       if (!orig_hash)
+               return;
+
+       cancel_delayed_work_sync(&purge_orig_wq);
+
+       spin_lock(&orig_hash_lock);
+       hash_delete(orig_hash, free_orig_node);
+       orig_hash = NULL;
+       spin_unlock(&orig_hash_lock);
+}
+
+static struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, uint8_t *neigh, struct batman_if *if_incoming)
+{
+       struct neigh_node *neigh_node;
+
+       debug_log(LOG_TYPE_BATMAN, "Creating new last-hop neighbour of originator\n");
+
+       neigh_node = kmalloc(sizeof(struct neigh_node), GFP_ATOMIC);
+       memset(neigh_node, 0, sizeof(struct neigh_node));
+       INIT_LIST_HEAD(&neigh_node->list);
+
+       memcpy(neigh_node->addr, neigh, ETH_ALEN);
+       neigh_node->orig_node = orig_neigh_node;
+       neigh_node->if_incoming = if_incoming;
+
+       list_add_tail(&neigh_node->list, &orig_node->neigh_list);
+       return neigh_node;
+}
+
+void free_orig_node(void *data)
+{
+       struct list_head *list_pos, *list_pos_tmp;
+       struct neigh_node *neigh_node;
+       struct orig_node *orig_node = (struct orig_node *)data;
+
+       /* for all neighbours towards this originator ... */
+       list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) {
+               neigh_node = list_entry(list_pos, struct neigh_node, list);
+
+               list_del(list_pos);
+               kfree(neigh_node);
+       }
+
+       hna_global_del_orig(orig_node, "originator timed out");
+
+       kfree(orig_node->bcast_own);
+       kfree(orig_node->bcast_own_sum);
+       kfree(orig_node);
+}
+
+/* this function finds or creates an originator entry for the given address if it does not exits */
+static struct orig_node *get_orig_node(uint8_t *addr)
+{
+       struct orig_node *orig_node;
+       struct hashtable_t *swaphash;
+       char orig_str[ETH_STR_LEN];
+
+       orig_node = ((struct orig_node *)hash_find(orig_hash, addr));
+
+       if (orig_node != NULL)
+               return orig_node;
+
+       addr_to_string(orig_str, addr);
+       debug_log(LOG_TYPE_BATMAN, "Creating new originator: %s \n", orig_str);
+
+       orig_node = kmalloc(sizeof(struct orig_node), GFP_ATOMIC);
+       memset(orig_node, 0, sizeof(struct orig_node));
+       INIT_LIST_HEAD(&orig_node->neigh_list);
+
+       memcpy(orig_node->orig, addr, ETH_ALEN);
+       orig_node->router = NULL;
+       orig_node->batman_if = NULL;
+       orig_node->hna_buff = NULL;
+
+       orig_node->bcast_own = kmalloc(num_ifs * sizeof(TYPE_OF_WORD) * NUM_WORDS, GFP_ATOMIC);
+       memset(orig_node->bcast_own, 0, num_ifs * sizeof(TYPE_OF_WORD) * NUM_WORDS);
+
+       orig_node->bcast_own_sum = kmalloc(num_ifs * sizeof(uint8_t), GFP_ATOMIC);
+       memset(orig_node->bcast_own_sum, 0, num_ifs * sizeof(uint8_t));
+
+       hash_add(orig_hash, orig_node);
+
+       if (orig_hash->elements * 4 > orig_hash->size) {
+               swaphash = hash_resize(orig_hash, orig_hash->size * 2);
+
+               if (swaphash == NULL)
+                       debug_log(LOG_TYPE_CRIT, "Couldn't resize orig hash table \n");
+               else
+                       orig_hash = swaphash;
+       }
+
+       return orig_node;
+}
+
+void slide_own_bcast_window(struct batman_if *batman_if)
+{
+       struct hash_it_t *hashit = NULL;
+       struct orig_node *orig_node;
+
+       spin_lock(&orig_hash_lock);
+
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+               orig_node = hashit->bucket->data;
+
+               bit_get_packet((TYPE_OF_WORD *)&(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]), 1, 0);
+               orig_node->bcast_own_sum[batman_if->if_num] = bit_packet_count((TYPE_OF_WORD *)&(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]));
+       }
+
+       spin_unlock(&orig_hash_lock);
+}
+
+static void update_routes(struct orig_node *orig_node, struct neigh_node *neigh_node, unsigned char *hna_buff, int hna_buff_len)
+{
+       char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN], router_str[ETH_STR_LEN];
+
+       if (orig_node == NULL)
+               return;
+
+       if (orig_node->router != neigh_node) {
+               addr_to_string(orig_str, orig_node->orig);
+
+               /* route deleted */
+               if ((orig_node->router != NULL) && (neigh_node == NULL)) {
+
+                       debug_log(LOG_TYPE_ROUTES, "Deleting route towards: %s\n", orig_str);
+                       hna_global_del_orig(orig_node, "originator timed out");
+
+               /* route added */
+               } else if ((orig_node->router == NULL) && (neigh_node != NULL)) {
+
+                       addr_to_string(neigh_str, neigh_node->addr);
+                       debug_log(LOG_TYPE_ROUTES, "Adding route towards: %s (via %s)\n", orig_str, neigh_str);
+                       hna_global_add_orig(orig_node, hna_buff, hna_buff_len);
+
+               /* route changed */
+               } else {
+
+                       addr_to_string(neigh_str, neigh_node->addr);
+                       addr_to_string(router_str, orig_node->router->addr);
+                       debug_log(LOG_TYPE_ROUTES, "Changing route towards: %s (now via %s - was via %s)\n", orig_str, neigh_str, router_str);
+
+               }
+
+               if (neigh_node != NULL)
+                       orig_node->batman_if = neigh_node->if_incoming;
+               else
+                       orig_node->batman_if = NULL;
+
+               orig_node->router = neigh_node;
+
+       /* may be just HNA changed */
+       } else {
+
+               if ((hna_buff_len != orig_node->hna_buff_len) || ((hna_buff_len > 0) && (orig_node->hna_buff_len > 0) && (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) {
+
+                       if (orig_node->hna_buff_len > 0)
+                               hna_global_del_orig(orig_node, "originator changed hna");
+
+                       if ((hna_buff_len > 0) && (hna_buff != NULL))
+                               hna_global_add_orig(orig_node, hna_buff, hna_buff_len);
+
+               }
+
+       }
+}
+
+static int isBidirectionalNeigh(struct orig_node *orig_node, struct orig_node *orig_neigh_node, struct batman_packet *batman_packet, struct batman_if *if_incoming)
+{
+       struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
+       char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN];
+       unsigned char total_count;
+
+       addr_to_string(orig_str, orig_node->orig);
+       addr_to_string(neigh_str, orig_neigh_node->orig);
+
+       if (orig_node == orig_neigh_node) {
+               list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
+
+                       if (compare_orig(tmp_neigh_node->addr, orig_neigh_node->orig) && (tmp_neigh_node->if_incoming == if_incoming))
+                               neigh_node = tmp_neigh_node;
+               }
+
+               if (neigh_node == NULL)
+                       neigh_node = create_neighbor(orig_node, orig_neigh_node, orig_neigh_node->orig, if_incoming);
+
+               neigh_node->last_valid = jiffies;
+       } else {
+               /* find packet count of corresponding one hop neighbor */
+               list_for_each_entry(tmp_neigh_node, &orig_neigh_node->neigh_list, list) {
+
+                       if (compare_orig(tmp_neigh_node->addr, orig_neigh_node->orig) && (tmp_neigh_node->if_incoming == if_incoming))
+                               neigh_node = tmp_neigh_node;
+               }
+
+               if (neigh_node == NULL)
+                       neigh_node = create_neighbor(orig_neigh_node, orig_neigh_node, orig_neigh_node->orig, if_incoming);
+       }
+
+       orig_node->last_valid = jiffies;
+
+       /* pay attention to not get a value bigger than 100 % */
+       total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > neigh_node->real_packet_count ? neigh_node->real_packet_count : orig_neigh_node->bcast_own_sum[if_incoming->if_num]);
+
+       /* if we have too few packets (too less data) we set tq_own to zero */
+       /* if we receive too few packets it is not considered bidirectional */
+       if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM))
+               orig_neigh_node->tq_own = 0;
+       else
+               /* neigh_node->real_packet_count is never zero as we only purge old information when getting new information */
+               orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / neigh_node->real_packet_count;
+
+       /*
+        * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE
+        * this does affect the nearly-symmetric links only a little,
+        * but punishes asymmetric links more.
+        * this will give a value between 0 and TQ_MAX_VALUE
+        */
+       orig_neigh_node->tq_asym_penalty = TQ_MAX_VALUE - (TQ_MAX_VALUE *
+                       (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) *
+                       (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) *
+                       (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) /
+                       (TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE);
+
+       batman_packet->tq = ((batman_packet->tq * orig_neigh_node->tq_own * orig_neigh_node->tq_asym_penalty) / (TQ_MAX_VALUE *  TQ_MAX_VALUE));
+
+       debug_log(LOG_TYPE_BATMAN, "bidirectional: orig = %-15s neigh = %-15s => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, total tq: %3i \n",
+                 orig_str, neigh_str, total_count, neigh_node->real_packet_count, orig_neigh_node->tq_own, orig_neigh_node->tq_asym_penalty, batman_packet->tq);
+
+       /* if link has the minimum required transmission quality consider it bidirectional */
+       if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT)
+               return 1;
+
+       return 0;
+}
+
+static void update_orig(struct orig_node *orig_node, struct ethhdr *ethhdr, struct batman_packet *batman_packet, struct batman_if *if_incoming, unsigned char *hna_buff, int hna_buff_len, char is_duplicate)
+{
+       struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
+       int tmp_hna_buff_len;
+
+       debug_log(LOG_TYPE_BATMAN, "update_originator(): Searching and updating originator entry of received packet \n");
+
+       list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
+               if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming)) {
+                       neigh_node = tmp_neigh_node;
+                       continue;
+               }
+
+               if (is_duplicate)
+                       continue;
+
+               ring_buffer_set(tmp_neigh_node->tq_recv, &tmp_neigh_node->tq_index, 0);
+               tmp_neigh_node->tq_avg = ring_buffer_avg(tmp_neigh_node->tq_recv);
+       }
+
+       if (neigh_node == NULL)
+               neigh_node = create_neighbor(orig_node, get_orig_node(ethhdr->h_source), ethhdr->h_source, if_incoming);
+       else
+               debug_log(LOG_TYPE_BATMAN, "Updating existing last-hop neighbour of originator\n");
+
+       orig_node->flags = batman_packet->flags;
+       neigh_node->last_valid = jiffies;
+
+       ring_buffer_set(neigh_node->tq_recv, &neigh_node->tq_index, batman_packet->tq);
+       neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv);
+
+       if (!is_duplicate) {
+               orig_node->last_ttl = batman_packet->ttl;
+               neigh_node->last_ttl = batman_packet->ttl;
+       }
+
+       tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? batman_packet->num_hna * ETH_ALEN : hna_buff_len);
+
+       /* if this neighbor already is our next hop there is nothing to change */
+       if (orig_node->router == neigh_node)
+               goto update_hna;
+
+       /* if this neighbor does not offer a better TQ we won't consider it */
+       if ((orig_node->router) &&
+           (orig_node->router->tq_avg > neigh_node->tq_avg))
+               goto update_hna;
+
+       /* if the TQ is the same and the link not more symetric we won't consider it either */
+       if ((orig_node->router) &&
+            ((neigh_node->tq_avg == orig_node->router->tq_avg) &&
+            (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] >=
+             neigh_node->orig_node->bcast_own_sum[if_incoming->if_num])))
+               goto update_hna;
+
+       update_routes(orig_node, neigh_node, hna_buff, tmp_hna_buff_len);
+       return;
+
+update_hna:
+       update_routes(orig_node, orig_node->router, hna_buff, tmp_hna_buff_len);
+       return;
+}
+
+static char count_real_packets(struct ethhdr *ethhdr, struct batman_packet *batman_packet, struct batman_if *if_incoming)
+{
+       struct orig_node *orig_node;
+       struct neigh_node *tmp_neigh_node;
+       char is_duplicate = 0;
+
+
+       orig_node = get_orig_node(batman_packet->orig);
+       if (orig_node == NULL)
+               return 0;
+
+
+       list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
+
+               if (!is_duplicate)
+                       is_duplicate = get_bit_status(tmp_neigh_node->real_bits, orig_node->last_real_seqno, batman_packet->seqno);
+
+               if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming))
+                       bit_get_packet(tmp_neigh_node->real_bits, batman_packet->seqno - orig_node->last_real_seqno, 1);
+               else
+                       bit_get_packet(tmp_neigh_node->real_bits, batman_packet->seqno - orig_node->last_real_seqno, 0);
+
+               tmp_neigh_node->real_packet_count = bit_packet_count(tmp_neigh_node->real_bits);
+       }
+
+       if (!is_duplicate) {
+               debug_log(LOG_TYPE_BATMAN, "updating last_seqno: old %d, new %d \n", orig_node->last_real_seqno, batman_packet->seqno);
+               orig_node->last_real_seqno = batman_packet->seqno;
+       }
+
+       return is_duplicate;
+}
+
+void receive_bat_packet(struct ethhdr *ethhdr, struct batman_packet *batman_packet, unsigned char *hna_buff, int hna_buff_len, struct batman_if *if_incoming)
+{
+       struct batman_if *batman_if;
+       struct orig_node *orig_neigh_node, *orig_node;
+       char orig_str[ETH_STR_LEN], prev_sender_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN];
+       char has_directlink_flag;
+       char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0, is_broadcast = 0, is_bidirectional, is_single_hop_neigh, is_duplicate;
+       unsigned short if_incoming_seqno;
+
+       /* Silently drop when the batman packet is actually not a correct packet.
+        *
+        * This might happen if a packet is padded (e.g. Ethernet has a
+        * minimum frame length of 64 byte) and the aggregation interprets
+        * it as an additional length.
+        *
+        * TODO: A more sane solution would be to have a bit in the batman_packet
+        * to detect whether the packet is the last packet in an aggregation.
+        * Here we expect that the padding is always zero (or not 0x01)
+        */
+       if (batman_packet->packet_type != BAT_PACKET)
+               return;
+
+       /* could be changed by schedule_own_packet() */
+       if_incoming_seqno = atomic_read(&if_incoming->seqno);
+
+       addr_to_string(orig_str, batman_packet->orig);
+       addr_to_string(prev_sender_str, batman_packet->prev_sender);
+       addr_to_string(neigh_str, ethhdr->h_source);
+
+       has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0);
+
+       is_single_hop_neigh = (compare_orig(ethhdr->h_source, batman_packet->orig) ? 1 : 0);
+
+       debug_log(LOG_TYPE_BATMAN, "Received BATMAN packet via NB: %s, IF: %s [%s] (from OG: %s, via prev OG: %s, seqno %d, tq %d, TTL %d, V %d, IDF %d) \n", neigh_str, if_incoming->dev, if_incoming->addr_str, orig_str, prev_sender_str, batman_packet->seqno, batman_packet->tq, batman_packet->ttl, batman_packet->version, has_directlink_flag);
+
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               if (batman_if->if_active != IF_ACTIVE)
+                       continue;
+
+               if (compare_orig(ethhdr->h_source, batman_if->net_dev->dev_addr))
+                       is_my_addr = 1;
+
+               if (compare_orig(batman_packet->orig, batman_if->net_dev->dev_addr))
+                       is_my_orig = 1;
+
+               if (compare_orig(batman_packet->prev_sender, batman_if->net_dev->dev_addr))
+                       is_my_oldorig = 1;
+
+               if (compare_orig(ethhdr->h_source, broadcastAddr))
+                       is_broadcast = 1;
+       }
+
+       if (batman_packet->version != COMPAT_VERSION) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: incompatible batman version (%i) \n", batman_packet->version);
+               return;
+       }
+
+       if (is_my_addr) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: received my own broadcast (sender: %s) \n", neigh_str);
+               return;
+       }
+
+       if (is_broadcast) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all packets with broadcast source addr (sender: %s) \n", neigh_str);
+               return;
+       }
+
+       if (is_my_orig) {
+               orig_neigh_node = get_orig_node(ethhdr->h_source);
+
+               /* neighbour has to indicate direct link and it has to come via the corresponding interface */
+               /* if received seqno equals last send seqno save new seqno for bidirectional check */
+               if (has_directlink_flag && compare_orig(if_incoming->net_dev->dev_addr, batman_packet->orig) &&
+                                 (batman_packet->seqno - if_incoming_seqno + 2 == 0)) {
+                       bit_mark((TYPE_OF_WORD *)&(orig_neigh_node->bcast_own[if_incoming->if_num * NUM_WORDS]), 0);
+                       orig_neigh_node->bcast_own_sum[if_incoming->if_num] = bit_packet_count((TYPE_OF_WORD *)&(orig_neigh_node->bcast_own[if_incoming->if_num * NUM_WORDS]));
+               }
+
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: originator packet from myself (via neighbour) \n");
+               return;
+       }
+
+       if (batman_packet->tq == 0) {
+               count_real_packets(ethhdr, batman_packet, if_incoming);
+
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: originator packet with tq equal 0 \n");
+               return;
+       }
+
+       if (is_my_oldorig) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all rebroadcast echos (sender: %s) \n", neigh_str);
+               return;
+       }
+
+       is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming);
+
+       orig_node = get_orig_node(batman_packet->orig);
+       if (orig_node == NULL)
+               return;
+
+       /* avoid temporary routing loops */
+       if ((orig_node->router) && (orig_node->router->orig_node->router) &&
+           (compare_orig(orig_node->router->addr, batman_packet->prev_sender)) &&
+           !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) &&
+           (compare_orig(orig_node->router->addr, orig_node->router->orig_node->router->addr))) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %s) \n", neigh_str);
+               return;
+       }
+
+       /* if sender is a direct neighbor the sender mac equals originator mac */
+       orig_neigh_node = (is_single_hop_neigh ? orig_node : get_orig_node(ethhdr->h_source));
+       if (orig_neigh_node == NULL)
+               return;
+
+       /* drop packet if sender is not a direct neighbor and if we don't route towards it */
+       if (!is_single_hop_neigh && (orig_neigh_node->router == NULL)) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: OGM via unknown neighbor! \n");
+               return;
+       }
+
+       is_bidirectional = isBidirectionalNeigh(orig_node, orig_neigh_node, batman_packet, if_incoming);
+
+       /* update ranking if it is not a duplicate or has the same seqno and similar ttl as the non-duplicate */
+       if (is_bidirectional && (!is_duplicate ||
+               ((orig_node->last_real_seqno == batman_packet->seqno) &&
+                       (orig_node->last_ttl - 3 <= batman_packet->ttl))))
+               update_orig(orig_node, ethhdr, batman_packet, if_incoming, hna_buff, hna_buff_len, is_duplicate);
+
+       /* is single hop (direct) neighbour */
+       if (is_single_hop_neigh) {
+
+               /* mark direct link on incoming interface */
+               schedule_forward_packet(orig_node, ethhdr, batman_packet, 1, hna_buff_len, if_incoming);
+
+               debug_log(LOG_TYPE_BATMAN, "Forwarding packet: rebroadcast neighbour packet with direct link flag \n");
+               return;
+       }
+
+       /* multihop originator */
+       if (!is_bidirectional) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: not received via bidirectional link\n");
+               return;
+       }
+
+       if (is_duplicate) {
+               debug_log(LOG_TYPE_BATMAN, "Drop packet: duplicate packet received\n");
+               return;
+       }
+
+       debug_log(LOG_TYPE_BATMAN, "Forwarding packet: rebroadcast originator packet \n");
+       schedule_forward_packet(orig_node, ethhdr, batman_packet, 0, hna_buff_len, if_incoming);
+}
+
+void purge_orig(struct work_struct *work)
+{
+       struct list_head *list_pos, *list_pos_tmp;
+       struct hash_it_t *hashit = NULL;
+       struct orig_node *orig_node;
+       struct neigh_node *neigh_node, *best_neigh_node;
+       char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN], neigh_purged;
+
+       spin_lock(&orig_hash_lock);
+
+       /* for all origins... */
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+
+               orig_node = hashit->bucket->data;
+               addr_to_string(orig_str, orig_node->orig);
+
+               if (time_after(jiffies, orig_node->last_valid + ((2 * PURGE_TIMEOUT * HZ) / 1000))) {
+
+                       debug_log(LOG_TYPE_BATMAN, "Originator timeout: originator %s, last_valid %u \n", orig_str, (orig_node->last_valid / HZ));
+
+                       hash_remove_bucket(orig_hash, hashit);
+                       free_orig_node(orig_node);
+
+               } else {
+
+                       best_neigh_node = NULL;
+                       neigh_purged = 0;
+
+                       /* for all neighbours towards this originator ... */
+                       list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) {
+                               neigh_node = list_entry(list_pos, struct neigh_node, list);
+
+                               if (time_after(jiffies, neigh_node->last_valid + ((PURGE_TIMEOUT * HZ) / 1000))) {
+
+                                       addr_to_string(neigh_str, neigh_node->addr);
+                                       debug_log(LOG_TYPE_BATMAN, "Neighbour timeout: originator %s, neighbour: %s, last_valid %u \n", orig_str, neigh_str, (neigh_node->last_valid / HZ));
+
+                                       neigh_purged = 1;
+                                       list_del(list_pos);
+                                       kfree(neigh_node);
+
+                               } else {
+
+                                       if ((best_neigh_node == NULL) || (neigh_node->tq_avg > best_neigh_node->tq_avg))
+                                               best_neigh_node = neigh_node;
+
+                               }
+
+                       }
+
+                       if (neigh_purged)
+                               update_routes(orig_node, best_neigh_node, orig_node->hna_buff, orig_node->hna_buff_len);
+
+               }
+
+       }
+
+       spin_unlock(&orig_hash_lock);
+
+       start_purge_timer();
+}
+
+static int receive_raw_packet(struct socket *raw_sock, unsigned char *packet_buff, int packet_buff_len)
+{
+       struct kvec iov;
+       struct msghdr msg;
+
+       iov.iov_base = packet_buff;
+       iov.iov_len = packet_buff_len;
+
+       msg.msg_flags = MSG_DONTWAIT;   /* non-blocking */
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+       msg.msg_control = NULL;
+
+       return kernel_recvmsg(raw_sock, &msg, &iov, 1, packet_buff_len, MSG_DONTWAIT);
+}
+
+int packet_recv_thread(void *data)
+{
+       struct batman_if *batman_if;
+       struct ethhdr *ethhdr;
+       struct batman_packet *batman_packet;
+       struct unicast_packet *unicast_packet;
+       struct bcast_packet *bcast_packet;
+       struct icmp_packet *icmp_packet;
+       struct vis_packet *vis_packet;
+       struct orig_node *orig_node;
+       unsigned char *packet_buff, src_str[ETH_STR_LEN], dst_str[ETH_STR_LEN];
+       int vis_info_len;
+       int result;
+
+       atomic_set(&data_ready_cond, 0);
+       atomic_set(&exit_cond, 0);
+       packet_buff = kmalloc(PACKBUFF_SIZE, GFP_KERNEL);
+       if (!packet_buff) {
+               debug_log(LOG_TYPE_CRIT, "Could allocate memory for the packet buffer. :(\n");
+               return -1;
+       }
+
+       while ((!kthread_should_stop()) && (!atomic_read(&exit_cond))) {
+
+               wait_event_interruptible(thread_wait, (atomic_read(&data_ready_cond) || atomic_read(&exit_cond)));
+
+               atomic_set(&data_ready_cond, 0);
+
+               if (kthread_should_stop() || atomic_read(&exit_cond))
+                       break;
+
+               /* we only want to safely traverse the list, hard-interfaces
+                * won't be deleted anyway as long as this thread runs. */
+
+               rcu_read_lock();
+               list_for_each_entry_rcu(batman_if, &if_list, list) {
+                       rcu_read_unlock();
+
+                       result = -1;
+
+                       while (1) {
+                               if (batman_if->if_active != IF_ACTIVE) {
+                                       if (batman_if->if_active != IF_TO_BE_ACTIVATED)
+                                               debug_log(LOG_TYPE_NOTICE,
+                                                         "Could not read from deactivated interface %s!\n",
+                                                         batman_if->dev);
+
+                                       if (batman_if->raw_sock)
+                                               receive_raw_packet(batman_if->raw_sock, packet_buff, PACKBUFF_SIZE);
+                                       result = 0;
+                                       break;
+                               }
+
+                               result = receive_raw_packet(batman_if->raw_sock, packet_buff, PACKBUFF_SIZE);
+                               if (result <= 0)
+                                       break;
+
+                               if (result < sizeof(struct ethhdr) + 2)
+                                       continue;
+
+                               ethhdr = (struct ethhdr *)packet_buff;
+                               batman_packet = (struct batman_packet *)(packet_buff + sizeof(struct ethhdr));
+
+                               if (batman_packet->version != COMPAT_VERSION) {
+                                       debug_log(LOG_TYPE_BATMAN, "Drop packet: incompatible batman version (%i) \n", batman_packet->version);
+                                       continue;
+                               }
+
+                               switch (batman_packet->packet_type) {
+                               /* batman originator packet */
+                               case BAT_PACKET:
+                                       /* packet with broadcast indication but unicast recipient */
+                                       if (!is_bcast(ethhdr->h_dest))
+                                               continue;
+
+                                       /* packet with broadcast sender address */
+                                       if (is_bcast(ethhdr->h_source))
+                                               continue;
+
+                                       /* drop packet if it has not at least one batman packet as payload */
+                                       if (result < sizeof(struct ethhdr) + sizeof(struct batman_packet))
+                                               continue;
+
+                                       spin_lock(&orig_hash_lock);
+                                       receive_aggr_bat_packet(ethhdr,
+                                                               packet_buff + sizeof(struct ethhdr),
+                                                               result - sizeof(struct ethhdr),
+                                                               batman_if);
+                                       spin_unlock(&orig_hash_lock);
+
+                                       break;
+
+                               /* batman icmp packet */
+                               case BAT_ICMP:
+                                       /* packet with unicast indication but broadcast recipient */
+                                       if (is_bcast(ethhdr->h_dest))
+                                               continue;
+
+                                       /* packet with broadcast sender address */
+                                       if (is_bcast(ethhdr->h_source))
+                                               continue;
+
+                                       /* not for me */
+                                       if (!is_my_mac(ethhdr->h_dest))
+                                               continue;
+
+                                       /* drop packet if it has not necessary minimum size */
+                                       if (result < sizeof(struct ethhdr) + sizeof(struct icmp_packet))
+                                               continue;
+
+                                       icmp_packet = (struct icmp_packet *)(packet_buff + sizeof(struct ethhdr));
+
+                                       /* packet for me */
+                                       if (is_my_mac(icmp_packet->dst)) {
+
+                                               /* add data to device queue */
+                                               if (icmp_packet->msg_type != ECHO_REQUEST) {
+                                                       bat_device_receive_packet(icmp_packet);
+                                                       continue;
+                                               }
+
+                                               /* answer echo request (ping) */
+                                               /* get routing information */
+                                               spin_lock(&orig_hash_lock);
+                                               orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig));
+
+                                               if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
+
+                                                       memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
+                                                       memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN);
+                                                       icmp_packet->msg_type = ECHO_REPLY;
+                                                       icmp_packet->ttl = TTL;
+
+                                                       send_raw_packet(packet_buff + sizeof(struct ethhdr),
+                                                                       result - sizeof(struct ethhdr),
+                                                                       orig_node->batman_if,
+                                                                       orig_node->router->addr);
+
+                                               }
+
+                                               spin_unlock(&orig_hash_lock);
+                                               continue;
+
+                                       }
+
+                                       /* TTL exceeded */
+                                       if (icmp_packet->ttl < 2) {
+
+                                               addr_to_string(src_str, icmp_packet->orig);
+                                               addr_to_string(dst_str, icmp_packet->dst);
+
+                                               debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str);
+
+                                               /* send TTL exceeded if packet is an echo request (traceroute) */
+                                               if (icmp_packet->msg_type != ECHO_REQUEST)
+                                                       continue;
+
+                                               /* get routing information */
+                                               spin_lock(&orig_hash_lock);
+                                               orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig));
+
+                                               if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
+
+                                                       memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
+                                                       memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN);
+                                                       icmp_packet->msg_type = TTL_EXCEEDED;
+                                                       icmp_packet->ttl = TTL;
+
+                                                       send_raw_packet(packet_buff + sizeof(struct ethhdr),
+                                                                       result - sizeof(struct ethhdr),
+                                                                       orig_node->batman_if,
+                                                                       orig_node->router->addr);
+
+                                               }
+
+                                               spin_unlock(&orig_hash_lock);
+                                               continue;
+
+                                       }
+
+                                       /* get routing information */
+                                       spin_lock(&orig_hash_lock);
+                                       orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->dst));
+
+                                       if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
+
+                                               /* decrement ttl */
+                                               icmp_packet->ttl--;
+
+                                               /* route it */
+                                               send_raw_packet(packet_buff + sizeof(struct ethhdr),
+                                                               result - sizeof(struct ethhdr),
+                                                               orig_node->batman_if,
+                                                               orig_node->router->addr);
+                                       }
+
+                                       spin_unlock(&orig_hash_lock);
+                                       break;
+
+                               /* unicast packet */
+                               case BAT_UNICAST:
+                                       /* packet with unicast indication but broadcast recipient */
+                                       if (is_bcast(ethhdr->h_dest))
+                                               continue;
+
+                                       /* packet with broadcast sender address */
+                                       if (is_bcast(ethhdr->h_source))
+                                               continue;
+
+                                       /* not for me */
+                                       if (!is_my_mac(ethhdr->h_dest))
+                                               continue;
+
+                                       /* drop packet if it has not necessary minimum size */
+                                       if (result < sizeof(struct ethhdr) + sizeof(struct unicast_packet))
+                                               continue;
+
+                                       unicast_packet = (struct unicast_packet *)(packet_buff + sizeof(struct ethhdr));
+
+                                       /* packet for me */
+                                       if (is_my_mac(unicast_packet->dest)) {
+
+                                               interface_rx(soft_device, packet_buff + sizeof(struct ethhdr) + sizeof(struct unicast_packet), result - sizeof(struct ethhdr) - sizeof(struct unicast_packet));
+                                               continue;
+
+                                       }
+
+                                       /* TTL exceeded */
+                                       if (unicast_packet->ttl < 2) {
+                                               addr_to_string(src_str, ((struct ethhdr *)(unicast_packet + 1))->h_source);
+                                               addr_to_string(dst_str, unicast_packet->dest);
+
+                                               debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str);
+                                               continue;
+                                       }
+
+                                       /* get routing information */
+                                       spin_lock(&orig_hash_lock);
+                                       orig_node = ((struct orig_node *)hash_find(orig_hash, unicast_packet->dest));
+
+                                       if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
+                                               /* decrement ttl */
+                                               unicast_packet->ttl--;
+
+                                               /* route it */
+                                               send_raw_packet(packet_buff + sizeof(struct ethhdr),
+                                                               result - sizeof(struct ethhdr),
+                                                               orig_node->batman_if,
+                                                               orig_node->router->addr);
+                                       }
+
+                                       spin_unlock(&orig_hash_lock);
+                                       break;
+
+                               /* broadcast packet */
+                               case BAT_BCAST:
+                                       /* packet with broadcast indication but unicast recipient */
+                                       if (!is_bcast(ethhdr->h_dest))
+                                               continue;
+
+                                       /* packet with broadcast sender address */
+                                       if (is_bcast(ethhdr->h_source))
+                                               continue;
+
+                                       /* drop packet if it has not necessary minimum size */
+                                       if (result < sizeof(struct ethhdr) + sizeof(struct bcast_packet))
+                                               continue;
+
+                                       /* ignore broadcasts sent by myself */
+                                       if (is_my_mac(ethhdr->h_source))
+                                               continue;
+
+                                       bcast_packet = (struct bcast_packet *)(packet_buff + sizeof(struct ethhdr));
+
+                                       /* ignore broadcasts originated by myself */
+                                       if (is_my_mac(bcast_packet->orig))
+                                               continue;
+
+                                       spin_lock(&orig_hash_lock);
+                                       orig_node = ((struct orig_node *)hash_find(orig_hash, bcast_packet->orig));
+
+                                       if (orig_node == NULL) {
+                                               spin_unlock(&orig_hash_lock);
+                                               continue;
+                                       }
+
+                                       /* check flood history */
+                                       if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno, ntohs(bcast_packet->seqno))) {
+                                               spin_unlock(&orig_hash_lock);
+                                               continue;
+                                       }
+
+                                       /* mark broadcast in flood history */
+                                       if (bit_get_packet(orig_node->bcast_bits, ntohs(bcast_packet->seqno) - orig_node->last_bcast_seqno, 1))
+                                               orig_node->last_bcast_seqno = ntohs(bcast_packet->seqno);
+
+                                       spin_unlock(&orig_hash_lock);
+
+                                       /* broadcast for me */
+                                       interface_rx(soft_device, packet_buff + sizeof(struct ethhdr) + sizeof(struct bcast_packet), result - sizeof(struct ethhdr) - sizeof(struct bcast_packet));
+
+                                       /* rebroadcast packet */
+                                       add_bcast_packet_to_list(packet_buff + sizeof(struct ethhdr),
+                                                                       result - sizeof(struct ethhdr));
+
+                                       break;
+
+                               /* vis packet */
+                               case BAT_VIS:
+                                       /* drop if too short. */
+                                       if (result < sizeof(struct ethhdr) + sizeof(struct vis_packet))
+                                               continue;
+
+                                       /* not for me */
+                                       if (!is_my_mac(ethhdr->h_dest))
+                                               continue;
+
+                                       vis_packet = (struct vis_packet *)(packet_buff + sizeof(struct ethhdr));
+                                       vis_info_len = result  - sizeof(struct ethhdr) - sizeof(struct vis_packet);
+
+                                       /* ignore own packets */
+                                       if (is_my_mac(vis_packet->vis_orig))
+                                               continue;
+
+                                       if (is_my_mac(vis_packet->sender_orig))
+                                               continue;
+
+                                       switch (vis_packet->vis_type) {
+                                       case VIS_TYPE_SERVER_SYNC:
+                                               receive_server_sync_packet(vis_packet, vis_info_len);
+                                               break;
+
+                                       case VIS_TYPE_CLIENT_UPDATE:
+                                               receive_client_update_packet(vis_packet, vis_info_len);
+                                               break;
+
+                                       default:        /* ignore unknown packet */
+                                               break;
+                                       }
+
+                                       break;
+                               }
+
+                       }
+
+                       if ((result < 0) && (result != -EAGAIN))
+                               debug_log(LOG_TYPE_CRIT, "Could not receive packet from interface %s: %i\n", batman_if->dev, result);
+
+                       /* lock for the next iteration */
+                       rcu_read_lock();
+               }
+               rcu_read_unlock();
+
+       }
+       kfree(packet_buff);
+
+       /* do not exit until kthread_stop() is actually called, otherwise it will wait for us
+        * forever. */
+       while (!kthread_should_stop())
+               schedule();
+
+       return 0;
+}
+
+void batman_data_ready(struct sock *sk, int len)
+{
+       void (*data_ready)(struct sock *, int) = sk->sk_user_data;
+
+       data_ready(sk, len);
+
+       atomic_set(&data_ready_cond, 1);
+       wake_up_interruptible(&thread_wait);
+}
+
diff --git a/drivers/staging/batman-adv/routing.h b/drivers/staging/batman-adv/routing.h
new file mode 100644 (file)
index 0000000..0123ea8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "types.h"
+
+extern wait_queue_head_t thread_wait;
+extern atomic_t exit_cond;
+
+int originator_init(void);
+void free_orig_node(void *data);
+void originator_free(void);
+void slide_own_bcast_window(struct batman_if *batman_if);
+void batman_data_ready(struct sock *sk, int len);
+void purge_orig(struct work_struct *work);
+int packet_recv_thread(void *data);
+void receive_bat_packet(struct ethhdr *ethhdr, struct batman_packet *batman_packet, unsigned char *hna_buff, int hna_buff_len, struct batman_if *if_incoming);
diff --git a/drivers/staging/batman-adv/send.c b/drivers/staging/batman-adv/send.c
new file mode 100644 (file)
index 0000000..d724798
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "send.h"
+#include "log.h"
+#include "routing.h"
+#include "translation-table.h"
+#include "hard-interface.h"
+#include "types.h"
+#include "vis.h"
+#include "aggregation.h"
+
+#include "compat.h"
+
+/* apply hop penalty for a normal link */
+static uint8_t hop_penalty(const uint8_t tq)
+{
+       return (tq * (TQ_MAX_VALUE - TQ_HOP_PENALTY)) / (TQ_MAX_VALUE);
+}
+
+/* when do we schedule our own packet to be sent */
+static unsigned long own_send_time(void)
+{
+       return jiffies +
+               (((atomic_read(&originator_interval) - JITTER +
+                  (random32() % 2*JITTER)) * HZ) / 1000);
+}
+
+/* when do we schedule a forwarded packet to be sent */
+static unsigned long forward_send_time(void)
+{
+       unsigned long send_time = jiffies; /* Starting now plus... */
+
+       if (atomic_read(&aggregation_enabled))
+               send_time += (((MAX_AGGREGATION_MS - (JITTER/2) +
+                               (random32() % JITTER)) * HZ) / 1000);
+       else
+               send_time += (((random32() % (JITTER/2)) * HZ) / 1000);
+
+       return send_time;
+}
+
+/* sends a raw packet. */
+void send_raw_packet(unsigned char *pack_buff, int pack_buff_len,
+                    struct batman_if *batman_if, uint8_t *dst_addr)
+{
+       struct ethhdr *ethhdr;
+       struct sk_buff *skb;
+       int retval;
+       char *data;
+
+       if (batman_if->if_active != IF_ACTIVE)
+               return;
+
+       if (!(batman_if->net_dev->flags & IFF_UP)) {
+               debug_log(LOG_TYPE_WARN,
+                        "Interface %s is not up - can't send packet via that interface (IF_TO_BE_DEACTIVATED was here) !\n",
+                         batman_if->dev);
+               return;
+       }
+
+       skb = dev_alloc_skb(pack_buff_len + sizeof(struct ethhdr));
+       if (!skb)
+               return;
+       data = skb_put(skb, pack_buff_len + sizeof(struct ethhdr));
+
+       memcpy(data + sizeof(struct ethhdr), pack_buff, pack_buff_len);
+
+       ethhdr = (struct ethhdr *) data;
+       memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN);
+       memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
+       ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
+
+       skb_reset_mac_header(skb);
+       skb_set_network_header(skb, ETH_HLEN);
+       skb->priority = TC_PRIO_CONTROL;
+       skb->protocol = __constant_htons(ETH_P_BATMAN);
+       skb->dev = batman_if->net_dev;
+
+       /* dev_queue_xmit() returns a negative result on error.  However on
+        * congestion and traffic shaping, it drops and returns NET_XMIT_DROP
+        * (which is > 0). This will not be treated as an error. */
+       retval = dev_queue_xmit(skb);
+       if (retval < 0)
+               debug_log(LOG_TYPE_CRIT,
+                         "Can't write to raw socket (IF_TO_BE_DEACTIVATED was here): %i\n",
+                         retval);
+}
+
+/* Send a packet to a given interface */
+static void send_packet_to_if(struct forw_packet *forw_packet,
+                             struct batman_if *batman_if)
+{
+       char *fwd_str;
+       uint8_t packet_num;
+       int16_t buff_pos;
+       struct batman_packet *batman_packet;
+       char orig_str[ETH_STR_LEN];
+
+       if (batman_if->if_active != IF_ACTIVE)
+               return;
+
+       packet_num = buff_pos = 0;
+       batman_packet = (struct batman_packet *)
+               (forw_packet->packet_buff);
+
+       /* adjust all flags and log packets */
+       while (aggregated_packet(buff_pos,
+                                forw_packet->packet_len,
+                                batman_packet->num_hna)) {
+
+               /* we might have aggregated direct link packets with an
+                * ordinary base packet */
+               if ((forw_packet->direct_link_flags & (1 << packet_num)) &&
+                   (forw_packet->if_incoming == batman_if))
+                       batman_packet->flags |= DIRECTLINK;
+               else
+                       batman_packet->flags &= ~DIRECTLINK;
+
+               addr_to_string(orig_str, batman_packet->orig);
+               fwd_str = (packet_num > 0 ? "Forwarding" : (forw_packet->own ?
+                                                           "Sending own" :
+                                                           "Forwarding"));
+               debug_log(LOG_TYPE_BATMAN,
+                         "%s %spacket (originator %s, seqno %d, TQ %d, TTL %d, IDF %s) on interface %s [%s]\n",
+                         fwd_str,
+                         (packet_num > 0 ? "aggregated " : ""),
+                         orig_str, ntohs(batman_packet->seqno),
+                         batman_packet->tq, batman_packet->ttl,
+                         (batman_packet->flags & DIRECTLINK ?
+                          "on" : "off"),
+                         batman_if->dev, batman_if->addr_str);
+
+               buff_pos += sizeof(struct batman_packet) +
+                       (batman_packet->num_hna * ETH_ALEN);
+               packet_num++;
+               batman_packet = (struct batman_packet *)
+                       (forw_packet->packet_buff + buff_pos);
+       }
+
+       send_raw_packet(forw_packet->packet_buff,
+                       forw_packet->packet_len,
+                       batman_if, broadcastAddr);
+}
+
+/* send a batman packet */
+static void send_packet(struct forw_packet *forw_packet)
+{
+       struct batman_if *batman_if;
+       struct batman_packet *batman_packet =
+               (struct batman_packet *)(forw_packet->packet_buff);
+       char orig_str[ETH_STR_LEN];
+       unsigned char directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0);
+
+       if (!forw_packet->if_incoming) {
+               debug_log(LOG_TYPE_CRIT,
+                         "Error - can't forward packet: incoming iface not specified\n");
+               return;
+       }
+
+       if (forw_packet->if_incoming->if_active != IF_ACTIVE)
+               return;
+
+       addr_to_string(orig_str, batman_packet->orig);
+
+       /* multihomed peer assumed */
+       /* non-primary OGMs are only broadcasted on their interface */
+       if ((directlink && (batman_packet->ttl == 1)) ||
+           (forw_packet->own && (forw_packet->if_incoming->if_num > 0))) {
+
+               /* FIXME: what about aggregated packets ? */
+               debug_log(LOG_TYPE_BATMAN,
+                         "%s packet (originator %s, seqno %d, TTL %d) on interface %s [%s]\n",
+                         (forw_packet->own ? "Sending own" : "Forwarding"),
+                         orig_str, ntohs(batman_packet->seqno),
+                         batman_packet->ttl, forw_packet->if_incoming->dev,
+                         forw_packet->if_incoming->addr_str);
+
+               send_raw_packet(forw_packet->packet_buff,
+                               forw_packet->packet_len,
+                               forw_packet->if_incoming,
+                               broadcastAddr);
+               return;
+       }
+
+       /* broadcast on every interface */
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list)
+               send_packet_to_if(forw_packet, batman_if);
+       rcu_read_unlock();
+}
+
+static void rebuild_batman_packet(struct batman_if *batman_if)
+{
+       int new_len;
+       unsigned char *new_buff;
+       struct batman_packet *batman_packet;
+
+       new_len = sizeof(struct batman_packet) + (num_hna * ETH_ALEN);
+       new_buff = kmalloc(new_len, GFP_ATOMIC);
+
+       /* keep old buffer if kmalloc should fail */
+       if (new_buff) {
+               memcpy(new_buff, batman_if->packet_buff,
+                      sizeof(struct batman_packet));
+               batman_packet = (struct batman_packet *)new_buff;
+
+               batman_packet->num_hna = hna_local_fill_buffer(
+                       new_buff + sizeof(struct batman_packet),
+                       new_len - sizeof(struct batman_packet));
+
+               kfree(batman_if->packet_buff);
+               batman_if->packet_buff = new_buff;
+               batman_if->packet_len = new_len;
+       }
+}
+
+void schedule_own_packet(struct batman_if *batman_if)
+{
+       unsigned long send_time;
+       struct batman_packet *batman_packet;
+
+       /**
+        * the interface gets activated here to avoid race conditions between
+        * the moment of activating the interface in
+        * hardif_activate_interface() where the originator mac is set and
+        * outdated packets (especially uninitialized mac addresses) in the
+        * packet queue
+        */
+       if (batman_if->if_active == IF_TO_BE_ACTIVATED)
+               batman_if->if_active = IF_ACTIVE;
+
+       /* if local hna has changed and interface is a primary interface */
+       if ((atomic_read(&hna_local_changed)) && (batman_if->if_num == 0))
+               rebuild_batman_packet(batman_if);
+
+       /**
+        * NOTE: packet_buff might just have been re-allocated in
+        * rebuild_batman_packet()
+        */
+       batman_packet = (struct batman_packet *)batman_if->packet_buff;
+
+       /* change sequence number to network order */
+       batman_packet->seqno = htons((uint16_t)atomic_read(&batman_if->seqno));
+
+       if (is_vis_server())
+               batman_packet->flags = VIS_SERVER;
+       else
+               batman_packet->flags = 0;
+
+       /* could be read by receive_bat_packet() */
+       atomic_inc(&batman_if->seqno);
+
+       slide_own_bcast_window(batman_if);
+       send_time = own_send_time();
+       add_bat_packet_to_list(batman_if->packet_buff,
+                              batman_if->packet_len, batman_if, 1, send_time);
+}
+
+void schedule_forward_packet(struct orig_node *orig_node,
+                            struct ethhdr *ethhdr,
+                            struct batman_packet *batman_packet,
+                            uint8_t directlink, int hna_buff_len,
+                            struct batman_if *if_incoming)
+{
+       unsigned char in_tq, in_ttl, tq_avg = 0;
+       unsigned long send_time;
+
+       if (batman_packet->ttl <= 1) {
+               debug_log(LOG_TYPE_BATMAN, "ttl exceeded \n");
+               return;
+       }
+
+       in_tq = batman_packet->tq;
+       in_ttl = batman_packet->ttl;
+
+       batman_packet->ttl--;
+       memcpy(batman_packet->prev_sender, ethhdr->h_source, ETH_ALEN);
+
+       /* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast
+        * of our best tq value */
+       if ((orig_node->router) && (orig_node->router->tq_avg != 0)) {
+
+               /* rebroadcast ogm of best ranking neighbor as is */
+               if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) {
+                       batman_packet->tq = orig_node->router->tq_avg;
+
+                       if (orig_node->router->last_ttl)
+                               batman_packet->ttl = orig_node->router->last_ttl - 1;
+               }
+
+               tq_avg = orig_node->router->tq_avg;
+       }
+
+       /* apply hop penalty */
+       batman_packet->tq = hop_penalty(batman_packet->tq);
+
+       debug_log(LOG_TYPE_BATMAN, "Forwarding packet: tq_orig: %i, tq_avg: %i, tq_forw: %i, ttl_orig: %i, ttl_forw: %i \n",
+                 in_tq, tq_avg, batman_packet->tq, in_ttl - 1,
+                 batman_packet->ttl);
+
+       batman_packet->seqno = htons(batman_packet->seqno);
+
+       if (directlink)
+               batman_packet->flags |= DIRECTLINK;
+       else
+               batman_packet->flags &= ~DIRECTLINK;
+
+       send_time = forward_send_time();
+       add_bat_packet_to_list((unsigned char *)batman_packet,
+                              sizeof(struct batman_packet) + hna_buff_len,
+                              if_incoming, 0, send_time);
+}
+
+static void forw_packet_free(struct forw_packet *forw_packet)
+{
+       kfree(forw_packet->packet_buff);
+       kfree(forw_packet);
+}
+
+static void _add_bcast_packet_to_list(struct forw_packet *forw_packet,
+                                     unsigned long send_time)
+{
+       INIT_HLIST_NODE(&forw_packet->list);
+
+       /* add new packet to packet list */
+       spin_lock(&forw_bcast_list_lock);
+       hlist_add_head(&forw_packet->list, &forw_bcast_list);
+       spin_unlock(&forw_bcast_list_lock);
+
+       /* start timer for this packet */
+       INIT_DELAYED_WORK(&forw_packet->delayed_work,
+                         send_outstanding_bcast_packet);
+       queue_delayed_work(bat_event_workqueue, &forw_packet->delayed_work,
+                          send_time);
+}
+
+void add_bcast_packet_to_list(unsigned char *packet_buff, int packet_len)
+{
+       struct forw_packet *forw_packet;
+
+       forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC);
+       if (!forw_packet)
+               return;
+
+       forw_packet->packet_buff = kmalloc(packet_len, GFP_ATOMIC);
+       if (!forw_packet->packet_buff)
+               return;
+
+       forw_packet->packet_len = packet_len;
+       memcpy(forw_packet->packet_buff, packet_buff, forw_packet->packet_len);
+
+       /* how often did we send the bcast packet ? */
+       forw_packet->num_packets = 0;
+
+       _add_bcast_packet_to_list(forw_packet, 1);
+}
+
+void send_outstanding_bcast_packet(struct work_struct *work)
+{
+       struct batman_if *batman_if;
+       struct delayed_work *delayed_work =
+               container_of(work, struct delayed_work, work);
+       struct forw_packet *forw_packet =
+               container_of(delayed_work, struct forw_packet, delayed_work);
+
+       spin_lock(&forw_bcast_list_lock);
+       hlist_del(&forw_packet->list);
+       spin_unlock(&forw_bcast_list_lock);
+
+       /* rebroadcast packet */
+       rcu_read_lock();
+       list_for_each_entry_rcu(batman_if, &if_list, list) {
+               send_raw_packet(forw_packet->packet_buff,
+                               forw_packet->packet_len,
+                               batman_if, broadcastAddr);
+       }
+       rcu_read_unlock();
+
+       forw_packet->num_packets++;
+
+       /* if we still have some more bcasts to send and we are not shutting
+        * down */
+       if ((forw_packet->num_packets < 3) &&
+           (atomic_read(&module_state) != MODULE_DEACTIVATING))
+               _add_bcast_packet_to_list(forw_packet, ((5 * HZ) / 1000));
+       else
+               forw_packet_free(forw_packet);
+}
+
+void send_outstanding_bat_packet(struct work_struct *work)
+{
+       struct delayed_work *delayed_work =
+               container_of(work, struct delayed_work, work);
+       struct forw_packet *forw_packet =
+               container_of(delayed_work, struct forw_packet, delayed_work);
+
+       spin_lock(&forw_bat_list_lock);
+       hlist_del(&forw_packet->list);
+       spin_unlock(&forw_bat_list_lock);
+
+       send_packet(forw_packet);
+
+       /**
+        * we have to have at least one packet in the queue
+        * to determine the queues wake up time unless we are
+        * shutting down
+        */
+       if ((forw_packet->own) &&
+           (atomic_read(&module_state) != MODULE_DEACTIVATING))
+               schedule_own_packet(forw_packet->if_incoming);
+
+       forw_packet_free(forw_packet);
+}
+
+void purge_outstanding_packets(void)
+{
+       struct forw_packet *forw_packet;
+       struct hlist_node *tmp_node, *safe_tmp_node;
+
+       debug_log(LOG_TYPE_BATMAN, "purge_outstanding_packets()\n");
+
+       /* free bcast list */
+       spin_lock(&forw_bcast_list_lock);
+       hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node,
+                                 &forw_bcast_list, list) {
+
+               spin_unlock(&forw_bcast_list_lock);
+
+               /**
+                * send_outstanding_bcast_packet() will lock the list to
+                * delete the item from the list
+                */
+               cancel_delayed_work_sync(&forw_packet->delayed_work);
+               spin_lock(&forw_bcast_list_lock);
+       }
+       spin_unlock(&forw_bcast_list_lock);
+
+       /* free batman packet list */
+       spin_lock(&forw_bat_list_lock);
+       hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node,
+                                 &forw_bat_list, list) {
+
+               spin_unlock(&forw_bat_list_lock);
+
+               /**
+                * send_outstanding_bat_packet() will lock the list to
+                * delete the item from the list
+                */
+               cancel_delayed_work_sync(&forw_packet->delayed_work);
+               spin_lock(&forw_bat_list_lock);
+       }
+       spin_unlock(&forw_bat_list_lock);
+}
diff --git a/drivers/staging/batman-adv/send.h b/drivers/staging/batman-adv/send.h
new file mode 100644 (file)
index 0000000..59d5009
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "types.h"
+
+void send_own_packet_work(struct work_struct *work);
+void send_raw_packet(unsigned char *pack_buff, int pack_buff_len,
+                    struct batman_if *batman_if, uint8_t *dst_addr);
+void schedule_own_packet(struct batman_if *batman_if);
+void schedule_forward_packet(struct orig_node *orig_node,
+                            struct ethhdr *ethhdr,
+                            struct batman_packet *batman_packet,
+                            uint8_t directlink, int hna_buff_len,
+                            struct batman_if *if_outgoing);
+void add_bcast_packet_to_list(unsigned char *packet_buff, int packet_len);
+void send_outstanding_bcast_packet(struct work_struct *work);
+void send_outstanding_bat_packet(struct work_struct *work);
+void purge_outstanding_packets(void);
diff --git a/drivers/staging/batman-adv/soft-interface.c b/drivers/staging/batman-adv/soft-interface.c
new file mode 100644 (file)
index 0000000..d543f50
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "soft-interface.h"
+#include "hard-interface.h"
+#include "send.h"
+#include "translation-table.h"
+#include "log.h"
+#include "types.h"
+#include "hash.h"
+#include <linux/ethtool.h>
+#include <linux/etherdevice.h>
+#include "compat.h"
+
+static uint16_t bcast_seqno = 1; /* give own bcast messages seq numbers to avoid
+                                 * broadcast storms */
+static int32_t skb_packets;
+static int32_t skb_bad_packets;
+static int32_t lock_dropped;
+
+unsigned char mainIfAddr[ETH_ALEN];
+static unsigned char mainIfAddr_default[ETH_ALEN];
+static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
+static void bat_get_drvinfo(struct net_device *dev,
+                           struct ethtool_drvinfo *info);
+static u32 bat_get_msglevel(struct net_device *dev);
+static void bat_set_msglevel(struct net_device *dev, u32 value);
+static u32 bat_get_link(struct net_device *dev);
+static u32 bat_get_rx_csum(struct net_device *dev);
+static int bat_set_rx_csum(struct net_device *dev, u32 data);
+
+static const struct ethtool_ops bat_ethtool_ops = {
+       .get_settings = bat_get_settings,
+       .get_drvinfo = bat_get_drvinfo,
+       .get_msglevel = bat_get_msglevel,
+       .set_msglevel = bat_set_msglevel,
+       .get_link = bat_get_link,
+       .get_rx_csum = bat_get_rx_csum,
+       .set_rx_csum = bat_set_rx_csum
+};
+
+void set_main_if_addr(uint8_t *addr)
+{
+       memcpy(mainIfAddr, addr, ETH_ALEN);
+}
+
+int main_if_was_up(void)
+{
+       return (memcmp(mainIfAddr, mainIfAddr_default, ETH_ALEN) != 0 ? 1 : 0);
+}
+
+static int my_skb_push(struct sk_buff *skb, unsigned int len)
+{
+       int result = 0;
+
+       skb_packets++;
+       if (skb->data - len < skb->head) {
+               skb_bad_packets++;
+               result = pskb_expand_head(skb, len, 0, GFP_ATOMIC);
+
+               if (result < 0)
+                       return result;
+       }
+
+       skb_push(skb, len);
+       return 0;
+}
+
+#ifdef HAVE_NET_DEVICE_OPS
+static const struct net_device_ops bat_netdev_ops = {
+       .ndo_open = interface_open,
+       .ndo_stop = interface_release,
+       .ndo_get_stats = interface_stats,
+       .ndo_set_mac_address = interface_set_mac_addr,
+       .ndo_change_mtu = interface_change_mtu,
+       .ndo_start_xmit = interface_tx,
+       .ndo_validate_addr = eth_validate_addr
+};
+#endif
+
+void interface_setup(struct net_device *dev)
+{
+       struct bat_priv *priv = netdev_priv(dev);
+       char dev_addr[ETH_ALEN];
+
+       ether_setup(dev);
+
+#ifdef HAVE_NET_DEVICE_OPS
+       dev->netdev_ops = &bat_netdev_ops;
+#else
+       dev->open = interface_open;
+       dev->stop = interface_release;
+       dev->get_stats = interface_stats;
+       dev->set_mac_address = interface_set_mac_addr;
+       dev->change_mtu = interface_change_mtu;
+       dev->hard_start_xmit = interface_tx;
+#endif
+       dev->destructor = free_netdev;
+
+       dev->mtu = hardif_min_mtu();
+       dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the
+                                               * skbuff for our header */
+
+       /* generate random address */
+       random_ether_addr(dev_addr);
+       memcpy(dev->dev_addr, dev_addr, sizeof(dev->dev_addr));
+
+       SET_ETHTOOL_OPS(dev, &bat_ethtool_ops);
+
+       memset(priv, 0, sizeof(struct bat_priv));
+}
+
+int interface_open(struct net_device *dev)
+{
+       netif_start_queue(dev);
+       return 0;
+}
+
+int interface_release(struct net_device *dev)
+{
+       netif_stop_queue(dev);
+       return 0;
+}
+
+struct net_device_stats *interface_stats(struct net_device *dev)
+{
+       struct bat_priv *priv = netdev_priv(dev);
+       return &priv->stats;
+}
+
+int interface_set_mac_addr(struct net_device *dev, void *addr)
+{
+       return -EBUSY;
+}
+
+int interface_change_mtu(struct net_device *dev, int new_mtu)
+{
+       /* check ranges */
+       if ((new_mtu < 68) || (new_mtu > hardif_min_mtu()))
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+
+       return 0;
+}
+
+int interface_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct unicast_packet *unicast_packet;
+       struct bcast_packet *bcast_packet;
+       struct orig_node *orig_node;
+       struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
+       struct bat_priv *priv = netdev_priv(dev);
+       int data_len = skb->len;
+
+       if (atomic_read(&module_state) != MODULE_ACTIVE)
+               goto dropped;
+
+       dev->trans_start = jiffies;
+       /* TODO: check this for locks */
+       hna_local_add(ethhdr->h_source);
+
+       /* ethernet packet should be broadcasted */
+       if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) {
+
+               if (my_skb_push(skb, sizeof(struct bcast_packet)) < 0)
+                       goto dropped;
+
+               bcast_packet = (struct bcast_packet *)skb->data;
+
+               bcast_packet->version = COMPAT_VERSION;
+
+               /* batman packet type: broadcast */
+               bcast_packet->packet_type = BAT_BCAST;
+
+               /* hw address of first interface is the orig mac because only
+                * this mac is known throughout the mesh */
+               memcpy(bcast_packet->orig, mainIfAddr, ETH_ALEN);
+               /* set broadcast sequence number */
+               bcast_packet->seqno = htons(bcast_seqno);
+
+               bcast_seqno++;
+
+               /* broadcast packet */
+               add_bcast_packet_to_list(skb->data, skb->len);
+
+       /* unicast packet */
+       } else {
+
+               /* simply spin_lock()ing can deadlock when the lock is already
+                * hold. */
+               /* TODO: defer the work in a working queue instead of
+                * dropping */
+               if (!spin_trylock(&orig_hash_lock)) {
+                       lock_dropped++;
+                       debug_log(LOG_TYPE_NOTICE, "%d packets dropped because lock was hold\n", lock_dropped);
+                       goto dropped;
+               }
+
+               /* get routing information */
+               orig_node = ((struct orig_node *)hash_find(orig_hash,
+                                                          ethhdr->h_dest));
+
+               /* check for hna host */
+               if (!orig_node)
+                       orig_node = transtable_search(ethhdr->h_dest);
+
+               if ((orig_node) &&
+                   (orig_node->batman_if) &&
+                   (orig_node->router)) {
+                       if (my_skb_push(skb, sizeof(struct unicast_packet)) < 0)
+                               goto unlock;
+
+                       unicast_packet = (struct unicast_packet *)skb->data;
+
+                       unicast_packet->version = COMPAT_VERSION;
+                       /* batman packet type: unicast */
+                       unicast_packet->packet_type = BAT_UNICAST;
+                       /* set unicast ttl */
+                       unicast_packet->ttl = TTL;
+                       /* copy the destination for faster routing */
+                       memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
+
+                       /* net_dev won't be available when not active */
+                       if (orig_node->batman_if->if_active != IF_ACTIVE)
+                               goto unlock;
+
+                       send_raw_packet(skb->data, skb->len,
+                                       orig_node->batman_if,
+                                       orig_node->router->addr);
+               } else {
+                       goto unlock;
+               }
+
+               spin_unlock(&orig_hash_lock);
+       }
+
+       priv->stats.tx_packets++;
+       priv->stats.tx_bytes += data_len;
+       goto end;
+
+unlock:
+       spin_unlock(&orig_hash_lock);
+dropped:
+       priv->stats.tx_dropped++;
+end:
+       kfree_skb(skb);
+       return 0;
+}
+
+void interface_rx(struct net_device *dev, void *packet, int packet_len)
+{
+       struct sk_buff *skb;
+       struct bat_priv *priv = netdev_priv(dev);
+
+       skb = dev_alloc_skb(packet_len);
+
+       if (!skb) {
+               priv->stats.rx_dropped++;
+               goto out;
+       }
+
+       memcpy(skb_put(skb, packet_len), packet, packet_len);
+
+       /* Write metadata, and then pass to the receive level */
+       skb->dev = dev;
+       skb->protocol = eth_type_trans(skb, dev);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       priv->stats.rx_packets++;
+       priv->stats.rx_bytes += packet_len;
+
+       dev->last_rx = jiffies;
+
+       netif_rx(skb);
+
+out:
+       return;
+}
+
+/* ethtool */
+static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       cmd->supported = 0;
+       cmd->advertising = 0;
+       cmd->speed = SPEED_10;
+       cmd->duplex = DUPLEX_FULL;
+       cmd->port = PORT_TP;
+       cmd->phy_address = 0;
+       cmd->transceiver = XCVR_INTERNAL;
+       cmd->autoneg = AUTONEG_DISABLE;
+       cmd->maxtxpkt = 0;
+       cmd->maxrxpkt = 0;
+
+       return 0;
+}
+
+static void bat_get_drvinfo(struct net_device *dev,
+                           struct ethtool_drvinfo *info)
+{
+       strcpy(info->driver, "B.A.T.M.A.N. advanced");
+       strcpy(info->version, SOURCE_VERSION);
+       strcpy(info->fw_version, "N/A");
+       strcpy(info->bus_info, "batman");
+}
+
+static u32 bat_get_msglevel(struct net_device *dev)
+{
+       return -EOPNOTSUPP;
+}
+
+static void bat_set_msglevel(struct net_device *dev, u32 value)
+{
+       return;
+}
+
+static u32 bat_get_link(struct net_device *dev)
+{
+       return 1;
+}
+
+static u32 bat_get_rx_csum(struct net_device *dev)
+{
+       return 0;
+}
+
+static int bat_set_rx_csum(struct net_device *dev, u32 data)
+{
+       return -EOPNOTSUPP;
+}
diff --git a/drivers/staging/batman-adv/soft-interface.h b/drivers/staging/batman-adv/soft-interface.h
new file mode 100644 (file)
index 0000000..515e276
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007-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
+ *
+ */
+
+void set_main_if_addr(uint8_t *addr);
+int main_if_was_up(void);
+void interface_setup(struct net_device *dev);
+int interface_open(struct net_device *dev);
+int interface_release(struct net_device *dev);
+struct net_device_stats *interface_stats(struct net_device *dev);
+int interface_set_mac_addr(struct net_device *dev, void *addr);
+int interface_change_mtu(struct net_device *dev, int new_mtu);
+int interface_tx(struct sk_buff *skb, struct net_device *dev);
+void interface_rx(struct net_device *dev, void *packet, int packet_len);
+
+extern unsigned char mainIfAddr[];
diff --git a/drivers/staging/batman-adv/translation-table.c b/drivers/staging/batman-adv/translation-table.c
new file mode 100644 (file)
index 0000000..c2190e1
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "translation-table.h"
+#include "log.h"
+#include "soft-interface.h"
+#include "types.h"
+#include "hash.h"
+#include "compat.h"
+
+struct hashtable_t *hna_local_hash;
+static struct hashtable_t *hna_global_hash;
+atomic_t hna_local_changed;
+
+DEFINE_SPINLOCK(hna_local_hash_lock);
+static DEFINE_SPINLOCK(hna_global_hash_lock);
+
+static DECLARE_DELAYED_WORK(hna_local_purge_wq, hna_local_purge);
+
+static void hna_local_start_timer(void)
+{
+       queue_delayed_work(bat_event_workqueue, &hna_local_purge_wq, 10 * HZ);
+}
+
+int hna_local_init(void)
+{
+       if (hna_local_hash)
+               return 1;
+
+       hna_local_hash = hash_new(128, compare_orig, choose_orig);
+
+       if (!hna_local_hash)
+               return 0;
+
+       atomic_set(&hna_local_changed, 0);
+       hna_local_start_timer();
+
+       return 1;
+}
+
+void hna_local_add(uint8_t *addr)
+{
+       struct hna_local_entry *hna_local_entry;
+       struct hna_global_entry *hna_global_entry;
+       struct hashtable_t *swaphash;
+       char hna_str[ETH_STR_LEN];
+       unsigned long flags;
+
+       spin_lock_irqsave(&hna_local_hash_lock, flags);
+       hna_local_entry =
+               ((struct hna_local_entry *)hash_find(hna_local_hash, addr));
+       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+
+       if (hna_local_entry != NULL) {
+               hna_local_entry->last_seen = jiffies;
+               return;
+       }
+
+       addr_to_string(hna_str, addr);
+
+       /* only announce as many hosts as possible in the batman-packet and
+          space in batman_packet->num_hna That also should give a limit to
+          MAC-flooding. */
+       if ((num_hna + 1 > (ETH_DATA_LEN - BAT_PACKET_LEN) / ETH_ALEN) ||
+           (num_hna + 1 > 255)) {
+               debug_log(LOG_TYPE_ROUTES, "Can't add new local hna entry (%s): number of local hna entries exceeds packet size \n", hna_str);
+               return;
+       }
+
+       debug_log(LOG_TYPE_ROUTES, "Creating new local hna entry: %s \n",
+                 hna_str);
+
+       hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC);
+       if (!hna_local_entry)
+               return;
+
+       memcpy(hna_local_entry->addr, addr, ETH_ALEN);
+       hna_local_entry->last_seen = jiffies;
+
+       /* the batman interface mac address should never be purged */
+       if (compare_orig(addr, soft_device->dev_addr))
+               hna_local_entry->never_purge = 1;
+       else
+               hna_local_entry->never_purge = 0;
+
+       spin_lock_irqsave(&hna_local_hash_lock, flags);
+
+       hash_add(hna_local_hash, hna_local_entry);
+       num_hna++;
+       atomic_set(&hna_local_changed, 1);
+
+       if (hna_local_hash->elements * 4 > hna_local_hash->size) {
+               swaphash = hash_resize(hna_local_hash,
+                                      hna_local_hash->size * 2);
+
+               if (swaphash == NULL)
+                       debug_log(LOG_TYPE_CRIT, "Couldn't resize local hna hash table \n");
+               else
+                       hna_local_hash = swaphash;
+       }
+
+       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+
+       /* remove address from global hash if present */
+       spin_lock_irqsave(&hna_global_hash_lock, flags);
+
+       hna_global_entry =
+               ((struct hna_global_entry *)hash_find(hna_global_hash, addr));
+
+       if (hna_global_entry != NULL)
+               _hna_global_del_orig(hna_global_entry, "local hna received");
+
+       spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+}
+
+int hna_local_fill_buffer(unsigned char *buff, int buff_len)
+{
+       struct hna_local_entry *hna_local_entry;
+       struct hash_it_t *hashit = NULL;
+       int i = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hna_local_hash_lock, flags);
+
+       while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) {
+
+               if (buff_len < (i + 1) * ETH_ALEN)
+                       break;
+
+               hna_local_entry = hashit->bucket->data;
+               memcpy(buff + (i * ETH_ALEN), hna_local_entry->addr, ETH_ALEN);
+
+               i++;
+       }
+
+       /* if we did not get all new local hnas see you next time  ;-) */
+       if (i == num_hna)
+               atomic_set(&hna_local_changed, 0);
+
+       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+
+       return i;
+}
+
+int hna_local_fill_buffer_text(unsigned char *buff, int buff_len)
+{
+       struct hna_local_entry *hna_local_entry;
+       struct hash_it_t *hashit = NULL;
+       int bytes_written = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hna_local_hash_lock, flags);
+
+       while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) {
+
+               if (buff_len < bytes_written + ETH_STR_LEN + 4)
+                       break;
+
+               hna_local_entry = hashit->bucket->data;
+
+               bytes_written += snprintf(buff + bytes_written, ETH_STR_LEN + 4,
+                                         " * %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                         hna_local_entry->addr[0],
+                                         hna_local_entry->addr[1],
+                                         hna_local_entry->addr[2],
+                                         hna_local_entry->addr[3],
+                                         hna_local_entry->addr[4],
+                                         hna_local_entry->addr[5]);
+       }
+
+       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+
+       return bytes_written;
+}
+
+static void _hna_local_del(void *data)
+{
+       kfree(data);
+       num_hna--;
+       atomic_set(&hna_local_changed, 1);
+}
+
+static void hna_local_del(struct hna_local_entry *hna_local_entry,
+                         char *message)
+{
+       char hna_str[ETH_STR_LEN];
+
+       addr_to_string(hna_str, hna_local_entry->addr);
+       debug_log(LOG_TYPE_ROUTES, "Deleting local hna entry (%s): %s \n",
+                 hna_str, message);
+
+       hash_remove(hna_local_hash, hna_local_entry->addr);
+       _hna_local_del(hna_local_entry);
+}
+
+void hna_local_purge(struct work_struct *work)
+{
+       struct hna_local_entry *hna_local_entry;
+       struct hash_it_t *hashit = NULL;
+       unsigned long flags;
+       unsigned long timeout;
+
+       spin_lock_irqsave(&hna_local_hash_lock, flags);
+
+       while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) {
+               hna_local_entry = hashit->bucket->data;
+
+               timeout = hna_local_entry->last_seen +
+                       ((LOCAL_HNA_TIMEOUT / 1000) * HZ);
+               if ((!hna_local_entry->never_purge) &&
+                   time_after(jiffies, timeout))
+                       hna_local_del(hna_local_entry, "address timed out");
+       }
+
+       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+       hna_local_start_timer();
+}
+
+void hna_local_free(void)
+{
+       if (!hna_local_hash)
+               return;
+
+       cancel_delayed_work_sync(&hna_local_purge_wq);
+       hash_delete(hna_local_hash, _hna_local_del);
+       hna_local_hash = NULL;
+}
+
+int hna_global_init(void)
+{
+       if (hna_global_hash)
+               return 1;
+
+       hna_global_hash = hash_new(128, compare_orig, choose_orig);
+
+       if (!hna_global_hash)
+               return 0;
+
+       return 1;
+}
+
+void hna_global_add_orig(struct orig_node *orig_node,
+                        unsigned char *hna_buff, int hna_buff_len)
+{
+       struct hna_global_entry *hna_global_entry;
+       struct hna_local_entry *hna_local_entry;
+       struct hashtable_t *swaphash;
+       char hna_str[ETH_STR_LEN], orig_str[ETH_STR_LEN];
+       int hna_buff_count = 0;
+       unsigned long flags;
+       unsigned char *hna_ptr;
+
+       addr_to_string(orig_str, orig_node->orig);
+
+       while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) {
+               spin_lock_irqsave(&hna_global_hash_lock, flags);
+
+               hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
+               hna_global_entry = (struct hna_global_entry *)
+                       hash_find(hna_global_hash, hna_ptr);
+
+               if (hna_global_entry == NULL) {
+                       spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+
+                       hna_global_entry =
+                               kmalloc(sizeof(struct hna_global_entry),
+                                       GFP_ATOMIC);
+
+                       if (!hna_global_entry)
+                               break;
+
+                       memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN);
+
+                       addr_to_string(hna_str, hna_global_entry->addr);
+                       debug_log(LOG_TYPE_ROUTES, "Creating new global hna entry: %s (via %s)\n", hna_str, orig_str);
+
+                       spin_lock_irqsave(&hna_global_hash_lock, flags);
+                       hash_add(hna_global_hash, hna_global_entry);
+
+               }
+
+               hna_global_entry->orig_node = orig_node;
+               spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+
+               /* remove address from local hash if present */
+               spin_lock_irqsave(&hna_local_hash_lock, flags);
+
+               hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
+               hna_local_entry = (struct hna_local_entry *)
+                       hash_find(hna_local_hash, hna_ptr);
+
+               if (hna_local_entry != NULL)
+                       hna_local_del(hna_local_entry, "global hna received");
+
+               spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+
+               hna_buff_count++;
+       }
+
+       orig_node->hna_buff_len = hna_buff_len;
+
+       if (orig_node->hna_buff_len > 0) {
+               orig_node->hna_buff = kmalloc(orig_node->hna_buff_len,
+                                             GFP_ATOMIC);
+               memcpy(orig_node->hna_buff, hna_buff, orig_node->hna_buff_len);
+       } else {
+               orig_node->hna_buff = NULL;
+       }
+
+       spin_lock_irqsave(&hna_global_hash_lock, flags);
+
+       if (hna_global_hash->elements * 4 > hna_global_hash->size) {
+               swaphash = hash_resize(hna_global_hash,
+                                      hna_global_hash->size * 2);
+
+               if (swaphash == NULL)
+                       debug_log(LOG_TYPE_CRIT, "Couldn't resize global hna hash table \n");
+               else
+                       hna_global_hash = swaphash;
+       }
+
+       spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+}
+
+int hna_global_fill_buffer_text(unsigned char *buff, int buff_len)
+{
+       struct hna_global_entry *hna_global_entry;
+       struct hash_it_t *hashit = NULL;
+       int bytes_written = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hna_global_hash_lock, flags);
+
+       while (NULL != (hashit = hash_iterate(hna_global_hash, hashit))) {
+               if (buff_len < bytes_written + (2 * ETH_STR_LEN) + 10)
+                       break;
+
+               hna_global_entry = hashit->bucket->data;
+
+               bytes_written += snprintf(buff + bytes_written,
+                                         (2 * ETH_STR_LEN) + 10,
+                                         " * %02x:%02x:%02x:%02x:%02x:%02x via %02x:%02x:%02x:%02x:%02x:%02x \n",
+                                         hna_global_entry->addr[0],
+                                         hna_global_entry->addr[1],
+                                         hna_global_entry->addr[2],
+                                         hna_global_entry->addr[3],
+                                         hna_global_entry->addr[4],
+                                         hna_global_entry->addr[5],
+                                         hna_global_entry->orig_node->orig[0],
+                                         hna_global_entry->orig_node->orig[1],
+                                         hna_global_entry->orig_node->orig[2],
+                                         hna_global_entry->orig_node->orig[3],
+                                         hna_global_entry->orig_node->orig[4],
+                                         hna_global_entry->orig_node->orig[5]);
+       }
+
+       spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+
+       return bytes_written;
+}
+
+void _hna_global_del_orig(struct hna_global_entry *hna_global_entry,
+                         char *message)
+{
+       char hna_str[ETH_STR_LEN], orig_str[ETH_STR_LEN];
+
+       addr_to_string(orig_str, hna_global_entry->orig_node->orig);
+       addr_to_string(hna_str, hna_global_entry->addr);
+
+       debug_log(LOG_TYPE_ROUTES, "Deleting global hna entry %s (via %s): %s \n", hna_str, orig_str, message);
+
+       hash_remove(hna_global_hash, hna_global_entry->addr);
+       kfree(hna_global_entry);
+}
+
+void hna_global_del_orig(struct orig_node *orig_node, char *message)
+{
+       struct hna_global_entry *hna_global_entry;
+       int hna_buff_count = 0;
+       unsigned long flags;
+       unsigned char *hna_ptr;
+
+       if (orig_node->hna_buff_len == 0)
+               return;
+
+       spin_lock_irqsave(&hna_global_hash_lock, flags);
+
+       while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) {
+               hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN);
+               hna_global_entry = (struct hna_global_entry *)
+                       hash_find(hna_global_hash, hna_ptr);
+
+               if ((hna_global_entry != NULL) &&
+                   (hna_global_entry->orig_node == orig_node))
+                       _hna_global_del_orig(hna_global_entry, message);
+
+               hna_buff_count++;
+       }
+
+       spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+
+       orig_node->hna_buff_len = 0;
+       kfree(orig_node->hna_buff);
+       orig_node->hna_buff = NULL;
+}
+
+static void hna_global_del(void *data)
+{
+       kfree(data);
+}
+
+void hna_global_free(void)
+{
+       if (!hna_global_hash)
+               return;
+
+       hash_delete(hna_global_hash, hna_global_del);
+       hna_global_hash = NULL;
+}
+
+struct orig_node *transtable_search(uint8_t *addr)
+{
+       struct hna_global_entry *hna_global_entry;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hna_global_hash_lock, flags);
+       hna_global_entry = (struct hna_global_entry *)
+               hash_find(hna_global_hash, addr);
+       spin_unlock_irqrestore(&hna_global_hash_lock, flags);
+
+       if (hna_global_entry == NULL)
+               return NULL;
+
+       return hna_global_entry->orig_node;
+}
diff --git a/drivers/staging/batman-adv/translation-table.h b/drivers/staging/batman-adv/translation-table.h
new file mode 100644 (file)
index 0000000..f7da811
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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 "types.h"
+
+int hna_local_init(void);
+void hna_local_add(uint8_t *addr);
+int hna_local_fill_buffer(unsigned char *buff, int buff_len);
+int hna_local_fill_buffer_text(unsigned char *buff, int buff_len);
+void hna_local_purge(struct work_struct *work);
+void hna_local_free(void);
+int hna_global_init(void);
+void hna_global_add_orig(struct orig_node *orig_node, unsigned char *hna_buff,
+                        int hna_buff_len);
+int hna_global_fill_buffer_text(unsigned char *buff, int buff_len);
+void _hna_global_del_orig(struct hna_global_entry *hna_global_entry,
+                         char *orig_str);
+void hna_global_del_orig(struct orig_node *orig_node, char *message);
+void hna_global_free(void);
+struct orig_node *transtable_search(uint8_t *addr);
+
+extern spinlock_t hna_local_hash_lock;
+extern struct hashtable_t *hna_local_hash;
+extern atomic_t hna_local_changed;
diff --git a/drivers/staging/batman-adv/types.h b/drivers/staging/batman-adv/types.h
new file mode 100644 (file)
index 0000000..3a0ef0c
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner, Simon Wunderlich
+ *
+ * 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
+ *
+ */
+
+
+
+
+
+#ifndef TYPES_H
+#define TYPES_H
+
+#include "packet.h"
+#include "bitarray.h"
+
+#define BAT_HEADER_LEN (sizeof(struct ethhdr) + ((sizeof(struct unicast_packet) > sizeof(struct bcast_packet) ? sizeof(struct unicast_packet) : sizeof(struct bcast_packet))))
+
+
+struct batman_if {
+       struct list_head list;
+       int16_t if_num;
+       char *dev;
+       char if_active;
+       char addr_str[ETH_STR_LEN];
+       struct net_device *net_dev;
+       struct socket *raw_sock;
+       atomic_t seqno;
+       unsigned char *packet_buff;
+       int packet_len;
+       struct rcu_head rcu;
+
+};
+
+struct orig_node {               /* structure for orig_list maintaining nodes of mesh */
+       uint8_t orig[ETH_ALEN];
+       struct neigh_node *router;
+       struct batman_if *batman_if;
+       TYPE_OF_WORD *bcast_own;
+       uint8_t *bcast_own_sum;
+       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. */
+       unsigned char *hna_buff;
+       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];
+       uint16_t last_bcast_seqno;  /* last broadcast sequence number received by this host */
+       struct list_head neigh_list;
+};
+
+struct neigh_node {
+       struct list_head list;
+       uint8_t addr[ETH_ALEN];
+       uint8_t real_packet_count;
+       uint8_t tq_recv[TQ_GLOBAL_WINDOW_SIZE];
+       uint8_t tq_index;
+       uint8_t tq_avg;
+       uint8_t last_ttl;
+       unsigned long last_valid;            /* when last packet via this neighbour was received */
+       TYPE_OF_WORD real_bits[NUM_WORDS];
+       struct orig_node *orig_node;
+       struct batman_if *if_incoming;
+};
+
+struct bat_priv {
+       struct net_device_stats stats;
+};
+
+struct device_client {
+       struct list_head queue_list;
+       unsigned int queue_len;
+       unsigned char index;
+       spinlock_t lock;
+       wait_queue_head_t queue_wait;
+};
+
+struct device_packet {
+       struct list_head list;
+       struct icmp_packet icmp_packet;
+};
+
+struct hna_local_entry {
+       uint8_t addr[ETH_ALEN];
+       unsigned long last_seen;
+       char never_purge;
+};
+
+struct hna_global_entry {
+       uint8_t addr[ETH_ALEN];
+       struct orig_node *orig_node;
+};
+
+struct forw_packet {               /* structure for forw_list maintaining packets to be send/forwarded */
+       struct hlist_node list;
+       unsigned long send_time;
+       uint8_t own;
+       unsigned char *packet_buff;
+       uint16_t packet_len;
+       uint32_t direct_link_flags;
+       uint8_t num_packets;
+       struct delayed_work delayed_work;
+       struct batman_if *if_incoming;
+};
+
+#endif
diff --git a/drivers/staging/batman-adv/vis.c b/drivers/staging/batman-adv/vis.c
new file mode 100644 (file)
index 0000000..f6c9acb
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2008-2009 B.A.T.M.A.N. contributors:
+ *
+ * Simon Wunderlich
+ *
+ * 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 "send.h"
+#include "translation-table.h"
+#include "vis.h"
+#include "log.h"
+#include "soft-interface.h"
+#include "hard-interface.h"
+#include "hash.h"
+#include "compat.h"
+
+struct hashtable_t *vis_hash;
+DEFINE_SPINLOCK(vis_hash_lock);
+static struct vis_info *my_vis_info;
+static struct list_head send_list;     /* always locked with vis_hash_lock */
+
+static void start_vis_timer(void);
+
+/* free the info */
+static void free_info(void *data)
+{
+       struct vis_info *info = data;
+       struct recvlist_node *entry, *tmp;
+
+       list_del_init(&info->send_list);
+       list_for_each_entry_safe(entry, tmp, &info->recv_list, list) {
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       kfree(info);
+}
+
+/* set the mode of the visualization to client or server */
+void vis_set_mode(int mode)
+{
+       spin_lock(&vis_hash_lock);
+
+       if (my_vis_info != NULL)
+               my_vis_info->packet.vis_type = mode;
+
+       spin_unlock(&vis_hash_lock);
+}
+
+/* is_vis_server(), locked outside */
+static int is_vis_server_locked(void)
+{
+       if (my_vis_info != NULL)
+               if (my_vis_info->packet.vis_type == VIS_TYPE_SERVER_SYNC)
+                       return 1;
+
+       return 0;
+}
+
+/* get the current set mode */
+int is_vis_server(void)
+{
+       int ret = 0;
+
+       spin_lock(&vis_hash_lock);
+       ret = is_vis_server_locked();
+       spin_unlock(&vis_hash_lock);
+
+       return ret;
+}
+
+/* Compare two vis packets, used by the hashing algorithm */
+static int vis_info_cmp(void *data1, void *data2)
+{
+       struct vis_info *d1, *d2;
+       d1 = data1;
+       d2 = data2;
+       return compare_orig(d1->packet.vis_orig, d2->packet.vis_orig);
+}
+
+/* hash function to choose an entry in a hash table of given size */
+/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */
+static int vis_info_choose(void *data, int size)
+{
+       struct vis_info *vis_info = data;
+       unsigned char *key;
+       uint32_t hash = 0;
+       size_t i;
+
+       key = vis_info->packet.vis_orig;
+       for (i = 0; i < ETH_ALEN; i++) {
+               hash += key[i];
+               hash += (hash << 10);
+               hash ^= (hash >> 6);
+       }
+
+       hash += (hash << 3);
+       hash ^= (hash >> 11);
+       hash += (hash << 15);
+
+       return hash % size;
+}
+
+/* tries to add one entry to the receive list. */
+static void recv_list_add(struct list_head *recv_list, char *mac)
+{
+       struct recvlist_node *entry;
+       entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC);
+       if (!entry)
+               return;
+
+       memcpy(entry->mac, mac, ETH_ALEN);
+       list_add_tail(&entry->list, recv_list);
+}
+
+/* returns 1 if this mac is in the recv_list */
+static int recv_list_is_in(struct list_head *recv_list, char *mac)
+{
+       struct recvlist_node *entry;
+
+       list_for_each_entry(entry, recv_list, list) {
+               if (memcmp(entry->mac, mac, ETH_ALEN) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old,
+ * broken.. ).  vis hash must be locked outside.  is_new is set when the packet
+ * is newer than old entries in the hash. */
+static struct vis_info *add_packet(struct vis_packet *vis_packet,
+                                  int vis_info_len, int *is_new)
+{
+       struct vis_info *info, *old_info;
+       struct vis_info search_elem;
+
+       *is_new = 0;
+       /* sanity check */
+       if (vis_hash == NULL)
+               return NULL;
+
+       /* see if the packet is already in vis_hash */
+       memcpy(search_elem.packet.vis_orig, vis_packet->vis_orig, ETH_ALEN);
+       old_info = hash_find(vis_hash, &search_elem);
+
+       if (old_info != NULL) {
+               if (vis_packet->seqno - old_info->packet.seqno <= 0) {
+                       if (old_info->packet.seqno == vis_packet->seqno) {
+                               recv_list_add(&old_info->recv_list,
+                                             vis_packet->sender_orig);
+                               return old_info;
+                       } else {
+                               /* newer packet is already in hash. */
+                               return NULL;
+                       }
+               }
+               /* remove old entry */
+               hash_remove(vis_hash, old_info);
+               free_info(old_info);
+       }
+
+       info = kmalloc(sizeof(struct vis_info) + vis_info_len, GFP_ATOMIC);
+       if (info == NULL)
+               return NULL;
+
+       INIT_LIST_HEAD(&info->send_list);
+       INIT_LIST_HEAD(&info->recv_list);
+       info->first_seen = jiffies;
+       memcpy(&info->packet, vis_packet,
+              sizeof(struct vis_packet) + vis_info_len);
+
+       /* initialize and add new packet. */
+       *is_new = 1;
+
+       /* repair if entries is longer than packet. */
+       if (info->packet.entries * sizeof(struct vis_info_entry) > vis_info_len)
+               info->packet.entries = vis_info_len / sizeof(struct vis_info_entry);
+
+       recv_list_add(&info->recv_list, info->packet.sender_orig);
+
+       /* try to add it */
+       if (hash_add(vis_hash, info) < 0) {
+               /* did not work (for some reason) */
+               free_info(info);
+               info = NULL;
+       }
+
+       return info;
+}
+
+/* handle the server sync packet, forward if needed. */
+void receive_server_sync_packet(struct vis_packet *vis_packet, int vis_info_len)
+{
+       struct vis_info *info;
+       int is_new;
+
+       spin_lock(&vis_hash_lock);
+       info = add_packet(vis_packet, vis_info_len, &is_new);
+       if (info == NULL)
+               goto end;
+
+       /* only if we are server ourselves and packet is newer than the one in
+        * hash.*/
+       if (is_vis_server_locked() && is_new) {
+               memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN);
+               if (list_empty(&info->send_list))
+                       list_add_tail(&info->send_list, &send_list);
+       }
+end:
+       spin_unlock(&vis_hash_lock);
+}
+
+/* handle an incoming client update packet and schedule forward if needed. */
+void receive_client_update_packet(struct vis_packet *vis_packet,
+                                 int vis_info_len)
+{
+       struct vis_info *info;
+       int is_new;
+
+       /* clients shall not broadcast. */
+       if (is_bcast(vis_packet->target_orig))
+               return;
+
+       spin_lock(&vis_hash_lock);
+       info = add_packet(vis_packet, vis_info_len, &is_new);
+       if (info == NULL)
+               goto end;
+       /* note that outdated packets will be dropped at this point. */
+
+
+       /* send only if we're the target server or ... */
+       if (is_vis_server_locked() &&
+           is_my_mac(info->packet.target_orig) &&
+           is_new) {
+               info->packet.vis_type = VIS_TYPE_SERVER_SYNC;   /* upgrade! */
+               memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN);
+               if (list_empty(&info->send_list))
+                       list_add_tail(&info->send_list, &send_list);
+
+               /* ... we're not the recipient (and thus need to forward). */
+       } else if (!is_my_mac(info->packet.target_orig)) {
+               if (list_empty(&info->send_list))
+                       list_add_tail(&info->send_list, &send_list);
+       }
+end:
+       spin_unlock(&vis_hash_lock);
+}
+
+/* Walk the originators and find the VIS server with the best tq. Set the packet
+ * address to its address and return the best_tq.
+ *
+ * Must be called with the originator hash locked */
+static int find_best_vis_server(struct vis_info *info)
+{
+       struct hash_it_t *hashit = NULL;
+       struct orig_node *orig_node;
+       int best_tq = -1;
+
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+               orig_node = hashit->bucket->data;
+               if ((orig_node != NULL) &&
+                   (orig_node->router != NULL) &&
+                   (orig_node->flags & VIS_SERVER) &&
+                   (orig_node->router->tq_avg > best_tq)) {
+                       best_tq = orig_node->router->tq_avg;
+                       memcpy(info->packet.target_orig, orig_node->orig,
+                              ETH_ALEN);
+               }
+       }
+       return best_tq;
+}
+
+/* Return true if the vis packet is full. */
+static bool vis_packet_full(struct vis_info *info)
+{
+       if (info->packet.entries + 1 >
+           (1000 - sizeof(struct vis_info)) / sizeof(struct vis_info_entry))
+               return true;
+       return false;
+}
+
+/* generates a packet of own vis data,
+ * returns 0 on success, -1 if no packet could be generated */
+static int generate_vis_packet(void)
+{
+       struct hash_it_t *hashit = NULL;
+       struct orig_node *orig_node;
+       struct vis_info *info = (struct vis_info *)my_vis_info;
+       struct vis_info_entry *entry, *entry_array;
+       struct hna_local_entry *hna_local_entry;
+       int best_tq = -1;
+       unsigned long flags;
+
+       info->first_seen = jiffies;
+
+       spin_lock(&orig_hash_lock);
+       memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN);
+       info->packet.ttl = TTL;
+       info->packet.seqno++;
+       info->packet.entries = 0;
+
+       if (!is_vis_server_locked()) {
+               best_tq = find_best_vis_server(info);
+               if (best_tq < 0) {
+                       spin_unlock(&orig_hash_lock);
+                       return -1;
+               }
+       }
+       hashit = NULL;
+
+       entry_array = (struct vis_info_entry *)
+               ((char *)info + sizeof(struct vis_info));
+
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+               orig_node = hashit->bucket->data;
+               if (orig_node->router != NULL
+                       && compare_orig(orig_node->router->addr, orig_node->orig)
+                       && orig_node->batman_if
+                       && (orig_node->batman_if->if_active == IF_ACTIVE)
+                   && orig_node->router->tq_avg > 0) {
+
+                       /* fill one entry into buffer. */
+                       entry = &entry_array[info->packet.entries];
+                       memcpy(entry->src, orig_node->batman_if->net_dev->dev_addr, ETH_ALEN);
+                       memcpy(entry->dest, orig_node->orig, ETH_ALEN);
+                       entry->quality = orig_node->router->tq_avg;
+                       info->packet.entries++;
+
+                       if (vis_packet_full(info)) {
+                               spin_unlock(&orig_hash_lock);
+                               return 0;
+                       }
+               }
+       }
+
+       spin_unlock(&orig_hash_lock);
+
+       hashit = NULL;
+       spin_lock_irqsave(&hna_local_hash_lock, flags);
+       while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) {
+               hna_local_entry = hashit->bucket->data;
+               entry = &entry_array[info->packet.entries];
+               memset(entry->src, 0, ETH_ALEN);
+               memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN);
+               entry->quality = 0; /* 0 means HNA */
+               info->packet.entries++;
+
+               if (vis_packet_full(info)) {
+                       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+                       return 0;
+               }
+       }
+       spin_unlock_irqrestore(&hna_local_hash_lock, flags);
+       return 0;
+}
+
+static void purge_vis_packets(void)
+{
+       struct hash_it_t *hashit = NULL;
+       struct vis_info *info;
+
+       while (NULL != (hashit = hash_iterate(vis_hash, hashit))) {
+               info = hashit->bucket->data;
+               if (info == my_vis_info)        /* never purge own data. */
+                       continue;
+               if (time_after(jiffies,
+                              info->first_seen + (VIS_TIMEOUT/1000)*HZ)) {
+                       hash_remove_bucket(vis_hash, hashit);
+                       free_info(info);
+               }
+       }
+}
+
+static void broadcast_vis_packet(struct vis_info *info, int packet_length)
+{
+       struct hash_it_t *hashit = NULL;
+       struct orig_node *orig_node;
+
+       spin_lock(&orig_hash_lock);
+
+       /* send to all routers in range. */
+       while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
+               orig_node = hashit->bucket->data;
+
+               /* if it's a vis server and reachable, send it. */
+               if (orig_node &&
+                   (orig_node->flags & VIS_SERVER) &&
+                   orig_node->batman_if &&
+                   orig_node->router) {
+
+                       /* don't send it if we already received the packet from
+                        * this node. */
+                       if (recv_list_is_in(&info->recv_list, orig_node->orig))
+                               continue;
+
+                       memcpy(info->packet.target_orig,
+                              orig_node->orig, ETH_ALEN);
+
+                       send_raw_packet((unsigned char *) &info->packet,
+                                       packet_length,
+                                       orig_node->batman_if,
+                                       orig_node->router->addr);
+               }
+       }
+       memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN);
+       spin_unlock(&orig_hash_lock);
+}
+
+static void unicast_vis_packet(struct vis_info *info, int packet_length)
+{
+       struct orig_node *orig_node;
+
+       spin_lock(&orig_hash_lock);
+       orig_node = ((struct orig_node *)
+                    hash_find(orig_hash, info->packet.target_orig));
+
+       if ((orig_node != NULL) &&
+           (orig_node->batman_if != NULL) &&
+           (orig_node->router != NULL)) {
+               send_raw_packet((unsigned char *) &info->packet, packet_length,
+                               orig_node->batman_if,
+                               orig_node->router->addr);
+       }
+       spin_unlock(&orig_hash_lock);
+}
+
+/* only send one vis packet. called from send_vis_packets() */
+static void send_vis_packet(struct vis_info *info)
+{
+       int packet_length;
+
+       if (info->packet.ttl < 2) {
+               debug_log(LOG_TYPE_NOTICE,
+                         "Error - can't send vis packet: ttl exceeded\n");
+               return;
+       }
+
+       memcpy(info->packet.sender_orig, mainIfAddr, ETH_ALEN);
+       info->packet.ttl--;
+
+       packet_length = sizeof(struct vis_packet) +
+               info->packet.entries * sizeof(struct vis_info_entry);
+
+       if (is_bcast(info->packet.target_orig))
+               broadcast_vis_packet(info, packet_length);
+       else
+               unicast_vis_packet(info, packet_length);
+       info->packet.ttl++; /* restore TTL */
+}
+
+/* called from timer; send (and maybe generate) vis packet. */
+static void send_vis_packets(struct work_struct *work)
+{
+       struct vis_info *info, *temp;
+
+       spin_lock(&vis_hash_lock);
+       purge_vis_packets();
+
+       if (generate_vis_packet() == 0)
+               /* schedule if generation was successful */
+               list_add_tail(&my_vis_info->send_list, &send_list);
+
+       list_for_each_entry_safe(info, temp, &send_list, send_list) {
+               list_del_init(&info->send_list);
+               send_vis_packet(info);
+       }
+       spin_unlock(&vis_hash_lock);
+       start_vis_timer();
+}
+static DECLARE_DELAYED_WORK(vis_timer_wq, send_vis_packets);
+
+/* init the vis server. this may only be called when if_list is already
+ * initialized (e.g. bat0 is initialized, interfaces have been added) */
+int vis_init(void)
+{
+       if (vis_hash)
+               return 1;
+
+       spin_lock(&vis_hash_lock);
+
+       vis_hash = hash_new(256, vis_info_cmp, vis_info_choose);
+       if (!vis_hash) {
+               debug_log(LOG_TYPE_CRIT, "Can't initialize vis_hash\n");
+               goto err;
+       }
+
+       my_vis_info = kmalloc(1000, GFP_ATOMIC);
+       if (!my_vis_info) {
+               debug_log(LOG_TYPE_CRIT, "Can't initialize vis packet\n");
+               goto err;
+       }
+
+       /* prefill the vis info */
+       my_vis_info->first_seen = jiffies - atomic_read(&vis_interval);
+       INIT_LIST_HEAD(&my_vis_info->recv_list);
+       INIT_LIST_HEAD(&my_vis_info->send_list);
+       my_vis_info->packet.version = COMPAT_VERSION;
+       my_vis_info->packet.packet_type = BAT_VIS;
+       my_vis_info->packet.vis_type = VIS_TYPE_CLIENT_UPDATE;
+       my_vis_info->packet.ttl = TTL;
+       my_vis_info->packet.seqno = 0;
+       my_vis_info->packet.entries = 0;
+
+       INIT_LIST_HEAD(&send_list);
+
+       memcpy(my_vis_info->packet.vis_orig, mainIfAddr, ETH_ALEN);
+       memcpy(my_vis_info->packet.sender_orig, mainIfAddr, ETH_ALEN);
+
+       if (hash_add(vis_hash, my_vis_info) < 0) {
+               debug_log(LOG_TYPE_CRIT,
+                         "Can't add own vis packet into hash\n");
+               free_info(my_vis_info); /* not in hash, need to remove it
+                                        * manually. */
+               goto err;
+       }
+
+       spin_unlock(&vis_hash_lock);
+       start_vis_timer();
+       return 1;
+
+err:
+       spin_unlock(&vis_hash_lock);
+       vis_quit();
+       return 0;
+}
+
+/* shutdown vis-server */
+void vis_quit(void)
+{
+       if (!vis_hash)
+               return;
+
+       cancel_delayed_work_sync(&vis_timer_wq);
+
+       spin_lock(&vis_hash_lock);
+       /* properly remove, kill timers ... */
+       hash_delete(vis_hash, free_info);
+       vis_hash = NULL;
+       my_vis_info = NULL;
+       spin_unlock(&vis_hash_lock);
+}
+
+/* schedule packets for (re)transmission */
+static void start_vis_timer(void)
+{
+       queue_delayed_work(bat_event_workqueue, &vis_timer_wq,
+                          (atomic_read(&vis_interval)/1000) * HZ);
+}
+
diff --git a/drivers/staging/batman-adv/vis.h b/drivers/staging/batman-adv/vis.h
new file mode 100644 (file)
index 0000000..276faba
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008-2009 B.A.T.M.A.N. contributors:
+ *
+ * Simon Wunderlich, 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
+ *
+ */
+
+#define VIS_TIMEOUT            200000
+#define VIS_FORMAT_DD_NAME     "dot_draw"
+#define VIS_FORMAT_JSON_NAME   "json"
+
+struct vis_info {
+       unsigned long       first_seen;
+       struct list_head    recv_list;
+                           /* list of server-neighbors we received a vis-packet
+                            * from.  we should not reply to them. */
+       struct list_head send_list;
+       /* this packet might be part of the vis send queue. */
+       struct vis_packet packet;
+       /* vis_info may follow here*/
+} __attribute__((packed));
+
+struct vis_info_entry {
+       uint8_t  src[ETH_ALEN];
+       uint8_t  dest[ETH_ALEN];
+       uint8_t  quality;       /* quality = 0 means HNA */
+} __attribute__((packed));
+
+struct recvlist_node {
+       struct list_head list;
+       uint8_t mac[ETH_ALEN];
+};
+
+enum vis_formats {
+       DOT_DRAW,
+       JSON,
+};
+
+extern struct hashtable_t *vis_hash;
+extern spinlock_t vis_hash_lock;
+
+void vis_set_mode(int mode);
+int is_vis_server(void);
+void receive_server_sync_packet(struct vis_packet *vis_packet,
+                               int vis_info_len);
+void receive_client_update_packet(struct vis_packet *vis_packet,
+                                 int vis_info_len);
+int vis_init(void);
+void vis_quit(void);