forked from Minki/linux
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 <andrew@lunn.ch> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
6638db58db
commit
5beef3c9bf
@ -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"
|
||||
|
@ -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/
|
||||
|
37
drivers/staging/batman-adv/CHANGELOG
Normal file
37
drivers/staging/batman-adv/CHANGELOG
Normal file
@ -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
|
25
drivers/staging/batman-adv/Kconfig
Normal file
25
drivers/staging/batman-adv/Kconfig
Normal file
@ -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.
|
||||
|
22
drivers/staging/batman-adv/Makefile
Normal file
22
drivers/staging/batman-adv/Makefile
Normal file
@ -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
|
125
drivers/staging/batman-adv/README
Normal file
125
drivers/staging/batman-adv/README
Normal file
@ -0,0 +1,125 @@
|
||||
[state: 07-11-2009]
|
||||
|
||||
BATMAN-ADV
|
||||
----------
|
||||
|
||||
Batman-advanced is a new approach to wireless networking which does no longer
|
||||
operate on the IP basis. Unlike B.A.T.M.A.N, which exchanges information
|
||||
using UDP packets and sets routing tables, batman-advanced operates on ISO/OSI
|
||||
Layer 2 only and uses and routes (or better: bridges) Ethernet Frames. It
|
||||
emulates a virtual network switch of all nodes participating. Therefore all
|
||||
nodes appear to be link local, thus all higher operating protocols won't be
|
||||
affected by any changes within the network. You can run almost any protocol
|
||||
above B.A.T.M.A.N. Advanced, prominent examples are: IPv4, IPv6, DHCP, IPX.
|
||||
|
||||
This is batman-advanced implemented as Linux kernel driver. It does not depend
|
||||
on any network (other) driver, and can be used on wifi as well as ethernet,
|
||||
vpn, etc ... (anything with ethernet-style layer 2).
|
||||
It compiles against and should work with Linux 2.6.20 - 2.6.31. Supporting older
|
||||
versions is not planned, but it's probably easy to backport it. If you work on a
|
||||
backport, feel free to contact us. :-)
|
||||
|
||||
COMPILE
|
||||
-------
|
||||
To compile against your currently installed kernel, just type:
|
||||
|
||||
# make
|
||||
|
||||
if you want to compile against some other kernel, use:
|
||||
|
||||
# make KERNELPATH=/path/to/kernel
|
||||
|
||||
USAGE
|
||||
-----
|
||||
|
||||
insmod the batman-adv.ko in your kernel:
|
||||
|
||||
# insmod batman-adv.ko
|
||||
|
||||
the module is now waiting for activation. You must add some interfaces
|
||||
on which batman can operate. Each interface must be added separately:
|
||||
|
||||
# echo wlan0 > /proc/net/batman-adv/interfaces
|
||||
|
||||
( # echo wlan1 > /proc/net/batman-adv/interfaces )
|
||||
( # echo eth0 > /proc/net/batman-adv/interfaces )
|
||||
( ... )
|
||||
|
||||
Now batman starts broadcasting on this interface.
|
||||
You can now view the table of originators (mesh participants) with:
|
||||
|
||||
# cat /proc/net/batman-adv/originators
|
||||
|
||||
The module will create a new interface "bat0", which can be used as a
|
||||
regular interface:
|
||||
|
||||
# ifconfig bat0 inet 192.168.0.1 up
|
||||
# ping 192.168.0.2
|
||||
...
|
||||
|
||||
If you want topology visualization, your meshnode must be configured
|
||||
as VIS-server:
|
||||
|
||||
# echo "server" > /proc/net/batman-adv/vis
|
||||
|
||||
Each node is either configured as "server" or as "client" (default:
|
||||
"client"). Clients send their topology data to the server next to them,
|
||||
and server synchronize with other servers. If there is no server
|
||||
configured (default) within the mesh, no topology information will be
|
||||
transmitted. With these "synchronizing servers", there can be 1 or
|
||||
more vis servers sharing the same (or at least very similar) data.
|
||||
|
||||
When configured as server, you can get a topology snapshot of your mesh:
|
||||
|
||||
# cat /proc/net/batman-adv/vis
|
||||
|
||||
This output format is a graphviz formatted text file which can be
|
||||
processed with graphviz-tools like dot.
|
||||
The labels are similar/compatible to the ETX metric, 1.0 means perfect
|
||||
connection (100%), 2.0 means 50%, 3.0 means 33% and so on.
|
||||
|
||||
Alternatively, a JSON output format is available. The format can be set
|
||||
using by writing either "dot_draw" or "json" into the vis_format file.
|
||||
"dot_draw" is selected by default.
|
||||
|
||||
echo "json" > /proc/net/batman-adv/vis_format
|
||||
|
||||
In very mobile scenarios, you might want to adjust the originator
|
||||
interval to a lower value. This will make the mesh more responsive to
|
||||
topology changes, but will also increase the overhead. Please make sure
|
||||
that all nodes in your mesh use the same interval. The default value
|
||||
is 1000 ms (1 second).
|
||||
|
||||
# echo 1000 > /proc/net/batman-adv/orig_interval
|
||||
|
||||
To deactivate batman, do:
|
||||
|
||||
# echo "" > /proc/net/batman-adv/interfaces
|
||||
|
||||
BATCTL
|
||||
------
|
||||
|
||||
B.A.T.M.A.N. advanced operates on layer 2 and thus all hosts partici-
|
||||
pating in the virtual switch are completely transparent for all proto-
|
||||
cols above layer 2. Therefore the common diagnosis tools do not work as
|
||||
expected. To overcome these problems batctl was created. At the moment
|
||||
the batctl contains ping, traceroute, tcpdump and interfaces to the
|
||||
kernel module settings.
|
||||
|
||||
For more information, please see the manpage (man batctl).
|
||||
|
||||
batctl is available on http://www.open-mesh.net/
|
||||
|
||||
CONTACT
|
||||
-------
|
||||
|
||||
Please send us comments, experiences, questions, anything :)
|
||||
|
||||
IRC: #batman on irc.freenode.org
|
||||
Mailing-list: b.a.t.m.a.n@open-mesh.net
|
||||
(subscription at https://list.open-mesh.net/mm/listinfo/b.a.t.m.a.n )
|
||||
|
||||
You can also contact the Authors:
|
||||
|
||||
Marek Lindner <lindner_marek@yahoo.de>
|
||||
Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
|
51
drivers/staging/batman-adv/TODO
Normal file
51
drivers/staging/batman-adv/TODO
Normal file
@ -0,0 +1,51 @@
|
||||
=> proc interface
|
||||
* implement new interface to add/delete interfaces and setting options
|
||||
* /proc/sys/net/batman-adv/ as main folder
|
||||
* in interfaces/ list every available interface of the host
|
||||
* each interfaces/$iface/ contains the following files:
|
||||
-> enable (def: 0) [add/remove this interface to batman-adv]
|
||||
-> ogm_interval (def: 1000) [ogm interval of that interface]
|
||||
-> context (def: bat0) [later we want to support multiple mesh instances via
|
||||
-> bat0/bat1/bat2/..]
|
||||
-> status (read-only) [outputs the interface status from batman's
|
||||
-> perspective]
|
||||
* in mesh/batX/ list every available mesh subnet
|
||||
-> vis_server (def: 0) [enable/disable vis server for that mesh]
|
||||
-> vis_data (read-only) [outputs the vis data in a raw format]
|
||||
-> aggregate_ogm (def: 1) [enable/disable ogm aggregation for that mesh]
|
||||
-> originators (read-only) [outputs the originator table]
|
||||
-> transtable_global (read-only) [outputs the global translation table]
|
||||
-> transtable_local (read-only) [outputs the local translation table]
|
||||
|
||||
=> vis "raw" data output
|
||||
* the raw format shall replace dot draw / json to offer a neutral that can
|
||||
* be converted
|
||||
* the format (comma seperated entries):
|
||||
-> "mac" -> mac address of an originator (each line begins with it)
|
||||
-> "TQ mac value" -> src mac's link quality towards mac address
|
||||
-> "HNA mac" -> HNA announced by source mac
|
||||
-> "PRIMARY" -> this is a primary interface
|
||||
-> "SEC mac" -> secondary mac address of source (requires preceeding
|
||||
-> PRIMARY)
|
||||
|
||||
=> logging
|
||||
* the log level LOG_TYPE_CRIT, LOG_TYPE_WARN & LOG_TYPE_NOTICE will be
|
||||
* unified to use printk
|
||||
* LOG_TYPE_BATMAN & LOG_TYPE_ROUTES will also use printk but only after the
|
||||
* internal debug level has been raised
|
||||
* the internal debug level can be modified using a module parameter (debug)
|
||||
* or at run time via /sys/module/batman-adv/parameters/debug
|
||||
* make use of printk %pM support instead of converting mac addresses
|
||||
* manually
|
||||
|
||||
=> strip out all backward compatibility support to older kernels
|
||||
(only found in compat.h)
|
||||
|
||||
=> fix checkpatch.pl errors
|
||||
|
||||
Please send all patches to:
|
||||
Marek Lindner <lindner_marek@yahoo.de>
|
||||
Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
|
||||
Andrew Lunn <andrew@lunn.ch>
|
||||
b.a.t.m.a.n@lists.open-mesh.net
|
||||
Greg Kroah-Hartman <gregkh@suse.de>
|
232
drivers/staging/batman-adv/aggregation.c
Normal file
232
drivers/staging/batman-adv/aggregation.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
37
drivers/staging/batman-adv/aggregation.h
Normal file
37
drivers/staging/batman-adv/aggregation.h
Normal file
@ -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);
|
177
drivers/staging/batman-adv/bitarray.c
Normal file
177
drivers/staging/batman-adv/bitarray.c
Normal file
@ -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;
|
||||
}
|
45
drivers/staging/batman-adv/bitarray.h
Normal file
45
drivers/staging/batman-adv/bitarray.h
Normal file
@ -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);
|
75
drivers/staging/batman-adv/compat.h
Normal file
75
drivers/staging/batman-adv/compat.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*
|
||||
*
|
||||
* This file contains macros for maintaining compatibility with older versions
|
||||
* of the Linux kernel.
|
||||
*/
|
||||
|
||||
#include <linux/version.h> /* LINUX_VERSION_CODE */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
|
||||
|
||||
#define skb_set_network_header(_skb, _offset) \
|
||||
do { (_skb)->nh.raw = (_skb)->data + (_offset); } while (0)
|
||||
|
||||
#define skb_reset_mac_header(_skb) \
|
||||
do { (_skb)->mac.raw = (_skb)->data; } while (0)
|
||||
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
|
||||
|
||||
#define device_create(_cls, _parent, _devt, _device, _fmt) \
|
||||
class_device_create(_cls, _parent, _devt, _device, _fmt)
|
||||
|
||||
#define device_destroy(_cls, _device) \
|
||||
class_device_destroy(_cls, _device)
|
||||
|
||||
#else
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
|
||||
|
||||
#define device_create(_cls, _parent, _devt, _device, _fmt) \
|
||||
device_create_drvdata(_cls, _parent, _devt, _device, _fmt)
|
||||
|
||||
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) */
|
||||
|
||||
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
|
||||
|
||||
#define cancel_delayed_work_sync(wq) cancel_rearming_delayed_work(wq)
|
||||
|
||||
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
|
||||
#define strict_strtoul(cp, base, res) \
|
||||
({ \
|
||||
int ret = 0; \
|
||||
char *endp; \
|
||||
*res = simple_strtoul(cp, &endp, base); \
|
||||
if (cp == endp) \
|
||||
ret = -EINVAL; \
|
||||
ret; \
|
||||
})
|
||||
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */
|
337
drivers/staging/batman-adv/device.c
Normal file
337
drivers/staging/batman-adv/device.c
Normal file
@ -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);
|
||||
}
|
36
drivers/staging/batman-adv/device.h
Normal file
36
drivers/staging/batman-adv/device.h
Normal file
@ -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);
|
451
drivers/staging/batman-adv/hard-interface.c
Normal file
451
drivers/staging/batman-adv/hard-interface.c
Normal file
@ -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,
|
||||
};
|
36
drivers/staging/batman-adv/hard-interface.h
Normal file
36
drivers/staging/batman-adv/hard-interface.h
Normal file
@ -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);
|
313
drivers/staging/batman-adv/hash.c
Normal file
313
drivers/staging/batman-adv/hash.c
Normal file
@ -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;
|
||||
}
|
99
drivers/staging/batman-adv/hash.h
Normal file
99
drivers/staging/batman-adv/hash.h
Normal file
@ -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
|
179
drivers/staging/batman-adv/log.c
Normal file
179
drivers/staging/batman-adv/log.c
Normal file
@ -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;
|
||||
}
|
32
drivers/staging/batman-adv/log.h
Normal file
32
drivers/staging/batman-adv/log.h
Normal file
@ -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);
|
286
drivers/staging/batman-adv/main.c
Normal file
286
drivers/staging/batman-adv/main.c
Normal file
@ -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
|
151
drivers/staging/batman-adv/main.h
Normal file
151
drivers/staging/batman-adv/main.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* Kernel Programming */
|
||||
#define LINUX
|
||||
|
||||
#define DRIVER_AUTHOR "Marek Lindner <lindner_marek@yahoo.de>, Simon Wunderlich <siwu@hrz.tu-chemnitz.de>"
|
||||
#define DRIVER_DESC "B.A.T.M.A.N. advanced"
|
||||
#define DRIVER_DEVICE "batman-adv"
|
||||
|
||||
#define SOURCE_VERSION "0.2.1-beta"
|
||||
|
||||
|
||||
/* B.A.T.M.A.N. parameters */
|
||||
|
||||
#define TQ_MAX_VALUE 255
|
||||
#define JITTER 20
|
||||
#define TTL 50 /* Time To Live of broadcast messages */
|
||||
#define MAX_ADDR 16 /* number of interfaces which can be added to
|
||||
* batman. */
|
||||
|
||||
#define PURGE_TIMEOUT 200000 /* purge originators after time in ms if no
|
||||
* valid packet comes in -> TODO: check
|
||||
* influence on TQ_LOCAL_WINDOW_SIZE */
|
||||
#define LOCAL_HNA_TIMEOUT 3600000
|
||||
|
||||
#define TQ_LOCAL_WINDOW_SIZE 64 /* sliding packet range of received originator
|
||||
* messages in squence numbers (should be a
|
||||
* multiple of our word size) */
|
||||
#define TQ_GLOBAL_WINDOW_SIZE 5
|
||||
#define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1
|
||||
#define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1
|
||||
#define TQ_TOTAL_BIDRECT_LIMIT 1
|
||||
|
||||
#define TQ_HOP_PENALTY 10
|
||||
|
||||
#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE)
|
||||
|
||||
#define PACKBUFF_SIZE 2000
|
||||
#define LOG_BUF_LEN 8192 /* has to be a power of 2 */
|
||||
#define ETH_STR_LEN 20
|
||||
|
||||
#define MAX_AGGREGATION_BYTES 512 /* should not be bigger than 512 bytes or
|
||||
* change the size of
|
||||
* forw_packet->direct_link_flags */
|
||||
#define MAX_AGGREGATION_MS 100
|
||||
|
||||
#define MODULE_INACTIVE 0
|
||||
#define MODULE_ACTIVE 1
|
||||
#define MODULE_DEACTIVATING 2
|
||||
|
||||
|
||||
/*
|
||||
* Logging
|
||||
*/
|
||||
|
||||
#define LOG_TYPE_CRIT 0 /* highest priority for fatal errors such as
|
||||
* blocked sockets / failed packet delivery /
|
||||
* programming errors */
|
||||
#define LOG_TYPE_WARN 1 /* warnings for small errors like wrong user
|
||||
* input / damaged packets / etc */
|
||||
#define LOG_TYPE_NOTICE 2 /* notice information for new interfaces /
|
||||
* changed settings / new originators / etc */
|
||||
#define LOG_TYPE_BATMAN 4 /* all messages related to routing / flooding /
|
||||
* broadcasting / etc */
|
||||
#define LOG_TYPE_ROUTES 8 /* route or hna added / changed / deleted */
|
||||
#define LOG_TYPE_CRIT_NAME "critical"
|
||||
#define LOG_TYPE_WARN_NAME "warnings"
|
||||
#define LOG_TYPE_NOTICE_NAME "notices"
|
||||
#define LOG_TYPE_BATMAN_NAME "batman"
|
||||
#define LOG_TYPE_ROUTES_NAME "routes"
|
||||
|
||||
/*
|
||||
* Vis
|
||||
*/
|
||||
|
||||
/* #define VIS_SUBCLUSTERS_DISABLED */
|
||||
|
||||
/*
|
||||
* Kernel headers
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h> /* mutex */
|
||||
#include <linux/module.h> /* needed by all modules */
|
||||
#include <linux/netdevice.h> /* netdevice */
|
||||
#include <linux/if_ether.h> /* ethernet header */
|
||||
#include <linux/poll.h> /* poll_table */
|
||||
#include <linux/kthread.h> /* kernel threads */
|
||||
#include <linux/pkt_sched.h> /* schedule types */
|
||||
#include <linux/workqueue.h> /* workqueue */
|
||||
#include <net/sock.h> /* struct sock */
|
||||
#include <linux/jiffies.h>
|
||||
#include "types.h"
|
||||
|
||||
#ifndef REVISION_VERSION
|
||||
#define REVISION_VERSION_STR ""
|
||||
#else
|
||||
#define REVISION_VERSION_STR " "REVISION_VERSION
|
||||
#endif
|
||||
|
||||
extern struct list_head if_list;
|
||||
extern struct hlist_head forw_bat_list;
|
||||
extern struct hlist_head forw_bcast_list;
|
||||
extern struct hashtable_t *orig_hash;
|
||||
|
||||
extern spinlock_t orig_hash_lock;
|
||||
extern spinlock_t forw_bat_list_lock;
|
||||
extern spinlock_t forw_bcast_list_lock;
|
||||
|
||||
extern atomic_t originator_interval;
|
||||
extern atomic_t vis_interval;
|
||||
extern atomic_t aggregation_enabled;
|
||||
extern int16_t num_hna;
|
||||
extern int16_t num_ifs;
|
||||
|
||||
extern struct net_device *soft_device;
|
||||
|
||||
extern unsigned char broadcastAddr[];
|
||||
extern atomic_t module_state;
|
||||
extern struct workqueue_struct *bat_event_workqueue;
|
||||
|
||||
void activate_module(void);
|
||||
void shutdown_module(void);
|
||||
void inc_module_count(void);
|
||||
void dec_module_count(void);
|
||||
int addr_to_string(char *buff, uint8_t *addr);
|
||||
int compare_orig(void *data1, void *data2);
|
||||
int choose_orig(void *data, int32_t size);
|
||||
int is_my_mac(uint8_t *addr);
|
||||
int is_bcast(uint8_t *addr);
|
||||
int is_mcast(uint8_t *addr);
|
||||
|
||||
|
96
drivers/staging/batman-adv/packet.h
Normal file
96
drivers/staging/batman-adv/packet.h
Normal file
@ -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));
|
950
drivers/staging/batman-adv/proc.c
Normal file
950
drivers/staging/batman-adv/proc.c
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
49
drivers/staging/batman-adv/proc.h
Normal file
49
drivers/staging/batman-adv/proc.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#define PROC_ROOT_DIR "batman-adv"
|
||||
#define PROC_FILE_INTERFACES "interfaces"
|
||||
#define PROC_FILE_ORIG_INTERVAL "orig_interval"
|
||||
#define PROC_FILE_ORIGINATORS "originators"
|
||||
#define PROC_FILE_GATEWAYS "gateways"
|
||||
#define PROC_FILE_LOG "log"
|
||||
#define PROC_FILE_LOG_LEVEL "log_level"
|
||||
#define PROC_FILE_TRANST_LOCAL "transtable_local"
|
||||
#define PROC_FILE_TRANST_GLOBAL "transtable_global"
|
||||
#define PROC_FILE_VIS "vis"
|
||||
#define PROC_FILE_VIS_FORMAT "vis_format"
|
||||
#define PROC_FILE_AGGR "aggregate_ogm"
|
||||
|
||||
void cleanup_procfs(void);
|
||||
int setup_procfs(void);
|
||||
|
||||
/* While scanning for vis-entries of a particular vis-originator
|
||||
* this list collects its interfaces to create a subgraph/cluster
|
||||
* out of them later
|
||||
*/
|
||||
struct vis_if_list {
|
||||
uint8_t addr[ETH_ALEN];
|
||||
bool primary;
|
||||
struct vis_if_list *next;
|
||||
};
|
52
drivers/staging/batman-adv/ring_buffer.c
Normal file
52
drivers/staging/batman-adv/ring_buffer.c
Normal file
@ -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);
|
||||
}
|
23
drivers/staging/batman-adv/ring_buffer.h
Normal file
23
drivers/staging/batman-adv/ring_buffer.h
Normal file
@ -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[]);
|
1010
drivers/staging/batman-adv/routing.c
Normal file
1010
drivers/staging/batman-adv/routing.c
Normal file
File diff suppressed because it is too large
Load Diff
34
drivers/staging/batman-adv/routing.h
Normal file
34
drivers/staging/batman-adv/routing.h
Normal file
@ -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);
|
473
drivers/staging/batman-adv/send.c
Normal file
473
drivers/staging/batman-adv/send.c
Normal file
@ -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);
|
||||
}
|
36
drivers/staging/batman-adv/send.h
Normal file
36
drivers/staging/batman-adv/send.h
Normal file
@ -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);
|
349
drivers/staging/batman-adv/soft-interface.c
Normal file
349
drivers/staging/batman-adv/soft-interface.c
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
|
||||
*
|
||||
* Marek Lindner, Simon Wunderlich
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "soft-interface.h"
|
||||
#include "hard-interface.h"
|
||||
#include "send.h"
|
||||
#include "translation-table.h"
|
||||
#include "log.h"
|
||||
#include "types.h"
|
||||
#include "hash.h"
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include "compat.h"
|
||||
|
||||
static uint16_t bcast_seqno = 1; /* give own bcast messages seq numbers to avoid
|
||||
* broadcast storms */
|
||||
static int32_t skb_packets;
|
||||
static int32_t skb_bad_packets;
|
||||
static int32_t lock_dropped;
|
||||
|
||||
unsigned char mainIfAddr[ETH_ALEN];
|
||||
static unsigned char mainIfAddr_default[ETH_ALEN];
|
||||
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
|
||||
static void bat_get_drvinfo(struct net_device *dev,
|
||||
struct ethtool_drvinfo *info);
|
||||
static u32 bat_get_msglevel(struct net_device *dev);
|
||||
static void bat_set_msglevel(struct net_device *dev, u32 value);
|
||||
static u32 bat_get_link(struct net_device *dev);
|
||||
static u32 bat_get_rx_csum(struct net_device *dev);
|
||||
static int bat_set_rx_csum(struct net_device *dev, u32 data);
|
||||
|
||||
static const struct ethtool_ops bat_ethtool_ops = {
|
||||
.get_settings = bat_get_settings,
|
||||
.get_drvinfo = bat_get_drvinfo,
|
||||
.get_msglevel = bat_get_msglevel,
|
||||
.set_msglevel = bat_set_msglevel,
|
||||
.get_link = bat_get_link,
|
||||
.get_rx_csum = bat_get_rx_csum,
|
||||
.set_rx_csum = bat_set_rx_csum
|
||||
};
|
||||
|
||||
void set_main_if_addr(uint8_t *addr)
|
||||
{
|
||||
memcpy(mainIfAddr, addr, ETH_ALEN);
|
||||
}
|
||||
|
||||
int main_if_was_up(void)
|
||||
{
|
||||
return (memcmp(mainIfAddr, mainIfAddr_default, ETH_ALEN) != 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
static int my_skb_push(struct sk_buff *skb, unsigned int len)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
skb_packets++;
|
||||
if (skb->data - len < skb->head) {
|
||||
skb_bad_packets++;
|
||||
result = pskb_expand_head(skb, len, 0, GFP_ATOMIC);
|
||||
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
skb_push(skb, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_NET_DEVICE_OPS
|
||||
static const struct net_device_ops bat_netdev_ops = {
|
||||
.ndo_open = interface_open,
|
||||
.ndo_stop = interface_release,
|
||||
.ndo_get_stats = interface_stats,
|
||||
.ndo_set_mac_address = interface_set_mac_addr,
|
||||
.ndo_change_mtu = interface_change_mtu,
|
||||
.ndo_start_xmit = interface_tx,
|
||||
.ndo_validate_addr = eth_validate_addr
|
||||
};
|
||||
#endif
|
||||
|
||||
void interface_setup(struct net_device *dev)
|
||||
{
|
||||
struct bat_priv *priv = netdev_priv(dev);
|
||||
char dev_addr[ETH_ALEN];
|
||||
|
||||
ether_setup(dev);
|
||||
|
||||
#ifdef HAVE_NET_DEVICE_OPS
|
||||
dev->netdev_ops = &bat_netdev_ops;
|
||||
#else
|
||||
dev->open = interface_open;
|
||||
dev->stop = interface_release;
|
||||
dev->get_stats = interface_stats;
|
||||
dev->set_mac_address = interface_set_mac_addr;
|
||||
dev->change_mtu = interface_change_mtu;
|
||||
dev->hard_start_xmit = interface_tx;
|
||||
#endif
|
||||
dev->destructor = free_netdev;
|
||||
|
||||
dev->mtu = hardif_min_mtu();
|
||||
dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the
|
||||
* skbuff for our header */
|
||||
|
||||
/* generate random address */
|
||||
random_ether_addr(dev_addr);
|
||||
memcpy(dev->dev_addr, dev_addr, sizeof(dev->dev_addr));
|
||||
|
||||
SET_ETHTOOL_OPS(dev, &bat_ethtool_ops);
|
||||
|
||||
memset(priv, 0, sizeof(struct bat_priv));
|
||||
}
|
||||
|
||||
int interface_open(struct net_device *dev)
|
||||
{
|
||||
netif_start_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int interface_release(struct net_device *dev)
|
||||
{
|
||||
netif_stop_queue(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct net_device_stats *interface_stats(struct net_device *dev)
|
||||
{
|
||||
struct bat_priv *priv = netdev_priv(dev);
|
||||
return &priv->stats;
|
||||
}
|
||||
|
||||
int interface_set_mac_addr(struct net_device *dev, void *addr)
|
||||
{
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
int interface_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
/* check ranges */
|
||||
if ((new_mtu < 68) || (new_mtu > hardif_min_mtu()))
|
||||
return -EINVAL;
|
||||
|
||||
dev->mtu = new_mtu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int interface_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct unicast_packet *unicast_packet;
|
||||
struct bcast_packet *bcast_packet;
|
||||
struct orig_node *orig_node;
|
||||
struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
|
||||
struct bat_priv *priv = netdev_priv(dev);
|
||||
int data_len = skb->len;
|
||||
|
||||
if (atomic_read(&module_state) != MODULE_ACTIVE)
|
||||
goto dropped;
|
||||
|
||||
dev->trans_start = jiffies;
|
||||
/* TODO: check this for locks */
|
||||
hna_local_add(ethhdr->h_source);
|
||||
|
||||
/* ethernet packet should be broadcasted */
|
||||
if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) {
|
||||
|
||||
if (my_skb_push(skb, sizeof(struct bcast_packet)) < 0)
|
||||
goto dropped;
|
||||
|
||||
bcast_packet = (struct bcast_packet *)skb->data;
|
||||
|
||||
bcast_packet->version = COMPAT_VERSION;
|
||||
|
||||
/* batman packet type: broadcast */
|
||||
bcast_packet->packet_type = BAT_BCAST;
|
||||
|
||||
/* hw address of first interface is the orig mac because only
|
||||
* this mac is known throughout the mesh */
|
||||
memcpy(bcast_packet->orig, mainIfAddr, ETH_ALEN);
|
||||
/* set broadcast sequence number */
|
||||
bcast_packet->seqno = htons(bcast_seqno);
|
||||
|
||||
bcast_seqno++;
|
||||
|
||||
/* broadcast packet */
|
||||
add_bcast_packet_to_list(skb->data, skb->len);
|
||||
|
||||
/* unicast packet */
|
||||
} else {
|
||||
|
||||
/* simply spin_lock()ing can deadlock when the lock is already
|
||||
* hold. */
|
||||
/* TODO: defer the work in a working queue instead of
|
||||
* dropping */
|
||||
if (!spin_trylock(&orig_hash_lock)) {
|
||||
lock_dropped++;
|
||||
debug_log(LOG_TYPE_NOTICE, "%d packets dropped because lock was hold\n", lock_dropped);
|
||||
goto dropped;
|
||||
}
|
||||
|
||||
/* get routing information */
|
||||
orig_node = ((struct orig_node *)hash_find(orig_hash,
|
||||
ethhdr->h_dest));
|
||||
|
||||
/* check for hna host */
|
||||
if (!orig_node)
|
||||
orig_node = transtable_search(ethhdr->h_dest);
|
||||
|
||||
if ((orig_node) &&
|
||||
(orig_node->batman_if) &&
|
||||
(orig_node->router)) {
|
||||
if (my_skb_push(skb, sizeof(struct unicast_packet)) < 0)
|
||||
goto unlock;
|
||||
|
||||
unicast_packet = (struct unicast_packet *)skb->data;
|
||||
|
||||
unicast_packet->version = COMPAT_VERSION;
|
||||
/* batman packet type: unicast */
|
||||
unicast_packet->packet_type = BAT_UNICAST;
|
||||
/* set unicast ttl */
|
||||
unicast_packet->ttl = TTL;
|
||||
/* copy the destination for faster routing */
|
||||
memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
|
||||
|
||||
/* net_dev won't be available when not active */
|
||||
if (orig_node->batman_if->if_active != IF_ACTIVE)
|
||||
goto unlock;
|
||||
|
||||
send_raw_packet(skb->data, skb->len,
|
||||
orig_node->batman_if,
|
||||
orig_node->router->addr);
|
||||
} else {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
spin_unlock(&orig_hash_lock);
|
||||
}
|
||||
|
||||
priv->stats.tx_packets++;
|
||||
priv->stats.tx_bytes += data_len;
|
||||
goto end;
|
||||
|
||||
unlock:
|
||||
spin_unlock(&orig_hash_lock);
|
||||
dropped:
|
||||
priv->stats.tx_dropped++;
|
||||
end:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void interface_rx(struct net_device *dev, void *packet, int packet_len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct bat_priv *priv = netdev_priv(dev);
|
||||
|
||||
skb = dev_alloc_skb(packet_len);
|
||||
|
||||
if (!skb) {
|
||||
priv->stats.rx_dropped++;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(skb_put(skb, packet_len), packet, packet_len);
|
||||
|
||||
/* Write metadata, and then pass to the receive level */
|
||||
skb->dev = dev;
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
priv->stats.rx_packets++;
|
||||
priv->stats.rx_bytes += packet_len;
|
||||
|
||||
dev->last_rx = jiffies;
|
||||
|
||||
netif_rx(skb);
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
/* ethtool */
|
||||
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
||||
{
|
||||
cmd->supported = 0;
|
||||
cmd->advertising = 0;
|
||||
cmd->speed = SPEED_10;
|
||||
cmd->duplex = DUPLEX_FULL;
|
||||
cmd->port = PORT_TP;
|
||||
cmd->phy_address = 0;
|
||||
cmd->transceiver = XCVR_INTERNAL;
|
||||
cmd->autoneg = AUTONEG_DISABLE;
|
||||
cmd->maxtxpkt = 0;
|
||||
cmd->maxrxpkt = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bat_get_drvinfo(struct net_device *dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
strcpy(info->driver, "B.A.T.M.A.N. advanced");
|
||||
strcpy(info->version, SOURCE_VERSION);
|
||||
strcpy(info->fw_version, "N/A");
|
||||
strcpy(info->bus_info, "batman");
|
||||
}
|
||||
|
||||
static u32 bat_get_msglevel(struct net_device *dev)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static void bat_set_msglevel(struct net_device *dev, u32 value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static u32 bat_get_link(struct net_device *dev)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 bat_get_rx_csum(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bat_set_rx_csum(struct net_device *dev, u32 data)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
33
drivers/staging/batman-adv/soft-interface.h
Normal file
33
drivers/staging/batman-adv/soft-interface.h
Normal file
@ -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[];
|
454
drivers/staging/batman-adv/translation-table.c
Normal file
454
drivers/staging/batman-adv/translation-table.c
Normal file
@ -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;
|
||||
}
|
42
drivers/staging/batman-adv/translation-table.h
Normal file
42
drivers/staging/batman-adv/translation-table.h
Normal file
@ -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;
|
124
drivers/staging/batman-adv/types.h
Normal file
124
drivers/staging/batman-adv/types.h
Normal file
@ -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
|
564
drivers/staging/batman-adv/vis.c
Normal file
564
drivers/staging/batman-adv/vis.c
Normal file
@ -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);
|
||||
}
|
||||
|
63
drivers/staging/batman-adv/vis.h
Normal file
63
drivers/staging/batman-adv/vis.h
Normal file
@ -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);
|
Loading…
Reference in New Issue
Block a user