diff options
author | Andrew Lunn <andrew@lunn.ch> | 2009-11-09 15:20:10 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 15:23:22 -0500 |
commit | 5beef3c9bf7f967a4a70ddb0108fd3e459fed133 (patch) | |
tree | ef6e8f8e9b7a7febef2e478139b38f92b93e4ef2 | |
parent | 6638db58dbb831fa66ac583644d34ae3cf662431 (diff) |
staging: batman-adv meshing protocol
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
a routing protocol for multi-hop ad-hoc mesh networks. The
networks may be wired or wireless. See
http://www.open-mesh.org/ for more information and user space
tools.
This is the first submission for inclusion in staging.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
38 files changed, 7101 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6fbbedbc7fe7..d30fa451e6f3 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig | |||
@@ -129,6 +129,8 @@ source "drivers/staging/wlags49_h2/Kconfig" | |||
129 | 129 | ||
130 | source "drivers/staging/wlags49_h25/Kconfig" | 130 | source "drivers/staging/wlags49_h25/Kconfig" |
131 | 131 | ||
132 | source "drivers/staging/batman-adv/Kconfig" | ||
133 | |||
132 | source "drivers/staging/strip/Kconfig" | 134 | source "drivers/staging/strip/Kconfig" |
133 | 135 | ||
134 | source "drivers/staging/arlan/Kconfig" | 136 | source "drivers/staging/arlan/Kconfig" |
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 61a81c148c9f..910e55dd7914 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_IIO) += iio/ | |||
46 | obj-$(CONFIG_RAMZSWAP) += ramzswap/ | 46 | obj-$(CONFIG_RAMZSWAP) += ramzswap/ |
47 | obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ | 47 | obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ |
48 | obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ | 48 | obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ |
49 | obj-$(CONFIG_BATMAN_ADV) += batman-adv/ | ||
49 | obj-$(CONFIG_STRIP) += strip/ | 50 | obj-$(CONFIG_STRIP) += strip/ |
50 | obj-$(CONFIG_ARLAN) += arlan/ | 51 | obj-$(CONFIG_ARLAN) += arlan/ |
51 | obj-$(CONFIG_WAVELAN) += wavelan/ | 52 | obj-$(CONFIG_WAVELAN) += wavelan/ |
diff --git a/drivers/staging/batman-adv/CHANGELOG b/drivers/staging/batman-adv/CHANGELOG new file mode 100644 index 000000000000..8a181639ceaa --- /dev/null +++ b/drivers/staging/batman-adv/CHANGELOG | |||
@@ -0,0 +1,37 @@ | |||
1 | batman-adv 0.2: | ||
2 | |||
3 | * support latest kernels (2.6.20 - 2.6.31) | ||
4 | * temporary routing loops / TTL code bug / ghost entries in originator table fixed | ||
5 | * internal packet queue for packet aggregation & transmission retry (ARQ) | ||
6 | for payload broadcasts added | ||
7 | * interface detection converted to event based handling to avoid timers | ||
8 | * major linux coding style adjustments applied | ||
9 | * all kernel version compatibility functions has been moved to compat.h | ||
10 | * use random ethernet address generator from the kernel | ||
11 | * /sys/module/batman_adv/version to export kernel module version | ||
12 | * vis: secondary interface export for dot draw format + JSON output format added | ||
13 | * many bugs (alignment issues, race conditions, deadlocks, etc) squashed | ||
14 | |||
15 | -- Sat, 07 Nov 2009 15:44:31 +0100 | ||
16 | |||
17 | batman-adv 0.1: | ||
18 | |||
19 | * support latest kernels (2.6.20 - 2.6.28) | ||
20 | * LOTS of cleanup: locking, stack usage, memory leaks | ||
21 | * Change Ethertype from 0x0842 to 0x4305 | ||
22 | unregistered at IEEE, if you want to sponsor an official Ethertype ($2500) | ||
23 | please contact us | ||
24 | |||
25 | -- Sun, 28 Dec 2008 00:44:31 +0100 | ||
26 | |||
27 | batman-adv 0.1-beta: | ||
28 | |||
29 | * layer 2 meshing based on BATMAN TQ algorithm in kernelland | ||
30 | * operates on any ethernet like interface | ||
31 | * supports IPv4, IPv6, DHCP, etc | ||
32 | * is controlled via /proc/net/batman-adv/ | ||
33 | * bridging via brctl is supported | ||
34 | * interface watchdog (interfaces can be (de)activated dynamically) | ||
35 | * offers integrated vis server which meshes/syncs with other vis servers in range | ||
36 | |||
37 | -- Mon, 05 May 2008 14:10:04 +0200 | ||
diff --git a/drivers/staging/batman-adv/Kconfig b/drivers/staging/batman-adv/Kconfig new file mode 100644 index 000000000000..b9742e7c7d9e --- /dev/null +++ b/drivers/staging/batman-adv/Kconfig | |||
@@ -0,0 +1,25 @@ | |||
1 | # | ||
2 | # B.A.T.M.A.N meshing protocol | ||
3 | # | ||
4 | |||
5 | config BATMAN_ADV | ||
6 | tristate "B.A.T.M.A.N. Advanced Meshing Protocol" | ||
7 | default n | ||
8 | ---help--- | ||
9 | |||
10 | B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is | ||
11 | a routing protocol for multi-hop ad-hoc mesh networks. The | ||
12 | networks may be wired or wireless. See | ||
13 | http://www.open-mesh.org/ for more information and user space | ||
14 | tools. | ||
15 | |||
16 | config BATMAN_DEBUG | ||
17 | bool "B.A.T.M.A.N. debugging" | ||
18 | depends on BATMAN != n | ||
19 | help | ||
20 | |||
21 | This is an option for use by developers; most people should | ||
22 | say N here. This enables compilation of support for | ||
23 | outputting debugging information to the kernel log. The | ||
24 | output is controlled via the module parameter debug. | ||
25 | |||
diff --git a/drivers/staging/batman-adv/Makefile b/drivers/staging/batman-adv/Makefile new file mode 100644 index 000000000000..02da87134fce --- /dev/null +++ b/drivers/staging/batman-adv/Makefile | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | # | ||
4 | # Marek Lindner, Simon Wunderlich | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or | ||
7 | # modify it under the terms of version 2 of the GNU General Public | ||
8 | # License as published by the Free Software Foundation. | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | # General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License | ||
16 | # along with this program; if not, write to the Free Software | ||
17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | # 02110-1301, USA | ||
19 | # | ||
20 | |||
21 | obj-m += batman-adv.o | ||
22 | batman-adv-objs := main.o proc.o send.o routing.o soft-interface.o device.o translation-table.o bitarray.o hash.o ring_buffer.o vis.o hard-interface.o aggregation.o log.o | ||
diff --git a/drivers/staging/batman-adv/README b/drivers/staging/batman-adv/README new file mode 100644 index 000000000000..3aaf393ebaa7 --- /dev/null +++ b/drivers/staging/batman-adv/README | |||
@@ -0,0 +1,125 @@ | |||
1 | [state: 07-11-2009] | ||
2 | |||
3 | BATMAN-ADV | ||
4 | ---------- | ||
5 | |||
6 | Batman-advanced is a new approach to wireless networking which does no longer | ||
7 | operate on the IP basis. Unlike B.A.T.M.A.N, which exchanges information | ||
8 | using UDP packets and sets routing tables, batman-advanced operates on ISO/OSI | ||
9 | Layer 2 only and uses and routes (or better: bridges) Ethernet Frames. It | ||
10 | emulates a virtual network switch of all nodes participating. Therefore all | ||
11 | nodes appear to be link local, thus all higher operating protocols won't be | ||
12 | affected by any changes within the network. You can run almost any protocol | ||
13 | above B.A.T.M.A.N. Advanced, prominent examples are: IPv4, IPv6, DHCP, IPX. | ||
14 | |||
15 | This is batman-advanced implemented as Linux kernel driver. It does not depend | ||
16 | on any network (other) driver, and can be used on wifi as well as ethernet, | ||
17 | vpn, etc ... (anything with ethernet-style layer 2). | ||
18 | It compiles against and should work with Linux 2.6.20 - 2.6.31. Supporting older | ||
19 | versions is not planned, but it's probably easy to backport it. If you work on a | ||
20 | backport, feel free to contact us. :-) | ||
21 | |||
22 | COMPILE | ||
23 | ------- | ||
24 | To compile against your currently installed kernel, just type: | ||
25 | |||
26 | # make | ||
27 | |||
28 | if you want to compile against some other kernel, use: | ||
29 | |||
30 | # make KERNELPATH=/path/to/kernel | ||
31 | |||
32 | USAGE | ||
33 | ----- | ||
34 | |||
35 | insmod the batman-adv.ko in your kernel: | ||
36 | |||
37 | # insmod batman-adv.ko | ||
38 | |||
39 | the module is now waiting for activation. You must add some interfaces | ||
40 | on which batman can operate. Each interface must be added separately: | ||
41 | |||
42 | # echo wlan0 > /proc/net/batman-adv/interfaces | ||
43 | |||
44 | ( # echo wlan1 > /proc/net/batman-adv/interfaces ) | ||
45 | ( # echo eth0 > /proc/net/batman-adv/interfaces ) | ||
46 | ( ... ) | ||
47 | |||
48 | Now batman starts broadcasting on this interface. | ||
49 | You can now view the table of originators (mesh participants) with: | ||
50 | |||
51 | # cat /proc/net/batman-adv/originators | ||
52 | |||
53 | The module will create a new interface "bat0", which can be used as a | ||
54 | regular interface: | ||
55 | |||
56 | # ifconfig bat0 inet 192.168.0.1 up | ||
57 | # ping 192.168.0.2 | ||
58 | ... | ||
59 | |||
60 | If you want topology visualization, your meshnode must be configured | ||
61 | as VIS-server: | ||
62 | |||
63 | # echo "server" > /proc/net/batman-adv/vis | ||
64 | |||
65 | Each node is either configured as "server" or as "client" (default: | ||
66 | "client"). Clients send their topology data to the server next to them, | ||
67 | and server synchronize with other servers. If there is no server | ||
68 | configured (default) within the mesh, no topology information will be | ||
69 | transmitted. With these "synchronizing servers", there can be 1 or | ||
70 | more vis servers sharing the same (or at least very similar) data. | ||
71 | |||
72 | When configured as server, you can get a topology snapshot of your mesh: | ||
73 | |||
74 | # cat /proc/net/batman-adv/vis | ||
75 | |||
76 | This output format is a graphviz formatted text file which can be | ||
77 | processed with graphviz-tools like dot. | ||
78 | The labels are similar/compatible to the ETX metric, 1.0 means perfect | ||
79 | connection (100%), 2.0 means 50%, 3.0 means 33% and so on. | ||
80 | |||
81 | Alternatively, a JSON output format is available. The format can be set | ||
82 | using by writing either "dot_draw" or "json" into the vis_format file. | ||
83 | "dot_draw" is selected by default. | ||
84 | |||
85 | echo "json" > /proc/net/batman-adv/vis_format | ||
86 | |||
87 | In very mobile scenarios, you might want to adjust the originator | ||
88 | interval to a lower value. This will make the mesh more responsive to | ||
89 | topology changes, but will also increase the overhead. Please make sure | ||
90 | that all nodes in your mesh use the same interval. The default value | ||
91 | is 1000 ms (1 second). | ||
92 | |||
93 | # echo 1000 > /proc/net/batman-adv/orig_interval | ||
94 | |||
95 | To deactivate batman, do: | ||
96 | |||
97 | # echo "" > /proc/net/batman-adv/interfaces | ||
98 | |||
99 | BATCTL | ||
100 | ------ | ||
101 | |||
102 | B.A.T.M.A.N. advanced operates on layer 2 and thus all hosts partici- | ||
103 | pating in the virtual switch are completely transparent for all proto- | ||
104 | cols above layer 2. Therefore the common diagnosis tools do not work as | ||
105 | expected. To overcome these problems batctl was created. At the moment | ||
106 | the batctl contains ping, traceroute, tcpdump and interfaces to the | ||
107 | kernel module settings. | ||
108 | |||
109 | For more information, please see the manpage (man batctl). | ||
110 | |||
111 | batctl is available on http://www.open-mesh.net/ | ||
112 | |||
113 | CONTACT | ||
114 | ------- | ||
115 | |||
116 | Please send us comments, experiences, questions, anything :) | ||
117 | |||
118 | IRC: #batman on irc.freenode.org | ||
119 | Mailing-list: b.a.t.m.a.n@open-mesh.net | ||
120 | (subscription at https://list.open-mesh.net/mm/listinfo/b.a.t.m.a.n ) | ||
121 | |||
122 | You can also contact the Authors: | ||
123 | |||
124 | Marek Lindner <lindner_marek@yahoo.de> | ||
125 | Simon Wunderlich <siwu@hrz.tu-chemnitz.de> | ||
diff --git a/drivers/staging/batman-adv/TODO b/drivers/staging/batman-adv/TODO new file mode 100644 index 000000000000..ea6dcf94d661 --- /dev/null +++ b/drivers/staging/batman-adv/TODO | |||
@@ -0,0 +1,51 @@ | |||
1 | => proc interface | ||
2 | * implement new interface to add/delete interfaces and setting options | ||
3 | * /proc/sys/net/batman-adv/ as main folder | ||
4 | * in interfaces/ list every available interface of the host | ||
5 | * each interfaces/$iface/ contains the following files: | ||
6 | -> enable (def: 0) [add/remove this interface to batman-adv] | ||
7 | -> ogm_interval (def: 1000) [ogm interval of that interface] | ||
8 | -> context (def: bat0) [later we want to support multiple mesh instances via | ||
9 | -> bat0/bat1/bat2/..] | ||
10 | -> status (read-only) [outputs the interface status from batman's | ||
11 | -> perspective] | ||
12 | * in mesh/batX/ list every available mesh subnet | ||
13 | -> vis_server (def: 0) [enable/disable vis server for that mesh] | ||
14 | -> vis_data (read-only) [outputs the vis data in a raw format] | ||
15 | -> aggregate_ogm (def: 1) [enable/disable ogm aggregation for that mesh] | ||
16 | -> originators (read-only) [outputs the originator table] | ||
17 | -> transtable_global (read-only) [outputs the global translation table] | ||
18 | -> transtable_local (read-only) [outputs the local translation table] | ||
19 | |||
20 | => vis "raw" data output | ||
21 | * the raw format shall replace dot draw / json to offer a neutral that can | ||
22 | * be converted | ||
23 | * the format (comma seperated entries): | ||
24 | -> "mac" -> mac address of an originator (each line begins with it) | ||
25 | -> "TQ mac value" -> src mac's link quality towards mac address | ||
26 | -> "HNA mac" -> HNA announced by source mac | ||
27 | -> "PRIMARY" -> this is a primary interface | ||
28 | -> "SEC mac" -> secondary mac address of source (requires preceeding | ||
29 | -> PRIMARY) | ||
30 | |||
31 | => logging | ||
32 | * the log level LOG_TYPE_CRIT, LOG_TYPE_WARN & LOG_TYPE_NOTICE will be | ||
33 | * unified to use printk | ||
34 | * LOG_TYPE_BATMAN & LOG_TYPE_ROUTES will also use printk but only after the | ||
35 | * internal debug level has been raised | ||
36 | * the internal debug level can be modified using a module parameter (debug) | ||
37 | * or at run time via /sys/module/batman-adv/parameters/debug | ||
38 | * make use of printk %pM support instead of converting mac addresses | ||
39 | * manually | ||
40 | |||
41 | => strip out all backward compatibility support to older kernels | ||
42 | (only found in compat.h) | ||
43 | |||
44 | => fix checkpatch.pl errors | ||
45 | |||
46 | Please send all patches to: | ||
47 | Marek Lindner <lindner_marek@yahoo.de> | ||
48 | Simon Wunderlich <siwu@hrz.tu-chemnitz.de> | ||
49 | Andrew Lunn <andrew@lunn.ch> | ||
50 | b.a.t.m.a.n@lists.open-mesh.net | ||
51 | Greg Kroah-Hartman <gregkh@suse.de> | ||
diff --git a/drivers/staging/batman-adv/aggregation.c b/drivers/staging/batman-adv/aggregation.c new file mode 100644 index 000000000000..9c6e681f6fb6 --- /dev/null +++ b/drivers/staging/batman-adv/aggregation.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "aggregation.h" | ||
24 | #include "send.h" | ||
25 | #include "routing.h" | ||
26 | |||
27 | /* calculate the size of the hna information for a given packet */ | ||
28 | static int hna_len(struct batman_packet *batman_packet) | ||
29 | { | ||
30 | return batman_packet->num_hna * ETH_ALEN; | ||
31 | } | ||
32 | |||
33 | /* return true if new_packet can be aggregated with forw_packet */ | ||
34 | static bool can_aggregate_with(struct batman_packet *new_batman_packet, | ||
35 | int packet_len, | ||
36 | unsigned long send_time, | ||
37 | bool directlink, | ||
38 | struct batman_if *if_incoming, | ||
39 | struct forw_packet *forw_packet) | ||
40 | { | ||
41 | struct batman_packet *batman_packet = | ||
42 | (struct batman_packet *)forw_packet->packet_buff; | ||
43 | int aggregated_bytes = forw_packet->packet_len + packet_len; | ||
44 | |||
45 | /** | ||
46 | * we can aggregate the current packet to this aggregated packet | ||
47 | * if: | ||
48 | * | ||
49 | * - the send time is within our MAX_AGGREGATION_MS time | ||
50 | * - the resulting packet wont be bigger than | ||
51 | * MAX_AGGREGATION_BYTES | ||
52 | */ | ||
53 | |||
54 | if (time_before(send_time, forw_packet->send_time) && | ||
55 | (aggregated_bytes <= MAX_AGGREGATION_BYTES)) { | ||
56 | |||
57 | /** | ||
58 | * check aggregation compatibility | ||
59 | * -> direct link packets are broadcasted on | ||
60 | * their interface only | ||
61 | * -> aggregate packet if the current packet is | ||
62 | * a "global" packet as well as the base | ||
63 | * packet | ||
64 | */ | ||
65 | |||
66 | /* packets without direct link flag and high TTL | ||
67 | * are flooded through the net */ | ||
68 | if ((!directlink) && | ||
69 | (!(batman_packet->flags & DIRECTLINK)) && | ||
70 | (batman_packet->ttl != 1) && | ||
71 | |||
72 | /* own packets originating non-primary | ||
73 | * interfaces leave only that interface */ | ||
74 | ((!forw_packet->own) || | ||
75 | (forw_packet->if_incoming->if_num == 0))) | ||
76 | return true; | ||
77 | |||
78 | /* if the incoming packet is sent via this one | ||
79 | * interface only - we still can aggregate */ | ||
80 | if ((directlink) && | ||
81 | (new_batman_packet->ttl == 1) && | ||
82 | (forw_packet->if_incoming == if_incoming)) | ||
83 | return true; | ||
84 | |||
85 | } | ||
86 | |||
87 | return false; | ||
88 | } | ||
89 | |||
90 | /* create a new aggregated packet and add this packet to it */ | ||
91 | static void new_aggregated_packet(unsigned char *packet_buff, | ||
92 | int packet_len, | ||
93 | unsigned long send_time, | ||
94 | bool direct_link, | ||
95 | struct batman_if *if_incoming, | ||
96 | int own_packet) | ||
97 | { | ||
98 | struct forw_packet *forw_packet_aggr; | ||
99 | |||
100 | forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); | ||
101 | if (!forw_packet_aggr) | ||
102 | return; | ||
103 | |||
104 | forw_packet_aggr->packet_buff = kmalloc(MAX_AGGREGATION_BYTES, | ||
105 | GFP_ATOMIC); | ||
106 | if (!forw_packet_aggr->packet_buff) { | ||
107 | kfree(forw_packet_aggr); | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | INIT_HLIST_NODE(&forw_packet_aggr->list); | ||
112 | |||
113 | forw_packet_aggr->packet_len = packet_len; | ||
114 | memcpy(forw_packet_aggr->packet_buff, | ||
115 | packet_buff, | ||
116 | forw_packet_aggr->packet_len); | ||
117 | |||
118 | forw_packet_aggr->own = own_packet; | ||
119 | forw_packet_aggr->if_incoming = if_incoming; | ||
120 | forw_packet_aggr->num_packets = 0; | ||
121 | forw_packet_aggr->direct_link_flags = 0; | ||
122 | forw_packet_aggr->send_time = send_time; | ||
123 | |||
124 | /* save packet direct link flag status */ | ||
125 | if (direct_link) | ||
126 | forw_packet_aggr->direct_link_flags |= 1; | ||
127 | |||
128 | /* add new packet to packet list */ | ||
129 | spin_lock(&forw_bat_list_lock); | ||
130 | hlist_add_head(&forw_packet_aggr->list, &forw_bat_list); | ||
131 | spin_unlock(&forw_bat_list_lock); | ||
132 | |||
133 | /* start timer for this packet */ | ||
134 | INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work, | ||
135 | send_outstanding_bat_packet); | ||
136 | queue_delayed_work(bat_event_workqueue, | ||
137 | &forw_packet_aggr->delayed_work, | ||
138 | send_time - jiffies); | ||
139 | } | ||
140 | |||
141 | /* aggregate a new packet into the existing aggregation */ | ||
142 | static void aggregate(struct forw_packet *forw_packet_aggr, | ||
143 | unsigned char *packet_buff, | ||
144 | int packet_len, | ||
145 | bool direct_link) | ||
146 | { | ||
147 | memcpy((forw_packet_aggr->packet_buff + forw_packet_aggr->packet_len), | ||
148 | packet_buff, packet_len); | ||
149 | forw_packet_aggr->packet_len += packet_len; | ||
150 | forw_packet_aggr->num_packets++; | ||
151 | |||
152 | /* save packet direct link flag status */ | ||
153 | if (direct_link) | ||
154 | forw_packet_aggr->direct_link_flags |= | ||
155 | (1 << forw_packet_aggr->num_packets); | ||
156 | } | ||
157 | |||
158 | void add_bat_packet_to_list(unsigned char *packet_buff, int packet_len, | ||
159 | struct batman_if *if_incoming, char own_packet, | ||
160 | unsigned long send_time) | ||
161 | { | ||
162 | /** | ||
163 | * _aggr -> pointer to the packet we want to aggregate with | ||
164 | * _pos -> pointer to the position in the queue | ||
165 | */ | ||
166 | struct forw_packet *forw_packet_aggr = NULL, *forw_packet_pos = NULL; | ||
167 | struct hlist_node *tmp_node; | ||
168 | struct batman_packet *batman_packet = | ||
169 | (struct batman_packet *)packet_buff; | ||
170 | bool direct_link = batman_packet->flags & DIRECTLINK ? 1 : 0; | ||
171 | |||
172 | /* find position for the packet in the forward queue */ | ||
173 | spin_lock(&forw_bat_list_lock); | ||
174 | /* own packets are not to be aggregated */ | ||
175 | if ((atomic_read(&aggregation_enabled)) && (!own_packet)) { | ||
176 | hlist_for_each_entry(forw_packet_pos, tmp_node, &forw_bat_list, | ||
177 | list) { | ||
178 | if (can_aggregate_with(batman_packet, | ||
179 | packet_len, | ||
180 | send_time, | ||
181 | direct_link, | ||
182 | if_incoming, | ||
183 | forw_packet_pos)) { | ||
184 | forw_packet_aggr = forw_packet_pos; | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /* nothing to aggregate with - either aggregation disabled or no | ||
191 | * suitable aggregation packet found */ | ||
192 | if (forw_packet_aggr == NULL) { | ||
193 | /* the following section can run without the lock */ | ||
194 | spin_unlock(&forw_bat_list_lock); | ||
195 | new_aggregated_packet(packet_buff, packet_len, | ||
196 | send_time, direct_link, | ||
197 | if_incoming, own_packet); | ||
198 | } else { | ||
199 | aggregate(forw_packet_aggr, | ||
200 | packet_buff, packet_len, | ||
201 | direct_link); | ||
202 | spin_unlock(&forw_bat_list_lock); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* unpack the aggregated packets and process them one by one */ | ||
207 | void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, | ||
208 | int packet_len, struct batman_if *if_incoming) | ||
209 | { | ||
210 | struct batman_packet *batman_packet; | ||
211 | int buff_pos = 0; | ||
212 | unsigned char *hna_buff; | ||
213 | |||
214 | batman_packet = (struct batman_packet *)packet_buff; | ||
215 | |||
216 | while (aggregated_packet(buff_pos, packet_len, | ||
217 | batman_packet->num_hna)) { | ||
218 | |||
219 | /* network to host order for our 16bit seqno, and the | ||
220 | orig_interval. */ | ||
221 | batman_packet->seqno = ntohs(batman_packet->seqno); | ||
222 | |||
223 | hna_buff = packet_buff + buff_pos + BAT_PACKET_LEN; | ||
224 | receive_bat_packet(ethhdr, batman_packet, | ||
225 | hna_buff, hna_len(batman_packet), | ||
226 | if_incoming); | ||
227 | |||
228 | buff_pos += BAT_PACKET_LEN + hna_len(batman_packet); | ||
229 | batman_packet = (struct batman_packet *) | ||
230 | (packet_buff + buff_pos); | ||
231 | } | ||
232 | } | ||
diff --git a/drivers/staging/batman-adv/aggregation.h b/drivers/staging/batman-adv/aggregation.h new file mode 100644 index 000000000000..6da8df9f99b7 --- /dev/null +++ b/drivers/staging/batman-adv/aggregation.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | |||
24 | /* is there another aggregated packet here? */ | ||
25 | static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna) | ||
26 | { | ||
27 | int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_hna * ETH_ALEN); | ||
28 | |||
29 | return (next_buff_pos <= packet_len) && | ||
30 | (next_buff_pos <= MAX_AGGREGATION_BYTES); | ||
31 | } | ||
32 | |||
33 | void add_bat_packet_to_list(unsigned char *packet_buff, int packet_len, | ||
34 | struct batman_if *if_outgoing, char own_packet, | ||
35 | unsigned long send_time); | ||
36 | void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, | ||
37 | int packet_len, struct batman_if *if_incoming); | ||
diff --git a/drivers/staging/batman-adv/bitarray.c b/drivers/staging/batman-adv/bitarray.c new file mode 100644 index 000000000000..3c67f5f42b2b --- /dev/null +++ b/drivers/staging/batman-adv/bitarray.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Simon Wunderlich, Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "bitarray.h" | ||
24 | #include "log.h" | ||
25 | |||
26 | /* returns true if the corresponding bit in the given seq_bits indicates true | ||
27 | * and curr_seqno is within range of last_seqno */ | ||
28 | uint8_t get_bit_status(TYPE_OF_WORD *seq_bits, uint16_t last_seqno, | ||
29 | uint16_t curr_seqno) | ||
30 | { | ||
31 | int16_t diff, word_offset, word_num; | ||
32 | |||
33 | diff = last_seqno - curr_seqno; | ||
34 | if (diff < 0 || diff >= TQ_LOCAL_WINDOW_SIZE) { | ||
35 | return 0; | ||
36 | } else { | ||
37 | /* which word */ | ||
38 | word_num = (last_seqno - curr_seqno) / WORD_BIT_SIZE; | ||
39 | /* which position in the selected word */ | ||
40 | word_offset = (last_seqno - curr_seqno) % WORD_BIT_SIZE; | ||
41 | |||
42 | if (seq_bits[word_num] & 1 << word_offset) | ||
43 | return 1; | ||
44 | else | ||
45 | return 0; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | /* turn corresponding bit on, so we can remember that we got the packet */ | ||
50 | void bit_mark(TYPE_OF_WORD *seq_bits, int32_t n) | ||
51 | { | ||
52 | int32_t word_offset, word_num; | ||
53 | |||
54 | /* if too old, just drop it */ | ||
55 | if (n < 0 || n >= TQ_LOCAL_WINDOW_SIZE) | ||
56 | return; | ||
57 | |||
58 | /* which word */ | ||
59 | word_num = n / WORD_BIT_SIZE; | ||
60 | /* which position in the selected word */ | ||
61 | word_offset = n % WORD_BIT_SIZE; | ||
62 | |||
63 | seq_bits[word_num] |= 1 << word_offset; /* turn the position on */ | ||
64 | } | ||
65 | |||
66 | /* shift the packet array by n places. */ | ||
67 | void bit_shift(TYPE_OF_WORD *seq_bits, int32_t n) | ||
68 | { | ||
69 | int32_t word_offset, word_num; | ||
70 | int32_t i; | ||
71 | |||
72 | if (n <= 0) | ||
73 | return; | ||
74 | |||
75 | word_offset = n % WORD_BIT_SIZE;/* shift how much inside each word */ | ||
76 | word_num = n / WORD_BIT_SIZE; /* shift over how much (full) words */ | ||
77 | |||
78 | for (i = NUM_WORDS - 1; i > word_num; i--) { | ||
79 | /* going from old to new, so we don't overwrite the data we copy | ||
80 | * from. | ||
81 | * | ||
82 | * left is high, right is low: FEDC BA98 7654 3210 | ||
83 | * ^^ ^^ | ||
84 | * vvvv | ||
85 | * ^^^^ = from, vvvvv =to, we'd have word_num==1 and | ||
86 | * word_offset==WORD_BIT_SIZE/2 ????? in this example. | ||
87 | * (=24 bits) | ||
88 | * | ||
89 | * our desired output would be: 9876 5432 1000 0000 | ||
90 | * */ | ||
91 | |||
92 | seq_bits[i] = | ||
93 | (seq_bits[i - word_num] << word_offset) + | ||
94 | /* take the lower port from the left half, shift it left | ||
95 | * to its final position */ | ||
96 | (seq_bits[i - word_num - 1] >> | ||
97 | (WORD_BIT_SIZE-word_offset)); | ||
98 | /* and the upper part of the right half and shift it left to | ||
99 | * it's position */ | ||
100 | /* for our example that would be: word[0] = 9800 + 0076 = | ||
101 | * 9876 */ | ||
102 | } | ||
103 | /* now for our last word, i==word_num, we only have the it's "left" | ||
104 | * half. that's the 1000 word in our example.*/ | ||
105 | |||
106 | seq_bits[i] = (seq_bits[i - word_num] << word_offset); | ||
107 | |||
108 | /* pad the rest with 0, if there is anything */ | ||
109 | i--; | ||
110 | |||
111 | for (; i >= 0; i--) | ||
112 | seq_bits[i] = 0; | ||
113 | } | ||
114 | |||
115 | |||
116 | /* receive and process one packet, returns 1 if received seq_num is considered | ||
117 | * new, 0 if old */ | ||
118 | char bit_get_packet(TYPE_OF_WORD *seq_bits, int16_t seq_num_diff, | ||
119 | int8_t set_mark) | ||
120 | { | ||
121 | int i; | ||
122 | |||
123 | /* we already got a sequence number higher than this one, so we just | ||
124 | * mark it. this should wrap around the integer just fine */ | ||
125 | if ((seq_num_diff < 0) && (seq_num_diff >= -TQ_LOCAL_WINDOW_SIZE)) { | ||
126 | if (set_mark) | ||
127 | bit_mark(seq_bits, -seq_num_diff); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* it seems we missed a lot of packets or the other host restarted */ | ||
132 | if ((seq_num_diff > TQ_LOCAL_WINDOW_SIZE) || | ||
133 | (seq_num_diff < -TQ_LOCAL_WINDOW_SIZE)) { | ||
134 | |||
135 | if (seq_num_diff > TQ_LOCAL_WINDOW_SIZE) | ||
136 | debug_log(LOG_TYPE_BATMAN, | ||
137 | "We missed a lot of packets (%i) !\n", | ||
138 | seq_num_diff-1); | ||
139 | |||
140 | if (-seq_num_diff > TQ_LOCAL_WINDOW_SIZE) | ||
141 | debug_log(LOG_TYPE_BATMAN, | ||
142 | "Other host probably restarted !\n"); | ||
143 | |||
144 | for (i = 0; i < NUM_WORDS; i++) | ||
145 | seq_bits[i] = 0; | ||
146 | |||
147 | if (set_mark) | ||
148 | seq_bits[0] = 1; /* we only have the latest packet */ | ||
149 | } else { | ||
150 | bit_shift(seq_bits, seq_num_diff); | ||
151 | |||
152 | if (set_mark) | ||
153 | bit_mark(seq_bits, 0); | ||
154 | } | ||
155 | |||
156 | return 1; | ||
157 | } | ||
158 | |||
159 | /* count the hamming weight, how many good packets did we receive? just count | ||
160 | * the 1's. The inner loop uses the Kernighan algorithm, see | ||
161 | * http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan | ||
162 | */ | ||
163 | int bit_packet_count(TYPE_OF_WORD *seq_bits) | ||
164 | { | ||
165 | int i, hamming = 0; | ||
166 | TYPE_OF_WORD word; | ||
167 | |||
168 | for (i = 0; i < NUM_WORDS; i++) { | ||
169 | word = seq_bits[i]; | ||
170 | |||
171 | while (word) { | ||
172 | word &= word-1; | ||
173 | hamming++; | ||
174 | } | ||
175 | } | ||
176 | return hamming; | ||
177 | } | ||
diff --git a/drivers/staging/batman-adv/bitarray.h b/drivers/staging/batman-adv/bitarray.h new file mode 100644 index 000000000000..ec72dd784362 --- /dev/null +++ b/drivers/staging/batman-adv/bitarray.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Simon Wunderlich, Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | /* you should choose something big, if you don't want to waste cpu */ | ||
24 | #define TYPE_OF_WORD unsigned long | ||
25 | #define WORD_BIT_SIZE (sizeof(TYPE_OF_WORD) * 8) | ||
26 | |||
27 | /* returns true if the corresponding bit in the given seq_bits indicates true | ||
28 | * and curr_seqno is within range of last_seqno */ | ||
29 | uint8_t get_bit_status(TYPE_OF_WORD *seq_bits, uint16_t last_seqno, | ||
30 | uint16_t curr_seqno); | ||
31 | |||
32 | /* turn corresponding bit on, so we can remember that we got the packet */ | ||
33 | void bit_mark(TYPE_OF_WORD *seq_bits, int32_t n); | ||
34 | |||
35 | /* shift the packet array by n places. */ | ||
36 | void bit_shift(TYPE_OF_WORD *seq_bits, int32_t n); | ||
37 | |||
38 | |||
39 | /* receive and process one packet, returns 1 if received seq_num is considered | ||
40 | * new, 0 if old */ | ||
41 | char bit_get_packet(TYPE_OF_WORD *seq_bits, int16_t seq_num_diff, | ||
42 | int8_t set_mark); | ||
43 | |||
44 | /* count the hamming weight, how many good packets did we receive? */ | ||
45 | int bit_packet_count(TYPE_OF_WORD *seq_bits); | ||
diff --git a/drivers/staging/batman-adv/compat.h b/drivers/staging/batman-adv/compat.h new file mode 100644 index 000000000000..f4e0a4564ba7 --- /dev/null +++ b/drivers/staging/batman-adv/compat.h | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | * | ||
21 | * This file contains macros for maintaining compatibility with older versions | ||
22 | * of the Linux kernel. | ||
23 | */ | ||
24 | |||
25 | #include <linux/version.h> /* LINUX_VERSION_CODE */ | ||
26 | |||
27 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) | ||
28 | |||
29 | #define skb_set_network_header(_skb, _offset) \ | ||
30 | do { (_skb)->nh.raw = (_skb)->data + (_offset); } while (0) | ||
31 | |||
32 | #define skb_reset_mac_header(_skb) \ | ||
33 | do { (_skb)->mac.raw = (_skb)->data; } while (0) | ||
34 | |||
35 | #define list_first_entry(ptr, type, member) \ | ||
36 | list_entry((ptr)->next, type, member) | ||
37 | |||
38 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */ | ||
39 | |||
40 | |||
41 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) | ||
42 | |||
43 | #define device_create(_cls, _parent, _devt, _device, _fmt) \ | ||
44 | class_device_create(_cls, _parent, _devt, _device, _fmt) | ||
45 | |||
46 | #define device_destroy(_cls, _device) \ | ||
47 | class_device_destroy(_cls, _device) | ||
48 | |||
49 | #else | ||
50 | |||
51 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) | ||
52 | |||
53 | #define device_create(_cls, _parent, _devt, _device, _fmt) \ | ||
54 | device_create_drvdata(_cls, _parent, _devt, _device, _fmt) | ||
55 | |||
56 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) */ | ||
57 | |||
58 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) */ | ||
59 | |||
60 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) | ||
61 | |||
62 | #define cancel_delayed_work_sync(wq) cancel_rearming_delayed_work(wq) | ||
63 | |||
64 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) */ | ||
65 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) | ||
66 | #define strict_strtoul(cp, base, res) \ | ||
67 | ({ \ | ||
68 | int ret = 0; \ | ||
69 | char *endp; \ | ||
70 | *res = simple_strtoul(cp, &endp, base); \ | ||
71 | if (cp == endp) \ | ||
72 | ret = -EINVAL; \ | ||
73 | ret; \ | ||
74 | }) | ||
75 | #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) */ | ||
diff --git a/drivers/staging/batman-adv/device.c b/drivers/staging/batman-adv/device.c new file mode 100644 index 000000000000..1e7d1f88674f --- /dev/null +++ b/drivers/staging/batman-adv/device.c | |||
@@ -0,0 +1,337 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "device.h" | ||
24 | #include "log.h" | ||
25 | #include "send.h" | ||
26 | #include "types.h" | ||
27 | #include "hash.h" | ||
28 | |||
29 | #include "compat.h" | ||
30 | |||
31 | static struct class *batman_class; | ||
32 | |||
33 | static int Major; /* Major number assigned to our device driver */ | ||
34 | |||
35 | static const struct file_operations fops = { | ||
36 | .open = bat_device_open, | ||
37 | .release = bat_device_release, | ||
38 | .read = bat_device_read, | ||
39 | .write = bat_device_write, | ||
40 | .poll = bat_device_poll, | ||
41 | }; | ||
42 | |||
43 | static struct device_client *device_client_hash[256]; | ||
44 | |||
45 | void bat_device_init(void) | ||
46 | { | ||
47 | int i; | ||
48 | |||
49 | for (i = 0; i < 256; i++) | ||
50 | device_client_hash[i] = NULL; | ||
51 | } | ||
52 | |||
53 | int bat_device_setup(void) | ||
54 | { | ||
55 | int tmp_major; | ||
56 | |||
57 | if (Major) | ||
58 | return 1; | ||
59 | |||
60 | /* register our device - kernel assigns a free major number */ | ||
61 | tmp_major = register_chrdev(0, DRIVER_DEVICE, &fops); | ||
62 | if (tmp_major < 0) { | ||
63 | debug_log(LOG_TYPE_WARN, "Registering the character device failed with %d\n", | ||
64 | tmp_major); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | batman_class = class_create(THIS_MODULE, "batman-adv"); | ||
69 | |||
70 | if (IS_ERR(batman_class)) { | ||
71 | debug_log(LOG_TYPE_WARN, "Could not register class 'batman-adv' \n"); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | device_create(batman_class, NULL, MKDEV(tmp_major, 0), NULL, | ||
76 | "batman-adv"); | ||
77 | |||
78 | Major = tmp_major; | ||
79 | return 1; | ||
80 | } | ||
81 | |||
82 | void bat_device_destroy(void) | ||
83 | { | ||
84 | if (!Major) | ||
85 | return; | ||
86 | |||
87 | device_destroy(batman_class, MKDEV(Major, 0)); | ||
88 | class_destroy(batman_class); | ||
89 | |||
90 | /* Unregister the device */ | ||
91 | unregister_chrdev(Major, DRIVER_DEVICE); | ||
92 | |||
93 | Major = 0; | ||
94 | } | ||
95 | |||
96 | int bat_device_open(struct inode *inode, struct file *file) | ||
97 | { | ||
98 | unsigned int i; | ||
99 | struct device_client *device_client; | ||
100 | |||
101 | device_client = kmalloc(sizeof(struct device_client), GFP_KERNEL); | ||
102 | |||
103 | if (!device_client) | ||
104 | return -ENOMEM; | ||
105 | |||
106 | for (i = 0; i < 256; i++) { | ||
107 | if (!device_client_hash[i]) { | ||
108 | device_client_hash[i] = device_client; | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | if (device_client_hash[i] != device_client) { | ||
114 | debug_log(LOG_TYPE_WARN, "Error - can't add another packet client: maximum number of clients reached \n"); | ||
115 | kfree(device_client); | ||
116 | return -EXFULL; | ||
117 | } | ||
118 | |||
119 | INIT_LIST_HEAD(&device_client->queue_list); | ||
120 | device_client->queue_len = 0; | ||
121 | device_client->index = i; | ||
122 | device_client->lock = __SPIN_LOCK_UNLOCKED(device_client->lock); | ||
123 | init_waitqueue_head(&device_client->queue_wait); | ||
124 | |||
125 | file->private_data = device_client; | ||
126 | |||
127 | inc_module_count(); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | int bat_device_release(struct inode *inode, struct file *file) | ||
132 | { | ||
133 | struct device_client *device_client = | ||
134 | (struct device_client *)file->private_data; | ||
135 | struct device_packet *device_packet; | ||
136 | struct list_head *list_pos, *list_pos_tmp; | ||
137 | |||
138 | spin_lock(&device_client->lock); | ||
139 | |||
140 | /* for all packets in the queue ... */ | ||
141 | list_for_each_safe(list_pos, list_pos_tmp, &device_client->queue_list) { | ||
142 | device_packet = list_entry(list_pos, | ||
143 | struct device_packet, list); | ||
144 | |||
145 | list_del(list_pos); | ||
146 | kfree(device_packet); | ||
147 | } | ||
148 | |||
149 | device_client_hash[device_client->index] = NULL; | ||
150 | spin_unlock(&device_client->lock); | ||
151 | |||
152 | kfree(device_client); | ||
153 | dec_module_count(); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | ssize_t bat_device_read(struct file *file, char __user *buf, size_t count, | ||
159 | loff_t *ppos) | ||
160 | { | ||
161 | struct device_client *device_client = | ||
162 | (struct device_client *)file->private_data; | ||
163 | struct device_packet *device_packet; | ||
164 | int error; | ||
165 | |||
166 | if ((file->f_flags & O_NONBLOCK) && (device_client->queue_len == 0)) | ||
167 | return -EAGAIN; | ||
168 | |||
169 | if ((!buf) || (count < sizeof(struct icmp_packet))) | ||
170 | return -EINVAL; | ||
171 | |||
172 | if (!access_ok(VERIFY_WRITE, buf, count)) | ||
173 | return -EFAULT; | ||
174 | |||
175 | error = wait_event_interruptible(device_client->queue_wait, | ||
176 | device_client->queue_len); | ||
177 | |||
178 | if (error) | ||
179 | return error; | ||
180 | |||
181 | spin_lock(&device_client->lock); | ||
182 | |||
183 | device_packet = list_first_entry(&device_client->queue_list, | ||
184 | struct device_packet, list); | ||
185 | list_del(&device_packet->list); | ||
186 | device_client->queue_len--; | ||
187 | |||
188 | spin_unlock(&device_client->lock); | ||
189 | |||
190 | error = __copy_to_user(buf, &device_packet->icmp_packet, | ||
191 | sizeof(struct icmp_packet)); | ||
192 | |||
193 | kfree(device_packet); | ||
194 | |||
195 | if (error) | ||
196 | return error; | ||
197 | |||
198 | return sizeof(struct icmp_packet); | ||
199 | } | ||
200 | |||
201 | ssize_t bat_device_write(struct file *file, const char __user *buff, | ||
202 | size_t len, loff_t *off) | ||
203 | { | ||
204 | struct device_client *device_client = | ||
205 | (struct device_client *)file->private_data; | ||
206 | struct icmp_packet icmp_packet; | ||
207 | struct orig_node *orig_node; | ||
208 | struct batman_if *batman_if; | ||
209 | |||
210 | if (len < sizeof(struct icmp_packet)) { | ||
211 | debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from char device: invalid packet size\n"); | ||
212 | return -EINVAL; | ||
213 | } | ||
214 | |||
215 | if (!access_ok(VERIFY_READ, buff, sizeof(struct icmp_packet))) | ||
216 | return -EFAULT; | ||
217 | |||
218 | if (__copy_from_user(&icmp_packet, buff, sizeof(icmp_packet))) | ||
219 | return -EFAULT; | ||
220 | |||
221 | if (icmp_packet.packet_type != BAT_ICMP) { | ||
222 | debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n"); | ||
223 | return -EINVAL; | ||
224 | } | ||
225 | |||
226 | if (icmp_packet.msg_type != ECHO_REQUEST) { | ||
227 | debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n"); | ||
228 | return -EINVAL; | ||
229 | } | ||
230 | |||
231 | icmp_packet.uid = device_client->index; | ||
232 | |||
233 | if (icmp_packet.version != COMPAT_VERSION) { | ||
234 | icmp_packet.msg_type = PARAMETER_PROBLEM; | ||
235 | icmp_packet.ttl = COMPAT_VERSION; | ||
236 | bat_device_add_packet(device_client, &icmp_packet); | ||
237 | goto out; | ||
238 | } | ||
239 | |||
240 | if (atomic_read(&module_state) != MODULE_ACTIVE) | ||
241 | goto dst_unreach; | ||
242 | |||
243 | spin_lock(&orig_hash_lock); | ||
244 | orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet.dst)); | ||
245 | |||
246 | if (!orig_node) | ||
247 | goto unlock; | ||
248 | |||
249 | if (!orig_node->router) | ||
250 | goto unlock; | ||
251 | |||
252 | batman_if = orig_node->batman_if; | ||
253 | |||
254 | if (!batman_if) | ||
255 | goto unlock; | ||
256 | |||
257 | memcpy(icmp_packet.orig, | ||
258 | batman_if->net_dev->dev_addr, | ||
259 | ETH_ALEN); | ||
260 | |||
261 | send_raw_packet((unsigned char *)&icmp_packet, | ||
262 | sizeof(struct icmp_packet), | ||
263 | batman_if, orig_node->router->addr); | ||
264 | |||
265 | spin_unlock(&orig_hash_lock); | ||
266 | goto out; | ||
267 | |||
268 | unlock: | ||
269 | spin_unlock(&orig_hash_lock); | ||
270 | dst_unreach: | ||
271 | icmp_packet.msg_type = DESTINATION_UNREACHABLE; | ||
272 | bat_device_add_packet(device_client, &icmp_packet); | ||
273 | out: | ||
274 | return len; | ||
275 | } | ||
276 | |||
277 | unsigned int bat_device_poll(struct file *file, poll_table *wait) | ||
278 | { | ||
279 | struct device_client *device_client = | ||
280 | (struct device_client *)file->private_data; | ||
281 | |||
282 | poll_wait(file, &device_client->queue_wait, wait); | ||
283 | |||
284 | if (device_client->queue_len > 0) | ||
285 | return POLLIN | POLLRDNORM; | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | void bat_device_add_packet(struct device_client *device_client, | ||
291 | struct icmp_packet *icmp_packet) | ||
292 | { | ||
293 | struct device_packet *device_packet; | ||
294 | |||
295 | device_packet = kmalloc(sizeof(struct device_packet), GFP_KERNEL); | ||
296 | |||
297 | if (!device_packet) | ||
298 | return; | ||
299 | |||
300 | INIT_LIST_HEAD(&device_packet->list); | ||
301 | memcpy(&device_packet->icmp_packet, icmp_packet, | ||
302 | sizeof(struct icmp_packet)); | ||
303 | |||
304 | spin_lock(&device_client->lock); | ||
305 | |||
306 | /* while waiting for the lock the device_client could have been | ||
307 | * deleted */ | ||
308 | if (!device_client_hash[icmp_packet->uid]) { | ||
309 | spin_unlock(&device_client->lock); | ||
310 | kfree(device_packet); | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | list_add_tail(&device_packet->list, &device_client->queue_list); | ||
315 | device_client->queue_len++; | ||
316 | |||
317 | if (device_client->queue_len > 100) { | ||
318 | device_packet = list_first_entry(&device_client->queue_list, | ||
319 | struct device_packet, list); | ||
320 | |||
321 | list_del(&device_packet->list); | ||
322 | kfree(device_packet); | ||
323 | device_client->queue_len--; | ||
324 | } | ||
325 | |||
326 | spin_unlock(&device_client->lock); | ||
327 | |||
328 | wake_up(&device_client->queue_wait); | ||
329 | } | ||
330 | |||
331 | void bat_device_receive_packet(struct icmp_packet *icmp_packet) | ||
332 | { | ||
333 | struct device_client *hash = device_client_hash[icmp_packet->uid]; | ||
334 | |||
335 | if (hash) | ||
336 | bat_device_add_packet(hash, icmp_packet); | ||
337 | } | ||
diff --git a/drivers/staging/batman-adv/device.h b/drivers/staging/batman-adv/device.h new file mode 100644 index 000000000000..46c0f4496527 --- /dev/null +++ b/drivers/staging/batman-adv/device.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "types.h" | ||
23 | |||
24 | void bat_device_init(void); | ||
25 | int bat_device_setup(void); | ||
26 | void bat_device_destroy(void); | ||
27 | int bat_device_open(struct inode *inode, struct file *file); | ||
28 | int bat_device_release(struct inode *inode, struct file *file); | ||
29 | ssize_t bat_device_read(struct file *file, char __user *buf, size_t count, | ||
30 | loff_t *ppos); | ||
31 | ssize_t bat_device_write(struct file *file, const char __user *buff, | ||
32 | size_t len, loff_t *off); | ||
33 | unsigned int bat_device_poll(struct file *file, poll_table *wait); | ||
34 | void bat_device_add_packet(struct device_client *device_client, | ||
35 | struct icmp_packet *icmp_packet); | ||
36 | void bat_device_receive_packet(struct icmp_packet *icmp_packet); | ||
diff --git a/drivers/staging/batman-adv/hard-interface.c b/drivers/staging/batman-adv/hard-interface.c new file mode 100644 index 000000000000..5ea35da5ee7a --- /dev/null +++ b/drivers/staging/batman-adv/hard-interface.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "hard-interface.h" | ||
24 | #include "log.h" | ||
25 | #include "soft-interface.h" | ||
26 | #include "send.h" | ||
27 | #include "translation-table.h" | ||
28 | #include "routing.h" | ||
29 | #include "hash.h" | ||
30 | #include "compat.h" | ||
31 | |||
32 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) | ||
33 | |||
34 | static char avail_ifs; | ||
35 | static char active_ifs; | ||
36 | |||
37 | static void hardif_free_interface(struct rcu_head *rcu); | ||
38 | |||
39 | static struct batman_if *get_batman_if_by_name(char *name) | ||
40 | { | ||
41 | struct batman_if *batman_if; | ||
42 | |||
43 | rcu_read_lock(); | ||
44 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
45 | if (strncmp(batman_if->dev, name, IFNAMSIZ) == 0) | ||
46 | goto out; | ||
47 | } | ||
48 | |||
49 | batman_if = NULL; | ||
50 | |||
51 | out: | ||
52 | rcu_read_unlock(); | ||
53 | return batman_if; | ||
54 | } | ||
55 | |||
56 | int hardif_min_mtu(void) | ||
57 | { | ||
58 | struct batman_if *batman_if; | ||
59 | /* allow big frames if all devices are capable to do so | ||
60 | * (have MTU > 1500 + BAT_HEADER_LEN) */ | ||
61 | int min_mtu = ETH_DATA_LEN; | ||
62 | |||
63 | rcu_read_lock(); | ||
64 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
65 | if ((batman_if->if_active == IF_ACTIVE) || | ||
66 | (batman_if->if_active == IF_TO_BE_ACTIVATED)) | ||
67 | min_mtu = MIN(batman_if->net_dev->mtu - BAT_HEADER_LEN, | ||
68 | min_mtu); | ||
69 | } | ||
70 | rcu_read_unlock(); | ||
71 | |||
72 | return min_mtu; | ||
73 | } | ||
74 | |||
75 | static void check_known_mac_addr(uint8_t *addr) | ||
76 | { | ||
77 | struct batman_if *batman_if; | ||
78 | char mac_string[ETH_STR_LEN]; | ||
79 | |||
80 | rcu_read_lock(); | ||
81 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
82 | if ((batman_if->if_active != IF_ACTIVE) && | ||
83 | (batman_if->if_active != IF_TO_BE_ACTIVATED)) | ||
84 | continue; | ||
85 | |||
86 | if (!compare_orig(batman_if->net_dev->dev_addr, addr)) | ||
87 | continue; | ||
88 | |||
89 | addr_to_string(mac_string, addr); | ||
90 | debug_log(LOG_TYPE_WARN, "The newly added mac address (%s) already exists on: %s\n", | ||
91 | mac_string, batman_if->dev); | ||
92 | debug_log(LOG_TYPE_WARN, "It is strongly recommended to keep mac addresses unique to avoid problems!\n"); | ||
93 | } | ||
94 | rcu_read_unlock(); | ||
95 | } | ||
96 | |||
97 | /* adjusts the MTU if a new interface with a smaller MTU appeared. */ | ||
98 | void update_min_mtu(void) | ||
99 | { | ||
100 | int min_mtu; | ||
101 | |||
102 | min_mtu = hardif_min_mtu(); | ||
103 | if (soft_device->mtu != min_mtu) | ||
104 | soft_device->mtu = min_mtu; | ||
105 | } | ||
106 | |||
107 | /* checks if the interface is up. (returns 1 if it is) */ | ||
108 | static int hardif_is_interface_up(char *dev) | ||
109 | { | ||
110 | struct net_device *net_dev; | ||
111 | |||
112 | /** | ||
113 | * if we already have an interface in our interface list and | ||
114 | * the current interface is not the primary interface and | ||
115 | * the primary interface is not up and | ||
116 | * the primary interface has never been up - don't activate any | ||
117 | * secondary interface ! | ||
118 | */ | ||
119 | |||
120 | rcu_read_lock(); | ||
121 | if ((!list_empty(&if_list)) && | ||
122 | strncmp(((struct batman_if *)if_list.next)->dev, dev, IFNAMSIZ) && | ||
123 | !(((struct batman_if *)if_list.next)->if_active == IF_ACTIVE) && | ||
124 | !(((struct batman_if *)if_list.next)->if_active == IF_TO_BE_ACTIVATED) && | ||
125 | (!main_if_was_up())) { | ||
126 | rcu_read_unlock(); | ||
127 | goto end; | ||
128 | } | ||
129 | rcu_read_unlock(); | ||
130 | |||
131 | #ifdef __NET_NET_NAMESPACE_H | ||
132 | net_dev = dev_get_by_name(&init_net, dev); | ||
133 | #else | ||
134 | net_dev = dev_get_by_name(dev); | ||
135 | #endif | ||
136 | if (!net_dev) | ||
137 | goto end; | ||
138 | |||
139 | if (!(net_dev->flags & IFF_UP)) | ||
140 | goto failure; | ||
141 | |||
142 | dev_put(net_dev); | ||
143 | return 1; | ||
144 | |||
145 | failure: | ||
146 | dev_put(net_dev); | ||
147 | end: | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /* deactivates the interface. */ | ||
152 | void hardif_deactivate_interface(struct batman_if *batman_if) | ||
153 | { | ||
154 | if (batman_if->if_active != IF_ACTIVE) | ||
155 | return; | ||
156 | |||
157 | if (batman_if->raw_sock) | ||
158 | sock_release(batman_if->raw_sock); | ||
159 | |||
160 | /** | ||
161 | * batman_if->net_dev has been acquired by dev_get_by_name() in | ||
162 | * proc_interfaces_write() and has to be unreferenced. | ||
163 | */ | ||
164 | |||
165 | if (batman_if->net_dev) | ||
166 | dev_put(batman_if->net_dev); | ||
167 | |||
168 | batman_if->raw_sock = NULL; | ||
169 | batman_if->net_dev = NULL; | ||
170 | |||
171 | batman_if->if_active = IF_INACTIVE; | ||
172 | active_ifs--; | ||
173 | |||
174 | debug_log(LOG_TYPE_NOTICE, "Interface deactivated: %s\n", | ||
175 | batman_if->dev); | ||
176 | } | ||
177 | |||
178 | /* (re)activate given interface. */ | ||
179 | static void hardif_activate_interface(struct batman_if *batman_if) | ||
180 | { | ||
181 | struct sockaddr_ll bind_addr; | ||
182 | int retval; | ||
183 | |||
184 | if (batman_if->if_active != IF_INACTIVE) | ||
185 | return; | ||
186 | |||
187 | #ifdef __NET_NET_NAMESPACE_H | ||
188 | batman_if->net_dev = dev_get_by_name(&init_net, batman_if->dev); | ||
189 | #else | ||
190 | batman_if->net_dev = dev_get_by_name(batman_if->dev); | ||
191 | #endif | ||
192 | if (!batman_if->net_dev) | ||
193 | goto dev_err; | ||
194 | |||
195 | retval = sock_create_kern(PF_PACKET, SOCK_RAW, | ||
196 | __constant_htons(ETH_P_BATMAN), | ||
197 | &batman_if->raw_sock); | ||
198 | |||
199 | if (retval < 0) { | ||
200 | debug_log(LOG_TYPE_WARN, "Can't create raw socket: %i\n", | ||
201 | retval); | ||
202 | goto sock_err; | ||
203 | } | ||
204 | |||
205 | bind_addr.sll_family = AF_PACKET; | ||
206 | bind_addr.sll_ifindex = batman_if->net_dev->ifindex; | ||
207 | bind_addr.sll_protocol = 0; /* is set by the kernel */ | ||
208 | |||
209 | retval = kernel_bind(batman_if->raw_sock, | ||
210 | (struct sockaddr *)&bind_addr, sizeof(bind_addr)); | ||
211 | |||
212 | if (retval < 0) { | ||
213 | debug_log(LOG_TYPE_WARN, "Can't create bind raw socket: %i\n", | ||
214 | retval); | ||
215 | goto bind_err; | ||
216 | } | ||
217 | |||
218 | check_known_mac_addr(batman_if->net_dev->dev_addr); | ||
219 | |||
220 | batman_if->raw_sock->sk->sk_user_data = | ||
221 | batman_if->raw_sock->sk->sk_data_ready; | ||
222 | batman_if->raw_sock->sk->sk_data_ready = batman_data_ready; | ||
223 | |||
224 | addr_to_string(batman_if->addr_str, batman_if->net_dev->dev_addr); | ||
225 | |||
226 | memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig, | ||
227 | batman_if->net_dev->dev_addr, ETH_ALEN); | ||
228 | memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender, | ||
229 | batman_if->net_dev->dev_addr, ETH_ALEN); | ||
230 | |||
231 | batman_if->if_active = IF_TO_BE_ACTIVATED; | ||
232 | active_ifs++; | ||
233 | |||
234 | /* save the mac address if it is our primary interface */ | ||
235 | if (batman_if->if_num == 0) | ||
236 | set_main_if_addr(batman_if->net_dev->dev_addr); | ||
237 | |||
238 | debug_log(LOG_TYPE_NOTICE, "Interface activated: %s\n", | ||
239 | batman_if->dev); | ||
240 | |||
241 | return; | ||
242 | |||
243 | bind_err: | ||
244 | sock_release(batman_if->raw_sock); | ||
245 | sock_err: | ||
246 | dev_put(batman_if->net_dev); | ||
247 | dev_err: | ||
248 | batman_if->raw_sock = NULL; | ||
249 | batman_if->net_dev = NULL; | ||
250 | } | ||
251 | |||
252 | static void hardif_free_interface(struct rcu_head *rcu) | ||
253 | { | ||
254 | struct batman_if *batman_if = container_of(rcu, struct batman_if, rcu); | ||
255 | |||
256 | kfree(batman_if->packet_buff); | ||
257 | kfree(batman_if->dev); | ||
258 | kfree(batman_if); | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * called by | ||
263 | * - echo '' > /proc/.../interfaces | ||
264 | * - modprobe -r batman-adv-core | ||
265 | */ | ||
266 | /* removes and frees all interfaces */ | ||
267 | void hardif_remove_interfaces(void) | ||
268 | { | ||
269 | struct batman_if *batman_if = NULL; | ||
270 | |||
271 | avail_ifs = 0; | ||
272 | |||
273 | /* no lock needed - we don't delete somewhere else */ | ||
274 | list_for_each_entry(batman_if, &if_list, list) { | ||
275 | |||
276 | list_del_rcu(&batman_if->list); | ||
277 | |||
278 | /* first deactivate interface */ | ||
279 | if (batman_if->if_active != IF_INACTIVE) | ||
280 | hardif_deactivate_interface(batman_if); | ||
281 | |||
282 | call_rcu(&batman_if->rcu, hardif_free_interface); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static int resize_orig(struct orig_node *orig_node, int if_num) | ||
287 | { | ||
288 | void *data_ptr; | ||
289 | |||
290 | data_ptr = kmalloc((if_num + 1) * sizeof(TYPE_OF_WORD) * NUM_WORDS, | ||
291 | GFP_ATOMIC); | ||
292 | if (!data_ptr) { | ||
293 | debug_log(LOG_TYPE_WARN, "Can't resize orig: out of memory\n"); | ||
294 | return -1; | ||
295 | } | ||
296 | |||
297 | memcpy(data_ptr, orig_node->bcast_own, | ||
298 | if_num * sizeof(TYPE_OF_WORD) * NUM_WORDS); | ||
299 | kfree(orig_node->bcast_own); | ||
300 | orig_node->bcast_own = data_ptr; | ||
301 | |||
302 | data_ptr = kmalloc((if_num + 1) * sizeof(uint8_t), GFP_ATOMIC); | ||
303 | if (!data_ptr) { | ||
304 | debug_log(LOG_TYPE_WARN, "Can't resize orig: out of memory\n"); | ||
305 | return -1; | ||
306 | } | ||
307 | |||
308 | memcpy(data_ptr, orig_node->bcast_own_sum, if_num * sizeof(uint8_t)); | ||
309 | kfree(orig_node->bcast_own_sum); | ||
310 | orig_node->bcast_own_sum = data_ptr; | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | |||
316 | /* adds an interface the interface list and activate it, if possible */ | ||
317 | int hardif_add_interface(char *dev, int if_num) | ||
318 | { | ||
319 | struct batman_if *batman_if; | ||
320 | struct batman_packet *batman_packet; | ||
321 | struct orig_node *orig_node; | ||
322 | struct hash_it_t *hashit = NULL; | ||
323 | |||
324 | batman_if = kmalloc(sizeof(struct batman_if), GFP_KERNEL); | ||
325 | |||
326 | if (!batman_if) { | ||
327 | debug_log(LOG_TYPE_WARN, "Can't add interface (%s): out of memory\n", dev); | ||
328 | return -1; | ||
329 | } | ||
330 | |||
331 | batman_if->raw_sock = NULL; | ||
332 | batman_if->net_dev = NULL; | ||
333 | |||
334 | if ((if_num == 0) && (num_hna > 0)) | ||
335 | batman_if->packet_len = BAT_PACKET_LEN + num_hna * ETH_ALEN; | ||
336 | else | ||
337 | batman_if->packet_len = BAT_PACKET_LEN; | ||
338 | |||
339 | batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_KERNEL); | ||
340 | |||
341 | if (!batman_if->packet_buff) { | ||
342 | debug_log(LOG_TYPE_WARN, "Can't add interface packet (%s): out of memory\n", dev); | ||
343 | goto out; | ||
344 | } | ||
345 | |||
346 | batman_if->if_num = if_num; | ||
347 | batman_if->dev = dev; | ||
348 | batman_if->if_active = IF_INACTIVE; | ||
349 | INIT_RCU_HEAD(&batman_if->rcu); | ||
350 | |||
351 | debug_log(LOG_TYPE_NOTICE, "Adding interface: %s\n", dev); | ||
352 | avail_ifs++; | ||
353 | |||
354 | INIT_LIST_HEAD(&batman_if->list); | ||
355 | |||
356 | batman_packet = (struct batman_packet *)(batman_if->packet_buff); | ||
357 | batman_packet->packet_type = BAT_PACKET; | ||
358 | batman_packet->version = COMPAT_VERSION; | ||
359 | batman_packet->flags = 0x00; | ||
360 | batman_packet->ttl = (batman_if->if_num > 0 ? 2 : TTL); | ||
361 | batman_packet->flags = 0; | ||
362 | batman_packet->tq = TQ_MAX_VALUE; | ||
363 | batman_packet->num_hna = 0; | ||
364 | |||
365 | if (batman_if->packet_len != BAT_PACKET_LEN) { | ||
366 | unsigned char *hna_buff; | ||
367 | int hna_len; | ||
368 | |||
369 | hna_buff = batman_if->packet_buff + BAT_PACKET_LEN; | ||
370 | hna_len = batman_if->packet_len - BAT_PACKET_LEN; | ||
371 | batman_packet->num_hna = hna_local_fill_buffer(hna_buff, | ||
372 | hna_len); | ||
373 | } | ||
374 | |||
375 | atomic_set(&batman_if->seqno, 1); | ||
376 | |||
377 | /* resize all orig nodes because orig_node->bcast_own(_sum) depend on | ||
378 | * if_num */ | ||
379 | spin_lock(&orig_hash_lock); | ||
380 | |||
381 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
382 | orig_node = hashit->bucket->data; | ||
383 | if (resize_orig(orig_node, if_num) == -1) { | ||
384 | spin_unlock(&orig_hash_lock); | ||
385 | goto out; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | spin_unlock(&orig_hash_lock); | ||
390 | |||
391 | if (!hardif_is_interface_up(batman_if->dev)) | ||
392 | debug_log(LOG_TYPE_WARN, "Not using interface %s (retrying later): interface not active\n", batman_if->dev); | ||
393 | else | ||
394 | hardif_activate_interface(batman_if); | ||
395 | |||
396 | list_add_tail_rcu(&batman_if->list, &if_list); | ||
397 | |||
398 | /* begin sending originator messages on that interface */ | ||
399 | schedule_own_packet(batman_if); | ||
400 | return 1; | ||
401 | |||
402 | out: | ||
403 | if (batman_if->packet_buff) | ||
404 | kfree(batman_if->packet_buff); | ||
405 | kfree(batman_if); | ||
406 | kfree(dev); | ||
407 | return -1; | ||
408 | } | ||
409 | |||
410 | char hardif_get_active_if_num(void) | ||
411 | { | ||
412 | return active_ifs; | ||
413 | } | ||
414 | |||
415 | static int hard_if_event(struct notifier_block *this, | ||
416 | unsigned long event, void *ptr) | ||
417 | { | ||
418 | struct net_device *dev = (struct net_device *)ptr; | ||
419 | struct batman_if *batman_if = get_batman_if_by_name(dev->name); | ||
420 | |||
421 | if (!batman_if) | ||
422 | goto out; | ||
423 | |||
424 | switch (event) { | ||
425 | case NETDEV_GOING_DOWN: | ||
426 | case NETDEV_DOWN: | ||
427 | case NETDEV_UNREGISTER: | ||
428 | hardif_deactivate_interface(batman_if); | ||
429 | break; | ||
430 | case NETDEV_UP: | ||
431 | hardif_activate_interface(batman_if); | ||
432 | if ((atomic_read(&module_state) == MODULE_INACTIVE) && | ||
433 | (hardif_get_active_if_num() > 0)) { | ||
434 | activate_module(); | ||
435 | } | ||
436 | break; | ||
437 | /* NETDEV_CHANGEADDR - mac address change - what are we doing here ? */ | ||
438 | default: | ||
439 | /* debug_log(LOG_TYPE_CRIT, "hard_if_event: %s %i\n", dev->name, event); */ | ||
440 | break; | ||
441 | }; | ||
442 | |||
443 | update_min_mtu(); | ||
444 | |||
445 | out: | ||
446 | return NOTIFY_DONE; | ||
447 | } | ||
448 | |||
449 | struct notifier_block hard_if_notifier = { | ||
450 | .notifier_call = hard_if_event, | ||
451 | }; | ||
diff --git a/drivers/staging/batman-adv/hard-interface.h b/drivers/staging/batman-adv/hard-interface.h new file mode 100644 index 000000000000..742358c00c0e --- /dev/null +++ b/drivers/staging/batman-adv/hard-interface.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #define IF_INACTIVE 0 | ||
23 | #define IF_ACTIVE 1 | ||
24 | /* #define IF_TO_BE_DEACTIVATED 2 - not needed anymore */ | ||
25 | #define IF_TO_BE_ACTIVATED 3 | ||
26 | |||
27 | extern struct notifier_block hard_if_notifier; | ||
28 | |||
29 | void hardif_remove_interfaces(void); | ||
30 | int hardif_add_interface(char *dev, int if_num); | ||
31 | void hardif_deactivate_interface(struct batman_if *batman_if); | ||
32 | char hardif_get_active_if_num(void); | ||
33 | void hardif_check_interfaces_status(void); | ||
34 | void hardif_check_interfaces_status_wq(struct work_struct *work); | ||
35 | int hardif_min_mtu(void); | ||
36 | void update_min_mtu(void); | ||
diff --git a/drivers/staging/batman-adv/hash.c b/drivers/staging/batman-adv/hash.c new file mode 100644 index 000000000000..61cb4a20ebca --- /dev/null +++ b/drivers/staging/batman-adv/hash.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Simon Wunderlich, Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "hash.h" | ||
24 | |||
25 | /* clears the hash */ | ||
26 | void hash_init(struct hashtable_t *hash) | ||
27 | { | ||
28 | int i; | ||
29 | |||
30 | hash->elements = 0; | ||
31 | |||
32 | for (i = 0 ; i < hash->size; i++) | ||
33 | hash->table[i] = NULL; | ||
34 | } | ||
35 | |||
36 | /* remove the hash structure. if hashdata_free_cb != NULL, this function will be | ||
37 | * called to remove the elements inside of the hash. if you don't remove the | ||
38 | * elements, memory might be leaked. */ | ||
39 | void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb) | ||
40 | { | ||
41 | struct element_t *bucket, *last_bucket; | ||
42 | int i; | ||
43 | |||
44 | for (i = 0; i < hash->size; i++) { | ||
45 | bucket = hash->table[i]; | ||
46 | |||
47 | while (bucket != NULL) { | ||
48 | if (free_cb != NULL) | ||
49 | free_cb(bucket->data); | ||
50 | |||
51 | last_bucket = bucket; | ||
52 | bucket = bucket->next; | ||
53 | kfree(last_bucket); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | hash_destroy(hash); | ||
58 | } | ||
59 | |||
60 | /* free only the hashtable and the hash itself. */ | ||
61 | void hash_destroy(struct hashtable_t *hash) | ||
62 | { | ||
63 | kfree(hash->table); | ||
64 | kfree(hash); | ||
65 | } | ||
66 | |||
67 | /* iterate though the hash. first element is selected with iter_in NULL. use | ||
68 | * the returned iterator to access the elements until hash_it_t returns NULL. */ | ||
69 | struct hash_it_t *hash_iterate(struct hashtable_t *hash, | ||
70 | struct hash_it_t *iter_in) | ||
71 | { | ||
72 | struct hash_it_t *iter; | ||
73 | |||
74 | if (!hash) | ||
75 | return NULL; | ||
76 | |||
77 | if (iter_in == NULL) { | ||
78 | iter = kmalloc(sizeof(struct hash_it_t), GFP_ATOMIC); | ||
79 | iter->index = -1; | ||
80 | iter->bucket = NULL; | ||
81 | iter->prev_bucket = NULL; | ||
82 | } else { | ||
83 | iter = iter_in; | ||
84 | } | ||
85 | |||
86 | /* sanity checks first (if our bucket got deleted in the last | ||
87 | * iteration): */ | ||
88 | if (iter->bucket != NULL) { | ||
89 | if (iter->first_bucket != NULL) { | ||
90 | /* we're on the first element and it got removed after | ||
91 | * the last iteration. */ | ||
92 | if ((*iter->first_bucket) != iter->bucket) { | ||
93 | /* there are still other elements in the list */ | ||
94 | if ((*iter->first_bucket) != NULL) { | ||
95 | iter->prev_bucket = NULL; | ||
96 | iter->bucket = (*iter->first_bucket); | ||
97 | iter->first_bucket = | ||
98 | &hash->table[iter->index]; | ||
99 | return iter; | ||
100 | } else { | ||
101 | iter->bucket = NULL; | ||
102 | } | ||
103 | } | ||
104 | } else if (iter->prev_bucket != NULL) { | ||
105 | /* | ||
106 | * we're not on the first element, and the bucket got | ||
107 | * removed after the last iteration. the last bucket's | ||
108 | * next pointer is not pointing to our actual bucket | ||
109 | * anymore. select the next. | ||
110 | */ | ||
111 | if (iter->prev_bucket->next != iter->bucket) | ||
112 | iter->bucket = iter->prev_bucket; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /* now as we are sane, select the next one if there is some */ | ||
117 | if (iter->bucket != NULL) { | ||
118 | if (iter->bucket->next != NULL) { | ||
119 | iter->prev_bucket = iter->bucket; | ||
120 | iter->bucket = iter->bucket->next; | ||
121 | iter->first_bucket = NULL; | ||
122 | return iter; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | /* if not returned yet, we've reached the last one on the index and have | ||
127 | * to search forward */ | ||
128 | iter->index++; | ||
129 | /* go through the entries of the hash table */ | ||
130 | while (iter->index < hash->size) { | ||
131 | if ((hash->table[iter->index]) != NULL) { | ||
132 | iter->prev_bucket = NULL; | ||
133 | iter->bucket = hash->table[iter->index]; | ||
134 | iter->first_bucket = &hash->table[iter->index]; | ||
135 | return iter; | ||
136 | } else { | ||
137 | iter->index++; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /* nothing to iterate over anymore */ | ||
142 | kfree(iter); | ||
143 | return NULL; | ||
144 | } | ||
145 | |||
146 | /* allocates and clears the hash */ | ||
147 | struct hashtable_t *hash_new(int size, hashdata_compare_cb compare, | ||
148 | hashdata_choose_cb choose) | ||
149 | { | ||
150 | struct hashtable_t *hash; | ||
151 | |||
152 | hash = kmalloc(sizeof(struct hashtable_t) , GFP_ATOMIC); | ||
153 | |||
154 | if (hash == NULL) | ||
155 | return NULL; | ||
156 | |||
157 | hash->size = size; | ||
158 | hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC); | ||
159 | |||
160 | if (hash->table == NULL) { | ||
161 | kfree(hash); | ||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | hash_init(hash); | ||
166 | |||
167 | hash->compare = compare; | ||
168 | hash->choose = choose; | ||
169 | |||
170 | return hash; | ||
171 | } | ||
172 | |||
173 | /* adds data to the hashtable. returns 0 on success, -1 on error */ | ||
174 | int hash_add(struct hashtable_t *hash, void *data) | ||
175 | { | ||
176 | int index; | ||
177 | struct element_t *bucket, *prev_bucket = NULL; | ||
178 | |||
179 | if (!hash) | ||
180 | return -1; | ||
181 | |||
182 | index = hash->choose(data, hash->size); | ||
183 | bucket = hash->table[index]; | ||
184 | |||
185 | while (bucket != NULL) { | ||
186 | if (hash->compare(bucket->data, data)) | ||
187 | return -1; | ||
188 | |||
189 | prev_bucket = bucket; | ||
190 | bucket = bucket->next; | ||
191 | } | ||
192 | |||
193 | /* found the tail of the list, add new element */ | ||
194 | bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); | ||
195 | |||
196 | if (bucket == NULL) | ||
197 | return -1; | ||
198 | |||
199 | bucket->data = data; | ||
200 | bucket->next = NULL; | ||
201 | |||
202 | /* and link it */ | ||
203 | if (prev_bucket == NULL) | ||
204 | hash->table[index] = bucket; | ||
205 | else | ||
206 | prev_bucket->next = bucket; | ||
207 | |||
208 | hash->elements++; | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | /* finds data, based on the key in keydata. returns the found data on success, | ||
213 | * or NULL on error */ | ||
214 | void *hash_find(struct hashtable_t *hash, void *keydata) | ||
215 | { | ||
216 | int index; | ||
217 | struct element_t *bucket; | ||
218 | |||
219 | if (!hash) | ||
220 | return NULL; | ||
221 | |||
222 | index = hash->choose(keydata , hash->size); | ||
223 | bucket = hash->table[index]; | ||
224 | |||
225 | while (bucket != NULL) { | ||
226 | if (hash->compare(bucket->data, keydata)) | ||
227 | return bucket->data; | ||
228 | |||
229 | bucket = bucket->next; | ||
230 | } | ||
231 | |||
232 | return NULL; | ||
233 | } | ||
234 | |||
235 | /* remove bucket (this might be used in hash_iterate() if you already found the | ||
236 | * bucket you want to delete and don't need the overhead to find it again with | ||
237 | * hash_remove(). But usually, you don't want to use this function, as it | ||
238 | * fiddles with hash-internals. */ | ||
239 | void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t) | ||
240 | { | ||
241 | void *data_save; | ||
242 | |||
243 | data_save = hash_it_t->bucket->data; | ||
244 | |||
245 | if (hash_it_t->prev_bucket != NULL) | ||
246 | hash_it_t->prev_bucket->next = hash_it_t->bucket->next; | ||
247 | else if (hash_it_t->first_bucket != NULL) | ||
248 | (*hash_it_t->first_bucket) = hash_it_t->bucket->next; | ||
249 | |||
250 | kfree(hash_it_t->bucket); | ||
251 | hash->elements--; | ||
252 | |||
253 | return data_save; | ||
254 | } | ||
255 | |||
256 | /* removes data from hash, if found. returns pointer do data on success, so you | ||
257 | * can remove the used structure yourself, or NULL on error . data could be the | ||
258 | * structure you use with just the key filled, we just need the key for | ||
259 | * comparing. */ | ||
260 | void *hash_remove(struct hashtable_t *hash, void *data) | ||
261 | { | ||
262 | struct hash_it_t hash_it_t; | ||
263 | |||
264 | hash_it_t.index = hash->choose(data, hash->size); | ||
265 | hash_it_t.bucket = hash->table[hash_it_t.index]; | ||
266 | hash_it_t.prev_bucket = NULL; | ||
267 | |||
268 | while (hash_it_t.bucket != NULL) { | ||
269 | if (hash->compare(hash_it_t.bucket->data, data)) { | ||
270 | hash_it_t.first_bucket = | ||
271 | (hash_it_t.bucket == | ||
272 | hash->table[hash_it_t.index] ? | ||
273 | &hash->table[hash_it_t.index] : NULL); | ||
274 | return hash_remove_bucket(hash, &hash_it_t); | ||
275 | } | ||
276 | |||
277 | hash_it_t.prev_bucket = hash_it_t.bucket; | ||
278 | hash_it_t.bucket = hash_it_t.bucket->next; | ||
279 | } | ||
280 | |||
281 | return NULL; | ||
282 | } | ||
283 | |||
284 | /* resize the hash, returns the pointer to the new hash or NULL on | ||
285 | * error. removes the old hash on success. */ | ||
286 | struct hashtable_t *hash_resize(struct hashtable_t *hash, int size) | ||
287 | { | ||
288 | struct hashtable_t *new_hash; | ||
289 | struct element_t *bucket; | ||
290 | int i; | ||
291 | |||
292 | /* initialize a new hash with the new size */ | ||
293 | new_hash = hash_new(size, hash->compare, hash->choose); | ||
294 | |||
295 | if (new_hash == NULL) | ||
296 | return NULL; | ||
297 | |||
298 | /* copy the elements */ | ||
299 | for (i = 0; i < hash->size; i++) { | ||
300 | bucket = hash->table[i]; | ||
301 | |||
302 | while (bucket != NULL) { | ||
303 | hash_add(new_hash, bucket->data); | ||
304 | bucket = bucket->next; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | /* remove hash and eventual overflow buckets but not the content | ||
309 | * itself. */ | ||
310 | hash_delete(hash, NULL); | ||
311 | |||
312 | return new_hash; | ||
313 | } | ||
diff --git a/drivers/staging/batman-adv/hash.h b/drivers/staging/batman-adv/hash.h new file mode 100644 index 000000000000..bb60f082be6a --- /dev/null +++ b/drivers/staging/batman-adv/hash.h | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Simon Wunderlich, Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _BATMAN_HASH_H | ||
23 | #define _BATMAN_HASH_H | ||
24 | |||
25 | typedef int (*hashdata_compare_cb)(void *, void *); | ||
26 | typedef int (*hashdata_choose_cb)(void *, int); | ||
27 | typedef void (*hashdata_free_cb)(void *); | ||
28 | |||
29 | struct element_t { | ||
30 | void *data; /* pointer to the data */ | ||
31 | struct element_t *next; /* overflow bucket pointer */ | ||
32 | }; | ||
33 | |||
34 | struct hash_it_t { | ||
35 | int index; | ||
36 | struct element_t *bucket; | ||
37 | struct element_t *prev_bucket; | ||
38 | struct element_t **first_bucket; | ||
39 | }; | ||
40 | |||
41 | struct hashtable_t { | ||
42 | struct element_t **table; /* the hashtable itself, with the buckets */ | ||
43 | int elements; /* number of elements registered */ | ||
44 | int size; /* size of hashtable */ | ||
45 | hashdata_compare_cb compare;/* callback to a compare function. should | ||
46 | * compare 2 element datas for their keys, | ||
47 | * return 0 if same and not 0 if not | ||
48 | * same */ | ||
49 | hashdata_choose_cb choose; /* the hashfunction, should return an index | ||
50 | * based on the key in the data of the first | ||
51 | * argument and the size the second */ | ||
52 | }; | ||
53 | |||
54 | /* clears the hash */ | ||
55 | void hash_init(struct hashtable_t *hash); | ||
56 | |||
57 | /* allocates and clears the hash */ | ||
58 | struct hashtable_t *hash_new(int size, hashdata_compare_cb compare, | ||
59 | hashdata_choose_cb choose); | ||
60 | |||
61 | /* remove bucket (this might be used in hash_iterate() if you already found the | ||
62 | * bucket you want to delete and don't need the overhead to find it again with | ||
63 | * hash_remove(). But usually, you don't want to use this function, as it | ||
64 | * fiddles with hash-internals. */ | ||
65 | void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t); | ||
66 | |||
67 | /* remove the hash structure. if hashdata_free_cb != NULL, this function will be | ||
68 | * called to remove the elements inside of the hash. if you don't remove the | ||
69 | * elements, memory might be leaked. */ | ||
70 | void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb); | ||
71 | |||
72 | /* free only the hashtable and the hash itself. */ | ||
73 | void hash_destroy(struct hashtable_t *hash); | ||
74 | |||
75 | /* adds data to the hashtable. returns 0 on success, -1 on error */ | ||
76 | int hash_add(struct hashtable_t *hash, void *data); | ||
77 | |||
78 | /* removes data from hash, if found. returns pointer do data on success, so you | ||
79 | * can remove the used structure yourself, or NULL on error . data could be the | ||
80 | * structure you use with just the key filled, we just need the key for | ||
81 | * comparing. */ | ||
82 | void *hash_remove(struct hashtable_t *hash, void *data); | ||
83 | |||
84 | /* finds data, based on the key in keydata. returns the found data on success, | ||
85 | * or NULL on error */ | ||
86 | void *hash_find(struct hashtable_t *hash, void *keydata); | ||
87 | |||
88 | /* resize the hash, returns the pointer to the new hash or NULL on | ||
89 | * error. removes the old hash on success */ | ||
90 | struct hashtable_t *hash_resize(struct hashtable_t *hash, int size); | ||
91 | |||
92 | /* iterate though the hash. first element is selected with iter_in NULL. use | ||
93 | * the returned iterator to access the elements until hash_it_t returns NULL. */ | ||
94 | struct hash_it_t *hash_iterate(struct hashtable_t *hash, | ||
95 | struct hash_it_t *iter_in); | ||
96 | |||
97 | /* print the hash table for debugging */ | ||
98 | void hash_debug(struct hashtable_t *hash); | ||
99 | #endif | ||
diff --git a/drivers/staging/batman-adv/log.c b/drivers/staging/batman-adv/log.c new file mode 100644 index 000000000000..f37c7f01a9f5 --- /dev/null +++ b/drivers/staging/batman-adv/log.c | |||
@@ -0,0 +1,179 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "log.h" | ||
24 | |||
25 | #define LOG_BUF_MASK (log_buf_len-1) | ||
26 | #define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK]) | ||
27 | |||
28 | static char log_buf[LOG_BUF_LEN]; | ||
29 | static int log_buf_len = LOG_BUF_LEN; | ||
30 | static unsigned long log_start; | ||
31 | static unsigned long log_end; | ||
32 | uint8_t log_level; | ||
33 | |||
34 | static DEFINE_SPINLOCK(logbuf_lock); | ||
35 | |||
36 | const struct file_operations proc_log_operations = { | ||
37 | .open = log_open, | ||
38 | .release = log_release, | ||
39 | .read = log_read, | ||
40 | .write = log_write, | ||
41 | .poll = log_poll, | ||
42 | }; | ||
43 | |||
44 | static DECLARE_WAIT_QUEUE_HEAD(log_wait); | ||
45 | |||
46 | static void emit_log_char(char c) | ||
47 | { | ||
48 | LOG_BUF(log_end) = c; | ||
49 | log_end++; | ||
50 | |||
51 | if (log_end - log_start > log_buf_len) | ||
52 | log_start = log_end - log_buf_len; | ||
53 | } | ||
54 | |||
55 | static int fdebug_log(char *fmt, ...) | ||
56 | { | ||
57 | int printed_len; | ||
58 | char *p; | ||
59 | va_list args; | ||
60 | static char debug_log_buf[256]; | ||
61 | unsigned long flags; | ||
62 | |||
63 | spin_lock_irqsave(&logbuf_lock, flags); | ||
64 | va_start(args, fmt); | ||
65 | printed_len = vscnprintf(debug_log_buf, sizeof(debug_log_buf), fmt, | ||
66 | args); | ||
67 | va_end(args); | ||
68 | |||
69 | for (p = debug_log_buf; *p != 0; p++) | ||
70 | emit_log_char(*p); | ||
71 | |||
72 | spin_unlock_irqrestore(&logbuf_lock, flags); | ||
73 | |||
74 | wake_up(&log_wait); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | int debug_log(int type, char *fmt, ...) | ||
80 | { | ||
81 | va_list args; | ||
82 | int retval = 0; | ||
83 | char tmp_log_buf[256]; | ||
84 | |||
85 | /* only critical information get into the official kernel log */ | ||
86 | if (type == LOG_TYPE_CRIT) { | ||
87 | va_start(args, fmt); | ||
88 | vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); | ||
89 | printk(KERN_ERR "batman-adv: %s", tmp_log_buf); | ||
90 | va_end(args); | ||
91 | } | ||
92 | |||
93 | if ((type == LOG_TYPE_CRIT) || (log_level & type)) { | ||
94 | va_start(args, fmt); | ||
95 | vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); | ||
96 | fdebug_log("[%10u] %s", (jiffies / HZ), tmp_log_buf); | ||
97 | va_end(args); | ||
98 | } | ||
99 | |||
100 | return retval; | ||
101 | } | ||
102 | |||
103 | int log_open(struct inode *inode, struct file *file) | ||
104 | { | ||
105 | inc_module_count(); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | int log_release(struct inode *inode, struct file *file) | ||
110 | { | ||
111 | dec_module_count(); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | ssize_t log_read(struct file *file, char __user *buf, size_t count, | ||
116 | loff_t *ppos) | ||
117 | { | ||
118 | int error, i = 0; | ||
119 | char c; | ||
120 | unsigned long flags; | ||
121 | |||
122 | if ((file->f_flags & O_NONBLOCK) && !(log_end - log_start)) | ||
123 | return -EAGAIN; | ||
124 | |||
125 | if ((!buf) || (count < 0)) | ||
126 | return -EINVAL; | ||
127 | |||
128 | if (count == 0) | ||
129 | return 0; | ||
130 | |||
131 | if (!access_ok(VERIFY_WRITE, buf, count)) | ||
132 | return -EFAULT; | ||
133 | |||
134 | error = wait_event_interruptible(log_wait, (log_start - log_end)); | ||
135 | |||
136 | if (error) | ||
137 | return error; | ||
138 | |||
139 | spin_lock_irqsave(&logbuf_lock, flags); | ||
140 | |||
141 | while ((!error) && (log_start != log_end) && (i < count)) { | ||
142 | c = LOG_BUF(log_start); | ||
143 | |||
144 | log_start++; | ||
145 | |||
146 | spin_unlock_irqrestore(&logbuf_lock, flags); | ||
147 | |||
148 | error = __put_user(c, buf); | ||
149 | |||
150 | spin_lock_irqsave(&logbuf_lock, flags); | ||
151 | |||
152 | buf++; | ||
153 | i++; | ||
154 | |||
155 | } | ||
156 | |||
157 | spin_unlock_irqrestore(&logbuf_lock, flags); | ||
158 | |||
159 | if (!error) | ||
160 | return i; | ||
161 | |||
162 | return error; | ||
163 | } | ||
164 | |||
165 | ssize_t log_write(struct file *file, const char __user *buf, size_t count, | ||
166 | loff_t *ppos) | ||
167 | { | ||
168 | return count; | ||
169 | } | ||
170 | |||
171 | unsigned int log_poll(struct file *file, poll_table *wait) | ||
172 | { | ||
173 | poll_wait(file, &log_wait, wait); | ||
174 | |||
175 | if (log_end - log_start) | ||
176 | return POLLIN | POLLRDNORM; | ||
177 | |||
178 | return 0; | ||
179 | } | ||
diff --git a/drivers/staging/batman-adv/log.h b/drivers/staging/batman-adv/log.h new file mode 100644 index 000000000000..780e3abb48f9 --- /dev/null +++ b/drivers/staging/batman-adv/log.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | extern const struct file_operations proc_log_operations; | ||
23 | extern uint8_t log_level; | ||
24 | |||
25 | int debug_log(int type, char *fmt, ...); | ||
26 | int log_open(struct inode *inode, struct file *file); | ||
27 | int log_release(struct inode *inode, struct file *file); | ||
28 | ssize_t log_read(struct file *file, char __user *buf, size_t count, | ||
29 | loff_t *ppos); | ||
30 | ssize_t log_write(struct file *file, const char __user *buf, size_t count, | ||
31 | loff_t *ppos); | ||
32 | unsigned int log_poll(struct file *file, poll_table *wait); | ||
diff --git a/drivers/staging/batman-adv/main.c b/drivers/staging/batman-adv/main.c new file mode 100644 index 000000000000..bb89bfc5dda6 --- /dev/null +++ b/drivers/staging/batman-adv/main.c | |||
@@ -0,0 +1,286 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "proc.h" | ||
24 | #include "log.h" | ||
25 | #include "routing.h" | ||
26 | #include "send.h" | ||
27 | #include "soft-interface.h" | ||
28 | #include "device.h" | ||
29 | #include "translation-table.h" | ||
30 | #include "hard-interface.h" | ||
31 | #include "types.h" | ||
32 | #include "vis.h" | ||
33 | #include "hash.h" | ||
34 | #include "compat.h" | ||
35 | |||
36 | struct list_head if_list; | ||
37 | struct hlist_head forw_bat_list; | ||
38 | struct hlist_head forw_bcast_list; | ||
39 | struct hashtable_t *orig_hash; | ||
40 | |||
41 | DEFINE_SPINLOCK(orig_hash_lock); | ||
42 | DEFINE_SPINLOCK(forw_bat_list_lock); | ||
43 | DEFINE_SPINLOCK(forw_bcast_list_lock); | ||
44 | |||
45 | atomic_t originator_interval; | ||
46 | atomic_t vis_interval; | ||
47 | atomic_t aggregation_enabled; | ||
48 | int16_t num_hna; | ||
49 | int16_t num_ifs; | ||
50 | |||
51 | struct net_device *soft_device; | ||
52 | |||
53 | static struct task_struct *kthread_task; | ||
54 | |||
55 | unsigned char broadcastAddr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||
56 | atomic_t module_state; | ||
57 | |||
58 | struct workqueue_struct *bat_event_workqueue; | ||
59 | |||
60 | int init_module(void) | ||
61 | { | ||
62 | int retval; | ||
63 | |||
64 | INIT_LIST_HEAD(&if_list); | ||
65 | INIT_HLIST_HEAD(&forw_bat_list); | ||
66 | INIT_HLIST_HEAD(&forw_bcast_list); | ||
67 | |||
68 | atomic_set(&module_state, MODULE_INACTIVE); | ||
69 | |||
70 | atomic_set(&originator_interval, 1000); | ||
71 | atomic_set(&vis_interval, 1000);/* TODO: raise this later, this is only | ||
72 | * for debugging now. */ | ||
73 | atomic_set(&aggregation_enabled, 1); | ||
74 | |||
75 | /* the name should not be longer than 10 chars - see | ||
76 | * http://lwn.net/Articles/23634/ */ | ||
77 | bat_event_workqueue = create_singlethread_workqueue("bat_events"); | ||
78 | |||
79 | if (!bat_event_workqueue) | ||
80 | return -ENOMEM; | ||
81 | |||
82 | retval = setup_procfs(); | ||
83 | if (retval < 0) | ||
84 | return retval; | ||
85 | |||
86 | bat_device_init(); | ||
87 | |||
88 | /* initialize layer 2 interface */ | ||
89 | soft_device = alloc_netdev(sizeof(struct bat_priv) , "bat%d", | ||
90 | interface_setup); | ||
91 | |||
92 | if (!soft_device) { | ||
93 | debug_log(LOG_TYPE_CRIT, "Unable to allocate the batman interface\n"); | ||
94 | goto end; | ||
95 | } | ||
96 | |||
97 | retval = register_netdev(soft_device); | ||
98 | |||
99 | if (retval < 0) { | ||
100 | debug_log(LOG_TYPE_CRIT, "Unable to register the batman interface: %i\n", retval); | ||
101 | goto free_soft_device; | ||
102 | } | ||
103 | |||
104 | register_netdevice_notifier(&hard_if_notifier); | ||
105 | |||
106 | debug_log(LOG_TYPE_CRIT, "B.A.T.M.A.N. advanced %s%s (compatibility version %i) loaded \n", | ||
107 | SOURCE_VERSION, REVISION_VERSION_STR, COMPAT_VERSION); | ||
108 | |||
109 | return 0; | ||
110 | |||
111 | free_soft_device: | ||
112 | free_netdev(soft_device); | ||
113 | soft_device = NULL; | ||
114 | end: | ||
115 | return -ENOMEM; | ||
116 | } | ||
117 | |||
118 | void cleanup_module(void) | ||
119 | { | ||
120 | shutdown_module(); | ||
121 | |||
122 | if (soft_device) { | ||
123 | unregister_netdev(soft_device); | ||
124 | soft_device = NULL; | ||
125 | } | ||
126 | |||
127 | unregister_netdevice_notifier(&hard_if_notifier); | ||
128 | cleanup_procfs(); | ||
129 | |||
130 | destroy_workqueue(bat_event_workqueue); | ||
131 | bat_event_workqueue = NULL; | ||
132 | } | ||
133 | |||
134 | /* activates the module, creates bat device, starts timer ... */ | ||
135 | void activate_module(void) | ||
136 | { | ||
137 | if (originator_init() < 1) | ||
138 | goto err; | ||
139 | |||
140 | if (hna_local_init() < 1) | ||
141 | goto err; | ||
142 | |||
143 | if (hna_global_init() < 1) | ||
144 | goto err; | ||
145 | |||
146 | hna_local_add(soft_device->dev_addr); | ||
147 | |||
148 | if (bat_device_setup() < 1) | ||
149 | goto end; | ||
150 | |||
151 | if (vis_init() < 1) | ||
152 | goto err; | ||
153 | |||
154 | /* (re)start kernel thread for packet processing */ | ||
155 | if (!kthread_task) { | ||
156 | kthread_task = kthread_run(packet_recv_thread, NULL, "batman-adv"); | ||
157 | |||
158 | if (IS_ERR(kthread_task)) { | ||
159 | debug_log(LOG_TYPE_CRIT, "Unable to start packet receive thread\n"); | ||
160 | kthread_task = NULL; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | update_min_mtu(); | ||
165 | atomic_set(&module_state, MODULE_ACTIVE); | ||
166 | goto end; | ||
167 | |||
168 | err: | ||
169 | debug_log(LOG_TYPE_CRIT, "Unable to allocate memory for mesh information structures: out of mem ?\n"); | ||
170 | shutdown_module(); | ||
171 | end: | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | /* shuts down the whole module.*/ | ||
176 | void shutdown_module(void) | ||
177 | { | ||
178 | atomic_set(&module_state, MODULE_DEACTIVATING); | ||
179 | |||
180 | purge_outstanding_packets(); | ||
181 | flush_workqueue(bat_event_workqueue); | ||
182 | |||
183 | vis_quit(); | ||
184 | |||
185 | /* deactivate kernel thread for packet processing (if running) */ | ||
186 | if (kthread_task) { | ||
187 | atomic_set(&exit_cond, 1); | ||
188 | wake_up_interruptible(&thread_wait); | ||
189 | kthread_stop(kthread_task); | ||
190 | |||
191 | kthread_task = NULL; | ||
192 | } | ||
193 | |||
194 | originator_free(); | ||
195 | |||
196 | hna_local_free(); | ||
197 | hna_global_free(); | ||
198 | |||
199 | synchronize_net(); | ||
200 | bat_device_destroy(); | ||
201 | |||
202 | hardif_remove_interfaces(); | ||
203 | synchronize_rcu(); | ||
204 | atomic_set(&module_state, MODULE_INACTIVE); | ||
205 | } | ||
206 | |||
207 | void inc_module_count(void) | ||
208 | { | ||
209 | try_module_get(THIS_MODULE); | ||
210 | } | ||
211 | |||
212 | void dec_module_count(void) | ||
213 | { | ||
214 | module_put(THIS_MODULE); | ||
215 | } | ||
216 | |||
217 | int addr_to_string(char *buff, uint8_t *addr) | ||
218 | { | ||
219 | return sprintf(buff, "%02x:%02x:%02x:%02x:%02x:%02x", | ||
220 | addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); | ||
221 | } | ||
222 | |||
223 | /* returns 1 if they are the same originator */ | ||
224 | |||
225 | int compare_orig(void *data1, void *data2) | ||
226 | { | ||
227 | return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); | ||
228 | } | ||
229 | |||
230 | /* hashfunction to choose an entry in a hash table of given size */ | ||
231 | /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ | ||
232 | int choose_orig(void *data, int32_t size) | ||
233 | { | ||
234 | unsigned char *key = data; | ||
235 | uint32_t hash = 0; | ||
236 | size_t i; | ||
237 | |||
238 | for (i = 0; i < 6; i++) { | ||
239 | hash += key[i]; | ||
240 | hash += (hash << 10); | ||
241 | hash ^= (hash >> 6); | ||
242 | } | ||
243 | |||
244 | hash += (hash << 3); | ||
245 | hash ^= (hash >> 11); | ||
246 | hash += (hash << 15); | ||
247 | |||
248 | return hash % size; | ||
249 | } | ||
250 | |||
251 | int is_my_mac(uint8_t *addr) | ||
252 | { | ||
253 | struct batman_if *batman_if; | ||
254 | rcu_read_lock(); | ||
255 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
256 | if ((batman_if->net_dev) && | ||
257 | (compare_orig(batman_if->net_dev->dev_addr, addr))) { | ||
258 | rcu_read_unlock(); | ||
259 | return 1; | ||
260 | } | ||
261 | } | ||
262 | rcu_read_unlock(); | ||
263 | return 0; | ||
264 | |||
265 | } | ||
266 | |||
267 | int is_bcast(uint8_t *addr) | ||
268 | { | ||
269 | return (addr[0] == (uint8_t)0xff) && (addr[1] == (uint8_t)0xff); | ||
270 | } | ||
271 | |||
272 | int is_mcast(uint8_t *addr) | ||
273 | { | ||
274 | return *addr & 0x01; | ||
275 | } | ||
276 | |||
277 | MODULE_LICENSE("GPL"); | ||
278 | |||
279 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
280 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
281 | MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE); | ||
282 | #ifdef REVISION_VERSION | ||
283 | MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION); | ||
284 | #else | ||
285 | MODULE_VERSION(SOURCE_VERSION); | ||
286 | #endif | ||
diff --git a/drivers/staging/batman-adv/main.h b/drivers/staging/batman-adv/main.h new file mode 100644 index 000000000000..facb6b79ee52 --- /dev/null +++ b/drivers/staging/batman-adv/main.h | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | /* Kernel Programming */ | ||
23 | #define LINUX | ||
24 | |||
25 | #define DRIVER_AUTHOR "Marek Lindner <lindner_marek@yahoo.de>, Simon Wunderlich <siwu@hrz.tu-chemnitz.de>" | ||
26 | #define DRIVER_DESC "B.A.T.M.A.N. advanced" | ||
27 | #define DRIVER_DEVICE "batman-adv" | ||
28 | |||
29 | #define SOURCE_VERSION "0.2.1-beta" | ||
30 | |||
31 | |||
32 | /* B.A.T.M.A.N. parameters */ | ||
33 | |||
34 | #define TQ_MAX_VALUE 255 | ||
35 | #define JITTER 20 | ||
36 | #define TTL 50 /* Time To Live of broadcast messages */ | ||
37 | #define MAX_ADDR 16 /* number of interfaces which can be added to | ||
38 | * batman. */ | ||
39 | |||
40 | #define PURGE_TIMEOUT 200000 /* purge originators after time in ms if no | ||
41 | * valid packet comes in -> TODO: check | ||
42 | * influence on TQ_LOCAL_WINDOW_SIZE */ | ||
43 | #define LOCAL_HNA_TIMEOUT 3600000 | ||
44 | |||
45 | #define TQ_LOCAL_WINDOW_SIZE 64 /* sliding packet range of received originator | ||
46 | * messages in squence numbers (should be a | ||
47 | * multiple of our word size) */ | ||
48 | #define TQ_GLOBAL_WINDOW_SIZE 5 | ||
49 | #define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1 | ||
50 | #define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1 | ||
51 | #define TQ_TOTAL_BIDRECT_LIMIT 1 | ||
52 | |||
53 | #define TQ_HOP_PENALTY 10 | ||
54 | |||
55 | #define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE) | ||
56 | |||
57 | #define PACKBUFF_SIZE 2000 | ||
58 | #define LOG_BUF_LEN 8192 /* has to be a power of 2 */ | ||
59 | #define ETH_STR_LEN 20 | ||
60 | |||
61 | #define MAX_AGGREGATION_BYTES 512 /* should not be bigger than 512 bytes or | ||
62 | * change the size of | ||
63 | * forw_packet->direct_link_flags */ | ||
64 | #define MAX_AGGREGATION_MS 100 | ||
65 | |||
66 | #define MODULE_INACTIVE 0 | ||
67 | #define MODULE_ACTIVE 1 | ||
68 | #define MODULE_DEACTIVATING 2 | ||
69 | |||
70 | |||
71 | /* | ||
72 | * Logging | ||
73 | */ | ||
74 | |||
75 | #define LOG_TYPE_CRIT 0 /* highest priority for fatal errors such as | ||
76 | * blocked sockets / failed packet delivery / | ||
77 | * programming errors */ | ||
78 | #define LOG_TYPE_WARN 1 /* warnings for small errors like wrong user | ||
79 | * input / damaged packets / etc */ | ||
80 | #define LOG_TYPE_NOTICE 2 /* notice information for new interfaces / | ||
81 | * changed settings / new originators / etc */ | ||
82 | #define LOG_TYPE_BATMAN 4 /* all messages related to routing / flooding / | ||
83 | * broadcasting / etc */ | ||
84 | #define LOG_TYPE_ROUTES 8 /* route or hna added / changed / deleted */ | ||
85 | #define LOG_TYPE_CRIT_NAME "critical" | ||
86 | #define LOG_TYPE_WARN_NAME "warnings" | ||
87 | #define LOG_TYPE_NOTICE_NAME "notices" | ||
88 | #define LOG_TYPE_BATMAN_NAME "batman" | ||
89 | #define LOG_TYPE_ROUTES_NAME "routes" | ||
90 | |||
91 | /* | ||
92 | * Vis | ||
93 | */ | ||
94 | |||
95 | /* #define VIS_SUBCLUSTERS_DISABLED */ | ||
96 | |||
97 | /* | ||
98 | * Kernel headers | ||
99 | */ | ||
100 | |||
101 | #include <linux/mutex.h> /* mutex */ | ||
102 | #include <linux/module.h> /* needed by all modules */ | ||
103 | #include <linux/netdevice.h> /* netdevice */ | ||
104 | #include <linux/if_ether.h> /* ethernet header */ | ||
105 | #include <linux/poll.h> /* poll_table */ | ||
106 | #include <linux/kthread.h> /* kernel threads */ | ||
107 | #include <linux/pkt_sched.h> /* schedule types */ | ||
108 | #include <linux/workqueue.h> /* workqueue */ | ||
109 | #include <net/sock.h> /* struct sock */ | ||
110 | #include <linux/jiffies.h> | ||
111 | #include "types.h" | ||
112 | |||
113 | #ifndef REVISION_VERSION | ||
114 | #define REVISION_VERSION_STR "" | ||
115 | #else | ||
116 | #define REVISION_VERSION_STR " "REVISION_VERSION | ||
117 | #endif | ||
118 | |||
119 | extern struct list_head if_list; | ||
120 | extern struct hlist_head forw_bat_list; | ||
121 | extern struct hlist_head forw_bcast_list; | ||
122 | extern struct hashtable_t *orig_hash; | ||
123 | |||
124 | extern spinlock_t orig_hash_lock; | ||
125 | extern spinlock_t forw_bat_list_lock; | ||
126 | extern spinlock_t forw_bcast_list_lock; | ||
127 | |||
128 | extern atomic_t originator_interval; | ||
129 | extern atomic_t vis_interval; | ||
130 | extern atomic_t aggregation_enabled; | ||
131 | extern int16_t num_hna; | ||
132 | extern int16_t num_ifs; | ||
133 | |||
134 | extern struct net_device *soft_device; | ||
135 | |||
136 | extern unsigned char broadcastAddr[]; | ||
137 | extern atomic_t module_state; | ||
138 | extern struct workqueue_struct *bat_event_workqueue; | ||
139 | |||
140 | void activate_module(void); | ||
141 | void shutdown_module(void); | ||
142 | void inc_module_count(void); | ||
143 | void dec_module_count(void); | ||
144 | int addr_to_string(char *buff, uint8_t *addr); | ||
145 | int compare_orig(void *data1, void *data2); | ||
146 | int choose_orig(void *data, int32_t size); | ||
147 | int is_my_mac(uint8_t *addr); | ||
148 | int is_bcast(uint8_t *addr); | ||
149 | int is_mcast(uint8_t *addr); | ||
150 | |||
151 | |||
diff --git a/drivers/staging/batman-adv/packet.h b/drivers/staging/batman-adv/packet.h new file mode 100644 index 000000000000..5627ca326018 --- /dev/null +++ b/drivers/staging/batman-adv/packet.h | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ | ||
23 | |||
24 | #define BAT_PACKET 0x01 | ||
25 | #define BAT_ICMP 0x02 | ||
26 | #define BAT_UNICAST 0x03 | ||
27 | #define BAT_BCAST 0x04 | ||
28 | #define BAT_VIS 0x05 | ||
29 | |||
30 | /* this file is included by batctl which needs these defines */ | ||
31 | #define COMPAT_VERSION 8 | ||
32 | #define DIRECTLINK 0x40 | ||
33 | #define VIS_SERVER 0x20 | ||
34 | |||
35 | /* ICMP message types */ | ||
36 | #define ECHO_REPLY 0 | ||
37 | #define DESTINATION_UNREACHABLE 3 | ||
38 | #define ECHO_REQUEST 8 | ||
39 | #define TTL_EXCEEDED 11 | ||
40 | #define PARAMETER_PROBLEM 12 | ||
41 | |||
42 | /* vis defines */ | ||
43 | #define VIS_TYPE_SERVER_SYNC 0 | ||
44 | #define VIS_TYPE_CLIENT_UPDATE 1 | ||
45 | |||
46 | struct batman_packet { | ||
47 | uint8_t packet_type; | ||
48 | uint8_t version; /* batman version field */ | ||
49 | uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */ | ||
50 | uint8_t tq; | ||
51 | uint16_t seqno; | ||
52 | uint8_t orig[6]; | ||
53 | uint8_t prev_sender[6]; | ||
54 | uint8_t ttl; | ||
55 | uint8_t num_hna; | ||
56 | } __attribute__((packed)); | ||
57 | |||
58 | #define BAT_PACKET_LEN sizeof(struct batman_packet) | ||
59 | |||
60 | struct icmp_packet { | ||
61 | uint8_t packet_type; | ||
62 | uint8_t version; /* batman version field */ | ||
63 | uint8_t msg_type; /* see ICMP message types above */ | ||
64 | uint8_t ttl; | ||
65 | uint8_t dst[6]; | ||
66 | uint8_t orig[6]; | ||
67 | uint16_t seqno; | ||
68 | uint8_t uid; | ||
69 | } __attribute__((packed)); | ||
70 | |||
71 | struct unicast_packet { | ||
72 | uint8_t packet_type; | ||
73 | uint8_t version; /* batman version field */ | ||
74 | uint8_t dest[6]; | ||
75 | uint8_t ttl; | ||
76 | } __attribute__((packed)); | ||
77 | |||
78 | struct bcast_packet { | ||
79 | uint8_t packet_type; | ||
80 | uint8_t version; /* batman version field */ | ||
81 | uint8_t orig[6]; | ||
82 | uint16_t seqno; | ||
83 | } __attribute__((packed)); | ||
84 | |||
85 | struct vis_packet { | ||
86 | uint8_t packet_type; | ||
87 | uint8_t version; /* batman version field */ | ||
88 | uint8_t vis_type; /* which type of vis-participant sent this? */ | ||
89 | uint8_t seqno; /* sequence number */ | ||
90 | uint8_t entries; /* number of entries behind this struct */ | ||
91 | uint8_t ttl; /* TTL */ | ||
92 | uint8_t vis_orig[6]; /* originator that informs about its | ||
93 | * neighbours */ | ||
94 | uint8_t target_orig[6]; /* who should receive this packet */ | ||
95 | uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */ | ||
96 | } __attribute__((packed)); | ||
diff --git a/drivers/staging/batman-adv/proc.c b/drivers/staging/batman-adv/proc.c new file mode 100644 index 000000000000..aac3df7f13fb --- /dev/null +++ b/drivers/staging/batman-adv/proc.c | |||
@@ -0,0 +1,950 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "proc.h" | ||
24 | #include "log.h" | ||
25 | #include "routing.h" | ||
26 | #include "translation-table.h" | ||
27 | #include "hard-interface.h" | ||
28 | #include "types.h" | ||
29 | #include "hash.h" | ||
30 | #include "vis.h" | ||
31 | #include "compat.h" | ||
32 | |||
33 | static uint8_t vis_format = DOT_DRAW; | ||
34 | |||
35 | static struct proc_dir_entry *proc_batman_dir, *proc_interface_file; | ||
36 | static struct proc_dir_entry *proc_orig_interval_file, *proc_originators_file; | ||
37 | static struct proc_dir_entry *proc_log_file, *proc_log_level_file; | ||
38 | static struct proc_dir_entry *proc_transt_local_file; | ||
39 | static struct proc_dir_entry *proc_transt_global_file; | ||
40 | static struct proc_dir_entry *proc_vis_file, *proc_vis_format_file; | ||
41 | static struct proc_dir_entry *proc_aggr_file; | ||
42 | |||
43 | static int proc_interfaces_read(struct seq_file *seq, void *offset) | ||
44 | { | ||
45 | struct batman_if *batman_if; | ||
46 | |||
47 | rcu_read_lock(); | ||
48 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
49 | seq_printf(seq, "[%8s] %s %s \n", | ||
50 | (batman_if->if_active == IF_ACTIVE ? | ||
51 | "active" : "inactive"), | ||
52 | batman_if->dev, | ||
53 | (batman_if->if_active == IF_ACTIVE ? | ||
54 | batman_if->addr_str : " ")); | ||
55 | } | ||
56 | rcu_read_unlock(); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int proc_interfaces_open(struct inode *inode, struct file *file) | ||
62 | { | ||
63 | return single_open(file, proc_interfaces_read, NULL); | ||
64 | } | ||
65 | |||
66 | static ssize_t proc_interfaces_write(struct file *instance, | ||
67 | const char __user *userbuffer, | ||
68 | size_t count, loff_t *data) | ||
69 | { | ||
70 | char *if_string, *colon_ptr = NULL, *cr_ptr = NULL; | ||
71 | int not_copied = 0, if_num = 0; | ||
72 | struct batman_if *batman_if = NULL; | ||
73 | |||
74 | if_string = kmalloc(count, GFP_KERNEL); | ||
75 | |||
76 | if (!if_string) | ||
77 | return -ENOMEM; | ||
78 | |||
79 | if (count > IFNAMSIZ - 1) { | ||
80 | debug_log(LOG_TYPE_WARN, | ||
81 | "Can't add interface: device name is too long\n"); | ||
82 | goto end; | ||
83 | } | ||
84 | |||
85 | not_copied = copy_from_user(if_string, userbuffer, count); | ||
86 | if_string[count - not_copied - 1] = 0; | ||
87 | |||
88 | colon_ptr = strchr(if_string, ':'); | ||
89 | if (colon_ptr) | ||
90 | *colon_ptr = 0; | ||
91 | |||
92 | if (!colon_ptr) { | ||
93 | cr_ptr = strchr(if_string, '\n'); | ||
94 | if (cr_ptr) | ||
95 | *cr_ptr = 0; | ||
96 | } | ||
97 | |||
98 | if (strlen(if_string) == 0) { | ||
99 | shutdown_module(); | ||
100 | num_ifs = 0; | ||
101 | goto end; | ||
102 | } | ||
103 | |||
104 | /* add interface */ | ||
105 | rcu_read_lock(); | ||
106 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
107 | if (strncmp(batman_if->dev, if_string, count) == 0) { | ||
108 | debug_log(LOG_TYPE_WARN, "Given interface is already active: %s\n", if_string); | ||
109 | rcu_read_unlock(); | ||
110 | goto end; | ||
111 | |||
112 | } | ||
113 | |||
114 | if_num++; | ||
115 | } | ||
116 | rcu_read_unlock(); | ||
117 | |||
118 | hardif_add_interface(if_string, if_num); | ||
119 | |||
120 | if ((atomic_read(&module_state) == MODULE_INACTIVE) && | ||
121 | (hardif_get_active_if_num() > 0)) | ||
122 | activate_module(); | ||
123 | |||
124 | rcu_read_lock(); | ||
125 | if (list_empty(&if_list)) { | ||
126 | rcu_read_unlock(); | ||
127 | goto end; | ||
128 | } | ||
129 | rcu_read_unlock(); | ||
130 | |||
131 | num_ifs = if_num + 1; | ||
132 | return count; | ||
133 | |||
134 | end: | ||
135 | kfree(if_string); | ||
136 | return count; | ||
137 | } | ||
138 | |||
139 | static int proc_orig_interval_read(struct seq_file *seq, void *offset) | ||
140 | { | ||
141 | seq_printf(seq, "%i\n", atomic_read(&originator_interval)); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static ssize_t proc_orig_interval_write(struct file *file, | ||
147 | const char __user *buffer, | ||
148 | size_t count, loff_t *ppos) | ||
149 | { | ||
150 | char *interval_string; | ||
151 | int not_copied = 0; | ||
152 | unsigned long originator_interval_tmp; | ||
153 | int retval; | ||
154 | |||
155 | interval_string = kmalloc(count, GFP_KERNEL); | ||
156 | |||
157 | if (!interval_string) | ||
158 | return -ENOMEM; | ||
159 | |||
160 | not_copied = copy_from_user(interval_string, buffer, count); | ||
161 | interval_string[count - not_copied - 1] = 0; | ||
162 | |||
163 | retval = strict_strtoul(interval_string, 10, &originator_interval_tmp); | ||
164 | if (retval) { | ||
165 | debug_log(LOG_TYPE_WARN, "New originator interval invalid\n"); | ||
166 | goto end; | ||
167 | } | ||
168 | |||
169 | if (originator_interval_tmp <= JITTER * 2) { | ||
170 | debug_log(LOG_TYPE_WARN, | ||
171 | "New originator interval too small: %i (min: %i)\n", | ||
172 | originator_interval_tmp, JITTER * 2); | ||
173 | goto end; | ||
174 | } | ||
175 | |||
176 | debug_log(LOG_TYPE_NOTICE, | ||
177 | "Changing originator interval from: %i to: %i\n", | ||
178 | atomic_read(&originator_interval), originator_interval_tmp); | ||
179 | |||
180 | atomic_set(&originator_interval, originator_interval_tmp); | ||
181 | |||
182 | end: | ||
183 | kfree(interval_string); | ||
184 | return count; | ||
185 | } | ||
186 | |||
187 | static int proc_orig_interval_open(struct inode *inode, struct file *file) | ||
188 | { | ||
189 | return single_open(file, proc_orig_interval_read, NULL); | ||
190 | } | ||
191 | |||
192 | static int proc_originators_read(struct seq_file *seq, void *offset) | ||
193 | { | ||
194 | struct hash_it_t *hashit = NULL; | ||
195 | struct orig_node *orig_node; | ||
196 | struct neigh_node *neigh_node; | ||
197 | int batman_count = 0; | ||
198 | char orig_str[ETH_STR_LEN], router_str[ETH_STR_LEN]; | ||
199 | |||
200 | rcu_read_lock(); | ||
201 | if (list_empty(&if_list)) { | ||
202 | rcu_read_unlock(); | ||
203 | seq_printf(seq, "BATMAN disabled - please specify interfaces to enable it \n"); | ||
204 | goto end; | ||
205 | } | ||
206 | |||
207 | if (((struct batman_if *)if_list.next)->if_active != IF_ACTIVE) { | ||
208 | rcu_read_unlock(); | ||
209 | seq_printf(seq, "BATMAN disabled - primary interface not active \n"); | ||
210 | goto end; | ||
211 | } | ||
212 | |||
213 | seq_printf(seq, | ||
214 | " %-14s (%s/%i) %17s [%10s]: %20s ... [B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%s] \n", | ||
215 | "Originator", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF", | ||
216 | "Potential nexthops", SOURCE_VERSION, REVISION_VERSION_STR, | ||
217 | ((struct batman_if *)if_list.next)->dev, | ||
218 | ((struct batman_if *)if_list.next)->addr_str); | ||
219 | |||
220 | rcu_read_unlock(); | ||
221 | spin_lock(&orig_hash_lock); | ||
222 | |||
223 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
224 | |||
225 | orig_node = hashit->bucket->data; | ||
226 | |||
227 | if (!orig_node->router) | ||
228 | continue; | ||
229 | |||
230 | if (orig_node->router->tq_avg == 0) | ||
231 | continue; | ||
232 | |||
233 | batman_count++; | ||
234 | |||
235 | addr_to_string(orig_str, orig_node->orig); | ||
236 | addr_to_string(router_str, orig_node->router->addr); | ||
237 | |||
238 | seq_printf(seq, "%-17s (%3i) %17s [%10s]:", | ||
239 | orig_str, orig_node->router->tq_avg, | ||
240 | router_str, orig_node->router->if_incoming->dev); | ||
241 | |||
242 | list_for_each_entry(neigh_node, &orig_node->neigh_list, list) { | ||
243 | addr_to_string(orig_str, neigh_node->addr); | ||
244 | seq_printf(seq, " %17s (%3i)", | ||
245 | orig_str, neigh_node->tq_avg); | ||
246 | } | ||
247 | |||
248 | seq_printf(seq, "\n"); | ||
249 | |||
250 | } | ||
251 | |||
252 | spin_unlock(&orig_hash_lock); | ||
253 | |||
254 | if (batman_count == 0) | ||
255 | seq_printf(seq, "No batman nodes in range ... \n"); | ||
256 | |||
257 | end: | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int proc_originators_open(struct inode *inode, struct file *file) | ||
262 | { | ||
263 | return single_open(file, proc_originators_read, NULL); | ||
264 | } | ||
265 | |||
266 | static int proc_log_level_read(struct seq_file *seq, void *offset) | ||
267 | { | ||
268 | |||
269 | seq_printf(seq, "[x] %s (%d)\n", LOG_TYPE_CRIT_NAME, LOG_TYPE_CRIT); | ||
270 | seq_printf(seq, "[%c] %s (%d)\n", | ||
271 | (LOG_TYPE_WARN & log_level) ? 'x' : ' ', | ||
272 | LOG_TYPE_WARN_NAME, LOG_TYPE_WARN); | ||
273 | seq_printf(seq, "[%c] %s (%d)\n", | ||
274 | (LOG_TYPE_NOTICE & log_level) ? 'x' : ' ', | ||
275 | LOG_TYPE_NOTICE_NAME, LOG_TYPE_NOTICE); | ||
276 | seq_printf(seq, "[%c] %s (%d)\n", | ||
277 | (LOG_TYPE_BATMAN & log_level) ? 'x' : ' ', | ||
278 | LOG_TYPE_BATMAN_NAME, LOG_TYPE_BATMAN); | ||
279 | seq_printf(seq, "[%c] %s (%d)\n", | ||
280 | (LOG_TYPE_ROUTES & log_level) ? 'x' : ' ', | ||
281 | LOG_TYPE_ROUTES_NAME, LOG_TYPE_ROUTES); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int proc_log_level_open(struct inode *inode, struct file *file) | ||
286 | { | ||
287 | return single_open(file, proc_log_level_read, NULL); | ||
288 | } | ||
289 | |||
290 | static ssize_t proc_log_level_write(struct file *instance, | ||
291 | const char __user *userbuffer, | ||
292 | size_t count, loff_t *data) | ||
293 | { | ||
294 | char *log_level_string, *tokptr, *cp; | ||
295 | int finished, not_copied = 0; | ||
296 | unsigned long log_level_tmp = 0; | ||
297 | |||
298 | log_level_string = kmalloc(count, GFP_KERNEL); | ||
299 | |||
300 | if (!log_level_string) | ||
301 | return -ENOMEM; | ||
302 | |||
303 | not_copied = copy_from_user(log_level_string, userbuffer, count); | ||
304 | log_level_string[count - not_copied - 1] = 0; | ||
305 | |||
306 | if (strict_strtoul(log_level_string, 10, &log_level_tmp) < 0) { | ||
307 | /* was not a number, doing textual parsing */ | ||
308 | log_level_tmp = 0; | ||
309 | tokptr = log_level_string; | ||
310 | |||
311 | for (cp = log_level_string, finished = 0; !finished; cp++) { | ||
312 | switch (*cp) { | ||
313 | case 0: | ||
314 | finished = 1; | ||
315 | case ' ': | ||
316 | case '\n': | ||
317 | case '\t': | ||
318 | *cp = 0; | ||
319 | /* compare */ | ||
320 | if (strcmp(tokptr, LOG_TYPE_WARN_NAME) == 0) | ||
321 | log_level_tmp |= LOG_TYPE_WARN; | ||
322 | if (strcmp(tokptr, LOG_TYPE_NOTICE_NAME) == 0) | ||
323 | log_level_tmp |= LOG_TYPE_NOTICE; | ||
324 | if (strcmp(tokptr, LOG_TYPE_BATMAN_NAME) == 0) | ||
325 | log_level_tmp |= LOG_TYPE_BATMAN; | ||
326 | if (strcmp(tokptr, LOG_TYPE_ROUTES_NAME) == 0) | ||
327 | log_level_tmp |= LOG_TYPE_ROUTES; | ||
328 | tokptr = cp + 1; | ||
329 | break; | ||
330 | default: | ||
331 | ; | ||
332 | } | ||
333 | } | ||
334 | } | ||
335 | |||
336 | debug_log(LOG_TYPE_CRIT, "Changing log_level from: %i to: %i\n", | ||
337 | log_level, log_level_tmp); | ||
338 | log_level = log_level_tmp; | ||
339 | |||
340 | kfree(log_level_string); | ||
341 | return count; | ||
342 | } | ||
343 | |||
344 | static int proc_transt_local_read(struct seq_file *seq, void *offset) | ||
345 | { | ||
346 | char *buf; | ||
347 | |||
348 | buf = kmalloc(4096, GFP_KERNEL); | ||
349 | if (!buf) | ||
350 | return 0; | ||
351 | |||
352 | rcu_read_lock(); | ||
353 | if (list_empty(&if_list)) { | ||
354 | rcu_read_unlock(); | ||
355 | seq_printf(seq, "BATMAN disabled - please specify interfaces to enable it \n"); | ||
356 | goto end; | ||
357 | } | ||
358 | |||
359 | rcu_read_unlock(); | ||
360 | |||
361 | seq_printf(seq, "Locally retrieved addresses (from %s) announced via HNA:\n", soft_device->name); | ||
362 | |||
363 | hna_local_fill_buffer_text(buf, 4096); | ||
364 | seq_printf(seq, "%s", buf); | ||
365 | |||
366 | end: | ||
367 | kfree(buf); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int proc_transt_local_open(struct inode *inode, struct file *file) | ||
372 | { | ||
373 | return single_open(file, proc_transt_local_read, NULL); | ||
374 | } | ||
375 | |||
376 | static int proc_transt_global_read(struct seq_file *seq, void *offset) | ||
377 | { | ||
378 | char *buf; | ||
379 | |||
380 | buf = kmalloc(4096, GFP_KERNEL); | ||
381 | if (!buf) | ||
382 | return 0; | ||
383 | |||
384 | rcu_read_lock(); | ||
385 | if (list_empty(&if_list)) { | ||
386 | rcu_read_unlock(); | ||
387 | seq_printf(seq, "BATMAN disabled - please specify interfaces to enable it \n"); | ||
388 | goto end; | ||
389 | } | ||
390 | rcu_read_unlock(); | ||
391 | |||
392 | |||
393 | seq_printf(seq, "Globally announced HNAs received via the mesh (translation table):\n"); | ||
394 | |||
395 | hna_global_fill_buffer_text(buf, 4096); | ||
396 | seq_printf(seq, "%s", buf); | ||
397 | |||
398 | end: | ||
399 | kfree(buf); | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int proc_transt_global_open(struct inode *inode, struct file *file) | ||
404 | { | ||
405 | return single_open(file, proc_transt_global_read, NULL); | ||
406 | } | ||
407 | |||
408 | /* insert interface to the list of interfaces of one originator */ | ||
409 | |||
410 | static void proc_vis_insert_interface(const uint8_t *interface, | ||
411 | struct vis_if_list **if_entry, | ||
412 | bool primary) | ||
413 | { | ||
414 | /* Did we get an empty list? (then insert imediately) */ | ||
415 | if(*if_entry == NULL) { | ||
416 | *if_entry = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL); | ||
417 | if (*if_entry == NULL) | ||
418 | return; | ||
419 | |||
420 | (*if_entry)->primary = primary; | ||
421 | (*if_entry)->next = NULL; | ||
422 | memcpy((*if_entry)->addr, interface, ETH_ALEN); | ||
423 | } else { | ||
424 | struct vis_if_list *head_if_entry = *if_entry; | ||
425 | /* Do we already have this interface in our list? */ | ||
426 | while (!compare_orig((*if_entry)->addr, (void *)interface)) { | ||
427 | |||
428 | /* Or did we reach the end (then append the interface) */ | ||
429 | if ((*if_entry)->next == NULL) { | ||
430 | (*if_entry)->next = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL); | ||
431 | if ((*if_entry)->next == NULL) | ||
432 | return; | ||
433 | |||
434 | memcpy((*if_entry)->next->addr, interface, ETH_ALEN); | ||
435 | (*if_entry)->next->primary = primary; | ||
436 | (*if_entry)->next->next = NULL; | ||
437 | break; | ||
438 | } | ||
439 | *if_entry = (*if_entry)->next; | ||
440 | } | ||
441 | /* Rewind the list to its head */ | ||
442 | *if_entry = head_if_entry; | ||
443 | } | ||
444 | } | ||
445 | /* read an entry */ | ||
446 | |||
447 | static void proc_vis_read_entry(struct seq_file *seq, | ||
448 | struct vis_info_entry *entry, | ||
449 | struct vis_if_list **if_entry, | ||
450 | uint8_t *vis_orig, | ||
451 | uint8_t current_format, | ||
452 | uint8_t first_line) | ||
453 | { | ||
454 | char from[40]; | ||
455 | char to[40]; | ||
456 | int int_part, frac_part; | ||
457 | |||
458 | addr_to_string(to, entry->dest); | ||
459 | if (entry->quality == 0) { | ||
460 | #ifndef VIS_SUBCLUSTERS_DISABLED | ||
461 | proc_vis_insert_interface(vis_orig, if_entry, true); | ||
462 | #endif /* VIS_SUBCLUSTERS_DISABLED */ | ||
463 | addr_to_string(from, vis_orig); | ||
464 | if (current_format == DOT_DRAW) { | ||
465 | seq_printf(seq, "\t\"%s\" -> \"%s\" [label=\"HNA\"]\n", | ||
466 | from, to); | ||
467 | } else { | ||
468 | seq_printf(seq, | ||
469 | "%s\t{ router : \"%s\", gateway : \"%s\", label : \"HNA\" }", | ||
470 | (first_line ? "" : ",\n"), from, to); | ||
471 | } | ||
472 | } else { | ||
473 | #ifndef VIS_SUBCLUSTERS_DISABLED | ||
474 | proc_vis_insert_interface(entry->src, if_entry, compare_orig(entry->src, vis_orig)); | ||
475 | #endif /* VIS_SUBCLUSTERS_DISABLED */ | ||
476 | addr_to_string(from, entry->src); | ||
477 | |||
478 | /* kernel has no printf-support for %f? it'd be better to return | ||
479 | * this in float. */ | ||
480 | |||
481 | int_part = TQ_MAX_VALUE / entry->quality; | ||
482 | frac_part = 1000 * TQ_MAX_VALUE / entry->quality - int_part * 1000; | ||
483 | |||
484 | if (current_format == DOT_DRAW) { | ||
485 | seq_printf(seq, | ||
486 | "\t\"%s\" -> \"%s\" [label=\"%d.%d\"]\n", | ||
487 | from, to, int_part, frac_part); | ||
488 | } else { | ||
489 | seq_printf(seq, | ||
490 | "%s\t{ router : \"%s\", neighbour : \"%s\", label : %d.%d }", | ||
491 | (first_line ? "" : ",\n"), from, to, int_part, frac_part); | ||
492 | } | ||
493 | } | ||
494 | } | ||
495 | |||
496 | |||
497 | static int proc_vis_read(struct seq_file *seq, void *offset) | ||
498 | { | ||
499 | struct hash_it_t *hashit = NULL; | ||
500 | struct vis_info *info; | ||
501 | struct vis_info_entry *entries; | ||
502 | struct vis_if_list *if_entries = NULL; | ||
503 | int i; | ||
504 | uint8_t current_format, first_line = 1; | ||
505 | #ifndef VIS_SUBCLUSTERS_DISABLED | ||
506 | char tmp_addr_str[ETH_STR_LEN]; | ||
507 | struct vis_if_list *tmp_if_next; | ||
508 | #endif /* VIS_SUBCLUSTERS_DISABLED */ | ||
509 | |||
510 | current_format = vis_format; | ||
511 | |||
512 | rcu_read_lock(); | ||
513 | if (list_empty(&if_list) || (!is_vis_server())) { | ||
514 | rcu_read_unlock(); | ||
515 | if (current_format == DOT_DRAW) | ||
516 | seq_printf(seq, "digraph {\n}\n"); | ||
517 | goto end; | ||
518 | } | ||
519 | |||
520 | rcu_read_unlock(); | ||
521 | |||
522 | if (current_format == DOT_DRAW) | ||
523 | seq_printf(seq, "digraph {\n"); | ||
524 | |||
525 | spin_lock(&vis_hash_lock); | ||
526 | while (NULL != (hashit = hash_iterate(vis_hash, hashit))) { | ||
527 | info = hashit->bucket->data; | ||
528 | entries = (struct vis_info_entry *) | ||
529 | ((char *)info + sizeof(struct vis_info)); | ||
530 | |||
531 | for (i = 0; i < info->packet.entries; i++) { | ||
532 | proc_vis_read_entry(seq, &entries[i], &if_entries, | ||
533 | info->packet.vis_orig, | ||
534 | current_format, first_line); | ||
535 | if (first_line) | ||
536 | first_line = 0; | ||
537 | } | ||
538 | |||
539 | #ifndef VIS_SUBCLUSTERS_DISABLED | ||
540 | /* Generate subgraphs from the collected items */ | ||
541 | if (current_format == DOT_DRAW) { | ||
542 | |||
543 | addr_to_string(tmp_addr_str, info->packet.vis_orig); | ||
544 | seq_printf(seq, "\tsubgraph \"cluster_%s\" {\n", tmp_addr_str); | ||
545 | while (if_entries != NULL) { | ||
546 | |||
547 | addr_to_string(tmp_addr_str, if_entries->addr); | ||
548 | if (if_entries->primary) | ||
549 | seq_printf(seq, "\t\t\"%s\" [peripheries=2]\n", tmp_addr_str); | ||
550 | else | ||
551 | seq_printf(seq, "\t\t\"%s\"\n", tmp_addr_str); | ||
552 | |||
553 | /* ... and empty the list while doing this */ | ||
554 | tmp_if_next = if_entries->next; | ||
555 | kfree(if_entries); | ||
556 | if_entries = tmp_if_next; | ||
557 | } | ||
558 | seq_printf(seq, "\t}\n"); | ||
559 | } | ||
560 | #endif /* VIS_SUBCLUSTERS_DISABLED */ | ||
561 | } | ||
562 | spin_unlock(&vis_hash_lock); | ||
563 | |||
564 | if (current_format == DOT_DRAW) | ||
565 | seq_printf(seq, "}\n"); | ||
566 | else | ||
567 | seq_printf(seq, "\n"); | ||
568 | end: | ||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | /* setting the mode of the vis server by the user */ | ||
573 | static ssize_t proc_vis_write(struct file *file, const char __user * buffer, | ||
574 | size_t count, loff_t *ppos) | ||
575 | { | ||
576 | char *vis_mode_string; | ||
577 | int not_copied = 0; | ||
578 | |||
579 | vis_mode_string = kmalloc(count, GFP_KERNEL); | ||
580 | |||
581 | if (!vis_mode_string) | ||
582 | return -ENOMEM; | ||
583 | |||
584 | not_copied = copy_from_user(vis_mode_string, buffer, count); | ||
585 | vis_mode_string[count - not_copied - 1] = 0; | ||
586 | |||
587 | if (strcmp(vis_mode_string, "client") == 0) { | ||
588 | debug_log(LOG_TYPE_NOTICE, "Setting VIS mode to client\n"); | ||
589 | vis_set_mode(VIS_TYPE_CLIENT_UPDATE); | ||
590 | } else if (strcmp(vis_mode_string, "server") == 0) { | ||
591 | debug_log(LOG_TYPE_NOTICE, "Setting VIS mode to server\n"); | ||
592 | vis_set_mode(VIS_TYPE_SERVER_SYNC); | ||
593 | } else | ||
594 | debug_log(LOG_TYPE_WARN, "Unknown VIS mode: %s\n", | ||
595 | vis_mode_string); | ||
596 | |||
597 | kfree(vis_mode_string); | ||
598 | return count; | ||
599 | } | ||
600 | |||
601 | static int proc_vis_open(struct inode *inode, struct file *file) | ||
602 | { | ||
603 | return single_open(file, proc_vis_read, NULL); | ||
604 | } | ||
605 | |||
606 | static int proc_vis_format_read(struct seq_file *seq, void *offset) | ||
607 | { | ||
608 | uint8_t current_format = vis_format; | ||
609 | |||
610 | seq_printf(seq, "[%c] %s\n", | ||
611 | (current_format == DOT_DRAW) ? 'x' : ' ', | ||
612 | VIS_FORMAT_DD_NAME); | ||
613 | seq_printf(seq, "[%c] %s\n", | ||
614 | (current_format == JSON) ? 'x' : ' ', | ||
615 | VIS_FORMAT_JSON_NAME); | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int proc_vis_format_open(struct inode *inode, struct file *file) | ||
620 | { | ||
621 | return single_open(file, proc_vis_format_read, NULL); | ||
622 | } | ||
623 | |||
624 | static ssize_t proc_vis_format_write(struct file *file, | ||
625 | const char __user *buffer, | ||
626 | size_t count, loff_t *ppos) | ||
627 | { | ||
628 | char *vis_format_string; | ||
629 | int not_copied = 0; | ||
630 | |||
631 | vis_format_string = kmalloc(count, GFP_KERNEL); | ||
632 | |||
633 | if (!vis_format_string) | ||
634 | return -ENOMEM; | ||
635 | |||
636 | not_copied = copy_from_user(vis_format_string, buffer, count); | ||
637 | vis_format_string[count - not_copied - 1] = 0; | ||
638 | |||
639 | if (strcmp(vis_format_string, VIS_FORMAT_DD_NAME) == 0) { | ||
640 | debug_log(LOG_TYPE_NOTICE, "Setting VIS output format to: %s\n", | ||
641 | VIS_FORMAT_DD_NAME); | ||
642 | vis_format = DOT_DRAW; | ||
643 | } else if (strcmp(vis_format_string, VIS_FORMAT_JSON_NAME) == 0) { | ||
644 | debug_log(LOG_TYPE_NOTICE, "Setting VIS output format to: %s\n", | ||
645 | VIS_FORMAT_JSON_NAME); | ||
646 | vis_format = JSON; | ||
647 | } else | ||
648 | debug_log(LOG_TYPE_WARN, "Unknown VIS output format: %s\n", | ||
649 | vis_format_string); | ||
650 | |||
651 | kfree(vis_format_string); | ||
652 | return count; | ||
653 | } | ||
654 | |||
655 | static int proc_aggr_read(struct seq_file *seq, void *offset) | ||
656 | { | ||
657 | seq_printf(seq, "%i\n", atomic_read(&aggregation_enabled)); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static ssize_t proc_aggr_write(struct file *file, const char __user *buffer, | ||
663 | size_t count, loff_t *ppos) | ||
664 | { | ||
665 | char *aggr_string; | ||
666 | int not_copied = 0; | ||
667 | unsigned long aggregation_enabled_tmp; | ||
668 | |||
669 | aggr_string = kmalloc(count, GFP_KERNEL); | ||
670 | |||
671 | if (!aggr_string) | ||
672 | return -ENOMEM; | ||
673 | |||
674 | not_copied = copy_from_user(aggr_string, buffer, count); | ||
675 | aggr_string[count - not_copied - 1] = 0; | ||
676 | |||
677 | strict_strtoul(aggr_string, 10, &aggregation_enabled_tmp); | ||
678 | |||
679 | if ((aggregation_enabled_tmp != 0) && (aggregation_enabled_tmp != 1)) { | ||
680 | debug_log(LOG_TYPE_WARN, "Aggregation can only be enabled (1) or disabled (0), given value: %li\n", aggregation_enabled_tmp); | ||
681 | goto end; | ||
682 | } | ||
683 | |||
684 | debug_log(LOG_TYPE_NOTICE, "Changing aggregation from: %s (%i) to: %s (%li)\n", | ||
685 | (atomic_read(&aggregation_enabled) == 1 ? | ||
686 | "enabled" : "disabled"), | ||
687 | atomic_read(&aggregation_enabled), | ||
688 | (aggregation_enabled_tmp == 1 ? "enabled" : "disabled"), | ||
689 | aggregation_enabled_tmp); | ||
690 | |||
691 | atomic_set(&aggregation_enabled, (unsigned)aggregation_enabled_tmp); | ||
692 | end: | ||
693 | kfree(aggr_string); | ||
694 | return count; | ||
695 | } | ||
696 | |||
697 | static int proc_aggr_open(struct inode *inode, struct file *file) | ||
698 | { | ||
699 | return single_open(file, proc_aggr_read, NULL); | ||
700 | } | ||
701 | |||
702 | /* satisfying different prototypes ... */ | ||
703 | static ssize_t proc_dummy_write(struct file *file, const char __user *buffer, | ||
704 | size_t count, loff_t *ppos) | ||
705 | { | ||
706 | return count; | ||
707 | } | ||
708 | |||
709 | static const struct file_operations proc_aggr_fops = { | ||
710 | .owner = THIS_MODULE, | ||
711 | .open = proc_aggr_open, | ||
712 | .read = seq_read, | ||
713 | .write = proc_aggr_write, | ||
714 | .llseek = seq_lseek, | ||
715 | .release = single_release, | ||
716 | }; | ||
717 | |||
718 | static const struct file_operations proc_vis_format_fops = { | ||
719 | .owner = THIS_MODULE, | ||
720 | .open = proc_vis_format_open, | ||
721 | .read = seq_read, | ||
722 | .write = proc_vis_format_write, | ||
723 | .llseek = seq_lseek, | ||
724 | .release = single_release, | ||
725 | }; | ||
726 | |||
727 | static const struct file_operations proc_vis_fops = { | ||
728 | .owner = THIS_MODULE, | ||
729 | .open = proc_vis_open, | ||
730 | .read = seq_read, | ||
731 | .write = proc_vis_write, | ||
732 | .llseek = seq_lseek, | ||
733 | .release = single_release, | ||
734 | }; | ||
735 | |||
736 | static const struct file_operations proc_originators_fops = { | ||
737 | .owner = THIS_MODULE, | ||
738 | .open = proc_originators_open, | ||
739 | .read = seq_read, | ||
740 | .write = proc_dummy_write, | ||
741 | .llseek = seq_lseek, | ||
742 | .release = single_release, | ||
743 | }; | ||
744 | |||
745 | static const struct file_operations proc_transt_local_fops = { | ||
746 | .owner = THIS_MODULE, | ||
747 | .open = proc_transt_local_open, | ||
748 | .read = seq_read, | ||
749 | .write = proc_dummy_write, | ||
750 | .llseek = seq_lseek, | ||
751 | .release = single_release, | ||
752 | }; | ||
753 | |||
754 | static const struct file_operations proc_transt_global_fops = { | ||
755 | .owner = THIS_MODULE, | ||
756 | .open = proc_transt_global_open, | ||
757 | .read = seq_read, | ||
758 | .write = proc_dummy_write, | ||
759 | .llseek = seq_lseek, | ||
760 | .release = single_release, | ||
761 | }; | ||
762 | |||
763 | static const struct file_operations proc_log_level_fops = { | ||
764 | .owner = THIS_MODULE, | ||
765 | .open = proc_log_level_open, | ||
766 | .read = seq_read, | ||
767 | .write = proc_log_level_write, | ||
768 | .llseek = seq_lseek, | ||
769 | .release = single_release, | ||
770 | }; | ||
771 | |||
772 | static const struct file_operations proc_interfaces_fops = { | ||
773 | .owner = THIS_MODULE, | ||
774 | .open = proc_interfaces_open, | ||
775 | .read = seq_read, | ||
776 | .write = proc_interfaces_write, | ||
777 | .llseek = seq_lseek, | ||
778 | .release = single_release, | ||
779 | }; | ||
780 | |||
781 | static const struct file_operations proc_orig_interval_fops = { | ||
782 | .owner = THIS_MODULE, | ||
783 | .open = proc_orig_interval_open, | ||
784 | .read = seq_read, | ||
785 | .write = proc_orig_interval_write, | ||
786 | .llseek = seq_lseek, | ||
787 | .release = single_release, | ||
788 | }; | ||
789 | |||
790 | void cleanup_procfs(void) | ||
791 | { | ||
792 | if (proc_transt_global_file) | ||
793 | remove_proc_entry(PROC_FILE_TRANST_GLOBAL, proc_batman_dir); | ||
794 | |||
795 | if (proc_transt_local_file) | ||
796 | remove_proc_entry(PROC_FILE_TRANST_LOCAL, proc_batman_dir); | ||
797 | |||
798 | if (proc_log_file) | ||
799 | remove_proc_entry(PROC_FILE_LOG, proc_batman_dir); | ||
800 | |||
801 | if (proc_log_level_file) | ||
802 | remove_proc_entry(PROC_FILE_LOG_LEVEL, proc_batman_dir); | ||
803 | |||
804 | if (proc_originators_file) | ||
805 | remove_proc_entry(PROC_FILE_ORIGINATORS, proc_batman_dir); | ||
806 | |||
807 | if (proc_orig_interval_file) | ||
808 | remove_proc_entry(PROC_FILE_ORIG_INTERVAL, proc_batman_dir); | ||
809 | |||
810 | if (proc_interface_file) | ||
811 | remove_proc_entry(PROC_FILE_INTERFACES, proc_batman_dir); | ||
812 | |||
813 | if (proc_vis_file) | ||
814 | remove_proc_entry(PROC_FILE_VIS, proc_batman_dir); | ||
815 | |||
816 | if (proc_vis_format_file) | ||
817 | remove_proc_entry(PROC_FILE_VIS_FORMAT, proc_batman_dir); | ||
818 | |||
819 | if (proc_aggr_file) | ||
820 | remove_proc_entry(PROC_FILE_AGGR, proc_batman_dir); | ||
821 | |||
822 | if (proc_batman_dir) | ||
823 | #ifdef __NET_NET_NAMESPACE_H | ||
824 | remove_proc_entry(PROC_ROOT_DIR, init_net.proc_net); | ||
825 | #else | ||
826 | remove_proc_entry(PROC_ROOT_DIR, proc_net); | ||
827 | #endif | ||
828 | } | ||
829 | |||
830 | int setup_procfs(void) | ||
831 | { | ||
832 | #ifdef __NET_NET_NAMESPACE_H | ||
833 | proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, init_net.proc_net); | ||
834 | #else | ||
835 | proc_batman_dir = proc_mkdir(PROC_ROOT_DIR, proc_net); | ||
836 | #endif | ||
837 | |||
838 | if (!proc_batman_dir) { | ||
839 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s' folder failed\n", PROC_ROOT_DIR); | ||
840 | return -EFAULT; | ||
841 | } | ||
842 | |||
843 | proc_interface_file = create_proc_entry(PROC_FILE_INTERFACES, | ||
844 | S_IWUSR | S_IRUGO, | ||
845 | proc_batman_dir); | ||
846 | if (proc_interface_file) { | ||
847 | proc_interface_file->proc_fops = &proc_interfaces_fops; | ||
848 | } else { | ||
849 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_INTERFACES); | ||
850 | cleanup_procfs(); | ||
851 | return -EFAULT; | ||
852 | } | ||
853 | |||
854 | proc_orig_interval_file = create_proc_entry(PROC_FILE_ORIG_INTERVAL, | ||
855 | S_IWUSR | S_IRUGO, | ||
856 | proc_batman_dir); | ||
857 | if (proc_orig_interval_file) { | ||
858 | proc_orig_interval_file->proc_fops = &proc_orig_interval_fops; | ||
859 | } else { | ||
860 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIG_INTERVAL); | ||
861 | cleanup_procfs(); | ||
862 | return -EFAULT; | ||
863 | } | ||
864 | |||
865 | proc_log_level_file = create_proc_entry(PROC_FILE_LOG_LEVEL, | ||
866 | S_IWUSR | S_IRUGO, | ||
867 | proc_batman_dir); | ||
868 | if (proc_log_level_file) { | ||
869 | proc_log_level_file->proc_fops = &proc_log_level_fops; | ||
870 | } else { | ||
871 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_LOG_LEVEL); | ||
872 | cleanup_procfs(); | ||
873 | return -EFAULT; | ||
874 | } | ||
875 | |||
876 | proc_originators_file = create_proc_entry(PROC_FILE_ORIGINATORS, | ||
877 | S_IRUGO, proc_batman_dir); | ||
878 | if (proc_originators_file) { | ||
879 | proc_originators_file->proc_fops = &proc_originators_fops; | ||
880 | } else { | ||
881 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_ORIGINATORS); | ||
882 | cleanup_procfs(); | ||
883 | return -EFAULT; | ||
884 | } | ||
885 | |||
886 | proc_log_file = create_proc_entry(PROC_FILE_LOG, | ||
887 | S_IRUGO, proc_batman_dir); | ||
888 | if (proc_log_file) { | ||
889 | proc_log_file->proc_fops = &proc_log_operations; | ||
890 | } else { | ||
891 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_FILE_LOG, PROC_FILE_GATEWAYS); | ||
892 | cleanup_procfs(); | ||
893 | return -EFAULT; | ||
894 | } | ||
895 | |||
896 | proc_transt_local_file = create_proc_entry(PROC_FILE_TRANST_LOCAL, | ||
897 | S_IRUGO, proc_batman_dir); | ||
898 | if (proc_transt_local_file) { | ||
899 | proc_transt_local_file->proc_fops = &proc_transt_local_fops; | ||
900 | } else { | ||
901 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_TRANST_LOCAL); | ||
902 | cleanup_procfs(); | ||
903 | return -EFAULT; | ||
904 | } | ||
905 | |||
906 | proc_transt_global_file = create_proc_entry(PROC_FILE_TRANST_GLOBAL, | ||
907 | S_IRUGO, proc_batman_dir); | ||
908 | if (proc_transt_global_file) { | ||
909 | proc_transt_global_file->proc_fops = &proc_transt_global_fops; | ||
910 | } else { | ||
911 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_TRANST_GLOBAL); | ||
912 | cleanup_procfs(); | ||
913 | return -EFAULT; | ||
914 | } | ||
915 | |||
916 | proc_vis_file = create_proc_entry(PROC_FILE_VIS, S_IWUSR | S_IRUGO, | ||
917 | proc_batman_dir); | ||
918 | if (proc_vis_file) { | ||
919 | proc_vis_file->proc_fops = &proc_vis_fops; | ||
920 | } else { | ||
921 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS); | ||
922 | cleanup_procfs(); | ||
923 | return -EFAULT; | ||
924 | } | ||
925 | |||
926 | proc_vis_format_file = create_proc_entry(PROC_FILE_VIS_FORMAT, | ||
927 | S_IWUSR | S_IRUGO, | ||
928 | proc_batman_dir); | ||
929 | if (proc_vis_format_file) { | ||
930 | proc_vis_format_file->proc_fops = &proc_vis_format_fops; | ||
931 | } else { | ||
932 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_FORMAT); | ||
933 | cleanup_procfs(); | ||
934 | return -EFAULT; | ||
935 | } | ||
936 | |||
937 | proc_aggr_file = create_proc_entry(PROC_FILE_AGGR, S_IWUSR | S_IRUGO, | ||
938 | proc_batman_dir); | ||
939 | if (proc_aggr_file) { | ||
940 | proc_aggr_file->proc_fops = &proc_aggr_fops; | ||
941 | } else { | ||
942 | printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_AGGR); | ||
943 | cleanup_procfs(); | ||
944 | return -EFAULT; | ||
945 | } | ||
946 | |||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | |||
diff --git a/drivers/staging/batman-adv/proc.h b/drivers/staging/batman-adv/proc.h new file mode 100644 index 000000000000..16d3efdebe52 --- /dev/null +++ b/drivers/staging/batman-adv/proc.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/proc_fs.h> | ||
23 | #include <linux/seq_file.h> | ||
24 | |||
25 | #define PROC_ROOT_DIR "batman-adv" | ||
26 | #define PROC_FILE_INTERFACES "interfaces" | ||
27 | #define PROC_FILE_ORIG_INTERVAL "orig_interval" | ||
28 | #define PROC_FILE_ORIGINATORS "originators" | ||
29 | #define PROC_FILE_GATEWAYS "gateways" | ||
30 | #define PROC_FILE_LOG "log" | ||
31 | #define PROC_FILE_LOG_LEVEL "log_level" | ||
32 | #define PROC_FILE_TRANST_LOCAL "transtable_local" | ||
33 | #define PROC_FILE_TRANST_GLOBAL "transtable_global" | ||
34 | #define PROC_FILE_VIS "vis" | ||
35 | #define PROC_FILE_VIS_FORMAT "vis_format" | ||
36 | #define PROC_FILE_AGGR "aggregate_ogm" | ||
37 | |||
38 | void cleanup_procfs(void); | ||
39 | int setup_procfs(void); | ||
40 | |||
41 | /* While scanning for vis-entries of a particular vis-originator | ||
42 | * this list collects its interfaces to create a subgraph/cluster | ||
43 | * out of them later | ||
44 | */ | ||
45 | struct vis_if_list { | ||
46 | uint8_t addr[ETH_ALEN]; | ||
47 | bool primary; | ||
48 | struct vis_if_list *next; | ||
49 | }; | ||
diff --git a/drivers/staging/batman-adv/ring_buffer.c b/drivers/staging/batman-adv/ring_buffer.c new file mode 100644 index 000000000000..751c899f54c5 --- /dev/null +++ b/drivers/staging/batman-adv/ring_buffer.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "ring_buffer.h" | ||
24 | |||
25 | void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value) | ||
26 | { | ||
27 | lq_recv[*lq_index] = value; | ||
28 | *lq_index = (*lq_index + 1) % TQ_GLOBAL_WINDOW_SIZE; | ||
29 | } | ||
30 | |||
31 | uint8_t ring_buffer_avg(uint8_t lq_recv[]) | ||
32 | { | ||
33 | uint8_t *ptr; | ||
34 | uint16_t count = 0, i = 0, sum = 0; | ||
35 | |||
36 | ptr = lq_recv; | ||
37 | |||
38 | while (i < TQ_GLOBAL_WINDOW_SIZE) { | ||
39 | if (*ptr != 0) { | ||
40 | count++; | ||
41 | sum += *ptr; | ||
42 | } | ||
43 | |||
44 | i++; | ||
45 | ptr++; | ||
46 | } | ||
47 | |||
48 | if (count == 0) | ||
49 | return 0; | ||
50 | |||
51 | return (uint8_t)(sum / count); | ||
52 | } | ||
diff --git a/drivers/staging/batman-adv/ring_buffer.h b/drivers/staging/batman-adv/ring_buffer.h new file mode 100644 index 000000000000..6839ba97eeb3 --- /dev/null +++ b/drivers/staging/batman-adv/ring_buffer.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value); | ||
23 | uint8_t ring_buffer_avg(uint8_t lq_recv[]); | ||
diff --git a/drivers/staging/batman-adv/routing.c b/drivers/staging/batman-adv/routing.c new file mode 100644 index 000000000000..4a14c363ac2b --- /dev/null +++ b/drivers/staging/batman-adv/routing.c | |||
@@ -0,0 +1,1010 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | #include "main.h" | ||
27 | #include "routing.h" | ||
28 | #include "log.h" | ||
29 | #include "send.h" | ||
30 | #include "soft-interface.h" | ||
31 | #include "hard-interface.h" | ||
32 | #include "device.h" | ||
33 | #include "translation-table.h" | ||
34 | #include "types.h" | ||
35 | #include "hash.h" | ||
36 | #include "ring_buffer.h" | ||
37 | #include "vis.h" | ||
38 | #include "aggregation.h" | ||
39 | #include "compat.h" | ||
40 | |||
41 | |||
42 | |||
43 | DECLARE_WAIT_QUEUE_HEAD(thread_wait); | ||
44 | static DECLARE_DELAYED_WORK(purge_orig_wq, purge_orig); | ||
45 | |||
46 | static atomic_t data_ready_cond; | ||
47 | atomic_t exit_cond; | ||
48 | |||
49 | static void start_purge_timer(void) | ||
50 | { | ||
51 | queue_delayed_work(bat_event_workqueue, &purge_orig_wq, 1 * HZ); | ||
52 | } | ||
53 | |||
54 | int originator_init(void) | ||
55 | { | ||
56 | if (orig_hash) | ||
57 | return 1; | ||
58 | |||
59 | spin_lock(&orig_hash_lock); | ||
60 | orig_hash = hash_new(128, compare_orig, choose_orig); | ||
61 | |||
62 | if (!orig_hash) | ||
63 | goto err; | ||
64 | |||
65 | spin_unlock(&orig_hash_lock); | ||
66 | start_purge_timer(); | ||
67 | return 1; | ||
68 | |||
69 | err: | ||
70 | spin_unlock(&orig_hash_lock); | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | void originator_free(void) | ||
75 | { | ||
76 | if (!orig_hash) | ||
77 | return; | ||
78 | |||
79 | cancel_delayed_work_sync(&purge_orig_wq); | ||
80 | |||
81 | spin_lock(&orig_hash_lock); | ||
82 | hash_delete(orig_hash, free_orig_node); | ||
83 | orig_hash = NULL; | ||
84 | spin_unlock(&orig_hash_lock); | ||
85 | } | ||
86 | |||
87 | 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) | ||
88 | { | ||
89 | struct neigh_node *neigh_node; | ||
90 | |||
91 | debug_log(LOG_TYPE_BATMAN, "Creating new last-hop neighbour of originator\n"); | ||
92 | |||
93 | neigh_node = kmalloc(sizeof(struct neigh_node), GFP_ATOMIC); | ||
94 | memset(neigh_node, 0, sizeof(struct neigh_node)); | ||
95 | INIT_LIST_HEAD(&neigh_node->list); | ||
96 | |||
97 | memcpy(neigh_node->addr, neigh, ETH_ALEN); | ||
98 | neigh_node->orig_node = orig_neigh_node; | ||
99 | neigh_node->if_incoming = if_incoming; | ||
100 | |||
101 | list_add_tail(&neigh_node->list, &orig_node->neigh_list); | ||
102 | return neigh_node; | ||
103 | } | ||
104 | |||
105 | void free_orig_node(void *data) | ||
106 | { | ||
107 | struct list_head *list_pos, *list_pos_tmp; | ||
108 | struct neigh_node *neigh_node; | ||
109 | struct orig_node *orig_node = (struct orig_node *)data; | ||
110 | |||
111 | /* for all neighbours towards this originator ... */ | ||
112 | list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { | ||
113 | neigh_node = list_entry(list_pos, struct neigh_node, list); | ||
114 | |||
115 | list_del(list_pos); | ||
116 | kfree(neigh_node); | ||
117 | } | ||
118 | |||
119 | hna_global_del_orig(orig_node, "originator timed out"); | ||
120 | |||
121 | kfree(orig_node->bcast_own); | ||
122 | kfree(orig_node->bcast_own_sum); | ||
123 | kfree(orig_node); | ||
124 | } | ||
125 | |||
126 | /* this function finds or creates an originator entry for the given address if it does not exits */ | ||
127 | static struct orig_node *get_orig_node(uint8_t *addr) | ||
128 | { | ||
129 | struct orig_node *orig_node; | ||
130 | struct hashtable_t *swaphash; | ||
131 | char orig_str[ETH_STR_LEN]; | ||
132 | |||
133 | orig_node = ((struct orig_node *)hash_find(orig_hash, addr)); | ||
134 | |||
135 | if (orig_node != NULL) | ||
136 | return orig_node; | ||
137 | |||
138 | addr_to_string(orig_str, addr); | ||
139 | debug_log(LOG_TYPE_BATMAN, "Creating new originator: %s \n", orig_str); | ||
140 | |||
141 | orig_node = kmalloc(sizeof(struct orig_node), GFP_ATOMIC); | ||
142 | memset(orig_node, 0, sizeof(struct orig_node)); | ||
143 | INIT_LIST_HEAD(&orig_node->neigh_list); | ||
144 | |||
145 | memcpy(orig_node->orig, addr, ETH_ALEN); | ||
146 | orig_node->router = NULL; | ||
147 | orig_node->batman_if = NULL; | ||
148 | orig_node->hna_buff = NULL; | ||
149 | |||
150 | orig_node->bcast_own = kmalloc(num_ifs * sizeof(TYPE_OF_WORD) * NUM_WORDS, GFP_ATOMIC); | ||
151 | memset(orig_node->bcast_own, 0, num_ifs * sizeof(TYPE_OF_WORD) * NUM_WORDS); | ||
152 | |||
153 | orig_node->bcast_own_sum = kmalloc(num_ifs * sizeof(uint8_t), GFP_ATOMIC); | ||
154 | memset(orig_node->bcast_own_sum, 0, num_ifs * sizeof(uint8_t)); | ||
155 | |||
156 | hash_add(orig_hash, orig_node); | ||
157 | |||
158 | if (orig_hash->elements * 4 > orig_hash->size) { | ||
159 | swaphash = hash_resize(orig_hash, orig_hash->size * 2); | ||
160 | |||
161 | if (swaphash == NULL) | ||
162 | debug_log(LOG_TYPE_CRIT, "Couldn't resize orig hash table \n"); | ||
163 | else | ||
164 | orig_hash = swaphash; | ||
165 | } | ||
166 | |||
167 | return orig_node; | ||
168 | } | ||
169 | |||
170 | void slide_own_bcast_window(struct batman_if *batman_if) | ||
171 | { | ||
172 | struct hash_it_t *hashit = NULL; | ||
173 | struct orig_node *orig_node; | ||
174 | |||
175 | spin_lock(&orig_hash_lock); | ||
176 | |||
177 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
178 | orig_node = hashit->bucket->data; | ||
179 | |||
180 | bit_get_packet((TYPE_OF_WORD *)&(orig_node->bcast_own[batman_if->if_num * NUM_WORDS]), 1, 0); | ||
181 | 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])); | ||
182 | } | ||
183 | |||
184 | spin_unlock(&orig_hash_lock); | ||
185 | } | ||
186 | |||
187 | static void update_routes(struct orig_node *orig_node, struct neigh_node *neigh_node, unsigned char *hna_buff, int hna_buff_len) | ||
188 | { | ||
189 | char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN], router_str[ETH_STR_LEN]; | ||
190 | |||
191 | if (orig_node == NULL) | ||
192 | return; | ||
193 | |||
194 | if (orig_node->router != neigh_node) { | ||
195 | addr_to_string(orig_str, orig_node->orig); | ||
196 | |||
197 | /* route deleted */ | ||
198 | if ((orig_node->router != NULL) && (neigh_node == NULL)) { | ||
199 | |||
200 | debug_log(LOG_TYPE_ROUTES, "Deleting route towards: %s\n", orig_str); | ||
201 | hna_global_del_orig(orig_node, "originator timed out"); | ||
202 | |||
203 | /* route added */ | ||
204 | } else if ((orig_node->router == NULL) && (neigh_node != NULL)) { | ||
205 | |||
206 | addr_to_string(neigh_str, neigh_node->addr); | ||
207 | debug_log(LOG_TYPE_ROUTES, "Adding route towards: %s (via %s)\n", orig_str, neigh_str); | ||
208 | hna_global_add_orig(orig_node, hna_buff, hna_buff_len); | ||
209 | |||
210 | /* route changed */ | ||
211 | } else { | ||
212 | |||
213 | addr_to_string(neigh_str, neigh_node->addr); | ||
214 | addr_to_string(router_str, orig_node->router->addr); | ||
215 | debug_log(LOG_TYPE_ROUTES, "Changing route towards: %s (now via %s - was via %s)\n", orig_str, neigh_str, router_str); | ||
216 | |||
217 | } | ||
218 | |||
219 | if (neigh_node != NULL) | ||
220 | orig_node->batman_if = neigh_node->if_incoming; | ||
221 | else | ||
222 | orig_node->batman_if = NULL; | ||
223 | |||
224 | orig_node->router = neigh_node; | ||
225 | |||
226 | /* may be just HNA changed */ | ||
227 | } else { | ||
228 | |||
229 | 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))) { | ||
230 | |||
231 | if (orig_node->hna_buff_len > 0) | ||
232 | hna_global_del_orig(orig_node, "originator changed hna"); | ||
233 | |||
234 | if ((hna_buff_len > 0) && (hna_buff != NULL)) | ||
235 | hna_global_add_orig(orig_node, hna_buff, hna_buff_len); | ||
236 | |||
237 | } | ||
238 | |||
239 | } | ||
240 | } | ||
241 | |||
242 | static int isBidirectionalNeigh(struct orig_node *orig_node, struct orig_node *orig_neigh_node, struct batman_packet *batman_packet, struct batman_if *if_incoming) | ||
243 | { | ||
244 | struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; | ||
245 | char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN]; | ||
246 | unsigned char total_count; | ||
247 | |||
248 | addr_to_string(orig_str, orig_node->orig); | ||
249 | addr_to_string(neigh_str, orig_neigh_node->orig); | ||
250 | |||
251 | if (orig_node == orig_neigh_node) { | ||
252 | list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { | ||
253 | |||
254 | if (compare_orig(tmp_neigh_node->addr, orig_neigh_node->orig) && (tmp_neigh_node->if_incoming == if_incoming)) | ||
255 | neigh_node = tmp_neigh_node; | ||
256 | } | ||
257 | |||
258 | if (neigh_node == NULL) | ||
259 | neigh_node = create_neighbor(orig_node, orig_neigh_node, orig_neigh_node->orig, if_incoming); | ||
260 | |||
261 | neigh_node->last_valid = jiffies; | ||
262 | } else { | ||
263 | /* find packet count of corresponding one hop neighbor */ | ||
264 | list_for_each_entry(tmp_neigh_node, &orig_neigh_node->neigh_list, list) { | ||
265 | |||
266 | if (compare_orig(tmp_neigh_node->addr, orig_neigh_node->orig) && (tmp_neigh_node->if_incoming == if_incoming)) | ||
267 | neigh_node = tmp_neigh_node; | ||
268 | } | ||
269 | |||
270 | if (neigh_node == NULL) | ||
271 | neigh_node = create_neighbor(orig_neigh_node, orig_neigh_node, orig_neigh_node->orig, if_incoming); | ||
272 | } | ||
273 | |||
274 | orig_node->last_valid = jiffies; | ||
275 | |||
276 | /* pay attention to not get a value bigger than 100 % */ | ||
277 | 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]); | ||
278 | |||
279 | /* if we have too few packets (too less data) we set tq_own to zero */ | ||
280 | /* if we receive too few packets it is not considered bidirectional */ | ||
281 | if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) | ||
282 | orig_neigh_node->tq_own = 0; | ||
283 | else | ||
284 | /* neigh_node->real_packet_count is never zero as we only purge old information when getting new information */ | ||
285 | orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / neigh_node->real_packet_count; | ||
286 | |||
287 | /* | ||
288 | * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE | ||
289 | * this does affect the nearly-symmetric links only a little, | ||
290 | * but punishes asymmetric links more. | ||
291 | * this will give a value between 0 and TQ_MAX_VALUE | ||
292 | */ | ||
293 | orig_neigh_node->tq_asym_penalty = TQ_MAX_VALUE - (TQ_MAX_VALUE * | ||
294 | (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * | ||
295 | (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * | ||
296 | (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / | ||
297 | (TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE); | ||
298 | |||
299 | batman_packet->tq = ((batman_packet->tq * orig_neigh_node->tq_own * orig_neigh_node->tq_asym_penalty) / (TQ_MAX_VALUE * TQ_MAX_VALUE)); | ||
300 | |||
301 | 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", | ||
302 | 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); | ||
303 | |||
304 | /* if link has the minimum required transmission quality consider it bidirectional */ | ||
305 | if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) | ||
306 | return 1; | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | 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) | ||
312 | { | ||
313 | struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; | ||
314 | int tmp_hna_buff_len; | ||
315 | |||
316 | debug_log(LOG_TYPE_BATMAN, "update_originator(): Searching and updating originator entry of received packet \n"); | ||
317 | |||
318 | list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { | ||
319 | if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming)) { | ||
320 | neigh_node = tmp_neigh_node; | ||
321 | continue; | ||
322 | } | ||
323 | |||
324 | if (is_duplicate) | ||
325 | continue; | ||
326 | |||
327 | ring_buffer_set(tmp_neigh_node->tq_recv, &tmp_neigh_node->tq_index, 0); | ||
328 | tmp_neigh_node->tq_avg = ring_buffer_avg(tmp_neigh_node->tq_recv); | ||
329 | } | ||
330 | |||
331 | if (neigh_node == NULL) | ||
332 | neigh_node = create_neighbor(orig_node, get_orig_node(ethhdr->h_source), ethhdr->h_source, if_incoming); | ||
333 | else | ||
334 | debug_log(LOG_TYPE_BATMAN, "Updating existing last-hop neighbour of originator\n"); | ||
335 | |||
336 | orig_node->flags = batman_packet->flags; | ||
337 | neigh_node->last_valid = jiffies; | ||
338 | |||
339 | ring_buffer_set(neigh_node->tq_recv, &neigh_node->tq_index, batman_packet->tq); | ||
340 | neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); | ||
341 | |||
342 | if (!is_duplicate) { | ||
343 | orig_node->last_ttl = batman_packet->ttl; | ||
344 | neigh_node->last_ttl = batman_packet->ttl; | ||
345 | } | ||
346 | |||
347 | tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? batman_packet->num_hna * ETH_ALEN : hna_buff_len); | ||
348 | |||
349 | /* if this neighbor already is our next hop there is nothing to change */ | ||
350 | if (orig_node->router == neigh_node) | ||
351 | goto update_hna; | ||
352 | |||
353 | /* if this neighbor does not offer a better TQ we won't consider it */ | ||
354 | if ((orig_node->router) && | ||
355 | (orig_node->router->tq_avg > neigh_node->tq_avg)) | ||
356 | goto update_hna; | ||
357 | |||
358 | /* if the TQ is the same and the link not more symetric we won't consider it either */ | ||
359 | if ((orig_node->router) && | ||
360 | ((neigh_node->tq_avg == orig_node->router->tq_avg) && | ||
361 | (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] >= | ||
362 | neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) | ||
363 | goto update_hna; | ||
364 | |||
365 | update_routes(orig_node, neigh_node, hna_buff, tmp_hna_buff_len); | ||
366 | return; | ||
367 | |||
368 | update_hna: | ||
369 | update_routes(orig_node, orig_node->router, hna_buff, tmp_hna_buff_len); | ||
370 | return; | ||
371 | } | ||
372 | |||
373 | static char count_real_packets(struct ethhdr *ethhdr, struct batman_packet *batman_packet, struct batman_if *if_incoming) | ||
374 | { | ||
375 | struct orig_node *orig_node; | ||
376 | struct neigh_node *tmp_neigh_node; | ||
377 | char is_duplicate = 0; | ||
378 | |||
379 | |||
380 | orig_node = get_orig_node(batman_packet->orig); | ||
381 | if (orig_node == NULL) | ||
382 | return 0; | ||
383 | |||
384 | |||
385 | list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { | ||
386 | |||
387 | if (!is_duplicate) | ||
388 | is_duplicate = get_bit_status(tmp_neigh_node->real_bits, orig_node->last_real_seqno, batman_packet->seqno); | ||
389 | |||
390 | if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming)) | ||
391 | bit_get_packet(tmp_neigh_node->real_bits, batman_packet->seqno - orig_node->last_real_seqno, 1); | ||
392 | else | ||
393 | bit_get_packet(tmp_neigh_node->real_bits, batman_packet->seqno - orig_node->last_real_seqno, 0); | ||
394 | |||
395 | tmp_neigh_node->real_packet_count = bit_packet_count(tmp_neigh_node->real_bits); | ||
396 | } | ||
397 | |||
398 | if (!is_duplicate) { | ||
399 | debug_log(LOG_TYPE_BATMAN, "updating last_seqno: old %d, new %d \n", orig_node->last_real_seqno, batman_packet->seqno); | ||
400 | orig_node->last_real_seqno = batman_packet->seqno; | ||
401 | } | ||
402 | |||
403 | return is_duplicate; | ||
404 | } | ||
405 | |||
406 | 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) | ||
407 | { | ||
408 | struct batman_if *batman_if; | ||
409 | struct orig_node *orig_neigh_node, *orig_node; | ||
410 | char orig_str[ETH_STR_LEN], prev_sender_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN]; | ||
411 | char has_directlink_flag; | ||
412 | char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0, is_broadcast = 0, is_bidirectional, is_single_hop_neigh, is_duplicate; | ||
413 | unsigned short if_incoming_seqno; | ||
414 | |||
415 | /* Silently drop when the batman packet is actually not a correct packet. | ||
416 | * | ||
417 | * This might happen if a packet is padded (e.g. Ethernet has a | ||
418 | * minimum frame length of 64 byte) and the aggregation interprets | ||
419 | * it as an additional length. | ||
420 | * | ||
421 | * TODO: A more sane solution would be to have a bit in the batman_packet | ||
422 | * to detect whether the packet is the last packet in an aggregation. | ||
423 | * Here we expect that the padding is always zero (or not 0x01) | ||
424 | */ | ||
425 | if (batman_packet->packet_type != BAT_PACKET) | ||
426 | return; | ||
427 | |||
428 | /* could be changed by schedule_own_packet() */ | ||
429 | if_incoming_seqno = atomic_read(&if_incoming->seqno); | ||
430 | |||
431 | addr_to_string(orig_str, batman_packet->orig); | ||
432 | addr_to_string(prev_sender_str, batman_packet->prev_sender); | ||
433 | addr_to_string(neigh_str, ethhdr->h_source); | ||
434 | |||
435 | has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); | ||
436 | |||
437 | is_single_hop_neigh = (compare_orig(ethhdr->h_source, batman_packet->orig) ? 1 : 0); | ||
438 | |||
439 | 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); | ||
440 | |||
441 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
442 | if (batman_if->if_active != IF_ACTIVE) | ||
443 | continue; | ||
444 | |||
445 | if (compare_orig(ethhdr->h_source, batman_if->net_dev->dev_addr)) | ||
446 | is_my_addr = 1; | ||
447 | |||
448 | if (compare_orig(batman_packet->orig, batman_if->net_dev->dev_addr)) | ||
449 | is_my_orig = 1; | ||
450 | |||
451 | if (compare_orig(batman_packet->prev_sender, batman_if->net_dev->dev_addr)) | ||
452 | is_my_oldorig = 1; | ||
453 | |||
454 | if (compare_orig(ethhdr->h_source, broadcastAddr)) | ||
455 | is_broadcast = 1; | ||
456 | } | ||
457 | |||
458 | if (batman_packet->version != COMPAT_VERSION) { | ||
459 | debug_log(LOG_TYPE_BATMAN, "Drop packet: incompatible batman version (%i) \n", batman_packet->version); | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | if (is_my_addr) { | ||
464 | debug_log(LOG_TYPE_BATMAN, "Drop packet: received my own broadcast (sender: %s) \n", neigh_str); | ||
465 | return; | ||
466 | } | ||
467 | |||
468 | if (is_broadcast) { | ||
469 | debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all packets with broadcast source addr (sender: %s) \n", neigh_str); | ||
470 | return; | ||
471 | } | ||
472 | |||
473 | if (is_my_orig) { | ||
474 | orig_neigh_node = get_orig_node(ethhdr->h_source); | ||
475 | |||
476 | /* neighbour has to indicate direct link and it has to come via the corresponding interface */ | ||
477 | /* if received seqno equals last send seqno save new seqno for bidirectional check */ | ||
478 | if (has_directlink_flag && compare_orig(if_incoming->net_dev->dev_addr, batman_packet->orig) && | ||
479 | (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { | ||
480 | bit_mark((TYPE_OF_WORD *)&(orig_neigh_node->bcast_own[if_incoming->if_num * NUM_WORDS]), 0); | ||
481 | 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])); | ||
482 | } | ||
483 | |||
484 | debug_log(LOG_TYPE_BATMAN, "Drop packet: originator packet from myself (via neighbour) \n"); | ||
485 | return; | ||
486 | } | ||
487 | |||
488 | if (batman_packet->tq == 0) { | ||
489 | count_real_packets(ethhdr, batman_packet, if_incoming); | ||
490 | |||
491 | debug_log(LOG_TYPE_BATMAN, "Drop packet: originator packet with tq equal 0 \n"); | ||
492 | return; | ||
493 | } | ||
494 | |||
495 | if (is_my_oldorig) { | ||
496 | debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all rebroadcast echos (sender: %s) \n", neigh_str); | ||
497 | return; | ||
498 | } | ||
499 | |||
500 | is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming); | ||
501 | |||
502 | orig_node = get_orig_node(batman_packet->orig); | ||
503 | if (orig_node == NULL) | ||
504 | return; | ||
505 | |||
506 | /* avoid temporary routing loops */ | ||
507 | if ((orig_node->router) && (orig_node->router->orig_node->router) && | ||
508 | (compare_orig(orig_node->router->addr, batman_packet->prev_sender)) && | ||
509 | !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && | ||
510 | (compare_orig(orig_node->router->addr, orig_node->router->orig_node->router->addr))) { | ||
511 | debug_log(LOG_TYPE_BATMAN, "Drop packet: ignoring all rebroadcast packets that may make me loop (sender: %s) \n", neigh_str); | ||
512 | return; | ||
513 | } | ||
514 | |||
515 | /* if sender is a direct neighbor the sender mac equals originator mac */ | ||
516 | orig_neigh_node = (is_single_hop_neigh ? orig_node : get_orig_node(ethhdr->h_source)); | ||
517 | if (orig_neigh_node == NULL) | ||
518 | return; | ||
519 | |||
520 | /* drop packet if sender is not a direct neighbor and if we don't route towards it */ | ||
521 | if (!is_single_hop_neigh && (orig_neigh_node->router == NULL)) { | ||
522 | debug_log(LOG_TYPE_BATMAN, "Drop packet: OGM via unknown neighbor! \n"); | ||
523 | return; | ||
524 | } | ||
525 | |||
526 | is_bidirectional = isBidirectionalNeigh(orig_node, orig_neigh_node, batman_packet, if_incoming); | ||
527 | |||
528 | /* update ranking if it is not a duplicate or has the same seqno and similar ttl as the non-duplicate */ | ||
529 | if (is_bidirectional && (!is_duplicate || | ||
530 | ((orig_node->last_real_seqno == batman_packet->seqno) && | ||
531 | (orig_node->last_ttl - 3 <= batman_packet->ttl)))) | ||
532 | update_orig(orig_node, ethhdr, batman_packet, if_incoming, hna_buff, hna_buff_len, is_duplicate); | ||
533 | |||
534 | /* is single hop (direct) neighbour */ | ||
535 | if (is_single_hop_neigh) { | ||
536 | |||
537 | /* mark direct link on incoming interface */ | ||
538 | schedule_forward_packet(orig_node, ethhdr, batman_packet, 1, hna_buff_len, if_incoming); | ||
539 | |||
540 | debug_log(LOG_TYPE_BATMAN, "Forwarding packet: rebroadcast neighbour packet with direct link flag \n"); | ||
541 | return; | ||
542 | } | ||
543 | |||
544 | /* multihop originator */ | ||
545 | if (!is_bidirectional) { | ||
546 | debug_log(LOG_TYPE_BATMAN, "Drop packet: not received via bidirectional link\n"); | ||
547 | return; | ||
548 | } | ||
549 | |||
550 | if (is_duplicate) { | ||
551 | debug_log(LOG_TYPE_BATMAN, "Drop packet: duplicate packet received\n"); | ||
552 | return; | ||
553 | } | ||
554 | |||
555 | debug_log(LOG_TYPE_BATMAN, "Forwarding packet: rebroadcast originator packet \n"); | ||
556 | schedule_forward_packet(orig_node, ethhdr, batman_packet, 0, hna_buff_len, if_incoming); | ||
557 | } | ||
558 | |||
559 | void purge_orig(struct work_struct *work) | ||
560 | { | ||
561 | struct list_head *list_pos, *list_pos_tmp; | ||
562 | struct hash_it_t *hashit = NULL; | ||
563 | struct orig_node *orig_node; | ||
564 | struct neigh_node *neigh_node, *best_neigh_node; | ||
565 | char orig_str[ETH_STR_LEN], neigh_str[ETH_STR_LEN], neigh_purged; | ||
566 | |||
567 | spin_lock(&orig_hash_lock); | ||
568 | |||
569 | /* for all origins... */ | ||
570 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
571 | |||
572 | orig_node = hashit->bucket->data; | ||
573 | addr_to_string(orig_str, orig_node->orig); | ||
574 | |||
575 | if (time_after(jiffies, orig_node->last_valid + ((2 * PURGE_TIMEOUT * HZ) / 1000))) { | ||
576 | |||
577 | debug_log(LOG_TYPE_BATMAN, "Originator timeout: originator %s, last_valid %u \n", orig_str, (orig_node->last_valid / HZ)); | ||
578 | |||
579 | hash_remove_bucket(orig_hash, hashit); | ||
580 | free_orig_node(orig_node); | ||
581 | |||
582 | } else { | ||
583 | |||
584 | best_neigh_node = NULL; | ||
585 | neigh_purged = 0; | ||
586 | |||
587 | /* for all neighbours towards this originator ... */ | ||
588 | list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { | ||
589 | neigh_node = list_entry(list_pos, struct neigh_node, list); | ||
590 | |||
591 | if (time_after(jiffies, neigh_node->last_valid + ((PURGE_TIMEOUT * HZ) / 1000))) { | ||
592 | |||
593 | addr_to_string(neigh_str, neigh_node->addr); | ||
594 | debug_log(LOG_TYPE_BATMAN, "Neighbour timeout: originator %s, neighbour: %s, last_valid %u \n", orig_str, neigh_str, (neigh_node->last_valid / HZ)); | ||
595 | |||
596 | neigh_purged = 1; | ||
597 | list_del(list_pos); | ||
598 | kfree(neigh_node); | ||
599 | |||
600 | } else { | ||
601 | |||
602 | if ((best_neigh_node == NULL) || (neigh_node->tq_avg > best_neigh_node->tq_avg)) | ||
603 | best_neigh_node = neigh_node; | ||
604 | |||
605 | } | ||
606 | |||
607 | } | ||
608 | |||
609 | if (neigh_purged) | ||
610 | update_routes(orig_node, best_neigh_node, orig_node->hna_buff, orig_node->hna_buff_len); | ||
611 | |||
612 | } | ||
613 | |||
614 | } | ||
615 | |||
616 | spin_unlock(&orig_hash_lock); | ||
617 | |||
618 | start_purge_timer(); | ||
619 | } | ||
620 | |||
621 | static int receive_raw_packet(struct socket *raw_sock, unsigned char *packet_buff, int packet_buff_len) | ||
622 | { | ||
623 | struct kvec iov; | ||
624 | struct msghdr msg; | ||
625 | |||
626 | iov.iov_base = packet_buff; | ||
627 | iov.iov_len = packet_buff_len; | ||
628 | |||
629 | msg.msg_flags = MSG_DONTWAIT; /* non-blocking */ | ||
630 | msg.msg_name = NULL; | ||
631 | msg.msg_namelen = 0; | ||
632 | msg.msg_control = NULL; | ||
633 | |||
634 | return kernel_recvmsg(raw_sock, &msg, &iov, 1, packet_buff_len, MSG_DONTWAIT); | ||
635 | } | ||
636 | |||
637 | int packet_recv_thread(void *data) | ||
638 | { | ||
639 | struct batman_if *batman_if; | ||
640 | struct ethhdr *ethhdr; | ||
641 | struct batman_packet *batman_packet; | ||
642 | struct unicast_packet *unicast_packet; | ||
643 | struct bcast_packet *bcast_packet; | ||
644 | struct icmp_packet *icmp_packet; | ||
645 | struct vis_packet *vis_packet; | ||
646 | struct orig_node *orig_node; | ||
647 | unsigned char *packet_buff, src_str[ETH_STR_LEN], dst_str[ETH_STR_LEN]; | ||
648 | int vis_info_len; | ||
649 | int result; | ||
650 | |||
651 | atomic_set(&data_ready_cond, 0); | ||
652 | atomic_set(&exit_cond, 0); | ||
653 | packet_buff = kmalloc(PACKBUFF_SIZE, GFP_KERNEL); | ||
654 | if (!packet_buff) { | ||
655 | debug_log(LOG_TYPE_CRIT, "Could allocate memory for the packet buffer. :(\n"); | ||
656 | return -1; | ||
657 | } | ||
658 | |||
659 | while ((!kthread_should_stop()) && (!atomic_read(&exit_cond))) { | ||
660 | |||
661 | wait_event_interruptible(thread_wait, (atomic_read(&data_ready_cond) || atomic_read(&exit_cond))); | ||
662 | |||
663 | atomic_set(&data_ready_cond, 0); | ||
664 | |||
665 | if (kthread_should_stop() || atomic_read(&exit_cond)) | ||
666 | break; | ||
667 | |||
668 | /* we only want to safely traverse the list, hard-interfaces | ||
669 | * won't be deleted anyway as long as this thread runs. */ | ||
670 | |||
671 | rcu_read_lock(); | ||
672 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
673 | rcu_read_unlock(); | ||
674 | |||
675 | result = -1; | ||
676 | |||
677 | while (1) { | ||
678 | if (batman_if->if_active != IF_ACTIVE) { | ||
679 | if (batman_if->if_active != IF_TO_BE_ACTIVATED) | ||
680 | debug_log(LOG_TYPE_NOTICE, | ||
681 | "Could not read from deactivated interface %s!\n", | ||
682 | batman_if->dev); | ||
683 | |||
684 | if (batman_if->raw_sock) | ||
685 | receive_raw_packet(batman_if->raw_sock, packet_buff, PACKBUFF_SIZE); | ||
686 | result = 0; | ||
687 | break; | ||
688 | } | ||
689 | |||
690 | result = receive_raw_packet(batman_if->raw_sock, packet_buff, PACKBUFF_SIZE); | ||
691 | if (result <= 0) | ||
692 | break; | ||
693 | |||
694 | if (result < sizeof(struct ethhdr) + 2) | ||
695 | continue; | ||
696 | |||
697 | ethhdr = (struct ethhdr *)packet_buff; | ||
698 | batman_packet = (struct batman_packet *)(packet_buff + sizeof(struct ethhdr)); | ||
699 | |||
700 | if (batman_packet->version != COMPAT_VERSION) { | ||
701 | debug_log(LOG_TYPE_BATMAN, "Drop packet: incompatible batman version (%i) \n", batman_packet->version); | ||
702 | continue; | ||
703 | } | ||
704 | |||
705 | switch (batman_packet->packet_type) { | ||
706 | /* batman originator packet */ | ||
707 | case BAT_PACKET: | ||
708 | /* packet with broadcast indication but unicast recipient */ | ||
709 | if (!is_bcast(ethhdr->h_dest)) | ||
710 | continue; | ||
711 | |||
712 | /* packet with broadcast sender address */ | ||
713 | if (is_bcast(ethhdr->h_source)) | ||
714 | continue; | ||
715 | |||
716 | /* drop packet if it has not at least one batman packet as payload */ | ||
717 | if (result < sizeof(struct ethhdr) + sizeof(struct batman_packet)) | ||
718 | continue; | ||
719 | |||
720 | spin_lock(&orig_hash_lock); | ||
721 | receive_aggr_bat_packet(ethhdr, | ||
722 | packet_buff + sizeof(struct ethhdr), | ||
723 | result - sizeof(struct ethhdr), | ||
724 | batman_if); | ||
725 | spin_unlock(&orig_hash_lock); | ||
726 | |||
727 | break; | ||
728 | |||
729 | /* batman icmp packet */ | ||
730 | case BAT_ICMP: | ||
731 | /* packet with unicast indication but broadcast recipient */ | ||
732 | if (is_bcast(ethhdr->h_dest)) | ||
733 | continue; | ||
734 | |||
735 | /* packet with broadcast sender address */ | ||
736 | if (is_bcast(ethhdr->h_source)) | ||
737 | continue; | ||
738 | |||
739 | /* not for me */ | ||
740 | if (!is_my_mac(ethhdr->h_dest)) | ||
741 | continue; | ||
742 | |||
743 | /* drop packet if it has not necessary minimum size */ | ||
744 | if (result < sizeof(struct ethhdr) + sizeof(struct icmp_packet)) | ||
745 | continue; | ||
746 | |||
747 | icmp_packet = (struct icmp_packet *)(packet_buff + sizeof(struct ethhdr)); | ||
748 | |||
749 | /* packet for me */ | ||
750 | if (is_my_mac(icmp_packet->dst)) { | ||
751 | |||
752 | /* add data to device queue */ | ||
753 | if (icmp_packet->msg_type != ECHO_REQUEST) { | ||
754 | bat_device_receive_packet(icmp_packet); | ||
755 | continue; | ||
756 | } | ||
757 | |||
758 | /* answer echo request (ping) */ | ||
759 | /* get routing information */ | ||
760 | spin_lock(&orig_hash_lock); | ||
761 | orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig)); | ||
762 | |||
763 | if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { | ||
764 | |||
765 | memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); | ||
766 | memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); | ||
767 | icmp_packet->msg_type = ECHO_REPLY; | ||
768 | icmp_packet->ttl = TTL; | ||
769 | |||
770 | send_raw_packet(packet_buff + sizeof(struct ethhdr), | ||
771 | result - sizeof(struct ethhdr), | ||
772 | orig_node->batman_if, | ||
773 | orig_node->router->addr); | ||
774 | |||
775 | } | ||
776 | |||
777 | spin_unlock(&orig_hash_lock); | ||
778 | continue; | ||
779 | |||
780 | } | ||
781 | |||
782 | /* TTL exceeded */ | ||
783 | if (icmp_packet->ttl < 2) { | ||
784 | |||
785 | addr_to_string(src_str, icmp_packet->orig); | ||
786 | addr_to_string(dst_str, icmp_packet->dst); | ||
787 | |||
788 | debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str); | ||
789 | |||
790 | /* send TTL exceeded if packet is an echo request (traceroute) */ | ||
791 | if (icmp_packet->msg_type != ECHO_REQUEST) | ||
792 | continue; | ||
793 | |||
794 | /* get routing information */ | ||
795 | spin_lock(&orig_hash_lock); | ||
796 | orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->orig)); | ||
797 | |||
798 | if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { | ||
799 | |||
800 | memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); | ||
801 | memcpy(icmp_packet->orig, ethhdr->h_dest, ETH_ALEN); | ||
802 | icmp_packet->msg_type = TTL_EXCEEDED; | ||
803 | icmp_packet->ttl = TTL; | ||
804 | |||
805 | send_raw_packet(packet_buff + sizeof(struct ethhdr), | ||
806 | result - sizeof(struct ethhdr), | ||
807 | orig_node->batman_if, | ||
808 | orig_node->router->addr); | ||
809 | |||
810 | } | ||
811 | |||
812 | spin_unlock(&orig_hash_lock); | ||
813 | continue; | ||
814 | |||
815 | } | ||
816 | |||
817 | /* get routing information */ | ||
818 | spin_lock(&orig_hash_lock); | ||
819 | orig_node = ((struct orig_node *)hash_find(orig_hash, icmp_packet->dst)); | ||
820 | |||
821 | if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { | ||
822 | |||
823 | /* decrement ttl */ | ||
824 | icmp_packet->ttl--; | ||
825 | |||
826 | /* route it */ | ||
827 | send_raw_packet(packet_buff + sizeof(struct ethhdr), | ||
828 | result - sizeof(struct ethhdr), | ||
829 | orig_node->batman_if, | ||
830 | orig_node->router->addr); | ||
831 | } | ||
832 | |||
833 | spin_unlock(&orig_hash_lock); | ||
834 | break; | ||
835 | |||
836 | /* unicast packet */ | ||
837 | case BAT_UNICAST: | ||
838 | /* packet with unicast indication but broadcast recipient */ | ||
839 | if (is_bcast(ethhdr->h_dest)) | ||
840 | continue; | ||
841 | |||
842 | /* packet with broadcast sender address */ | ||
843 | if (is_bcast(ethhdr->h_source)) | ||
844 | continue; | ||
845 | |||
846 | /* not for me */ | ||
847 | if (!is_my_mac(ethhdr->h_dest)) | ||
848 | continue; | ||
849 | |||
850 | /* drop packet if it has not necessary minimum size */ | ||
851 | if (result < sizeof(struct ethhdr) + sizeof(struct unicast_packet)) | ||
852 | continue; | ||
853 | |||
854 | unicast_packet = (struct unicast_packet *)(packet_buff + sizeof(struct ethhdr)); | ||
855 | |||
856 | /* packet for me */ | ||
857 | if (is_my_mac(unicast_packet->dest)) { | ||
858 | |||
859 | interface_rx(soft_device, packet_buff + sizeof(struct ethhdr) + sizeof(struct unicast_packet), result - sizeof(struct ethhdr) - sizeof(struct unicast_packet)); | ||
860 | continue; | ||
861 | |||
862 | } | ||
863 | |||
864 | /* TTL exceeded */ | ||
865 | if (unicast_packet->ttl < 2) { | ||
866 | addr_to_string(src_str, ((struct ethhdr *)(unicast_packet + 1))->h_source); | ||
867 | addr_to_string(dst_str, unicast_packet->dest); | ||
868 | |||
869 | debug_log(LOG_TYPE_NOTICE, "Error - can't send packet from %s to %s: ttl exceeded\n", src_str, dst_str); | ||
870 | continue; | ||
871 | } | ||
872 | |||
873 | /* get routing information */ | ||
874 | spin_lock(&orig_hash_lock); | ||
875 | orig_node = ((struct orig_node *)hash_find(orig_hash, unicast_packet->dest)); | ||
876 | |||
877 | if ((orig_node != NULL) && (orig_node->batman_if != NULL) && (orig_node->router != NULL)) { | ||
878 | /* decrement ttl */ | ||
879 | unicast_packet->ttl--; | ||
880 | |||
881 | /* route it */ | ||
882 | send_raw_packet(packet_buff + sizeof(struct ethhdr), | ||
883 | result - sizeof(struct ethhdr), | ||
884 | orig_node->batman_if, | ||
885 | orig_node->router->addr); | ||
886 | } | ||
887 | |||
888 | spin_unlock(&orig_hash_lock); | ||
889 | break; | ||
890 | |||
891 | /* broadcast packet */ | ||
892 | case BAT_BCAST: | ||
893 | /* packet with broadcast indication but unicast recipient */ | ||
894 | if (!is_bcast(ethhdr->h_dest)) | ||
895 | continue; | ||
896 | |||
897 | /* packet with broadcast sender address */ | ||
898 | if (is_bcast(ethhdr->h_source)) | ||
899 | continue; | ||
900 | |||
901 | /* drop packet if it has not necessary minimum size */ | ||
902 | if (result < sizeof(struct ethhdr) + sizeof(struct bcast_packet)) | ||
903 | continue; | ||
904 | |||
905 | /* ignore broadcasts sent by myself */ | ||
906 | if (is_my_mac(ethhdr->h_source)) | ||
907 | continue; | ||
908 | |||
909 | bcast_packet = (struct bcast_packet *)(packet_buff + sizeof(struct ethhdr)); | ||
910 | |||
911 | /* ignore broadcasts originated by myself */ | ||
912 | if (is_my_mac(bcast_packet->orig)) | ||
913 | continue; | ||
914 | |||
915 | spin_lock(&orig_hash_lock); | ||
916 | orig_node = ((struct orig_node *)hash_find(orig_hash, bcast_packet->orig)); | ||
917 | |||
918 | if (orig_node == NULL) { | ||
919 | spin_unlock(&orig_hash_lock); | ||
920 | continue; | ||
921 | } | ||
922 | |||
923 | /* check flood history */ | ||
924 | if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno, ntohs(bcast_packet->seqno))) { | ||
925 | spin_unlock(&orig_hash_lock); | ||
926 | continue; | ||
927 | } | ||
928 | |||
929 | /* mark broadcast in flood history */ | ||
930 | if (bit_get_packet(orig_node->bcast_bits, ntohs(bcast_packet->seqno) - orig_node->last_bcast_seqno, 1)) | ||
931 | orig_node->last_bcast_seqno = ntohs(bcast_packet->seqno); | ||
932 | |||
933 | spin_unlock(&orig_hash_lock); | ||
934 | |||
935 | /* broadcast for me */ | ||
936 | interface_rx(soft_device, packet_buff + sizeof(struct ethhdr) + sizeof(struct bcast_packet), result - sizeof(struct ethhdr) - sizeof(struct bcast_packet)); | ||
937 | |||
938 | /* rebroadcast packet */ | ||
939 | add_bcast_packet_to_list(packet_buff + sizeof(struct ethhdr), | ||
940 | result - sizeof(struct ethhdr)); | ||
941 | |||
942 | break; | ||
943 | |||
944 | /* vis packet */ | ||
945 | case BAT_VIS: | ||
946 | /* drop if too short. */ | ||
947 | if (result < sizeof(struct ethhdr) + sizeof(struct vis_packet)) | ||
948 | continue; | ||
949 | |||
950 | /* not for me */ | ||
951 | if (!is_my_mac(ethhdr->h_dest)) | ||
952 | continue; | ||
953 | |||
954 | vis_packet = (struct vis_packet *)(packet_buff + sizeof(struct ethhdr)); | ||
955 | vis_info_len = result - sizeof(struct ethhdr) - sizeof(struct vis_packet); | ||
956 | |||
957 | /* ignore own packets */ | ||
958 | if (is_my_mac(vis_packet->vis_orig)) | ||
959 | continue; | ||
960 | |||
961 | if (is_my_mac(vis_packet->sender_orig)) | ||
962 | continue; | ||
963 | |||
964 | switch (vis_packet->vis_type) { | ||
965 | case VIS_TYPE_SERVER_SYNC: | ||
966 | receive_server_sync_packet(vis_packet, vis_info_len); | ||
967 | break; | ||
968 | |||
969 | case VIS_TYPE_CLIENT_UPDATE: | ||
970 | receive_client_update_packet(vis_packet, vis_info_len); | ||
971 | break; | ||
972 | |||
973 | default: /* ignore unknown packet */ | ||
974 | break; | ||
975 | } | ||
976 | |||
977 | break; | ||
978 | } | ||
979 | |||
980 | } | ||
981 | |||
982 | if ((result < 0) && (result != -EAGAIN)) | ||
983 | debug_log(LOG_TYPE_CRIT, "Could not receive packet from interface %s: %i\n", batman_if->dev, result); | ||
984 | |||
985 | /* lock for the next iteration */ | ||
986 | rcu_read_lock(); | ||
987 | } | ||
988 | rcu_read_unlock(); | ||
989 | |||
990 | } | ||
991 | kfree(packet_buff); | ||
992 | |||
993 | /* do not exit until kthread_stop() is actually called, otherwise it will wait for us | ||
994 | * forever. */ | ||
995 | while (!kthread_should_stop()) | ||
996 | schedule(); | ||
997 | |||
998 | return 0; | ||
999 | } | ||
1000 | |||
1001 | void batman_data_ready(struct sock *sk, int len) | ||
1002 | { | ||
1003 | void (*data_ready)(struct sock *, int) = sk->sk_user_data; | ||
1004 | |||
1005 | data_ready(sk, len); | ||
1006 | |||
1007 | atomic_set(&data_ready_cond, 1); | ||
1008 | wake_up_interruptible(&thread_wait); | ||
1009 | } | ||
1010 | |||
diff --git a/drivers/staging/batman-adv/routing.h b/drivers/staging/batman-adv/routing.h new file mode 100644 index 000000000000..0123ea86debb --- /dev/null +++ b/drivers/staging/batman-adv/routing.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "types.h" | ||
23 | |||
24 | extern wait_queue_head_t thread_wait; | ||
25 | extern atomic_t exit_cond; | ||
26 | |||
27 | int originator_init(void); | ||
28 | void free_orig_node(void *data); | ||
29 | void originator_free(void); | ||
30 | void slide_own_bcast_window(struct batman_if *batman_if); | ||
31 | void batman_data_ready(struct sock *sk, int len); | ||
32 | void purge_orig(struct work_struct *work); | ||
33 | int packet_recv_thread(void *data); | ||
34 | void receive_bat_packet(struct ethhdr *ethhdr, struct batman_packet *batman_packet, unsigned char *hna_buff, int hna_buff_len, struct batman_if *if_incoming); | ||
diff --git a/drivers/staging/batman-adv/send.c b/drivers/staging/batman-adv/send.c new file mode 100644 index 000000000000..d724798278d6 --- /dev/null +++ b/drivers/staging/batman-adv/send.c | |||
@@ -0,0 +1,473 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "send.h" | ||
24 | #include "log.h" | ||
25 | #include "routing.h" | ||
26 | #include "translation-table.h" | ||
27 | #include "hard-interface.h" | ||
28 | #include "types.h" | ||
29 | #include "vis.h" | ||
30 | #include "aggregation.h" | ||
31 | |||
32 | #include "compat.h" | ||
33 | |||
34 | /* apply hop penalty for a normal link */ | ||
35 | static uint8_t hop_penalty(const uint8_t tq) | ||
36 | { | ||
37 | return (tq * (TQ_MAX_VALUE - TQ_HOP_PENALTY)) / (TQ_MAX_VALUE); | ||
38 | } | ||
39 | |||
40 | /* when do we schedule our own packet to be sent */ | ||
41 | static unsigned long own_send_time(void) | ||
42 | { | ||
43 | return jiffies + | ||
44 | (((atomic_read(&originator_interval) - JITTER + | ||
45 | (random32() % 2*JITTER)) * HZ) / 1000); | ||
46 | } | ||
47 | |||
48 | /* when do we schedule a forwarded packet to be sent */ | ||
49 | static unsigned long forward_send_time(void) | ||
50 | { | ||
51 | unsigned long send_time = jiffies; /* Starting now plus... */ | ||
52 | |||
53 | if (atomic_read(&aggregation_enabled)) | ||
54 | send_time += (((MAX_AGGREGATION_MS - (JITTER/2) + | ||
55 | (random32() % JITTER)) * HZ) / 1000); | ||
56 | else | ||
57 | send_time += (((random32() % (JITTER/2)) * HZ) / 1000); | ||
58 | |||
59 | return send_time; | ||
60 | } | ||
61 | |||
62 | /* sends a raw packet. */ | ||
63 | void send_raw_packet(unsigned char *pack_buff, int pack_buff_len, | ||
64 | struct batman_if *batman_if, uint8_t *dst_addr) | ||
65 | { | ||
66 | struct ethhdr *ethhdr; | ||
67 | struct sk_buff *skb; | ||
68 | int retval; | ||
69 | char *data; | ||
70 | |||
71 | if (batman_if->if_active != IF_ACTIVE) | ||
72 | return; | ||
73 | |||
74 | if (!(batman_if->net_dev->flags & IFF_UP)) { | ||
75 | debug_log(LOG_TYPE_WARN, | ||
76 | "Interface %s is not up - can't send packet via that interface (IF_TO_BE_DEACTIVATED was here) !\n", | ||
77 | batman_if->dev); | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | skb = dev_alloc_skb(pack_buff_len + sizeof(struct ethhdr)); | ||
82 | if (!skb) | ||
83 | return; | ||
84 | data = skb_put(skb, pack_buff_len + sizeof(struct ethhdr)); | ||
85 | |||
86 | memcpy(data + sizeof(struct ethhdr), pack_buff, pack_buff_len); | ||
87 | |||
88 | ethhdr = (struct ethhdr *) data; | ||
89 | memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN); | ||
90 | memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); | ||
91 | ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); | ||
92 | |||
93 | skb_reset_mac_header(skb); | ||
94 | skb_set_network_header(skb, ETH_HLEN); | ||
95 | skb->priority = TC_PRIO_CONTROL; | ||
96 | skb->protocol = __constant_htons(ETH_P_BATMAN); | ||
97 | skb->dev = batman_if->net_dev; | ||
98 | |||
99 | /* dev_queue_xmit() returns a negative result on error. However on | ||
100 | * congestion and traffic shaping, it drops and returns NET_XMIT_DROP | ||
101 | * (which is > 0). This will not be treated as an error. */ | ||
102 | retval = dev_queue_xmit(skb); | ||
103 | if (retval < 0) | ||
104 | debug_log(LOG_TYPE_CRIT, | ||
105 | "Can't write to raw socket (IF_TO_BE_DEACTIVATED was here): %i\n", | ||
106 | retval); | ||
107 | } | ||
108 | |||
109 | /* Send a packet to a given interface */ | ||
110 | static void send_packet_to_if(struct forw_packet *forw_packet, | ||
111 | struct batman_if *batman_if) | ||
112 | { | ||
113 | char *fwd_str; | ||
114 | uint8_t packet_num; | ||
115 | int16_t buff_pos; | ||
116 | struct batman_packet *batman_packet; | ||
117 | char orig_str[ETH_STR_LEN]; | ||
118 | |||
119 | if (batman_if->if_active != IF_ACTIVE) | ||
120 | return; | ||
121 | |||
122 | packet_num = buff_pos = 0; | ||
123 | batman_packet = (struct batman_packet *) | ||
124 | (forw_packet->packet_buff); | ||
125 | |||
126 | /* adjust all flags and log packets */ | ||
127 | while (aggregated_packet(buff_pos, | ||
128 | forw_packet->packet_len, | ||
129 | batman_packet->num_hna)) { | ||
130 | |||
131 | /* we might have aggregated direct link packets with an | ||
132 | * ordinary base packet */ | ||
133 | if ((forw_packet->direct_link_flags & (1 << packet_num)) && | ||
134 | (forw_packet->if_incoming == batman_if)) | ||
135 | batman_packet->flags |= DIRECTLINK; | ||
136 | else | ||
137 | batman_packet->flags &= ~DIRECTLINK; | ||
138 | |||
139 | addr_to_string(orig_str, batman_packet->orig); | ||
140 | fwd_str = (packet_num > 0 ? "Forwarding" : (forw_packet->own ? | ||
141 | "Sending own" : | ||
142 | "Forwarding")); | ||
143 | debug_log(LOG_TYPE_BATMAN, | ||
144 | "%s %spacket (originator %s, seqno %d, TQ %d, TTL %d, IDF %s) on interface %s [%s]\n", | ||
145 | fwd_str, | ||
146 | (packet_num > 0 ? "aggregated " : ""), | ||
147 | orig_str, ntohs(batman_packet->seqno), | ||
148 | batman_packet->tq, batman_packet->ttl, | ||
149 | (batman_packet->flags & DIRECTLINK ? | ||
150 | "on" : "off"), | ||
151 | batman_if->dev, batman_if->addr_str); | ||
152 | |||
153 | buff_pos += sizeof(struct batman_packet) + | ||
154 | (batman_packet->num_hna * ETH_ALEN); | ||
155 | packet_num++; | ||
156 | batman_packet = (struct batman_packet *) | ||
157 | (forw_packet->packet_buff + buff_pos); | ||
158 | } | ||
159 | |||
160 | send_raw_packet(forw_packet->packet_buff, | ||
161 | forw_packet->packet_len, | ||
162 | batman_if, broadcastAddr); | ||
163 | } | ||
164 | |||
165 | /* send a batman packet */ | ||
166 | static void send_packet(struct forw_packet *forw_packet) | ||
167 | { | ||
168 | struct batman_if *batman_if; | ||
169 | struct batman_packet *batman_packet = | ||
170 | (struct batman_packet *)(forw_packet->packet_buff); | ||
171 | char orig_str[ETH_STR_LEN]; | ||
172 | unsigned char directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0); | ||
173 | |||
174 | if (!forw_packet->if_incoming) { | ||
175 | debug_log(LOG_TYPE_CRIT, | ||
176 | "Error - can't forward packet: incoming iface not specified\n"); | ||
177 | return; | ||
178 | } | ||
179 | |||
180 | if (forw_packet->if_incoming->if_active != IF_ACTIVE) | ||
181 | return; | ||
182 | |||
183 | addr_to_string(orig_str, batman_packet->orig); | ||
184 | |||
185 | /* multihomed peer assumed */ | ||
186 | /* non-primary OGMs are only broadcasted on their interface */ | ||
187 | if ((directlink && (batman_packet->ttl == 1)) || | ||
188 | (forw_packet->own && (forw_packet->if_incoming->if_num > 0))) { | ||
189 | |||
190 | /* FIXME: what about aggregated packets ? */ | ||
191 | debug_log(LOG_TYPE_BATMAN, | ||
192 | "%s packet (originator %s, seqno %d, TTL %d) on interface %s [%s]\n", | ||
193 | (forw_packet->own ? "Sending own" : "Forwarding"), | ||
194 | orig_str, ntohs(batman_packet->seqno), | ||
195 | batman_packet->ttl, forw_packet->if_incoming->dev, | ||
196 | forw_packet->if_incoming->addr_str); | ||
197 | |||
198 | send_raw_packet(forw_packet->packet_buff, | ||
199 | forw_packet->packet_len, | ||
200 | forw_packet->if_incoming, | ||
201 | broadcastAddr); | ||
202 | return; | ||
203 | } | ||
204 | |||
205 | /* broadcast on every interface */ | ||
206 | rcu_read_lock(); | ||
207 | list_for_each_entry_rcu(batman_if, &if_list, list) | ||
208 | send_packet_to_if(forw_packet, batman_if); | ||
209 | rcu_read_unlock(); | ||
210 | } | ||
211 | |||
212 | static void rebuild_batman_packet(struct batman_if *batman_if) | ||
213 | { | ||
214 | int new_len; | ||
215 | unsigned char *new_buff; | ||
216 | struct batman_packet *batman_packet; | ||
217 | |||
218 | new_len = sizeof(struct batman_packet) + (num_hna * ETH_ALEN); | ||
219 | new_buff = kmalloc(new_len, GFP_ATOMIC); | ||
220 | |||
221 | /* keep old buffer if kmalloc should fail */ | ||
222 | if (new_buff) { | ||
223 | memcpy(new_buff, batman_if->packet_buff, | ||
224 | sizeof(struct batman_packet)); | ||
225 | batman_packet = (struct batman_packet *)new_buff; | ||
226 | |||
227 | batman_packet->num_hna = hna_local_fill_buffer( | ||
228 | new_buff + sizeof(struct batman_packet), | ||
229 | new_len - sizeof(struct batman_packet)); | ||
230 | |||
231 | kfree(batman_if->packet_buff); | ||
232 | batman_if->packet_buff = new_buff; | ||
233 | batman_if->packet_len = new_len; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | void schedule_own_packet(struct batman_if *batman_if) | ||
238 | { | ||
239 | unsigned long send_time; | ||
240 | struct batman_packet *batman_packet; | ||
241 | |||
242 | /** | ||
243 | * the interface gets activated here to avoid race conditions between | ||
244 | * the moment of activating the interface in | ||
245 | * hardif_activate_interface() where the originator mac is set and | ||
246 | * outdated packets (especially uninitialized mac addresses) in the | ||
247 | * packet queue | ||
248 | */ | ||
249 | if (batman_if->if_active == IF_TO_BE_ACTIVATED) | ||
250 | batman_if->if_active = IF_ACTIVE; | ||
251 | |||
252 | /* if local hna has changed and interface is a primary interface */ | ||
253 | if ((atomic_read(&hna_local_changed)) && (batman_if->if_num == 0)) | ||
254 | rebuild_batman_packet(batman_if); | ||
255 | |||
256 | /** | ||
257 | * NOTE: packet_buff might just have been re-allocated in | ||
258 | * rebuild_batman_packet() | ||
259 | */ | ||
260 | batman_packet = (struct batman_packet *)batman_if->packet_buff; | ||
261 | |||
262 | /* change sequence number to network order */ | ||
263 | batman_packet->seqno = htons((uint16_t)atomic_read(&batman_if->seqno)); | ||
264 | |||
265 | if (is_vis_server()) | ||
266 | batman_packet->flags = VIS_SERVER; | ||
267 | else | ||
268 | batman_packet->flags = 0; | ||
269 | |||
270 | /* could be read by receive_bat_packet() */ | ||
271 | atomic_inc(&batman_if->seqno); | ||
272 | |||
273 | slide_own_bcast_window(batman_if); | ||
274 | send_time = own_send_time(); | ||
275 | add_bat_packet_to_list(batman_if->packet_buff, | ||
276 | batman_if->packet_len, batman_if, 1, send_time); | ||
277 | } | ||
278 | |||
279 | void schedule_forward_packet(struct orig_node *orig_node, | ||
280 | struct ethhdr *ethhdr, | ||
281 | struct batman_packet *batman_packet, | ||
282 | uint8_t directlink, int hna_buff_len, | ||
283 | struct batman_if *if_incoming) | ||
284 | { | ||
285 | unsigned char in_tq, in_ttl, tq_avg = 0; | ||
286 | unsigned long send_time; | ||
287 | |||
288 | if (batman_packet->ttl <= 1) { | ||
289 | debug_log(LOG_TYPE_BATMAN, "ttl exceeded \n"); | ||
290 | return; | ||
291 | } | ||
292 | |||
293 | in_tq = batman_packet->tq; | ||
294 | in_ttl = batman_packet->ttl; | ||
295 | |||
296 | batman_packet->ttl--; | ||
297 | memcpy(batman_packet->prev_sender, ethhdr->h_source, ETH_ALEN); | ||
298 | |||
299 | /* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast | ||
300 | * of our best tq value */ | ||
301 | if ((orig_node->router) && (orig_node->router->tq_avg != 0)) { | ||
302 | |||
303 | /* rebroadcast ogm of best ranking neighbor as is */ | ||
304 | if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) { | ||
305 | batman_packet->tq = orig_node->router->tq_avg; | ||
306 | |||
307 | if (orig_node->router->last_ttl) | ||
308 | batman_packet->ttl = orig_node->router->last_ttl - 1; | ||
309 | } | ||
310 | |||
311 | tq_avg = orig_node->router->tq_avg; | ||
312 | } | ||
313 | |||
314 | /* apply hop penalty */ | ||
315 | batman_packet->tq = hop_penalty(batman_packet->tq); | ||
316 | |||
317 | debug_log(LOG_TYPE_BATMAN, "Forwarding packet: tq_orig: %i, tq_avg: %i, tq_forw: %i, ttl_orig: %i, ttl_forw: %i \n", | ||
318 | in_tq, tq_avg, batman_packet->tq, in_ttl - 1, | ||
319 | batman_packet->ttl); | ||
320 | |||
321 | batman_packet->seqno = htons(batman_packet->seqno); | ||
322 | |||
323 | if (directlink) | ||
324 | batman_packet->flags |= DIRECTLINK; | ||
325 | else | ||
326 | batman_packet->flags &= ~DIRECTLINK; | ||
327 | |||
328 | send_time = forward_send_time(); | ||
329 | add_bat_packet_to_list((unsigned char *)batman_packet, | ||
330 | sizeof(struct batman_packet) + hna_buff_len, | ||
331 | if_incoming, 0, send_time); | ||
332 | } | ||
333 | |||
334 | static void forw_packet_free(struct forw_packet *forw_packet) | ||
335 | { | ||
336 | kfree(forw_packet->packet_buff); | ||
337 | kfree(forw_packet); | ||
338 | } | ||
339 | |||
340 | static void _add_bcast_packet_to_list(struct forw_packet *forw_packet, | ||
341 | unsigned long send_time) | ||
342 | { | ||
343 | INIT_HLIST_NODE(&forw_packet->list); | ||
344 | |||
345 | /* add new packet to packet list */ | ||
346 | spin_lock(&forw_bcast_list_lock); | ||
347 | hlist_add_head(&forw_packet->list, &forw_bcast_list); | ||
348 | spin_unlock(&forw_bcast_list_lock); | ||
349 | |||
350 | /* start timer for this packet */ | ||
351 | INIT_DELAYED_WORK(&forw_packet->delayed_work, | ||
352 | send_outstanding_bcast_packet); | ||
353 | queue_delayed_work(bat_event_workqueue, &forw_packet->delayed_work, | ||
354 | send_time); | ||
355 | } | ||
356 | |||
357 | void add_bcast_packet_to_list(unsigned char *packet_buff, int packet_len) | ||
358 | { | ||
359 | struct forw_packet *forw_packet; | ||
360 | |||
361 | forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); | ||
362 | if (!forw_packet) | ||
363 | return; | ||
364 | |||
365 | forw_packet->packet_buff = kmalloc(packet_len, GFP_ATOMIC); | ||
366 | if (!forw_packet->packet_buff) | ||
367 | return; | ||
368 | |||
369 | forw_packet->packet_len = packet_len; | ||
370 | memcpy(forw_packet->packet_buff, packet_buff, forw_packet->packet_len); | ||
371 | |||
372 | /* how often did we send the bcast packet ? */ | ||
373 | forw_packet->num_packets = 0; | ||
374 | |||
375 | _add_bcast_packet_to_list(forw_packet, 1); | ||
376 | } | ||
377 | |||
378 | void send_outstanding_bcast_packet(struct work_struct *work) | ||
379 | { | ||
380 | struct batman_if *batman_if; | ||
381 | struct delayed_work *delayed_work = | ||
382 | container_of(work, struct delayed_work, work); | ||
383 | struct forw_packet *forw_packet = | ||
384 | container_of(delayed_work, struct forw_packet, delayed_work); | ||
385 | |||
386 | spin_lock(&forw_bcast_list_lock); | ||
387 | hlist_del(&forw_packet->list); | ||
388 | spin_unlock(&forw_bcast_list_lock); | ||
389 | |||
390 | /* rebroadcast packet */ | ||
391 | rcu_read_lock(); | ||
392 | list_for_each_entry_rcu(batman_if, &if_list, list) { | ||
393 | send_raw_packet(forw_packet->packet_buff, | ||
394 | forw_packet->packet_len, | ||
395 | batman_if, broadcastAddr); | ||
396 | } | ||
397 | rcu_read_unlock(); | ||
398 | |||
399 | forw_packet->num_packets++; | ||
400 | |||
401 | /* if we still have some more bcasts to send and we are not shutting | ||
402 | * down */ | ||
403 | if ((forw_packet->num_packets < 3) && | ||
404 | (atomic_read(&module_state) != MODULE_DEACTIVATING)) | ||
405 | _add_bcast_packet_to_list(forw_packet, ((5 * HZ) / 1000)); | ||
406 | else | ||
407 | forw_packet_free(forw_packet); | ||
408 | } | ||
409 | |||
410 | void send_outstanding_bat_packet(struct work_struct *work) | ||
411 | { | ||
412 | struct delayed_work *delayed_work = | ||
413 | container_of(work, struct delayed_work, work); | ||
414 | struct forw_packet *forw_packet = | ||
415 | container_of(delayed_work, struct forw_packet, delayed_work); | ||
416 | |||
417 | spin_lock(&forw_bat_list_lock); | ||
418 | hlist_del(&forw_packet->list); | ||
419 | spin_unlock(&forw_bat_list_lock); | ||
420 | |||
421 | send_packet(forw_packet); | ||
422 | |||
423 | /** | ||
424 | * we have to have at least one packet in the queue | ||
425 | * to determine the queues wake up time unless we are | ||
426 | * shutting down | ||
427 | */ | ||
428 | if ((forw_packet->own) && | ||
429 | (atomic_read(&module_state) != MODULE_DEACTIVATING)) | ||
430 | schedule_own_packet(forw_packet->if_incoming); | ||
431 | |||
432 | forw_packet_free(forw_packet); | ||
433 | } | ||
434 | |||
435 | void purge_outstanding_packets(void) | ||
436 | { | ||
437 | struct forw_packet *forw_packet; | ||
438 | struct hlist_node *tmp_node, *safe_tmp_node; | ||
439 | |||
440 | debug_log(LOG_TYPE_BATMAN, "purge_outstanding_packets()\n"); | ||
441 | |||
442 | /* free bcast list */ | ||
443 | spin_lock(&forw_bcast_list_lock); | ||
444 | hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, | ||
445 | &forw_bcast_list, list) { | ||
446 | |||
447 | spin_unlock(&forw_bcast_list_lock); | ||
448 | |||
449 | /** | ||
450 | * send_outstanding_bcast_packet() will lock the list to | ||
451 | * delete the item from the list | ||
452 | */ | ||
453 | cancel_delayed_work_sync(&forw_packet->delayed_work); | ||
454 | spin_lock(&forw_bcast_list_lock); | ||
455 | } | ||
456 | spin_unlock(&forw_bcast_list_lock); | ||
457 | |||
458 | /* free batman packet list */ | ||
459 | spin_lock(&forw_bat_list_lock); | ||
460 | hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, | ||
461 | &forw_bat_list, list) { | ||
462 | |||
463 | spin_unlock(&forw_bat_list_lock); | ||
464 | |||
465 | /** | ||
466 | * send_outstanding_bat_packet() will lock the list to | ||
467 | * delete the item from the list | ||
468 | */ | ||
469 | cancel_delayed_work_sync(&forw_packet->delayed_work); | ||
470 | spin_lock(&forw_bat_list_lock); | ||
471 | } | ||
472 | spin_unlock(&forw_bat_list_lock); | ||
473 | } | ||
diff --git a/drivers/staging/batman-adv/send.h b/drivers/staging/batman-adv/send.h new file mode 100644 index 000000000000..59d500917a35 --- /dev/null +++ b/drivers/staging/batman-adv/send.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "types.h" | ||
23 | |||
24 | void send_own_packet_work(struct work_struct *work); | ||
25 | void send_raw_packet(unsigned char *pack_buff, int pack_buff_len, | ||
26 | struct batman_if *batman_if, uint8_t *dst_addr); | ||
27 | void schedule_own_packet(struct batman_if *batman_if); | ||
28 | void schedule_forward_packet(struct orig_node *orig_node, | ||
29 | struct ethhdr *ethhdr, | ||
30 | struct batman_packet *batman_packet, | ||
31 | uint8_t directlink, int hna_buff_len, | ||
32 | struct batman_if *if_outgoing); | ||
33 | void add_bcast_packet_to_list(unsigned char *packet_buff, int packet_len); | ||
34 | void send_outstanding_bcast_packet(struct work_struct *work); | ||
35 | void send_outstanding_bat_packet(struct work_struct *work); | ||
36 | void purge_outstanding_packets(void); | ||
diff --git a/drivers/staging/batman-adv/soft-interface.c b/drivers/staging/batman-adv/soft-interface.c new file mode 100644 index 000000000000..d543f50b647f --- /dev/null +++ b/drivers/staging/batman-adv/soft-interface.c | |||
@@ -0,0 +1,349 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "soft-interface.h" | ||
24 | #include "hard-interface.h" | ||
25 | #include "send.h" | ||
26 | #include "translation-table.h" | ||
27 | #include "log.h" | ||
28 | #include "types.h" | ||
29 | #include "hash.h" | ||
30 | #include <linux/ethtool.h> | ||
31 | #include <linux/etherdevice.h> | ||
32 | #include "compat.h" | ||
33 | |||
34 | static uint16_t bcast_seqno = 1; /* give own bcast messages seq numbers to avoid | ||
35 | * broadcast storms */ | ||
36 | static int32_t skb_packets; | ||
37 | static int32_t skb_bad_packets; | ||
38 | static int32_t lock_dropped; | ||
39 | |||
40 | unsigned char mainIfAddr[ETH_ALEN]; | ||
41 | static unsigned char mainIfAddr_default[ETH_ALEN]; | ||
42 | static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); | ||
43 | static void bat_get_drvinfo(struct net_device *dev, | ||
44 | struct ethtool_drvinfo *info); | ||
45 | static u32 bat_get_msglevel(struct net_device *dev); | ||
46 | static void bat_set_msglevel(struct net_device *dev, u32 value); | ||
47 | static u32 bat_get_link(struct net_device *dev); | ||
48 | static u32 bat_get_rx_csum(struct net_device *dev); | ||
49 | static int bat_set_rx_csum(struct net_device *dev, u32 data); | ||
50 | |||
51 | static const struct ethtool_ops bat_ethtool_ops = { | ||
52 | .get_settings = bat_get_settings, | ||
53 | .get_drvinfo = bat_get_drvinfo, | ||
54 | .get_msglevel = bat_get_msglevel, | ||
55 | .set_msglevel = bat_set_msglevel, | ||
56 | .get_link = bat_get_link, | ||
57 | .get_rx_csum = bat_get_rx_csum, | ||
58 | .set_rx_csum = bat_set_rx_csum | ||
59 | }; | ||
60 | |||
61 | void set_main_if_addr(uint8_t *addr) | ||
62 | { | ||
63 | memcpy(mainIfAddr, addr, ETH_ALEN); | ||
64 | } | ||
65 | |||
66 | int main_if_was_up(void) | ||
67 | { | ||
68 | return (memcmp(mainIfAddr, mainIfAddr_default, ETH_ALEN) != 0 ? 1 : 0); | ||
69 | } | ||
70 | |||
71 | static int my_skb_push(struct sk_buff *skb, unsigned int len) | ||
72 | { | ||
73 | int result = 0; | ||
74 | |||
75 | skb_packets++; | ||
76 | if (skb->data - len < skb->head) { | ||
77 | skb_bad_packets++; | ||
78 | result = pskb_expand_head(skb, len, 0, GFP_ATOMIC); | ||
79 | |||
80 | if (result < 0) | ||
81 | return result; | ||
82 | } | ||
83 | |||
84 | skb_push(skb, len); | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | #ifdef HAVE_NET_DEVICE_OPS | ||
89 | static const struct net_device_ops bat_netdev_ops = { | ||
90 | .ndo_open = interface_open, | ||
91 | .ndo_stop = interface_release, | ||
92 | .ndo_get_stats = interface_stats, | ||
93 | .ndo_set_mac_address = interface_set_mac_addr, | ||
94 | .ndo_change_mtu = interface_change_mtu, | ||
95 | .ndo_start_xmit = interface_tx, | ||
96 | .ndo_validate_addr = eth_validate_addr | ||
97 | }; | ||
98 | #endif | ||
99 | |||
100 | void interface_setup(struct net_device *dev) | ||
101 | { | ||
102 | struct bat_priv *priv = netdev_priv(dev); | ||
103 | char dev_addr[ETH_ALEN]; | ||
104 | |||
105 | ether_setup(dev); | ||
106 | |||
107 | #ifdef HAVE_NET_DEVICE_OPS | ||
108 | dev->netdev_ops = &bat_netdev_ops; | ||
109 | #else | ||
110 | dev->open = interface_open; | ||
111 | dev->stop = interface_release; | ||
112 | dev->get_stats = interface_stats; | ||
113 | dev->set_mac_address = interface_set_mac_addr; | ||
114 | dev->change_mtu = interface_change_mtu; | ||
115 | dev->hard_start_xmit = interface_tx; | ||
116 | #endif | ||
117 | dev->destructor = free_netdev; | ||
118 | |||
119 | dev->mtu = hardif_min_mtu(); | ||
120 | dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the | ||
121 | * skbuff for our header */ | ||
122 | |||
123 | /* generate random address */ | ||
124 | random_ether_addr(dev_addr); | ||
125 | memcpy(dev->dev_addr, dev_addr, sizeof(dev->dev_addr)); | ||
126 | |||
127 | SET_ETHTOOL_OPS(dev, &bat_ethtool_ops); | ||
128 | |||
129 | memset(priv, 0, sizeof(struct bat_priv)); | ||
130 | } | ||
131 | |||
132 | int interface_open(struct net_device *dev) | ||
133 | { | ||
134 | netif_start_queue(dev); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | int interface_release(struct net_device *dev) | ||
139 | { | ||
140 | netif_stop_queue(dev); | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | struct net_device_stats *interface_stats(struct net_device *dev) | ||
145 | { | ||
146 | struct bat_priv *priv = netdev_priv(dev); | ||
147 | return &priv->stats; | ||
148 | } | ||
149 | |||
150 | int interface_set_mac_addr(struct net_device *dev, void *addr) | ||
151 | { | ||
152 | return -EBUSY; | ||
153 | } | ||
154 | |||
155 | int interface_change_mtu(struct net_device *dev, int new_mtu) | ||
156 | { | ||
157 | /* check ranges */ | ||
158 | if ((new_mtu < 68) || (new_mtu > hardif_min_mtu())) | ||
159 | return -EINVAL; | ||
160 | |||
161 | dev->mtu = new_mtu; | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | int interface_tx(struct sk_buff *skb, struct net_device *dev) | ||
167 | { | ||
168 | struct unicast_packet *unicast_packet; | ||
169 | struct bcast_packet *bcast_packet; | ||
170 | struct orig_node *orig_node; | ||
171 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; | ||
172 | struct bat_priv *priv = netdev_priv(dev); | ||
173 | int data_len = skb->len; | ||
174 | |||
175 | if (atomic_read(&module_state) != MODULE_ACTIVE) | ||
176 | goto dropped; | ||
177 | |||
178 | dev->trans_start = jiffies; | ||
179 | /* TODO: check this for locks */ | ||
180 | hna_local_add(ethhdr->h_source); | ||
181 | |||
182 | /* ethernet packet should be broadcasted */ | ||
183 | if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) { | ||
184 | |||
185 | if (my_skb_push(skb, sizeof(struct bcast_packet)) < 0) | ||
186 | goto dropped; | ||
187 | |||
188 | bcast_packet = (struct bcast_packet *)skb->data; | ||
189 | |||
190 | bcast_packet->version = COMPAT_VERSION; | ||
191 | |||
192 | /* batman packet type: broadcast */ | ||
193 | bcast_packet->packet_type = BAT_BCAST; | ||
194 | |||
195 | /* hw address of first interface is the orig mac because only | ||
196 | * this mac is known throughout the mesh */ | ||
197 | memcpy(bcast_packet->orig, mainIfAddr, ETH_ALEN); | ||
198 | /* set broadcast sequence number */ | ||
199 | bcast_packet->seqno = htons(bcast_seqno); | ||
200 | |||
201 | bcast_seqno++; | ||
202 | |||
203 | /* broadcast packet */ | ||
204 | add_bcast_packet_to_list(skb->data, skb->len); | ||
205 | |||
206 | /* unicast packet */ | ||
207 | } else { | ||
208 | |||
209 | /* simply spin_lock()ing can deadlock when the lock is already | ||
210 | * hold. */ | ||
211 | /* TODO: defer the work in a working queue instead of | ||
212 | * dropping */ | ||
213 | if (!spin_trylock(&orig_hash_lock)) { | ||
214 | lock_dropped++; | ||
215 | debug_log(LOG_TYPE_NOTICE, "%d packets dropped because lock was hold\n", lock_dropped); | ||
216 | goto dropped; | ||
217 | } | ||
218 | |||
219 | /* get routing information */ | ||
220 | orig_node = ((struct orig_node *)hash_find(orig_hash, | ||
221 | ethhdr->h_dest)); | ||
222 | |||
223 | /* check for hna host */ | ||
224 | if (!orig_node) | ||
225 | orig_node = transtable_search(ethhdr->h_dest); | ||
226 | |||
227 | if ((orig_node) && | ||
228 | (orig_node->batman_if) && | ||
229 | (orig_node->router)) { | ||
230 | if (my_skb_push(skb, sizeof(struct unicast_packet)) < 0) | ||
231 | goto unlock; | ||
232 | |||
233 | unicast_packet = (struct unicast_packet *)skb->data; | ||
234 | |||
235 | unicast_packet->version = COMPAT_VERSION; | ||
236 | /* batman packet type: unicast */ | ||
237 | unicast_packet->packet_type = BAT_UNICAST; | ||
238 | /* set unicast ttl */ | ||
239 | unicast_packet->ttl = TTL; | ||
240 | /* copy the destination for faster routing */ | ||
241 | memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); | ||
242 | |||
243 | /* net_dev won't be available when not active */ | ||
244 | if (orig_node->batman_if->if_active != IF_ACTIVE) | ||
245 | goto unlock; | ||
246 | |||
247 | send_raw_packet(skb->data, skb->len, | ||
248 | orig_node->batman_if, | ||
249 | orig_node->router->addr); | ||
250 | } else { | ||
251 | goto unlock; | ||
252 | } | ||
253 | |||
254 | spin_unlock(&orig_hash_lock); | ||
255 | } | ||
256 | |||
257 | priv->stats.tx_packets++; | ||
258 | priv->stats.tx_bytes += data_len; | ||
259 | goto end; | ||
260 | |||
261 | unlock: | ||
262 | spin_unlock(&orig_hash_lock); | ||
263 | dropped: | ||
264 | priv->stats.tx_dropped++; | ||
265 | end: | ||
266 | kfree_skb(skb); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | void interface_rx(struct net_device *dev, void *packet, int packet_len) | ||
271 | { | ||
272 | struct sk_buff *skb; | ||
273 | struct bat_priv *priv = netdev_priv(dev); | ||
274 | |||
275 | skb = dev_alloc_skb(packet_len); | ||
276 | |||
277 | if (!skb) { | ||
278 | priv->stats.rx_dropped++; | ||
279 | goto out; | ||
280 | } | ||
281 | |||
282 | memcpy(skb_put(skb, packet_len), packet, packet_len); | ||
283 | |||
284 | /* Write metadata, and then pass to the receive level */ | ||
285 | skb->dev = dev; | ||
286 | skb->protocol = eth_type_trans(skb, dev); | ||
287 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
288 | |||
289 | priv->stats.rx_packets++; | ||
290 | priv->stats.rx_bytes += packet_len; | ||
291 | |||
292 | dev->last_rx = jiffies; | ||
293 | |||
294 | netif_rx(skb); | ||
295 | |||
296 | out: | ||
297 | return; | ||
298 | } | ||
299 | |||
300 | /* ethtool */ | ||
301 | static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||
302 | { | ||
303 | cmd->supported = 0; | ||
304 | cmd->advertising = 0; | ||
305 | cmd->speed = SPEED_10; | ||
306 | cmd->duplex = DUPLEX_FULL; | ||
307 | cmd->port = PORT_TP; | ||
308 | cmd->phy_address = 0; | ||
309 | cmd->transceiver = XCVR_INTERNAL; | ||
310 | cmd->autoneg = AUTONEG_DISABLE; | ||
311 | cmd->maxtxpkt = 0; | ||
312 | cmd->maxrxpkt = 0; | ||
313 | |||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static void bat_get_drvinfo(struct net_device *dev, | ||
318 | struct ethtool_drvinfo *info) | ||
319 | { | ||
320 | strcpy(info->driver, "B.A.T.M.A.N. advanced"); | ||
321 | strcpy(info->version, SOURCE_VERSION); | ||
322 | strcpy(info->fw_version, "N/A"); | ||
323 | strcpy(info->bus_info, "batman"); | ||
324 | } | ||
325 | |||
326 | static u32 bat_get_msglevel(struct net_device *dev) | ||
327 | { | ||
328 | return -EOPNOTSUPP; | ||
329 | } | ||
330 | |||
331 | static void bat_set_msglevel(struct net_device *dev, u32 value) | ||
332 | { | ||
333 | return; | ||
334 | } | ||
335 | |||
336 | static u32 bat_get_link(struct net_device *dev) | ||
337 | { | ||
338 | return 1; | ||
339 | } | ||
340 | |||
341 | static u32 bat_get_rx_csum(struct net_device *dev) | ||
342 | { | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int bat_set_rx_csum(struct net_device *dev, u32 data) | ||
347 | { | ||
348 | return -EOPNOTSUPP; | ||
349 | } | ||
diff --git a/drivers/staging/batman-adv/soft-interface.h b/drivers/staging/batman-adv/soft-interface.h new file mode 100644 index 000000000000..515e276ef53d --- /dev/null +++ b/drivers/staging/batman-adv/soft-interface.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | void set_main_if_addr(uint8_t *addr); | ||
23 | int main_if_was_up(void); | ||
24 | void interface_setup(struct net_device *dev); | ||
25 | int interface_open(struct net_device *dev); | ||
26 | int interface_release(struct net_device *dev); | ||
27 | struct net_device_stats *interface_stats(struct net_device *dev); | ||
28 | int interface_set_mac_addr(struct net_device *dev, void *addr); | ||
29 | int interface_change_mtu(struct net_device *dev, int new_mtu); | ||
30 | int interface_tx(struct sk_buff *skb, struct net_device *dev); | ||
31 | void interface_rx(struct net_device *dev, void *packet, int packet_len); | ||
32 | |||
33 | extern unsigned char mainIfAddr[]; | ||
diff --git a/drivers/staging/batman-adv/translation-table.c b/drivers/staging/batman-adv/translation-table.c new file mode 100644 index 000000000000..c2190e177c56 --- /dev/null +++ b/drivers/staging/batman-adv/translation-table.c | |||
@@ -0,0 +1,454 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "translation-table.h" | ||
24 | #include "log.h" | ||
25 | #include "soft-interface.h" | ||
26 | #include "types.h" | ||
27 | #include "hash.h" | ||
28 | #include "compat.h" | ||
29 | |||
30 | struct hashtable_t *hna_local_hash; | ||
31 | static struct hashtable_t *hna_global_hash; | ||
32 | atomic_t hna_local_changed; | ||
33 | |||
34 | DEFINE_SPINLOCK(hna_local_hash_lock); | ||
35 | static DEFINE_SPINLOCK(hna_global_hash_lock); | ||
36 | |||
37 | static DECLARE_DELAYED_WORK(hna_local_purge_wq, hna_local_purge); | ||
38 | |||
39 | static void hna_local_start_timer(void) | ||
40 | { | ||
41 | queue_delayed_work(bat_event_workqueue, &hna_local_purge_wq, 10 * HZ); | ||
42 | } | ||
43 | |||
44 | int hna_local_init(void) | ||
45 | { | ||
46 | if (hna_local_hash) | ||
47 | return 1; | ||
48 | |||
49 | hna_local_hash = hash_new(128, compare_orig, choose_orig); | ||
50 | |||
51 | if (!hna_local_hash) | ||
52 | return 0; | ||
53 | |||
54 | atomic_set(&hna_local_changed, 0); | ||
55 | hna_local_start_timer(); | ||
56 | |||
57 | return 1; | ||
58 | } | ||
59 | |||
60 | void hna_local_add(uint8_t *addr) | ||
61 | { | ||
62 | struct hna_local_entry *hna_local_entry; | ||
63 | struct hna_global_entry *hna_global_entry; | ||
64 | struct hashtable_t *swaphash; | ||
65 | char hna_str[ETH_STR_LEN]; | ||
66 | unsigned long flags; | ||
67 | |||
68 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
69 | hna_local_entry = | ||
70 | ((struct hna_local_entry *)hash_find(hna_local_hash, addr)); | ||
71 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
72 | |||
73 | if (hna_local_entry != NULL) { | ||
74 | hna_local_entry->last_seen = jiffies; | ||
75 | return; | ||
76 | } | ||
77 | |||
78 | addr_to_string(hna_str, addr); | ||
79 | |||
80 | /* only announce as many hosts as possible in the batman-packet and | ||
81 | space in batman_packet->num_hna That also should give a limit to | ||
82 | MAC-flooding. */ | ||
83 | if ((num_hna + 1 > (ETH_DATA_LEN - BAT_PACKET_LEN) / ETH_ALEN) || | ||
84 | (num_hna + 1 > 255)) { | ||
85 | debug_log(LOG_TYPE_ROUTES, "Can't add new local hna entry (%s): number of local hna entries exceeds packet size \n", hna_str); | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | debug_log(LOG_TYPE_ROUTES, "Creating new local hna entry: %s \n", | ||
90 | hna_str); | ||
91 | |||
92 | hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC); | ||
93 | if (!hna_local_entry) | ||
94 | return; | ||
95 | |||
96 | memcpy(hna_local_entry->addr, addr, ETH_ALEN); | ||
97 | hna_local_entry->last_seen = jiffies; | ||
98 | |||
99 | /* the batman interface mac address should never be purged */ | ||
100 | if (compare_orig(addr, soft_device->dev_addr)) | ||
101 | hna_local_entry->never_purge = 1; | ||
102 | else | ||
103 | hna_local_entry->never_purge = 0; | ||
104 | |||
105 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
106 | |||
107 | hash_add(hna_local_hash, hna_local_entry); | ||
108 | num_hna++; | ||
109 | atomic_set(&hna_local_changed, 1); | ||
110 | |||
111 | if (hna_local_hash->elements * 4 > hna_local_hash->size) { | ||
112 | swaphash = hash_resize(hna_local_hash, | ||
113 | hna_local_hash->size * 2); | ||
114 | |||
115 | if (swaphash == NULL) | ||
116 | debug_log(LOG_TYPE_CRIT, "Couldn't resize local hna hash table \n"); | ||
117 | else | ||
118 | hna_local_hash = swaphash; | ||
119 | } | ||
120 | |||
121 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
122 | |||
123 | /* remove address from global hash if present */ | ||
124 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
125 | |||
126 | hna_global_entry = | ||
127 | ((struct hna_global_entry *)hash_find(hna_global_hash, addr)); | ||
128 | |||
129 | if (hna_global_entry != NULL) | ||
130 | _hna_global_del_orig(hna_global_entry, "local hna received"); | ||
131 | |||
132 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
133 | } | ||
134 | |||
135 | int hna_local_fill_buffer(unsigned char *buff, int buff_len) | ||
136 | { | ||
137 | struct hna_local_entry *hna_local_entry; | ||
138 | struct hash_it_t *hashit = NULL; | ||
139 | int i = 0; | ||
140 | unsigned long flags; | ||
141 | |||
142 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
143 | |||
144 | while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) { | ||
145 | |||
146 | if (buff_len < (i + 1) * ETH_ALEN) | ||
147 | break; | ||
148 | |||
149 | hna_local_entry = hashit->bucket->data; | ||
150 | memcpy(buff + (i * ETH_ALEN), hna_local_entry->addr, ETH_ALEN); | ||
151 | |||
152 | i++; | ||
153 | } | ||
154 | |||
155 | /* if we did not get all new local hnas see you next time ;-) */ | ||
156 | if (i == num_hna) | ||
157 | atomic_set(&hna_local_changed, 0); | ||
158 | |||
159 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
160 | |||
161 | return i; | ||
162 | } | ||
163 | |||
164 | int hna_local_fill_buffer_text(unsigned char *buff, int buff_len) | ||
165 | { | ||
166 | struct hna_local_entry *hna_local_entry; | ||
167 | struct hash_it_t *hashit = NULL; | ||
168 | int bytes_written = 0; | ||
169 | unsigned long flags; | ||
170 | |||
171 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
172 | |||
173 | while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) { | ||
174 | |||
175 | if (buff_len < bytes_written + ETH_STR_LEN + 4) | ||
176 | break; | ||
177 | |||
178 | hna_local_entry = hashit->bucket->data; | ||
179 | |||
180 | bytes_written += snprintf(buff + bytes_written, ETH_STR_LEN + 4, | ||
181 | " * %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
182 | hna_local_entry->addr[0], | ||
183 | hna_local_entry->addr[1], | ||
184 | hna_local_entry->addr[2], | ||
185 | hna_local_entry->addr[3], | ||
186 | hna_local_entry->addr[4], | ||
187 | hna_local_entry->addr[5]); | ||
188 | } | ||
189 | |||
190 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
191 | |||
192 | return bytes_written; | ||
193 | } | ||
194 | |||
195 | static void _hna_local_del(void *data) | ||
196 | { | ||
197 | kfree(data); | ||
198 | num_hna--; | ||
199 | atomic_set(&hna_local_changed, 1); | ||
200 | } | ||
201 | |||
202 | static void hna_local_del(struct hna_local_entry *hna_local_entry, | ||
203 | char *message) | ||
204 | { | ||
205 | char hna_str[ETH_STR_LEN]; | ||
206 | |||
207 | addr_to_string(hna_str, hna_local_entry->addr); | ||
208 | debug_log(LOG_TYPE_ROUTES, "Deleting local hna entry (%s): %s \n", | ||
209 | hna_str, message); | ||
210 | |||
211 | hash_remove(hna_local_hash, hna_local_entry->addr); | ||
212 | _hna_local_del(hna_local_entry); | ||
213 | } | ||
214 | |||
215 | void hna_local_purge(struct work_struct *work) | ||
216 | { | ||
217 | struct hna_local_entry *hna_local_entry; | ||
218 | struct hash_it_t *hashit = NULL; | ||
219 | unsigned long flags; | ||
220 | unsigned long timeout; | ||
221 | |||
222 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
223 | |||
224 | while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) { | ||
225 | hna_local_entry = hashit->bucket->data; | ||
226 | |||
227 | timeout = hna_local_entry->last_seen + | ||
228 | ((LOCAL_HNA_TIMEOUT / 1000) * HZ); | ||
229 | if ((!hna_local_entry->never_purge) && | ||
230 | time_after(jiffies, timeout)) | ||
231 | hna_local_del(hna_local_entry, "address timed out"); | ||
232 | } | ||
233 | |||
234 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
235 | hna_local_start_timer(); | ||
236 | } | ||
237 | |||
238 | void hna_local_free(void) | ||
239 | { | ||
240 | if (!hna_local_hash) | ||
241 | return; | ||
242 | |||
243 | cancel_delayed_work_sync(&hna_local_purge_wq); | ||
244 | hash_delete(hna_local_hash, _hna_local_del); | ||
245 | hna_local_hash = NULL; | ||
246 | } | ||
247 | |||
248 | int hna_global_init(void) | ||
249 | { | ||
250 | if (hna_global_hash) | ||
251 | return 1; | ||
252 | |||
253 | hna_global_hash = hash_new(128, compare_orig, choose_orig); | ||
254 | |||
255 | if (!hna_global_hash) | ||
256 | return 0; | ||
257 | |||
258 | return 1; | ||
259 | } | ||
260 | |||
261 | void hna_global_add_orig(struct orig_node *orig_node, | ||
262 | unsigned char *hna_buff, int hna_buff_len) | ||
263 | { | ||
264 | struct hna_global_entry *hna_global_entry; | ||
265 | struct hna_local_entry *hna_local_entry; | ||
266 | struct hashtable_t *swaphash; | ||
267 | char hna_str[ETH_STR_LEN], orig_str[ETH_STR_LEN]; | ||
268 | int hna_buff_count = 0; | ||
269 | unsigned long flags; | ||
270 | unsigned char *hna_ptr; | ||
271 | |||
272 | addr_to_string(orig_str, orig_node->orig); | ||
273 | |||
274 | while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) { | ||
275 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
276 | |||
277 | hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); | ||
278 | hna_global_entry = (struct hna_global_entry *) | ||
279 | hash_find(hna_global_hash, hna_ptr); | ||
280 | |||
281 | if (hna_global_entry == NULL) { | ||
282 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
283 | |||
284 | hna_global_entry = | ||
285 | kmalloc(sizeof(struct hna_global_entry), | ||
286 | GFP_ATOMIC); | ||
287 | |||
288 | if (!hna_global_entry) | ||
289 | break; | ||
290 | |||
291 | memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN); | ||
292 | |||
293 | addr_to_string(hna_str, hna_global_entry->addr); | ||
294 | debug_log(LOG_TYPE_ROUTES, "Creating new global hna entry: %s (via %s)\n", hna_str, orig_str); | ||
295 | |||
296 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
297 | hash_add(hna_global_hash, hna_global_entry); | ||
298 | |||
299 | } | ||
300 | |||
301 | hna_global_entry->orig_node = orig_node; | ||
302 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
303 | |||
304 | /* remove address from local hash if present */ | ||
305 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
306 | |||
307 | hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); | ||
308 | hna_local_entry = (struct hna_local_entry *) | ||
309 | hash_find(hna_local_hash, hna_ptr); | ||
310 | |||
311 | if (hna_local_entry != NULL) | ||
312 | hna_local_del(hna_local_entry, "global hna received"); | ||
313 | |||
314 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
315 | |||
316 | hna_buff_count++; | ||
317 | } | ||
318 | |||
319 | orig_node->hna_buff_len = hna_buff_len; | ||
320 | |||
321 | if (orig_node->hna_buff_len > 0) { | ||
322 | orig_node->hna_buff = kmalloc(orig_node->hna_buff_len, | ||
323 | GFP_ATOMIC); | ||
324 | memcpy(orig_node->hna_buff, hna_buff, orig_node->hna_buff_len); | ||
325 | } else { | ||
326 | orig_node->hna_buff = NULL; | ||
327 | } | ||
328 | |||
329 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
330 | |||
331 | if (hna_global_hash->elements * 4 > hna_global_hash->size) { | ||
332 | swaphash = hash_resize(hna_global_hash, | ||
333 | hna_global_hash->size * 2); | ||
334 | |||
335 | if (swaphash == NULL) | ||
336 | debug_log(LOG_TYPE_CRIT, "Couldn't resize global hna hash table \n"); | ||
337 | else | ||
338 | hna_global_hash = swaphash; | ||
339 | } | ||
340 | |||
341 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
342 | } | ||
343 | |||
344 | int hna_global_fill_buffer_text(unsigned char *buff, int buff_len) | ||
345 | { | ||
346 | struct hna_global_entry *hna_global_entry; | ||
347 | struct hash_it_t *hashit = NULL; | ||
348 | int bytes_written = 0; | ||
349 | unsigned long flags; | ||
350 | |||
351 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
352 | |||
353 | while (NULL != (hashit = hash_iterate(hna_global_hash, hashit))) { | ||
354 | if (buff_len < bytes_written + (2 * ETH_STR_LEN) + 10) | ||
355 | break; | ||
356 | |||
357 | hna_global_entry = hashit->bucket->data; | ||
358 | |||
359 | bytes_written += snprintf(buff + bytes_written, | ||
360 | (2 * ETH_STR_LEN) + 10, | ||
361 | " * %02x:%02x:%02x:%02x:%02x:%02x via %02x:%02x:%02x:%02x:%02x:%02x \n", | ||
362 | hna_global_entry->addr[0], | ||
363 | hna_global_entry->addr[1], | ||
364 | hna_global_entry->addr[2], | ||
365 | hna_global_entry->addr[3], | ||
366 | hna_global_entry->addr[4], | ||
367 | hna_global_entry->addr[5], | ||
368 | hna_global_entry->orig_node->orig[0], | ||
369 | hna_global_entry->orig_node->orig[1], | ||
370 | hna_global_entry->orig_node->orig[2], | ||
371 | hna_global_entry->orig_node->orig[3], | ||
372 | hna_global_entry->orig_node->orig[4], | ||
373 | hna_global_entry->orig_node->orig[5]); | ||
374 | } | ||
375 | |||
376 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
377 | |||
378 | return bytes_written; | ||
379 | } | ||
380 | |||
381 | void _hna_global_del_orig(struct hna_global_entry *hna_global_entry, | ||
382 | char *message) | ||
383 | { | ||
384 | char hna_str[ETH_STR_LEN], orig_str[ETH_STR_LEN]; | ||
385 | |||
386 | addr_to_string(orig_str, hna_global_entry->orig_node->orig); | ||
387 | addr_to_string(hna_str, hna_global_entry->addr); | ||
388 | |||
389 | debug_log(LOG_TYPE_ROUTES, "Deleting global hna entry %s (via %s): %s \n", hna_str, orig_str, message); | ||
390 | |||
391 | hash_remove(hna_global_hash, hna_global_entry->addr); | ||
392 | kfree(hna_global_entry); | ||
393 | } | ||
394 | |||
395 | void hna_global_del_orig(struct orig_node *orig_node, char *message) | ||
396 | { | ||
397 | struct hna_global_entry *hna_global_entry; | ||
398 | int hna_buff_count = 0; | ||
399 | unsigned long flags; | ||
400 | unsigned char *hna_ptr; | ||
401 | |||
402 | if (orig_node->hna_buff_len == 0) | ||
403 | return; | ||
404 | |||
405 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
406 | |||
407 | while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { | ||
408 | hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); | ||
409 | hna_global_entry = (struct hna_global_entry *) | ||
410 | hash_find(hna_global_hash, hna_ptr); | ||
411 | |||
412 | if ((hna_global_entry != NULL) && | ||
413 | (hna_global_entry->orig_node == orig_node)) | ||
414 | _hna_global_del_orig(hna_global_entry, message); | ||
415 | |||
416 | hna_buff_count++; | ||
417 | } | ||
418 | |||
419 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
420 | |||
421 | orig_node->hna_buff_len = 0; | ||
422 | kfree(orig_node->hna_buff); | ||
423 | orig_node->hna_buff = NULL; | ||
424 | } | ||
425 | |||
426 | static void hna_global_del(void *data) | ||
427 | { | ||
428 | kfree(data); | ||
429 | } | ||
430 | |||
431 | void hna_global_free(void) | ||
432 | { | ||
433 | if (!hna_global_hash) | ||
434 | return; | ||
435 | |||
436 | hash_delete(hna_global_hash, hna_global_del); | ||
437 | hna_global_hash = NULL; | ||
438 | } | ||
439 | |||
440 | struct orig_node *transtable_search(uint8_t *addr) | ||
441 | { | ||
442 | struct hna_global_entry *hna_global_entry; | ||
443 | unsigned long flags; | ||
444 | |||
445 | spin_lock_irqsave(&hna_global_hash_lock, flags); | ||
446 | hna_global_entry = (struct hna_global_entry *) | ||
447 | hash_find(hna_global_hash, addr); | ||
448 | spin_unlock_irqrestore(&hna_global_hash_lock, flags); | ||
449 | |||
450 | if (hna_global_entry == NULL) | ||
451 | return NULL; | ||
452 | |||
453 | return hna_global_entry->orig_node; | ||
454 | } | ||
diff --git a/drivers/staging/batman-adv/translation-table.h b/drivers/staging/batman-adv/translation-table.h new file mode 100644 index 000000000000..f7da81129318 --- /dev/null +++ b/drivers/staging/batman-adv/translation-table.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "types.h" | ||
23 | |||
24 | int hna_local_init(void); | ||
25 | void hna_local_add(uint8_t *addr); | ||
26 | int hna_local_fill_buffer(unsigned char *buff, int buff_len); | ||
27 | int hna_local_fill_buffer_text(unsigned char *buff, int buff_len); | ||
28 | void hna_local_purge(struct work_struct *work); | ||
29 | void hna_local_free(void); | ||
30 | int hna_global_init(void); | ||
31 | void hna_global_add_orig(struct orig_node *orig_node, unsigned char *hna_buff, | ||
32 | int hna_buff_len); | ||
33 | int hna_global_fill_buffer_text(unsigned char *buff, int buff_len); | ||
34 | void _hna_global_del_orig(struct hna_global_entry *hna_global_entry, | ||
35 | char *orig_str); | ||
36 | void hna_global_del_orig(struct orig_node *orig_node, char *message); | ||
37 | void hna_global_free(void); | ||
38 | struct orig_node *transtable_search(uint8_t *addr); | ||
39 | |||
40 | extern spinlock_t hna_local_hash_lock; | ||
41 | extern struct hashtable_t *hna_local_hash; | ||
42 | extern atomic_t hna_local_changed; | ||
diff --git a/drivers/staging/batman-adv/types.h b/drivers/staging/batman-adv/types.h new file mode 100644 index 000000000000..3a0ef0c38c93 --- /dev/null +++ b/drivers/staging/batman-adv/types.h | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | #ifndef TYPES_H | ||
27 | #define TYPES_H | ||
28 | |||
29 | #include "packet.h" | ||
30 | #include "bitarray.h" | ||
31 | |||
32 | #define BAT_HEADER_LEN (sizeof(struct ethhdr) + ((sizeof(struct unicast_packet) > sizeof(struct bcast_packet) ? sizeof(struct unicast_packet) : sizeof(struct bcast_packet)))) | ||
33 | |||
34 | |||
35 | struct batman_if { | ||
36 | struct list_head list; | ||
37 | int16_t if_num; | ||
38 | char *dev; | ||
39 | char if_active; | ||
40 | char addr_str[ETH_STR_LEN]; | ||
41 | struct net_device *net_dev; | ||
42 | struct socket *raw_sock; | ||
43 | atomic_t seqno; | ||
44 | unsigned char *packet_buff; | ||
45 | int packet_len; | ||
46 | struct rcu_head rcu; | ||
47 | |||
48 | }; | ||
49 | |||
50 | struct orig_node { /* structure for orig_list maintaining nodes of mesh */ | ||
51 | uint8_t orig[ETH_ALEN]; | ||
52 | struct neigh_node *router; | ||
53 | struct batman_if *batman_if; | ||
54 | TYPE_OF_WORD *bcast_own; | ||
55 | uint8_t *bcast_own_sum; | ||
56 | uint8_t tq_own; | ||
57 | int tq_asym_penalty; | ||
58 | unsigned long last_valid; /* when last packet from this node was received */ | ||
59 | /* uint8_t gwflags; * flags related to gateway functions: gateway class */ | ||
60 | uint8_t flags; /* for now only VIS_SERVER flag. */ | ||
61 | unsigned char *hna_buff; | ||
62 | int16_t hna_buff_len; | ||
63 | uint16_t last_real_seqno; /* last and best known squence number */ | ||
64 | uint8_t last_ttl; /* ttl of last received packet */ | ||
65 | TYPE_OF_WORD bcast_bits[NUM_WORDS]; | ||
66 | uint16_t last_bcast_seqno; /* last broadcast sequence number received by this host */ | ||
67 | struct list_head neigh_list; | ||
68 | }; | ||
69 | |||
70 | struct neigh_node { | ||
71 | struct list_head list; | ||
72 | uint8_t addr[ETH_ALEN]; | ||
73 | uint8_t real_packet_count; | ||
74 | uint8_t tq_recv[TQ_GLOBAL_WINDOW_SIZE]; | ||
75 | uint8_t tq_index; | ||
76 | uint8_t tq_avg; | ||
77 | uint8_t last_ttl; | ||
78 | unsigned long last_valid; /* when last packet via this neighbour was received */ | ||
79 | TYPE_OF_WORD real_bits[NUM_WORDS]; | ||
80 | struct orig_node *orig_node; | ||
81 | struct batman_if *if_incoming; | ||
82 | }; | ||
83 | |||
84 | struct bat_priv { | ||
85 | struct net_device_stats stats; | ||
86 | }; | ||
87 | |||
88 | struct device_client { | ||
89 | struct list_head queue_list; | ||
90 | unsigned int queue_len; | ||
91 | unsigned char index; | ||
92 | spinlock_t lock; | ||
93 | wait_queue_head_t queue_wait; | ||
94 | }; | ||
95 | |||
96 | struct device_packet { | ||
97 | struct list_head list; | ||
98 | struct icmp_packet icmp_packet; | ||
99 | }; | ||
100 | |||
101 | struct hna_local_entry { | ||
102 | uint8_t addr[ETH_ALEN]; | ||
103 | unsigned long last_seen; | ||
104 | char never_purge; | ||
105 | }; | ||
106 | |||
107 | struct hna_global_entry { | ||
108 | uint8_t addr[ETH_ALEN]; | ||
109 | struct orig_node *orig_node; | ||
110 | }; | ||
111 | |||
112 | struct forw_packet { /* structure for forw_list maintaining packets to be send/forwarded */ | ||
113 | struct hlist_node list; | ||
114 | unsigned long send_time; | ||
115 | uint8_t own; | ||
116 | unsigned char *packet_buff; | ||
117 | uint16_t packet_len; | ||
118 | uint32_t direct_link_flags; | ||
119 | uint8_t num_packets; | ||
120 | struct delayed_work delayed_work; | ||
121 | struct batman_if *if_incoming; | ||
122 | }; | ||
123 | |||
124 | #endif | ||
diff --git a/drivers/staging/batman-adv/vis.c b/drivers/staging/batman-adv/vis.c new file mode 100644 index 000000000000..f6c9acb289ed --- /dev/null +++ b/drivers/staging/batman-adv/vis.c | |||
@@ -0,0 +1,564 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Simon Wunderlich | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "main.h" | ||
23 | #include "send.h" | ||
24 | #include "translation-table.h" | ||
25 | #include "vis.h" | ||
26 | #include "log.h" | ||
27 | #include "soft-interface.h" | ||
28 | #include "hard-interface.h" | ||
29 | #include "hash.h" | ||
30 | #include "compat.h" | ||
31 | |||
32 | struct hashtable_t *vis_hash; | ||
33 | DEFINE_SPINLOCK(vis_hash_lock); | ||
34 | static struct vis_info *my_vis_info; | ||
35 | static struct list_head send_list; /* always locked with vis_hash_lock */ | ||
36 | |||
37 | static void start_vis_timer(void); | ||
38 | |||
39 | /* free the info */ | ||
40 | static void free_info(void *data) | ||
41 | { | ||
42 | struct vis_info *info = data; | ||
43 | struct recvlist_node *entry, *tmp; | ||
44 | |||
45 | list_del_init(&info->send_list); | ||
46 | list_for_each_entry_safe(entry, tmp, &info->recv_list, list) { | ||
47 | list_del(&entry->list); | ||
48 | kfree(entry); | ||
49 | } | ||
50 | kfree(info); | ||
51 | } | ||
52 | |||
53 | /* set the mode of the visualization to client or server */ | ||
54 | void vis_set_mode(int mode) | ||
55 | { | ||
56 | spin_lock(&vis_hash_lock); | ||
57 | |||
58 | if (my_vis_info != NULL) | ||
59 | my_vis_info->packet.vis_type = mode; | ||
60 | |||
61 | spin_unlock(&vis_hash_lock); | ||
62 | } | ||
63 | |||
64 | /* is_vis_server(), locked outside */ | ||
65 | static int is_vis_server_locked(void) | ||
66 | { | ||
67 | if (my_vis_info != NULL) | ||
68 | if (my_vis_info->packet.vis_type == VIS_TYPE_SERVER_SYNC) | ||
69 | return 1; | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | /* get the current set mode */ | ||
75 | int is_vis_server(void) | ||
76 | { | ||
77 | int ret = 0; | ||
78 | |||
79 | spin_lock(&vis_hash_lock); | ||
80 | ret = is_vis_server_locked(); | ||
81 | spin_unlock(&vis_hash_lock); | ||
82 | |||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | /* Compare two vis packets, used by the hashing algorithm */ | ||
87 | static int vis_info_cmp(void *data1, void *data2) | ||
88 | { | ||
89 | struct vis_info *d1, *d2; | ||
90 | d1 = data1; | ||
91 | d2 = data2; | ||
92 | return compare_orig(d1->packet.vis_orig, d2->packet.vis_orig); | ||
93 | } | ||
94 | |||
95 | /* hash function to choose an entry in a hash table of given size */ | ||
96 | /* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ | ||
97 | static int vis_info_choose(void *data, int size) | ||
98 | { | ||
99 | struct vis_info *vis_info = data; | ||
100 | unsigned char *key; | ||
101 | uint32_t hash = 0; | ||
102 | size_t i; | ||
103 | |||
104 | key = vis_info->packet.vis_orig; | ||
105 | for (i = 0; i < ETH_ALEN; i++) { | ||
106 | hash += key[i]; | ||
107 | hash += (hash << 10); | ||
108 | hash ^= (hash >> 6); | ||
109 | } | ||
110 | |||
111 | hash += (hash << 3); | ||
112 | hash ^= (hash >> 11); | ||
113 | hash += (hash << 15); | ||
114 | |||
115 | return hash % size; | ||
116 | } | ||
117 | |||
118 | /* tries to add one entry to the receive list. */ | ||
119 | static void recv_list_add(struct list_head *recv_list, char *mac) | ||
120 | { | ||
121 | struct recvlist_node *entry; | ||
122 | entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); | ||
123 | if (!entry) | ||
124 | return; | ||
125 | |||
126 | memcpy(entry->mac, mac, ETH_ALEN); | ||
127 | list_add_tail(&entry->list, recv_list); | ||
128 | } | ||
129 | |||
130 | /* returns 1 if this mac is in the recv_list */ | ||
131 | static int recv_list_is_in(struct list_head *recv_list, char *mac) | ||
132 | { | ||
133 | struct recvlist_node *entry; | ||
134 | |||
135 | list_for_each_entry(entry, recv_list, list) { | ||
136 | if (memcmp(entry->mac, mac, ETH_ALEN) == 0) | ||
137 | return 1; | ||
138 | } | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | /* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old, | ||
144 | * broken.. ). vis hash must be locked outside. is_new is set when the packet | ||
145 | * is newer than old entries in the hash. */ | ||
146 | static struct vis_info *add_packet(struct vis_packet *vis_packet, | ||
147 | int vis_info_len, int *is_new) | ||
148 | { | ||
149 | struct vis_info *info, *old_info; | ||
150 | struct vis_info search_elem; | ||
151 | |||
152 | *is_new = 0; | ||
153 | /* sanity check */ | ||
154 | if (vis_hash == NULL) | ||
155 | return NULL; | ||
156 | |||
157 | /* see if the packet is already in vis_hash */ | ||
158 | memcpy(search_elem.packet.vis_orig, vis_packet->vis_orig, ETH_ALEN); | ||
159 | old_info = hash_find(vis_hash, &search_elem); | ||
160 | |||
161 | if (old_info != NULL) { | ||
162 | if (vis_packet->seqno - old_info->packet.seqno <= 0) { | ||
163 | if (old_info->packet.seqno == vis_packet->seqno) { | ||
164 | recv_list_add(&old_info->recv_list, | ||
165 | vis_packet->sender_orig); | ||
166 | return old_info; | ||
167 | } else { | ||
168 | /* newer packet is already in hash. */ | ||
169 | return NULL; | ||
170 | } | ||
171 | } | ||
172 | /* remove old entry */ | ||
173 | hash_remove(vis_hash, old_info); | ||
174 | free_info(old_info); | ||
175 | } | ||
176 | |||
177 | info = kmalloc(sizeof(struct vis_info) + vis_info_len, GFP_ATOMIC); | ||
178 | if (info == NULL) | ||
179 | return NULL; | ||
180 | |||
181 | INIT_LIST_HEAD(&info->send_list); | ||
182 | INIT_LIST_HEAD(&info->recv_list); | ||
183 | info->first_seen = jiffies; | ||
184 | memcpy(&info->packet, vis_packet, | ||
185 | sizeof(struct vis_packet) + vis_info_len); | ||
186 | |||
187 | /* initialize and add new packet. */ | ||
188 | *is_new = 1; | ||
189 | |||
190 | /* repair if entries is longer than packet. */ | ||
191 | if (info->packet.entries * sizeof(struct vis_info_entry) > vis_info_len) | ||
192 | info->packet.entries = vis_info_len / sizeof(struct vis_info_entry); | ||
193 | |||
194 | recv_list_add(&info->recv_list, info->packet.sender_orig); | ||
195 | |||
196 | /* try to add it */ | ||
197 | if (hash_add(vis_hash, info) < 0) { | ||
198 | /* did not work (for some reason) */ | ||
199 | free_info(info); | ||
200 | info = NULL; | ||
201 | } | ||
202 | |||
203 | return info; | ||
204 | } | ||
205 | |||
206 | /* handle the server sync packet, forward if needed. */ | ||
207 | void receive_server_sync_packet(struct vis_packet *vis_packet, int vis_info_len) | ||
208 | { | ||
209 | struct vis_info *info; | ||
210 | int is_new; | ||
211 | |||
212 | spin_lock(&vis_hash_lock); | ||
213 | info = add_packet(vis_packet, vis_info_len, &is_new); | ||
214 | if (info == NULL) | ||
215 | goto end; | ||
216 | |||
217 | /* only if we are server ourselves and packet is newer than the one in | ||
218 | * hash.*/ | ||
219 | if (is_vis_server_locked() && is_new) { | ||
220 | memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); | ||
221 | if (list_empty(&info->send_list)) | ||
222 | list_add_tail(&info->send_list, &send_list); | ||
223 | } | ||
224 | end: | ||
225 | spin_unlock(&vis_hash_lock); | ||
226 | } | ||
227 | |||
228 | /* handle an incoming client update packet and schedule forward if needed. */ | ||
229 | void receive_client_update_packet(struct vis_packet *vis_packet, | ||
230 | int vis_info_len) | ||
231 | { | ||
232 | struct vis_info *info; | ||
233 | int is_new; | ||
234 | |||
235 | /* clients shall not broadcast. */ | ||
236 | if (is_bcast(vis_packet->target_orig)) | ||
237 | return; | ||
238 | |||
239 | spin_lock(&vis_hash_lock); | ||
240 | info = add_packet(vis_packet, vis_info_len, &is_new); | ||
241 | if (info == NULL) | ||
242 | goto end; | ||
243 | /* note that outdated packets will be dropped at this point. */ | ||
244 | |||
245 | |||
246 | /* send only if we're the target server or ... */ | ||
247 | if (is_vis_server_locked() && | ||
248 | is_my_mac(info->packet.target_orig) && | ||
249 | is_new) { | ||
250 | info->packet.vis_type = VIS_TYPE_SERVER_SYNC; /* upgrade! */ | ||
251 | memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); | ||
252 | if (list_empty(&info->send_list)) | ||
253 | list_add_tail(&info->send_list, &send_list); | ||
254 | |||
255 | /* ... we're not the recipient (and thus need to forward). */ | ||
256 | } else if (!is_my_mac(info->packet.target_orig)) { | ||
257 | if (list_empty(&info->send_list)) | ||
258 | list_add_tail(&info->send_list, &send_list); | ||
259 | } | ||
260 | end: | ||
261 | spin_unlock(&vis_hash_lock); | ||
262 | } | ||
263 | |||
264 | /* Walk the originators and find the VIS server with the best tq. Set the packet | ||
265 | * address to its address and return the best_tq. | ||
266 | * | ||
267 | * Must be called with the originator hash locked */ | ||
268 | static int find_best_vis_server(struct vis_info *info) | ||
269 | { | ||
270 | struct hash_it_t *hashit = NULL; | ||
271 | struct orig_node *orig_node; | ||
272 | int best_tq = -1; | ||
273 | |||
274 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
275 | orig_node = hashit->bucket->data; | ||
276 | if ((orig_node != NULL) && | ||
277 | (orig_node->router != NULL) && | ||
278 | (orig_node->flags & VIS_SERVER) && | ||
279 | (orig_node->router->tq_avg > best_tq)) { | ||
280 | best_tq = orig_node->router->tq_avg; | ||
281 | memcpy(info->packet.target_orig, orig_node->orig, | ||
282 | ETH_ALEN); | ||
283 | } | ||
284 | } | ||
285 | return best_tq; | ||
286 | } | ||
287 | |||
288 | /* Return true if the vis packet is full. */ | ||
289 | static bool vis_packet_full(struct vis_info *info) | ||
290 | { | ||
291 | if (info->packet.entries + 1 > | ||
292 | (1000 - sizeof(struct vis_info)) / sizeof(struct vis_info_entry)) | ||
293 | return true; | ||
294 | return false; | ||
295 | } | ||
296 | |||
297 | /* generates a packet of own vis data, | ||
298 | * returns 0 on success, -1 if no packet could be generated */ | ||
299 | static int generate_vis_packet(void) | ||
300 | { | ||
301 | struct hash_it_t *hashit = NULL; | ||
302 | struct orig_node *orig_node; | ||
303 | struct vis_info *info = (struct vis_info *)my_vis_info; | ||
304 | struct vis_info_entry *entry, *entry_array; | ||
305 | struct hna_local_entry *hna_local_entry; | ||
306 | int best_tq = -1; | ||
307 | unsigned long flags; | ||
308 | |||
309 | info->first_seen = jiffies; | ||
310 | |||
311 | spin_lock(&orig_hash_lock); | ||
312 | memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); | ||
313 | info->packet.ttl = TTL; | ||
314 | info->packet.seqno++; | ||
315 | info->packet.entries = 0; | ||
316 | |||
317 | if (!is_vis_server_locked()) { | ||
318 | best_tq = find_best_vis_server(info); | ||
319 | if (best_tq < 0) { | ||
320 | spin_unlock(&orig_hash_lock); | ||
321 | return -1; | ||
322 | } | ||
323 | } | ||
324 | hashit = NULL; | ||
325 | |||
326 | entry_array = (struct vis_info_entry *) | ||
327 | ((char *)info + sizeof(struct vis_info)); | ||
328 | |||
329 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
330 | orig_node = hashit->bucket->data; | ||
331 | if (orig_node->router != NULL | ||
332 | && compare_orig(orig_node->router->addr, orig_node->orig) | ||
333 | && orig_node->batman_if | ||
334 | && (orig_node->batman_if->if_active == IF_ACTIVE) | ||
335 | && orig_node->router->tq_avg > 0) { | ||
336 | |||
337 | /* fill one entry into buffer. */ | ||
338 | entry = &entry_array[info->packet.entries]; | ||
339 | memcpy(entry->src, orig_node->batman_if->net_dev->dev_addr, ETH_ALEN); | ||
340 | memcpy(entry->dest, orig_node->orig, ETH_ALEN); | ||
341 | entry->quality = orig_node->router->tq_avg; | ||
342 | info->packet.entries++; | ||
343 | |||
344 | if (vis_packet_full(info)) { | ||
345 | spin_unlock(&orig_hash_lock); | ||
346 | return 0; | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | spin_unlock(&orig_hash_lock); | ||
352 | |||
353 | hashit = NULL; | ||
354 | spin_lock_irqsave(&hna_local_hash_lock, flags); | ||
355 | while (NULL != (hashit = hash_iterate(hna_local_hash, hashit))) { | ||
356 | hna_local_entry = hashit->bucket->data; | ||
357 | entry = &entry_array[info->packet.entries]; | ||
358 | memset(entry->src, 0, ETH_ALEN); | ||
359 | memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN); | ||
360 | entry->quality = 0; /* 0 means HNA */ | ||
361 | info->packet.entries++; | ||
362 | |||
363 | if (vis_packet_full(info)) { | ||
364 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
365 | return 0; | ||
366 | } | ||
367 | } | ||
368 | spin_unlock_irqrestore(&hna_local_hash_lock, flags); | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static void purge_vis_packets(void) | ||
373 | { | ||
374 | struct hash_it_t *hashit = NULL; | ||
375 | struct vis_info *info; | ||
376 | |||
377 | while (NULL != (hashit = hash_iterate(vis_hash, hashit))) { | ||
378 | info = hashit->bucket->data; | ||
379 | if (info == my_vis_info) /* never purge own data. */ | ||
380 | continue; | ||
381 | if (time_after(jiffies, | ||
382 | info->first_seen + (VIS_TIMEOUT/1000)*HZ)) { | ||
383 | hash_remove_bucket(vis_hash, hashit); | ||
384 | free_info(info); | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | |||
389 | static void broadcast_vis_packet(struct vis_info *info, int packet_length) | ||
390 | { | ||
391 | struct hash_it_t *hashit = NULL; | ||
392 | struct orig_node *orig_node; | ||
393 | |||
394 | spin_lock(&orig_hash_lock); | ||
395 | |||
396 | /* send to all routers in range. */ | ||
397 | while (NULL != (hashit = hash_iterate(orig_hash, hashit))) { | ||
398 | orig_node = hashit->bucket->data; | ||
399 | |||
400 | /* if it's a vis server and reachable, send it. */ | ||
401 | if (orig_node && | ||
402 | (orig_node->flags & VIS_SERVER) && | ||
403 | orig_node->batman_if && | ||
404 | orig_node->router) { | ||
405 | |||
406 | /* don't send it if we already received the packet from | ||
407 | * this node. */ | ||
408 | if (recv_list_is_in(&info->recv_list, orig_node->orig)) | ||
409 | continue; | ||
410 | |||
411 | memcpy(info->packet.target_orig, | ||
412 | orig_node->orig, ETH_ALEN); | ||
413 | |||
414 | send_raw_packet((unsigned char *) &info->packet, | ||
415 | packet_length, | ||
416 | orig_node->batman_if, | ||
417 | orig_node->router->addr); | ||
418 | } | ||
419 | } | ||
420 | memcpy(info->packet.target_orig, broadcastAddr, ETH_ALEN); | ||
421 | spin_unlock(&orig_hash_lock); | ||
422 | } | ||
423 | |||
424 | static void unicast_vis_packet(struct vis_info *info, int packet_length) | ||
425 | { | ||
426 | struct orig_node *orig_node; | ||
427 | |||
428 | spin_lock(&orig_hash_lock); | ||
429 | orig_node = ((struct orig_node *) | ||
430 | hash_find(orig_hash, info->packet.target_orig)); | ||
431 | |||
432 | if ((orig_node != NULL) && | ||
433 | (orig_node->batman_if != NULL) && | ||
434 | (orig_node->router != NULL)) { | ||
435 | send_raw_packet((unsigned char *) &info->packet, packet_length, | ||
436 | orig_node->batman_if, | ||
437 | orig_node->router->addr); | ||
438 | } | ||
439 | spin_unlock(&orig_hash_lock); | ||
440 | } | ||
441 | |||
442 | /* only send one vis packet. called from send_vis_packets() */ | ||
443 | static void send_vis_packet(struct vis_info *info) | ||
444 | { | ||
445 | int packet_length; | ||
446 | |||
447 | if (info->packet.ttl < 2) { | ||
448 | debug_log(LOG_TYPE_NOTICE, | ||
449 | "Error - can't send vis packet: ttl exceeded\n"); | ||
450 | return; | ||
451 | } | ||
452 | |||
453 | memcpy(info->packet.sender_orig, mainIfAddr, ETH_ALEN); | ||
454 | info->packet.ttl--; | ||
455 | |||
456 | packet_length = sizeof(struct vis_packet) + | ||
457 | info->packet.entries * sizeof(struct vis_info_entry); | ||
458 | |||
459 | if (is_bcast(info->packet.target_orig)) | ||
460 | broadcast_vis_packet(info, packet_length); | ||
461 | else | ||
462 | unicast_vis_packet(info, packet_length); | ||
463 | info->packet.ttl++; /* restore TTL */ | ||
464 | } | ||
465 | |||
466 | /* called from timer; send (and maybe generate) vis packet. */ | ||
467 | static void send_vis_packets(struct work_struct *work) | ||
468 | { | ||
469 | struct vis_info *info, *temp; | ||
470 | |||
471 | spin_lock(&vis_hash_lock); | ||
472 | purge_vis_packets(); | ||
473 | |||
474 | if (generate_vis_packet() == 0) | ||
475 | /* schedule if generation was successful */ | ||
476 | list_add_tail(&my_vis_info->send_list, &send_list); | ||
477 | |||
478 | list_for_each_entry_safe(info, temp, &send_list, send_list) { | ||
479 | list_del_init(&info->send_list); | ||
480 | send_vis_packet(info); | ||
481 | } | ||
482 | spin_unlock(&vis_hash_lock); | ||
483 | start_vis_timer(); | ||
484 | } | ||
485 | static DECLARE_DELAYED_WORK(vis_timer_wq, send_vis_packets); | ||
486 | |||
487 | /* init the vis server. this may only be called when if_list is already | ||
488 | * initialized (e.g. bat0 is initialized, interfaces have been added) */ | ||
489 | int vis_init(void) | ||
490 | { | ||
491 | if (vis_hash) | ||
492 | return 1; | ||
493 | |||
494 | spin_lock(&vis_hash_lock); | ||
495 | |||
496 | vis_hash = hash_new(256, vis_info_cmp, vis_info_choose); | ||
497 | if (!vis_hash) { | ||
498 | debug_log(LOG_TYPE_CRIT, "Can't initialize vis_hash\n"); | ||
499 | goto err; | ||
500 | } | ||
501 | |||
502 | my_vis_info = kmalloc(1000, GFP_ATOMIC); | ||
503 | if (!my_vis_info) { | ||
504 | debug_log(LOG_TYPE_CRIT, "Can't initialize vis packet\n"); | ||
505 | goto err; | ||
506 | } | ||
507 | |||
508 | /* prefill the vis info */ | ||
509 | my_vis_info->first_seen = jiffies - atomic_read(&vis_interval); | ||
510 | INIT_LIST_HEAD(&my_vis_info->recv_list); | ||
511 | INIT_LIST_HEAD(&my_vis_info->send_list); | ||
512 | my_vis_info->packet.version = COMPAT_VERSION; | ||
513 | my_vis_info->packet.packet_type = BAT_VIS; | ||
514 | my_vis_info->packet.vis_type = VIS_TYPE_CLIENT_UPDATE; | ||
515 | my_vis_info->packet.ttl = TTL; | ||
516 | my_vis_info->packet.seqno = 0; | ||
517 | my_vis_info->packet.entries = 0; | ||
518 | |||
519 | INIT_LIST_HEAD(&send_list); | ||
520 | |||
521 | memcpy(my_vis_info->packet.vis_orig, mainIfAddr, ETH_ALEN); | ||
522 | memcpy(my_vis_info->packet.sender_orig, mainIfAddr, ETH_ALEN); | ||
523 | |||
524 | if (hash_add(vis_hash, my_vis_info) < 0) { | ||
525 | debug_log(LOG_TYPE_CRIT, | ||
526 | "Can't add own vis packet into hash\n"); | ||
527 | free_info(my_vis_info); /* not in hash, need to remove it | ||
528 | * manually. */ | ||
529 | goto err; | ||
530 | } | ||
531 | |||
532 | spin_unlock(&vis_hash_lock); | ||
533 | start_vis_timer(); | ||
534 | return 1; | ||
535 | |||
536 | err: | ||
537 | spin_unlock(&vis_hash_lock); | ||
538 | vis_quit(); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | /* shutdown vis-server */ | ||
543 | void vis_quit(void) | ||
544 | { | ||
545 | if (!vis_hash) | ||
546 | return; | ||
547 | |||
548 | cancel_delayed_work_sync(&vis_timer_wq); | ||
549 | |||
550 | spin_lock(&vis_hash_lock); | ||
551 | /* properly remove, kill timers ... */ | ||
552 | hash_delete(vis_hash, free_info); | ||
553 | vis_hash = NULL; | ||
554 | my_vis_info = NULL; | ||
555 | spin_unlock(&vis_hash_lock); | ||
556 | } | ||
557 | |||
558 | /* schedule packets for (re)transmission */ | ||
559 | static void start_vis_timer(void) | ||
560 | { | ||
561 | queue_delayed_work(bat_event_workqueue, &vis_timer_wq, | ||
562 | (atomic_read(&vis_interval)/1000) * HZ); | ||
563 | } | ||
564 | |||
diff --git a/drivers/staging/batman-adv/vis.h b/drivers/staging/batman-adv/vis.h new file mode 100644 index 000000000000..276fabab4e88 --- /dev/null +++ b/drivers/staging/batman-adv/vis.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008-2009 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Simon Wunderlich, Marek Lindner | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of version 2 of the GNU General Public | ||
8 | * License as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
18 | * 02110-1301, USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #define VIS_TIMEOUT 200000 | ||
23 | #define VIS_FORMAT_DD_NAME "dot_draw" | ||
24 | #define VIS_FORMAT_JSON_NAME "json" | ||
25 | |||
26 | struct vis_info { | ||
27 | unsigned long first_seen; | ||
28 | struct list_head recv_list; | ||
29 | /* list of server-neighbors we received a vis-packet | ||
30 | * from. we should not reply to them. */ | ||
31 | struct list_head send_list; | ||
32 | /* this packet might be part of the vis send queue. */ | ||
33 | struct vis_packet packet; | ||
34 | /* vis_info may follow here*/ | ||
35 | } __attribute__((packed)); | ||
36 | |||
37 | struct vis_info_entry { | ||
38 | uint8_t src[ETH_ALEN]; | ||
39 | uint8_t dest[ETH_ALEN]; | ||
40 | uint8_t quality; /* quality = 0 means HNA */ | ||
41 | } __attribute__((packed)); | ||
42 | |||
43 | struct recvlist_node { | ||
44 | struct list_head list; | ||
45 | uint8_t mac[ETH_ALEN]; | ||
46 | }; | ||
47 | |||
48 | enum vis_formats { | ||
49 | DOT_DRAW, | ||
50 | JSON, | ||
51 | }; | ||
52 | |||
53 | extern struct hashtable_t *vis_hash; | ||
54 | extern spinlock_t vis_hash_lock; | ||
55 | |||
56 | void vis_set_mode(int mode); | ||
57 | int is_vis_server(void); | ||
58 | void receive_server_sync_packet(struct vis_packet *vis_packet, | ||
59 | int vis_info_len); | ||
60 | void receive_client_update_packet(struct vis_packet *vis_packet, | ||
61 | int vis_info_len); | ||
62 | int vis_init(void); | ||
63 | void vis_quit(void); | ||