Commit 5beef3c9 authored by Andrew Lunn's avatar Andrew Lunn Committed by Greg Kroah-Hartman

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: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6638db58
......@@ -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/
......
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
#
# 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.
#
# 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
[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>
=> 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>
/*
* 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);
}
}
/*
* 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);
/*
* 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;
}
/*
* 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);
/*
* 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) */
/*
* 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);
}
/*
* 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);
/*
* 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,
};
/*
* 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);
/*
* 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;
}
/*
* 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
/*
* 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;
}
/*
* 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);
/*
* 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
/*
* 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);
/*
* 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));
/*
* 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;
}
/*
* 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;
};
/*
* 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);
}
/*
* 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[]);
/*
* Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
*
* Marek Lindner, Simon Wunderlich
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*
*/
#include "main.h"
#include "routing.h"
#include "log.h"
#include "send.h"
#include "soft-interface.h"
#include "hard-interface.h"
#include "device.h"
#include "translation-table.h"
#include "types.h"
#include "hash.h"
#include "ring_buffer.h"
#include "vis.h"
#include "aggregation.h"
#include "compat.h"
DECLARE_WAIT_QUEUE_HEAD(thread_wait);
static DECLARE_DELAYED_WORK(purge_orig_wq, purge_orig);
static atomic_t data_ready_cond;
atomic_t exit_cond;
static void start_purge_timer(void)
{
queue_delayed_work(bat_event_workqueue, &purge_orig_wq, 1 * HZ);
}
int originator_init(void)
{
if (orig_hash)
return 1;
spin_lock(&orig_hash_lock);
orig_hash = hash_new(128, compare_orig, choose_orig);
if (!orig_hash)
goto err;
spin_unlock(&orig_hash_lock);
start_purge_timer();
return 1;
err:
spin_unlock(&orig_hash_lock);
return 0;
}
void originator_free(void)
{
if (!orig_hash)
return;
cancel_delayed_work_sync(&purge_orig_wq);
spin_lock(&orig_hash_lock);
hash_delete(orig_hash, free_orig_node);
orig_hash = NULL;
spin_unlock(&orig_hash_lock);
}
static struct neigh_node *create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, uint8_t *neigh, struct batman_if *if_incoming)
{
struct neigh_node *neigh_node;
debug_log(LOG_TYPE_BATMAN, "Creating new last-hop neighbour of originator\n");
neigh_node = kmalloc(sizeof(struct neigh_node), GFP_ATOMIC);
memset(neigh_node, 0, sizeof(struct neigh_node));
INIT_LIST_HEAD(&neigh_node->list);
memcpy(neigh_node->addr, neigh, ETH_ALEN);
neigh_node->orig_node = orig_neigh_node;
neigh_node->if_incoming = if_incoming;
list_add_tail(&neigh_node->list, &orig_node->neigh_list);
return neigh_node;
}
void free_orig_node(void *data)
{
struct list_head *list_pos, *list_pos_tmp;
struct neigh_node *neigh_node;
struct orig_node *orig_node = (struct orig_node *)data;
/* for all neighbours towards this originator ... */
list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) {
neigh_node = list_entry(list_pos, struct neigh_node, list);
list_del(list_pos);
kfree(neigh_node);
}
hna_global_del_orig(orig_node, "originator timed out");
kfree(orig_node->bcast_own);
kfree(orig_node->bcast_own_sum);
kfree(orig_node);
}
/* this function finds or creates an originator entry for the given address if it does not exits */
static struct orig_node *get_orig_node(uint8_t *addr)
{
struct orig_node *orig_node;
struct hashtable_t *swaphash;
char orig_str[ETH_STR_LEN];
orig_node = ((struct orig_node *)hash_find(orig_hash, addr));
if (orig_node != NULL)
return orig_node;
addr_to_string(orig_str, addr);
debug_log(LOG_TYPE_BATMAN, "Creating new originator: %s \n", orig_str);
orig_node = kmalloc(sizeof(struct orig_node), GFP_ATOMIC);
memset(orig_node, 0, sizeof(struct orig_node));
INIT_LIST_HEAD(&orig_node->neigh_list);
memcpy(orig_node->orig, addr, ETH_ALEN);
orig_node->router = NULL;
orig_node->batman_if = NULL;
orig_node->hna_buff = NULL;
orig_node->bcast_own = kmalloc(num_ifs * sizeof(TYPE_OF_WORD) * NUM_WORDS, GFP_ATOMIC);
memset(orig_node->bcast_own, 0, num_ifs * sizeof(TYPE_OF_WORD) * NUM_WORDS);
orig_node->bcast_own_sum = kmalloc(num_ifs * sizeof(uint8_t), GFP_ATOMIC);
memset(orig_node->bcast_own_sum, 0, num_ifs * sizeof(uint8_t));
hash_add(orig_hash, orig_node);
if (orig_hash->elements * 4 > orig_hash->size) {
swaphash = hash_resize(orig_hash, orig_hash->size * 2);
if (swaphash == NULL)
debug_log(LOG_TYPE_CRIT, "Couldn't resize orig hash table \n");
else
orig_hash = swaphash;
}
return orig_node;
}
void slide_own_bcast_window(struct batman_if *batman_if)
{
struct hash_it_t *hashit = NULL;
struct orig_node *orig_node;
spin_lock(&orig_hash_lock);
while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
orig_node = hashit->bucket->data;
bit_get_packet((TYPE_OF_WORD *)&(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]), 1, 0);
orig_node->bcast_own_sum[batman_if->if_num] = bit_packet_count((TYPE_OF_WORD *)&(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]));
}
spin_unlock(&orig_hash_lock);
}
static void update_routes(struct orig_node *orig_node, struct neigh_node *neigh_node, unsigned char *hna_buff, int hna_buff_len)
{
char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN], router_str[ETH_STR_LEN];
if (orig_node == NULL)
return;
if (orig_node->router != neigh_node) {
addr_to_string(orig_str, orig_node->orig);
/* route deleted */
if ((orig_node->router != NULL) && (neigh_node == NULL)) {
debug_log(LOG_TYPE_ROUTES, "Deleting route towards: %s\n", orig_str);
hna_global_del_orig(orig_node, "originator timed out");
/* route added */
} else if ((orig_node->router == NULL) && (neigh_node != NULL)) {
addr_to_string(neigh_str, neigh_node->addr);
debug_log(LOG_TYPE_ROUTES, "Adding route towards: %s (via %s)\n", orig_str, neigh_str);
hna_global_add_orig(orig_node, hna_buff, hna_buff_len);
/* route changed */
} else {
addr_to_string(neigh_str, neigh_node->addr);
addr_to_string(router_str, orig_node->router->addr);
debug_log(LOG_TYPE_ROUTES, "Changing route towards: %s (now via %s - was via %s)\n", orig_str, neigh_str, router_str);
}
if (neigh_node != NULL)
orig_node->batman_if = neigh_node->if_incoming;
else
orig_node->batman_if = NULL;
orig_node->router = neigh_node;
/* may be just HNA changed */
} else {
if ((hna_buff_len != orig_node->hna_buff_len) || ((hna_buff_len > 0) && (orig_node->hna_buff_len > 0) && (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) {
if (orig_node->hna_buff_len > 0)
hna_global_del_orig(orig_node, "originator changed hna");
if ((hna_buff_len > 0) && (hna_buff != NULL))
hna_global_add_orig(orig_node, hna_buff, hna_buff_len);
}
}
}
static int isBidirectionalNeigh(struct orig_node *orig_node, struct orig_node *orig_neigh_node, struct batman_packet *batman_packet, struct batman_if *if_incoming)
{
struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN];
unsigned char total_count;
addr_to_string(orig_str, orig_node->orig);
addr_to_string(neigh_str, orig_neigh_node->orig);
if (orig_node == orig_neigh_node) {
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
if (compare_orig(tmp_neigh_node->addr, orig_neigh_node->orig) && (tmp_neigh_node->if_incoming == if_incoming))
neigh_node = tmp_neigh_node;
}
if (neigh_node == NULL)
neigh_node = create_neighbor(orig_node, orig_neigh_node, orig_neigh_node->orig, if_incoming);
neigh_node->last_valid = jiffies;
} else {
/* find packet count of corresponding one hop neighbor */
list_for_each_entry(tmp_neigh_node, &orig_neigh_node->neigh_list, list) {
if (compare_orig(tmp_neigh_node->addr, orig_neigh_node->orig) && (tmp_neigh_node->if_incoming == if_incoming))
neigh_node = tmp_neigh_node;
}
if (neigh_node == NULL)
neigh_node = create_neighbor(orig_neigh_node, orig_neigh_node, orig_neigh_node->orig, if_incoming);
}
orig_node->last_valid = jiffies;
/* pay attention to not get a value bigger than 100 % */
total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > neigh_node->real_packet_count ? neigh_node->real_packet_count : orig_neigh_node->bcast_own_sum[if_incoming->if_num]);
/* if we have too few packets (too less data) we set tq_own to zero */
/* if we receive too few packets it is not considered bidirectional */
if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM))
orig_neigh_node->tq_own = 0;
else
/* neigh_node->real_packet_count is never zero as we only purge old information when getting new information */
orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / neigh_node->real_packet_count;
/*
* 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE
* this does affect the nearly-symmetric links only a little,
* but punishes asymmetric links more.
* this will give a value between 0 and TQ_MAX_VALUE
*/
orig_neigh_node->tq_asym_penalty = TQ_MAX_VALUE - (TQ_MAX_VALUE *
(TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) *
(TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) *
(TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) /
(TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE);
batman_packet->tq = ((batman_packet->tq * orig_neigh_node->tq_own * orig_neigh_node->tq_asym_penalty) / (TQ_MAX_VALUE * TQ_MAX_VALUE));
debug_log(LOG_TYPE_BATMAN, "bidirectional: orig = %-15s neigh = %-15s => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, total tq: %3i \n",
orig_str, neigh_str, total_count, neigh_node->real_packet_count, orig_neigh_node->tq_own, orig_neigh_node->tq_asym_penalty, batman_packet->tq);
/* if link has the minimum required transmission quality consider it bidirectional */
if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT)
return 1;
return 0;
}
static void update_orig(struct orig_node *orig_node, struct ethhdr *ethhdr, struct batman_packet *batman_packet, struct batman_if *if_incoming, unsigned char *hna_buff, int hna_buff_len, char is_duplicate)
{
struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
int tmp_hna_buff_len;
debug_log(LOG_TYPE_BATMAN, "update_originator(): Searching and updating originator entry of received packet \n");
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming)) {
neigh_node = tmp_neigh_node;
continue;
}
if (is_duplicate)
continue;
ring_buffer_set(tmp_neigh_node->tq_recv, &tmp_neigh_node->tq_index, 0);
tmp_neigh_node->tq_avg = ring_buffer_avg(tmp_neigh_node->tq_recv);
}
if (neigh_node == NULL)
neigh_node = create_neighbor(orig_node, get_orig_node(ethhdr->h_source), ethhdr->h_source, if_incoming);
else
debug_log(LOG_TYPE_BATMAN, "Updating existing last-hop neighbour of originator\n");
orig_node->flags = batman_packet->flags;
neigh_node->last_valid = jiffies;
ring_buffer_set(neigh_node->tq_recv, &neigh_node->tq_index, batman_packet->tq);
neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv);
if (!is_duplicate) {
orig_node->last_ttl = batman_packet->ttl;
neigh_node->last_ttl = batman_packet->ttl;
}
tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? batman_packet->num_hna * ETH_ALEN : hna_buff_len);
/* if this neighbor already is our next hop there is nothing to change */
if (orig_node->router == neigh_node)
goto update_hna;
/* if this neighbor does not offer a better TQ we won't consider it */
if ((orig_node->router) &&
(orig_node->router->tq_avg > neigh_node->tq_avg))
goto update_hna;
/* if the TQ is the same and the link not more symetric we won't consider it either */
if ((orig_node->router) &&
((neigh_node->tq_avg == orig_node->router->tq_avg) &&
(orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] >=
neigh_node->orig_node->bcast_own_sum[if_incoming->if_num])))
goto update_hna;
update_routes(orig_node, neigh_node, hna_buff, tmp_hna_buff_len);
return;
update_hna:
update_routes(orig_node, orig_node->router, hna_buff, tmp_hna_buff_len);
return;
}
static char count_real_packets(struct ethhdr *ethhdr, struct batman_packet *batman_packet, struct batman_if *if_incoming)
{
struct orig_node *orig_node;
struct neigh_node *tmp_neigh_node;
char is_duplicate = 0;
orig_node = get_orig_node(batman_packet->orig);
if (orig_node == NULL)
return 0;
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
if (!is_duplicate)
is_duplicate = get_bit_status(tmp_neigh_node->real_bits, orig_node->last_real_seqno, batman_packet->seqno);
if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming))
bit_get_packet(tmp_neigh_node->real_bits, batman_packet->seqno - orig_node->last_real_seqno, 1);
else
bit_get_packet(tmp_neigh_node->real_bits, batman_packet->seqno - orig_node->last_real_seqno, 0);
tmp_neigh_node->real_packet_count = bit_packet_count(tmp_neigh_node->real_bits);
}
if (!is_duplicate) {
debug_log(LOG_TYPE_BATMAN, "updating last_seqno: old %d, new %d \n", orig_node->last_real_seqno, batman_packet->seqno);
orig_node->last_real_seqno = batman_packet->seqno;
}
return is_duplicate;
}
void receive_bat_packet(struct ethhdr *ethhdr, struct batman_packet *batman_packet, unsigned char *hna_buff, int hna_buff_len, struct batman_if *if_incoming)
{
struct batman_if *batman_if;
struct orig_node *orig_neigh_node, *orig_node;
char orig_str[ETH_STR_LEN], prev_sender_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN];
char has_directlink_flag;
char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0, is_broadcast = 0, is_bidirectional, is_single_hop_neigh, is_duplicate;
unsigned short if_incoming_seqno;
/* Silently drop when the batman packet is actually not a correct packet.
*
* This might happen if a packet is padded (e.g. Ethernet has a
* minimum frame length of 64 byte) and the aggregation interprets
* it as an additional length.
*
* TODO: A more sane solution would be to have a bit in the batman_packet
* to detect whether the packet is the last packet in an aggregation.
* Here we expect that the padding is always zero (or not 0x01)
*/
if (batman_packet->packet_type != BAT_PACKET)
return;
/* could be changed by schedule_own_packet() */
if_incoming_seqno = atomic_read(&if_incoming->seqno);
addr_to_string(orig_str, batman_packet->orig);
addr_to_string(prev_sender_str, batman_packet->prev_sender);
addr_to_string(neigh_str, ethhdr->h_source);
has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0);
is_single_hop_neigh = (compare_orig(ethhdr->h_source, batman_packet->orig) ? 1 : 0);
debug_log(LOG_TYPE_BATMAN, "Received BATMAN packet via NB: %s, IF: %s [%s] (from OG: %s, via prev OG: %s, seqno %d, tq %d, TTL %d, V %d, IDF %d) \n", neigh_str, if_incoming->dev, if_incoming->addr_str, orig_str, prev_sender_str, batman_packet->seqno, batman_packet->tq, batman_packet->ttl, batman_packet->version, has_directlink_flag);
list_for_each_entry_rcu(batman_if, &if_list, list) {
if (batman_if->if_active != IF_ACTIVE)
continue;
if (compare_orig(ethhdr->h_source, batman_if->net_dev->dev_addr))
is_my_addr = 1;
if (compare_orig(batman_packet->orig, batman_if->net_dev->dev_addr))
is_my_orig = 1;
if (compare_orig(batman_packet->prev_sender, batman_if->net_dev->dev_addr))
is_my_oldorig = 1;
if (compare_orig(ethhdr->h_source, broadcastAddr))
is_broadcast = 1;
}
if (batman_packet->version != COMPAT_VERSION) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: incompatible batman version (%i) \n", batman_packet->version);
return;
}
if (is_my_addr) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: received my own broadcast (sender: %s) \n", neigh_str);
return;
}
if (is_broadcast) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all packets with broadcast source addr (sender: %s) \n", neigh_str);
return;
}
if (is_my_orig) {
orig_neigh_node = get_orig_node(ethhdr->h_source);
/* neighbour has to indicate direct link and it has to come via the corresponding interface */
/* if received seqno equals last send seqno save new seqno for bidirectional check */
if (has_directlink_flag && compare_orig(if_incoming->net_dev->dev_addr, batman_packet->orig) &&
(batman_packet->seqno - if_incoming_seqno + 2 == 0)) {
bit_mark((TYPE_OF_WORD *)&(orig_neigh_node->bcast_own[if_incoming->if_num * NUM_WORDS]), 0);
orig_neigh_node->bcast_own_sum[if_incoming->if_num] = bit_packet_count((TYPE_OF_WORD *)&(orig_neigh_node->bcast_own[if_incoming->if_num * NUM_WORDS]));
}
debug_log(LOG_TYPE_BATMAN, "Drop packet: originator packet from myself (via neighbour) \n");
return;
}
if (batman_packet->tq == 0) {
count_real_packets(ethhdr, batman_packet, if_incoming);
debug_log(LOG_TYPE_BATMAN, "Drop packet: originator packet with tq equal 0 \n");
return;
}
if (is_my_oldorig) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all rebroadcast echos (sender: %s) \n", neigh_str);
return;
}
is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming);
orig_node = get_orig_node(batman_packet->orig);
if (orig_node == NULL)
return;
/* avoid temporary routing loops */
if ((orig_node->router) && (orig_node->router->orig_node->router) &&
(compare_orig(orig_node->router->addr, batman_packet->prev_sender)) &&
!(compare_orig(batman_packet->orig, batman_packet->prev_sender)) &&
(compare_orig(orig_node->router->addr, orig_node->router->orig_node->router->addr))) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %s) \n", neigh_str);
return;
}
/* if sender is a direct neighbor the sender mac equals originator mac */
orig_neigh_node = (is_single_hop_neigh ? orig_node : get_orig_node(ethhdr->h_source));
if (orig_neigh_node == NULL)
return;
/* drop packet if sender is not a direct neighbor and if we don't route towards it */
if (!is_single_hop_neigh && (orig_neigh_node->router == NULL)) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: OGM via unknown neighbor! \n");
return;
}
is_bidirectional = isBidirectionalNeigh(orig_node, orig_neigh_node, batman_packet, if_incoming);
/* update ranking if it is not a duplicate or has the same seqno and similar ttl as the non-duplicate */
if (is_bidirectional && (!is_duplicate ||
((orig_node->last_real_seqno == batman_packet->seqno) &&
(orig_node->last_ttl - 3 <= batman_packet->ttl))))
update_orig(orig_node, ethhdr, batman_packet, if_incoming, hna_buff, hna_buff_len, is_duplicate);
/* is single hop (direct) neighbour */
if (is_single_hop_neigh) {
/* mark direct link on incoming interface */
schedule_forward_packet(orig_node, ethhdr, batman_packet, 1, hna_buff_len, if_incoming);
debug_log(LOG_TYPE_BATMAN, "Forwarding packet: rebroadcast neighbour packet with direct link flag \n");
return;
}
/* multihop originator */
if (!is_bidirectional) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: not received via bidirectional link\n");
return;
}
if (is_duplicate) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: duplicate packet received\n");
return;
}
debug_log(LOG_TYPE_BATMAN, "Forwarding packet: rebroadcast originator packet \n");
schedule_forward_packet(orig_node, ethhdr, batman_packet, 0, hna_buff_len, if_incoming);
}
void purge_orig(struct work_struct *work)
{
struct list_head *list_pos, *list_pos_tmp;
struct hash_it_t *hashit = NULL;
struct orig_node *orig_node;
struct neigh_node *neigh_node, *best_neigh_node;
char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN], neigh_purged;
spin_lock(&orig_hash_lock);
/* for all origins... */
while (NULL != (hashit = hash_iterate(orig_hash, hashit))) {
orig_node = hashit->bucket->data;
addr_to_string(orig_str, orig_node->orig);
if (time_after(jiffies, orig_node->last_valid + ((2 * PURGE_TIMEOUT * HZ) / 1000))) {
debug_log(LOG_TYPE_BATMAN, "Originator timeout: originator %s, last_valid %u \n", orig_str, (orig_node->last_valid / HZ));
hash_remove_bucket(orig_hash, hashit);
free_orig_node(orig_node);
} else {
best_neigh_node = NULL;
neigh_purged = 0;
/* for all neighbours towards this originator ... */
list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) {
neigh_node = list_entry(list_pos, struct neigh_node, list);
if (time_after(jiffies, neigh_node->last_valid + ((PURGE_TIMEOUT * HZ) / 1000))) {
addr_to_string(neigh_str, neigh_node->addr);
debug_log(LOG_TYPE_BATMAN, "Neighbour timeout: originator %s, neighbour: %s, last_valid %u \n", orig_str, neigh_str, (neigh_node->last_valid / HZ));
neigh_purged = 1;
list_del(list_pos);
kfree(neigh_node);
} else {
if ((best_neigh_node == NULL) || (neigh_node->tq_avg > best_neigh_node->tq_avg))
best_neigh_node = neigh_node;
}
}
if (neigh_purged)
update_routes(orig_node, best_neigh_node, orig_node->hna_buff, orig_node->hna_buff_len);
}
}
spin_unlock(&orig_hash_lock);
start_purge_timer();
}
static int receive_raw_packet(struct socket *raw_sock, unsigned char *packet_buff, int packet_buff_len)
{
struct kvec iov;
struct msghdr msg;
iov.iov_base = packet_buff;
iov.iov_len = packet_buff_len;
msg.msg_flags = MSG_DONTWAIT; /* non-blocking */
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
return kernel_recvmsg(raw_sock, &msg, &iov, 1, packet_buff_len, MSG_DONTWAIT);
}
int packet_recv_thread(void *data)
{
struct batman_if *batman_if;
struct ethhdr *ethhdr;
struct batman_packet *batman_packet;
struct unicast_packet *unicast_packet;
struct bcast_packet *bcast_packet;
struct icmp_packet *icmp_packet;
struct vis_packet *vis_packet;
struct orig_node *orig_node;
unsigned char *packet_buff, src_str[ETH_STR_LEN], dst_str[ETH_STR_LEN];
int vis_info_len;
int result;
atomic_set(&data_ready_cond, 0);
atomic_set(&exit_cond, 0);
packet_buff = kmalloc(PACKBUFF_SIZE, GFP_KERNEL);
if (!packet_buff) {
debug_log(LOG_TYPE_CRIT, "Could allocate memory for the packet buffer. :(\n");
return -1;
}
while ((!kthread_should_stop()) && (!atomic_read(&exit_cond))) {
wait_event_interruptible(thread_wait, (atomic_read(&data_ready_cond) || atomic_read(&exit_cond)));
atomic_set(&data_ready_cond, 0);
if (kthread_should_stop() || atomic_read(&exit_cond))
break;
/* we only want to safely traverse the list, hard-interfaces
* won't be deleted anyway as long as this thread runs. */
rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) {
rcu_read_unlock();
result = -1;
while (1) {
if (batman_if->if_active != IF_ACTIVE) {
if (batman_if->if_active != IF_TO_BE_ACTIVATED)
debug_log(LOG_TYPE_NOTICE,
"Could not read from deactivated interface %s!\n",
batman_if->dev);
if (batman_if->raw_sock)
receive_raw_packet(batman_if->raw_sock, packet_buff, PACKBUFF_SIZE);
result = 0;
break;
}
result = receive_raw_packet(batman_if->raw_sock, packet_buff, PACKBUFF_SIZE);
if (result <= 0)
break;
if (result < sizeof(struct ethhdr) + 2)
continue;
ethhdr = (struct ethhdr *)packet_buff;
batman_packet = (struct batman_packet *)(packet_buff + sizeof(struct ethhdr));
if (batman_packet->version != COMPAT_VERSION) {
debug_log(LOG_TYPE_BATMAN, "Drop packet: incompatible batman version (%i) \n", batman_packet->version);
continue;
}
switch (batman_packet->packet_type) {
/* batman originator packet */
case BAT_PACKET:
/* packet with broadcast indication but unicast recipient */
if (!is_bcast(ethhdr->h_dest))
continue;
/* packet with broadcast sender address */
if (is_bcast(ethhdr->h_source))
continue;
/* drop packet if it has not at least one batman packet as payload */
if (result < sizeof(struct ethhdr) + sizeof(struct batman_packet))
continue;
spin_lock(&orig_hash_lock);
receive_aggr_bat_packet(ethhdr,
packet_buff + sizeof(struct ethhdr),
result - sizeof(struct ethhdr),
batman_if);
spin_unlock(&orig_hash_lock);
break;
/* batman icmp packet */
case BAT_ICMP:
/* packet with unicast indication but broadcast recipient */
if (is_bcast(ethhdr->h_dest))
continue;
/* packet with broadcast sender address */
if (is_bcast(ethhdr->h_source))
continue;
/* not for me */
if (!is_my_mac(ethhdr->h_dest))
continue;
/* drop packet if it has not necessary minimum size */
if (result < sizeof(struct ethhdr) + sizeof(struct icmp_packet))
continue;
icmp_packet = (struct icmp_packet *)(packet_buff + sizeof(struct ethhdr));
/* packet for me */
if (is_my_mac(icmp_packet->dst)) {
/* add data to device queue */
if (icmp_packet->msg_type != ECHO_REQUEST) {
bat_device_receive_packet(icmp_packet);
continue;
}
/* answer echo request (ping) */
/* get routing information */
spin_lock(&orig_hash_lock);
orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig));
if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN);
icmp_packet->msg_type = ECHO_REPLY;
icmp_packet->ttl = TTL;
send_raw_packet(packet_buff + sizeof(struct ethhdr),
result - sizeof(struct ethhdr),
orig_node->batman_if,
orig_node->router->addr);
}
spin_unlock(&orig_hash_lock);
continue;
}
/* TTL exceeded */
if (icmp_packet->ttl < 2) {
addr_to_string(src_str, icmp_packet->orig);
addr_to_string(dst_str, icmp_packet->dst);
debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str);
/* send TTL exceeded if packet is an echo request (traceroute) */
if (icmp_packet->msg_type != ECHO_REQUEST)
continue;
/* get routing information */
spin_lock(&orig_hash_lock);
orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig));
if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN);
icmp_packet->msg_type = TTL_EXCEEDED;
icmp_packet->ttl = TTL;
send_raw_packet(packet_buff + sizeof(struct ethhdr),
result - sizeof(struct ethhdr),
orig_node->batman_if,
orig_node->router->addr);
}
spin_unlock(&orig_hash_lock);
continue;
}
/* get routing information */
spin_lock(&orig_hash_lock);
orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->dst));
if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
/* decrement ttl */
icmp_packet->ttl--;
/* route it */
send_raw_packet(packet_buff + sizeof(struct ethhdr),
result - sizeof(struct ethhdr),
orig_node->batman_if,
orig_node->router->addr);
}
spin_unlock(&orig_hash_lock);
break;
/* unicast packet */
case BAT_UNICAST:
/* packet with unicast indication but broadcast recipient */
if (is_bcast(ethhdr->h_dest))
continue;
/* packet with broadcast sender address */
if (is_bcast(ethhdr->h_source))
continue;
/* not for me */
if (!is_my_mac(ethhdr->h_dest))
continue;
/* drop packet if it has not necessary minimum size */
if (result < sizeof(struct ethhdr) + sizeof(struct unicast_packet))
continue;
unicast_packet = (struct unicast_packet *)(packet_buff + sizeof(struct ethhdr));
/* packet for me */
if (is_my_mac(unicast_packet->dest)) {
interface_rx(soft_device, packet_buff + sizeof(struct ethhdr) + sizeof(struct unicast_packet), result - sizeof(struct ethhdr) - sizeof(struct unicast_packet));
continue;
}
/* TTL exceeded */
if (unicast_packet->ttl < 2) {
addr_to_string(src_str, ((struct ethhdr *)(unicast_packet + 1))->h_source);
addr_to_string(dst_str, unicast_packet->dest);
debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str);
continue;
}
/* get routing information */
spin_lock(&orig_hash_lock);
orig_node = ((struct orig_node *)hash_find(orig_hash, unicast_packet->dest));
if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) {
/* decrement ttl */
unicast_packet->ttl--;
/* route it */
send_raw_packet(packet_buff + sizeof(struct ethhdr),
result - sizeof(struct ethhdr),
orig_node->batman_if,
orig_node->router->addr);
}
spin_unlock(&orig_hash_lock);
break;
/* broadcast packet */
case BAT_BCAST:
/* packet with broadcast indication but unicast recipient */
if (!is_bcast(ethhdr->h_dest))
continue;
/* packet with broadcast sender address */
if (is_bcast(ethhdr->h_source))
continue;
/* drop packet if it has not necessary minimum size */
if (result < sizeof(struct ethhdr) + sizeof(struct bcast_packet))
continue;
/* ignore broadcasts sent by myself */
if (is_my_mac(ethhdr->h_source))
continue;
bcast_packet = (struct bcast_packet *)(packet_buff + sizeof(struct ethhdr));
/* ignore broadcasts originated by myself */
if (is_my_mac(bcast_packet->orig))
continue;
spin_lock(&orig_hash_lock);
orig_node = ((struct orig_node *)hash_find(orig_hash, bcast_packet->orig));
if (orig_node == NULL) {
spin_unlock(&orig_hash_lock);
continue;
}
/* check flood history */
if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno, ntohs(bcast_packet->seqno))) {
spin_unlock(&orig_hash_lock);
continue;
}
/* mark broadcast in flood history */
if (bit_get_packet(orig_node->bcast_bits, ntohs(bcast_packet->seqno) - orig_node->last_bcast_seqno, 1))
orig_node->last_bcast_seqno = ntohs(bcast_packet->seqno);
spin_unlock(&orig_hash_lock);
/* broadcast for me */
interface_rx(soft_device, packet_buff + sizeof(struct ethhdr) + sizeof(struct bcast_packet), result - sizeof(struct ethhdr) - sizeof(struct bcast_packet));
/* rebroadcast packet */
add_bcast_packet_to_list(packet_buff + sizeof(struct ethhdr),
result - sizeof(struct ethhdr));
break;
/* vis packet */
case BAT_VIS:
/* drop if too short. */
if (result < sizeof(struct ethhdr) + sizeof(struct vis_packet))
continue;
/* not for me */
if (!is_my_mac(ethhdr->h_dest))
continue;
vis_packet = (struct vis_packet *)(packet_buff + sizeof(struct ethhdr));
vis_info_len = result - sizeof(struct ethhdr) - sizeof(struct vis_packet);
/* ignore own packets */
if (is_my_mac(vis_packet->vis_orig))
continue;
if (is_my_mac(vis_packet->sender_orig))
continue;
switch (vis_packet->vis_type) {
case VIS_TYPE_SERVER_SYNC:
receive_server_sync_packet(vis_packet, vis_info_len);
break;
case VIS_TYPE_CLIENT_UPDATE:
receive_client_update_packet(vis_packet, vis_info_len);
break;
default: /* ignore unknown packet */
break;
}
break;
}
}
if ((result < 0) && (result != -EAGAIN))
debug_log(LOG_TYPE_CRIT, "Could not receive packet from interface %s: %i\n", batman_if->dev, result);
/* lock for the next iteration */
rcu_read_lock();
}
rcu_read_unlock();
}
kfree(packet_buff);
/* do not exit until kthread_stop() is actually called, otherwise it will wait for us
* forever. */
while (!kthread_should_stop())
schedule();
return 0;
}
void batman_data_ready(struct sock *sk, int len)
{
void (*data_ready)(struct sock *, int) = sk->sk_user_data;
data_ready(sk, len);
atomic_set(&data_ready_cond, 1);
wake_up_interruptible(&thread_wait);
}
/*
* 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);
/*
* 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);
}
/*
* 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);
/*
* 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;
}
/*
* 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[];
/*
* 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;
}
/*
* 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;
/*
* 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
/*
* 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);
}
/*
* 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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment