From 5beef3c9bf7f967a4a70ddb0108fd3e459fed133 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 9 Nov 2009 21:20:10 +0100 Subject: [PATCH] staging: batman-adv meshing protocol 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 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/batman-adv/CHANGELOG | 37 + drivers/staging/batman-adv/Kconfig | 25 + drivers/staging/batman-adv/Makefile | 22 + drivers/staging/batman-adv/README | 125 ++ drivers/staging/batman-adv/TODO | 51 + drivers/staging/batman-adv/aggregation.c | 232 ++++ drivers/staging/batman-adv/aggregation.h | 37 + drivers/staging/batman-adv/bitarray.c | 177 +++ drivers/staging/batman-adv/bitarray.h | 45 + drivers/staging/batman-adv/compat.h | 75 ++ drivers/staging/batman-adv/device.c | 337 ++++++ drivers/staging/batman-adv/device.h | 36 + drivers/staging/batman-adv/hard-interface.c | 451 ++++++++ drivers/staging/batman-adv/hard-interface.h | 36 + drivers/staging/batman-adv/hash.c | 313 +++++ drivers/staging/batman-adv/hash.h | 99 ++ drivers/staging/batman-adv/log.c | 179 +++ drivers/staging/batman-adv/log.h | 32 + drivers/staging/batman-adv/main.c | 286 +++++ drivers/staging/batman-adv/main.h | 151 +++ drivers/staging/batman-adv/packet.h | 96 ++ drivers/staging/batman-adv/proc.c | 950 ++++++++++++++++ drivers/staging/batman-adv/proc.h | 49 + drivers/staging/batman-adv/ring_buffer.c | 52 + drivers/staging/batman-adv/ring_buffer.h | 23 + drivers/staging/batman-adv/routing.c | 1010 +++++++++++++++++ drivers/staging/batman-adv/routing.h | 34 + drivers/staging/batman-adv/send.c | 473 ++++++++ drivers/staging/batman-adv/send.h | 36 + drivers/staging/batman-adv/soft-interface.c | 349 ++++++ drivers/staging/batman-adv/soft-interface.h | 33 + .../staging/batman-adv/translation-table.c | 454 ++++++++ .../staging/batman-adv/translation-table.h | 42 + drivers/staging/batman-adv/types.h | 124 ++ drivers/staging/batman-adv/vis.c | 564 +++++++++ drivers/staging/batman-adv/vis.h | 63 + 38 files changed, 7101 insertions(+) create mode 100644 drivers/staging/batman-adv/CHANGELOG create mode 100644 drivers/staging/batman-adv/Kconfig create mode 100644 drivers/staging/batman-adv/Makefile create mode 100644 drivers/staging/batman-adv/README create mode 100644 drivers/staging/batman-adv/TODO create mode 100644 drivers/staging/batman-adv/aggregation.c create mode 100644 drivers/staging/batman-adv/aggregation.h create mode 100644 drivers/staging/batman-adv/bitarray.c create mode 100644 drivers/staging/batman-adv/bitarray.h create mode 100644 drivers/staging/batman-adv/compat.h create mode 100644 drivers/staging/batman-adv/device.c create mode 100644 drivers/staging/batman-adv/device.h create mode 100644 drivers/staging/batman-adv/hard-interface.c create mode 100644 drivers/staging/batman-adv/hard-interface.h create mode 100644 drivers/staging/batman-adv/hash.c create mode 100644 drivers/staging/batman-adv/hash.h create mode 100644 drivers/staging/batman-adv/log.c create mode 100644 drivers/staging/batman-adv/log.h create mode 100644 drivers/staging/batman-adv/main.c create mode 100644 drivers/staging/batman-adv/main.h create mode 100644 drivers/staging/batman-adv/packet.h create mode 100644 drivers/staging/batman-adv/proc.c create mode 100644 drivers/staging/batman-adv/proc.h create mode 100644 drivers/staging/batman-adv/ring_buffer.c create mode 100644 drivers/staging/batman-adv/ring_buffer.h create mode 100644 drivers/staging/batman-adv/routing.c create mode 100644 drivers/staging/batman-adv/routing.h create mode 100644 drivers/staging/batman-adv/send.c create mode 100644 drivers/staging/batman-adv/send.h create mode 100644 drivers/staging/batman-adv/soft-interface.c create mode 100644 drivers/staging/batman-adv/soft-interface.h create mode 100644 drivers/staging/batman-adv/translation-table.c create mode 100644 drivers/staging/batman-adv/translation-table.h create mode 100644 drivers/staging/batman-adv/types.h create mode 100644 drivers/staging/batman-adv/vis.c create mode 100644 drivers/staging/batman-adv/vis.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6fbbedbc7fe7..d30fa451e6f3 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -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" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 61a81c148c9f..910e55dd7914 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -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 index 000000000000..8a181639ceaa --- /dev/null +++ b/drivers/staging/batman-adv/CHANGELOG @@ -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 index 000000000000..b9742e7c7d9e --- /dev/null +++ b/drivers/staging/batman-adv/Kconfig @@ -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 index 000000000000..02da87134fce --- /dev/null +++ b/drivers/staging/batman-adv/Makefile @@ -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 index 000000000000..3aaf393ebaa7 --- /dev/null +++ b/drivers/staging/batman-adv/README @@ -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 +Simon Wunderlich diff --git a/drivers/staging/batman-adv/TODO b/drivers/staging/batman-adv/TODO new file mode 100644 index 000000000000..ea6dcf94d661 --- /dev/null +++ b/drivers/staging/batman-adv/TODO @@ -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 + Simon Wunderlich + Andrew Lunn + b.a.t.m.a.n@lists.open-mesh.net + Greg Kroah-Hartman diff --git a/drivers/staging/batman-adv/aggregation.c b/drivers/staging/batman-adv/aggregation.c new file mode 100644 index 000000000000..9c6e681f6fb6 --- /dev/null +++ b/drivers/staging/batman-adv/aggregation.c @@ -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 index 000000000000..6da8df9f99b7 --- /dev/null +++ b/drivers/staging/batman-adv/aggregation.h @@ -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 index 000000000000..3c67f5f42b2b --- /dev/null +++ b/drivers/staging/batman-adv/bitarray.c @@ -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 index 000000000000..ec72dd784362 --- /dev/null +++ b/drivers/staging/batman-adv/bitarray.h @@ -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 index 000000000000..f4e0a4564ba7 --- /dev/null +++ b/drivers/staging/batman-adv/compat.h @@ -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_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 index 000000000000..1e7d1f88674f --- /dev/null +++ b/drivers/staging/batman-adv/device.c @@ -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 index 000000000000..46c0f4496527 --- /dev/null +++ b/drivers/staging/batman-adv/device.h @@ -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 index 000000000000..5ea35da5ee7a --- /dev/null +++ b/drivers/staging/batman-adv/hard-interface.c @@ -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 index 000000000000..742358c00c0e --- /dev/null +++ b/drivers/staging/batman-adv/hard-interface.h @@ -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 index 000000000000..61cb4a20ebca --- /dev/null +++ b/drivers/staging/batman-adv/hash.c @@ -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 index 000000000000..bb60f082be6a --- /dev/null +++ b/drivers/staging/batman-adv/hash.h @@ -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 index 000000000000..f37c7f01a9f5 --- /dev/null +++ b/drivers/staging/batman-adv/log.c @@ -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 index 000000000000..780e3abb48f9 --- /dev/null +++ b/drivers/staging/batman-adv/log.h @@ -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 index 000000000000..bb89bfc5dda6 --- /dev/null +++ b/drivers/staging/batman-adv/main.c @@ -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 index 000000000000..facb6b79ee52 --- /dev/null +++ b/drivers/staging/batman-adv/main.h @@ -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 , Simon Wunderlich " +#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 /* mutex */ +#include /* needed by all modules */ +#include /* netdevice */ +#include /* ethernet header */ +#include /* poll_table */ +#include /* kernel threads */ +#include /* schedule types */ +#include /* workqueue */ +#include /* struct sock */ +#include +#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 index 000000000000..5627ca326018 --- /dev/null +++ b/drivers/staging/batman-adv/packet.h @@ -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 index 000000000000..aac3df7f13fb --- /dev/null +++ b/drivers/staging/batman-adv/proc.c @@ -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 index 000000000000..16d3efdebe52 --- /dev/null +++ b/drivers/staging/batman-adv/proc.h @@ -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 +#include + +#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 index 000000000000..751c899f54c5 --- /dev/null +++ b/drivers/staging/batman-adv/ring_buffer.c @@ -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 index 000000000000..6839ba97eeb3 --- /dev/null +++ b/drivers/staging/batman-adv/ring_buffer.h @@ -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 index 000000000000..4a14c363ac2b --- /dev/null +++ b/drivers/staging/batman-adv/routing.c @@ -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 index 000000000000..0123ea86debb --- /dev/null +++ b/drivers/staging/batman-adv/routing.h @@ -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 index 000000000000..d724798278d6 --- /dev/null +++ b/drivers/staging/batman-adv/send.c @@ -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 index 000000000000..59d500917a35 --- /dev/null +++ b/drivers/staging/batman-adv/send.h @@ -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 index 000000000000..d543f50b647f --- /dev/null +++ b/drivers/staging/batman-adv/soft-interface.c @@ -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 +#include +#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 index 000000000000..515e276ef53d --- /dev/null +++ b/drivers/staging/batman-adv/soft-interface.h @@ -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 index 000000000000..c2190e177c56 --- /dev/null +++ b/drivers/staging/batman-adv/translation-table.c @@ -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 index 000000000000..f7da81129318 --- /dev/null +++ b/drivers/staging/batman-adv/translation-table.h @@ -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 index 000000000000..3a0ef0c38c93 --- /dev/null +++ b/drivers/staging/batman-adv/types.h @@ -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 index 000000000000..f6c9acb289ed --- /dev/null +++ b/drivers/staging/batman-adv/vis.c @@ -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 index 000000000000..276fabab4e88 --- /dev/null +++ b/drivers/staging/batman-adv/vis.h @@ -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); -- 2.30.2