aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Hundebøll <martin@hundeboll.net>2013-05-23 10:53:02 -0400
committerAntonio Quartulli <antonio@meshcoding.com>2013-10-12 05:58:34 -0400
commit610bfc6bc99bc83680d190ebc69359a05fc7f605 (patch)
tree355ae39e185e72ca129360037525fd5f7f5ff875
parentf097e25dbe9144447f46b6b61ca3da1a2ba432d4 (diff)
batman-adv: Receive fragmented packets and merge
Fragments arriving at their destination are buffered for later merge. Merged packets are passed to the main receive function as had they never been fragmented. Fragments are forwarded without merging if the MTU of the outgoing interface is smaller than the size of the merged packet. Signed-off-by: Martin Hundebøll <martin@hundeboll.net> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de> Signed-off-by: Antonio Quartulli <antonio@meshcoding.com>
-rw-r--r--net/batman-adv/Makefile1
-rw-r--r--net/batman-adv/fragmentation.c370
-rw-r--r--net/batman-adv/fragmentation.h47
-rw-r--r--net/batman-adv/main.c4
-rw-r--r--net/batman-adv/main.h9
-rw-r--r--net/batman-adv/originator.c14
-rw-r--r--net/batman-adv/packet.h27
-rw-r--r--net/batman-adv/routing.c59
-rw-r--r--net/batman-adv/routing.h2
-rw-r--r--net/batman-adv/soft-interface.c4
-rw-r--r--net/batman-adv/types.h38
11 files changed, 574 insertions, 1 deletions
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile
index f9b465b00555..4f4aabbd8eab 100644
--- a/net/batman-adv/Makefile
+++ b/net/batman-adv/Makefile
@@ -24,6 +24,7 @@ batman-adv-y += bitarray.o
24batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o 24batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
25batman-adv-y += debugfs.o 25batman-adv-y += debugfs.o
26batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o 26batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
27batman-adv-y += fragmentation.o
27batman-adv-y += gateway_client.o 28batman-adv-y += gateway_client.o
28batman-adv-y += gateway_common.o 29batman-adv-y += gateway_common.o
29batman-adv-y += hard-interface.o 30batman-adv-y += hard-interface.o
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
new file mode 100644
index 000000000000..c829d3cee89c
--- /dev/null
+++ b/net/batman-adv/fragmentation.c
@@ -0,0 +1,370 @@
1/* Copyright (C) 2013 B.A.T.M.A.N. contributors:
2 *
3 * Martin Hundebøll <martin@hundeboll.net>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU General Public
7 * License as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA
18 */
19
20#include "main.h"
21#include "fragmentation.h"
22#include "send.h"
23#include "originator.h"
24#include "routing.h"
25#include "hard-interface.h"
26#include "soft-interface.h"
27
28
29/**
30 * batadv_frag_clear_chain - delete entries in the fragment buffer chain
31 * @head: head of chain with entries.
32 *
33 * Free fragments in the passed hlist. Should be called with appropriate lock.
34 */
35static void batadv_frag_clear_chain(struct hlist_head *head)
36{
37 struct batadv_frag_list_entry *entry;
38 struct hlist_node *node;
39
40 hlist_for_each_entry_safe(entry, node, head, list) {
41 hlist_del(&entry->list);
42 kfree_skb(entry->skb);
43 kfree(entry);
44 }
45}
46
47/**
48 * batadv_frag_purge_orig - free fragments associated to an orig
49 * @orig_node: originator to free fragments from
50 * @check_cb: optional function to tell if an entry should be purged
51 */
52void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
53 bool (*check_cb)(struct batadv_frag_table_entry *))
54{
55 struct batadv_frag_table_entry *chain;
56 uint8_t i;
57
58 for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
59 chain = &orig_node->fragments[i];
60 spin_lock_bh(&orig_node->fragments[i].lock);
61
62 if (!check_cb || check_cb(chain)) {
63 batadv_frag_clear_chain(&orig_node->fragments[i].head);
64 orig_node->fragments[i].size = 0;
65 }
66
67 spin_unlock_bh(&orig_node->fragments[i].lock);
68 }
69}
70
71/**
72 * batadv_frag_size_limit - maximum possible size of packet to be fragmented
73 *
74 * Returns the maximum size of payload that can be fragmented.
75 */
76static int batadv_frag_size_limit(void)
77{
78 int limit = BATADV_FRAG_MAX_FRAG_SIZE;
79
80 limit -= sizeof(struct batadv_frag_packet);
81 limit *= BATADV_FRAG_MAX_FRAGMENTS;
82
83 return limit;
84}
85
86/**
87 * batadv_frag_init_chain - check and prepare fragment chain for new fragment
88 * @chain: chain in fragments table to init
89 * @seqno: sequence number of the received fragment
90 *
91 * Make chain ready for a fragment with sequence number "seqno". Delete existing
92 * entries if they have an "old" sequence number.
93 *
94 * Caller must hold chain->lock.
95 *
96 * Returns true if chain is empty and caller can just insert the new fragment
97 * without searching for the right position.
98 */
99static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
100 uint16_t seqno)
101{
102 if (chain->seqno == seqno)
103 return false;
104
105 if (!hlist_empty(&chain->head))
106 batadv_frag_clear_chain(&chain->head);
107
108 chain->size = 0;
109 chain->seqno = seqno;
110
111 return true;
112}
113
114/**
115 * batadv_frag_insert_packet - insert a fragment into a fragment chain
116 * @orig_node: originator that the fragment was received from
117 * @skb: skb to insert
118 * @chain_out: list head to attach complete chains of fragments to
119 *
120 * Insert a new fragment into the reverse ordered chain in the right table
121 * entry. The hash table entry is cleared if "old" fragments exist in it.
122 *
123 * Returns true if skb is buffered, false on error. If the chain has all the
124 * fragments needed to merge the packet, the chain is moved to the passed head
125 * to avoid locking the chain in the table.
126 */
127static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
128 struct sk_buff *skb,
129 struct hlist_head *chain_out)
130{
131 struct batadv_frag_table_entry *chain;
132 struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
133 struct batadv_frag_packet *frag_packet;
134 uint8_t bucket;
135 uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet);
136 bool ret = false;
137
138 /* Linearize packet to avoid linearizing 16 packets in a row when doing
139 * the later merge. Non-linear merge should be added to remove this
140 * linearization.
141 */
142 if (skb_linearize(skb) < 0)
143 goto err;
144
145 frag_packet = (struct batadv_frag_packet *)skb->data;
146 seqno = ntohs(frag_packet->seqno);
147 bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
148
149 frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC);
150 if (!frag_entry_new)
151 goto err;
152
153 frag_entry_new->skb = skb;
154 frag_entry_new->no = frag_packet->no;
155
156 /* Select entry in the "chain table" and delete any prior fragments
157 * with another sequence number. batadv_frag_init_chain() returns true,
158 * if the list is empty at return.
159 */
160 chain = &orig_node->fragments[bucket];
161 spin_lock_bh(&chain->lock);
162 if (batadv_frag_init_chain(chain, seqno)) {
163 hlist_add_head(&frag_entry_new->list, &chain->head);
164 chain->size = skb->len - hdr_size;
165 chain->timestamp = jiffies;
166 ret = true;
167 goto out;
168 }
169
170 /* Find the position for the new fragment. */
171 hlist_for_each_entry(frag_entry_curr, &chain->head, list) {
172 /* Drop packet if fragment already exists. */
173 if (frag_entry_curr->no == frag_entry_new->no)
174 goto err_unlock;
175
176 /* Order fragments from highest to lowest. */
177 if (frag_entry_curr->no < frag_entry_new->no) {
178 hlist_add_before(&frag_entry_new->list,
179 &frag_entry_curr->list);
180 chain->size += skb->len - hdr_size;
181 chain->timestamp = jiffies;
182 ret = true;
183 goto out;
184 }
185 }
186
187 /* Reached the end of the list, so insert after 'frag_entry_curr'. */
188 if (likely(frag_entry_curr)) {
189 hlist_add_after(&frag_entry_curr->list, &frag_entry_new->list);
190 chain->size += skb->len - hdr_size;
191 chain->timestamp = jiffies;
192 ret = true;
193 }
194
195out:
196 if (chain->size > batadv_frag_size_limit() ||
197 ntohs(frag_packet->total_size) > batadv_frag_size_limit()) {
198 /* Clear chain if total size of either the list or the packet
199 * exceeds the maximum size of one merged packet.
200 */
201 batadv_frag_clear_chain(&chain->head);
202 chain->size = 0;
203 } else if (ntohs(frag_packet->total_size) == chain->size) {
204 /* All fragments received. Hand over chain to caller. */
205 hlist_move_list(&chain->head, chain_out);
206 chain->size = 0;
207 }
208
209err_unlock:
210 spin_unlock_bh(&chain->lock);
211
212err:
213 if (!ret)
214 kfree(frag_entry_new);
215
216 return ret;
217}
218
219/**
220 * batadv_frag_merge_packets - merge a chain of fragments
221 * @chain: head of chain with fragments
222 * @skb: packet with total size of skb after merging
223 *
224 * Expand the first skb in the chain and copy the content of the remaining
225 * skb's into the expanded one. After doing so, clear the chain.
226 *
227 * Returns the merged skb or NULL on error.
228 */
229static struct sk_buff *
230batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
231{
232 struct batadv_frag_packet *packet;
233 struct batadv_frag_list_entry *entry;
234 struct sk_buff *skb_out = NULL;
235 int size, hdr_size = sizeof(struct batadv_frag_packet);
236
237 /* Make sure incoming skb has non-bogus data. */
238 packet = (struct batadv_frag_packet *)skb->data;
239 size = ntohs(packet->total_size);
240 if (size > batadv_frag_size_limit())
241 goto free;
242
243 /* Remove first entry, as this is the destination for the rest of the
244 * fragments.
245 */
246 entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list);
247 hlist_del(&entry->list);
248 skb_out = entry->skb;
249 kfree(entry);
250
251 /* Make room for the rest of the fragments. */
252 if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) {
253 kfree_skb(skb_out);
254 skb_out = NULL;
255 goto free;
256 }
257
258 /* Move the existing MAC header to just before the payload. (Override
259 * the fragment header.)
260 */
261 skb_pull_rcsum(skb_out, hdr_size);
262 memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
263 skb_set_mac_header(skb_out, -ETH_HLEN);
264 skb_reset_network_header(skb_out);
265 skb_reset_transport_header(skb_out);
266
267 /* Copy the payload of the each fragment into the last skb */
268 hlist_for_each_entry(entry, chain, list) {
269 size = entry->skb->len - hdr_size;
270 memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size,
271 size);
272 }
273
274free:
275 /* Locking is not needed, because 'chain' is not part of any orig. */
276 batadv_frag_clear_chain(chain);
277 return skb_out;
278}
279
280/**
281 * batadv_frag_skb_buffer - buffer fragment for later merge
282 * @skb: skb to buffer
283 * @orig_node_src: originator that the skb is received from
284 *
285 * Add fragment to buffer and merge fragments if possible.
286 *
287 * There are three possible outcomes: 1) Packet is merged: Return true and
288 * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
289 * to NULL; 3) Error: Return false and leave skb as is.
290 */
291bool batadv_frag_skb_buffer(struct sk_buff **skb,
292 struct batadv_orig_node *orig_node_src)
293{
294 struct sk_buff *skb_out = NULL;
295 struct hlist_head head = HLIST_HEAD_INIT;
296 bool ret = false;
297
298 /* Add packet to buffer and table entry if merge is possible. */
299 if (!batadv_frag_insert_packet(orig_node_src, *skb, &head))
300 goto out_err;
301
302 /* Leave if more fragments are needed to merge. */
303 if (hlist_empty(&head))
304 goto out;
305
306 skb_out = batadv_frag_merge_packets(&head, *skb);
307 if (!skb_out)
308 goto out_err;
309
310out:
311 *skb = skb_out;
312 ret = true;
313out_err:
314 return ret;
315}
316
317/**
318 * batadv_frag_skb_fwd - forward fragments that would exceed MTU when merged
319 * @skb: skb to forward
320 * @recv_if: interface that the skb is received on
321 * @orig_node_src: originator that the skb is received from
322 *
323 * Look up the next-hop of the fragments payload and check if the merged packet
324 * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
325 * without merging it.
326 *
327 * Returns true if the fragment is consumed/forwarded, false otherwise.
328 */
329bool batadv_frag_skb_fwd(struct sk_buff *skb,
330 struct batadv_hard_iface *recv_if,
331 struct batadv_orig_node *orig_node_src)
332{
333 struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
334 struct batadv_orig_node *orig_node_dst = NULL;
335 struct batadv_neigh_node *neigh_node = NULL;
336 struct batadv_frag_packet *packet;
337 uint16_t total_size;
338 bool ret = false;
339
340 packet = (struct batadv_frag_packet *)skb->data;
341 orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest);
342 if (!orig_node_dst)
343 goto out;
344
345 neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if);
346 if (!neigh_node)
347 goto out;
348
349 /* Forward the fragment, if the merged packet would be too big to
350 * be assembled.
351 */
352 total_size = ntohs(packet->total_size);
353 if (total_size > neigh_node->if_incoming->net_dev->mtu) {
354 batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
355 batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
356 skb->len + ETH_HLEN);
357
358 packet->header.ttl--;
359 batadv_send_skb_packet(skb, neigh_node->if_incoming,
360 neigh_node->addr);
361 ret = true;
362 }
363
364out:
365 if (orig_node_dst)
366 batadv_orig_node_free_ref(orig_node_dst);
367 if (neigh_node)
368 batadv_neigh_node_free_ref(neigh_node);
369 return ret;
370}
diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h
new file mode 100644
index 000000000000..883a6f46005e
--- /dev/null
+++ b/net/batman-adv/fragmentation.h
@@ -0,0 +1,47 @@
1/* Copyright (C) 2013 B.A.T.M.A.N. contributors:
2 *
3 * Martin Hundebøll <martin@hundeboll.net>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU General Public
7 * License as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA
18 */
19
20#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_
21#define _NET_BATMAN_ADV_FRAGMENTATION_H_
22
23void batadv_frag_purge_orig(struct batadv_orig_node *orig,
24 bool (*check_cb)(struct batadv_frag_table_entry *));
25bool batadv_frag_skb_fwd(struct sk_buff *skb,
26 struct batadv_hard_iface *recv_if,
27 struct batadv_orig_node *orig_node_src);
28bool batadv_frag_skb_buffer(struct sk_buff **skb,
29 struct batadv_orig_node *orig_node);
30
31/**
32 * batadv_frag_check_entry - check if a list of fragments has timed out
33 * @frags_entry: table entry to check
34 *
35 * Returns true if the frags entry has timed out, false otherwise.
36 */
37static inline bool
38batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry)
39{
40 if (!hlist_empty(&frags_entry->head) &&
41 batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT))
42 return true;
43 else
44 return false;
45}
46
47#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 8822fad5694e..ca6f1340d70c 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -40,6 +40,7 @@
40#include "hash.h" 40#include "hash.h"
41#include "bat_algo.h" 41#include "bat_algo.h"
42#include "network-coding.h" 42#include "network-coding.h"
43#include "fragmentation.h"
43 44
44 45
45/* List manipulations on hardif_list have to be rtnl_lock()'ed, 46/* List manipulations on hardif_list have to be rtnl_lock()'ed,
@@ -399,6 +400,7 @@ static void batadv_recv_handler_init(void)
399 BUILD_BUG_ON(offsetof(struct batadv_unicast_4addr_packet, src) != 10); 400 BUILD_BUG_ON(offsetof(struct batadv_unicast_4addr_packet, src) != 10);
400 BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, dest) != 4); 401 BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, dest) != 4);
401 BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, dst) != 4); 402 BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, dst) != 4);
403 BUILD_BUG_ON(offsetof(struct batadv_frag_packet, dest) != 4);
402 BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, dst) != 4); 404 BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, dst) != 4);
403 BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, dst) != 4); 405 BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, dst) != 4);
404 406
@@ -414,6 +416,8 @@ static void batadv_recv_handler_init(void)
414 batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv; 416 batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv;
415 /* batman icmp packet */ 417 /* batman icmp packet */
416 batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; 418 batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
419 /* Fragmented packets */
420 batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_frag_packet;
417} 421}
418 422
419int 423int
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index e11c2ec7a739..6a74a4225326 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -131,6 +131,15 @@ enum batadv_uev_type {
131 131
132#define BATADV_GW_THRESHOLD 50 132#define BATADV_GW_THRESHOLD 50
133 133
134/* Number of fragment chains for each orig_node */
135#define BATADV_FRAG_BUFFER_COUNT 8
136/* Maximum number of fragments for one packet */
137#define BATADV_FRAG_MAX_FRAGMENTS 16
138/* Maxumim size of each fragment */
139#define BATADV_FRAG_MAX_FRAG_SIZE 1400
140/* Time to keep fragments while waiting for rest of the fragments */
141#define BATADV_FRAG_TIMEOUT 10000
142
134#define BATADV_DAT_CANDIDATE_NOT_FOUND 0 143#define BATADV_DAT_CANDIDATE_NOT_FOUND 0
135#define BATADV_DAT_CANDIDATE_ORIG 1 144#define BATADV_DAT_CANDIDATE_ORIG 1
136 145
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 898b0ce82cc2..a591dc5c321e 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -28,6 +28,7 @@
28#include "soft-interface.h" 28#include "soft-interface.h"
29#include "bridge_loop_avoidance.h" 29#include "bridge_loop_avoidance.h"
30#include "network-coding.h" 30#include "network-coding.h"
31#include "fragmentation.h"
31 32
32/* hash class keys */ 33/* hash class keys */
33static struct lock_class_key batadv_orig_hash_lock_class_key; 34static struct lock_class_key batadv_orig_hash_lock_class_key;
@@ -145,6 +146,8 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
145 /* Free nc_nodes */ 146 /* Free nc_nodes */
146 batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL); 147 batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
147 148
149 batadv_frag_purge_orig(orig_node, NULL);
150
148 batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, 151 batadv_tt_global_del_orig(orig_node->bat_priv, orig_node,
149 "originator timed out"); 152 "originator timed out");
150 153
@@ -215,7 +218,7 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
215 const uint8_t *addr) 218 const uint8_t *addr)
216{ 219{
217 struct batadv_orig_node *orig_node; 220 struct batadv_orig_node *orig_node;
218 int size; 221 int size, i;
219 int hash_added; 222 int hash_added;
220 unsigned long reset_time; 223 unsigned long reset_time;
221 224
@@ -267,6 +270,12 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
267 size = bat_priv->num_ifaces * sizeof(uint8_t); 270 size = bat_priv->num_ifaces * sizeof(uint8_t);
268 orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC); 271 orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
269 272
273 for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
274 INIT_HLIST_HEAD(&orig_node->fragments[i].head);
275 spin_lock_init(&orig_node->fragments[i].lock);
276 orig_node->fragments[i].size = 0;
277 }
278
270 if (!orig_node->bcast_own_sum) 279 if (!orig_node->bcast_own_sum)
271 goto free_bcast_own; 280 goto free_bcast_own;
272 281
@@ -388,6 +397,9 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv)
388 batadv_orig_node_free_ref(orig_node); 397 batadv_orig_node_free_ref(orig_node);
389 continue; 398 continue;
390 } 399 }
400
401 batadv_frag_purge_orig(orig_node,
402 batadv_frag_check_entry);
391 } 403 }
392 spin_unlock_bh(list_lock); 404 spin_unlock_bh(list_lock);
393 } 405 }
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 5e3b1026b509..aa46c2778ad8 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -249,6 +249,33 @@ struct batadv_unicast_4addr_packet {
249 */ 249 */
250}; 250};
251 251
252/**
253 * struct batadv_frag_packet - fragmented packet
254 * @header: common batman packet header with type, compatversion, and ttl
255 * @dest: final destination used when routing fragments
256 * @orig: originator of the fragment used when merging the packet
257 * @no: fragment number within this sequence
258 * @reserved: reserved byte for alignment
259 * @seqno: sequence identification
260 * @total_size: size of the merged packet
261 */
262struct batadv_frag_packet {
263 struct batadv_header header;
264#if defined(__BIG_ENDIAN_BITFIELD)
265 uint8_t no:4;
266 uint8_t reserved:4;
267#elif defined(__LITTLE_ENDIAN_BITFIELD)
268 uint8_t reserved:4;
269 uint8_t no:4;
270#else
271#error "unknown bitfield endianess"
272#endif
273 uint8_t dest[ETH_ALEN];
274 uint8_t orig[ETH_ALEN];
275 __be16 seqno;
276 __be16 total_size;
277};
278
252struct batadv_bcast_packet { 279struct batadv_bcast_packet {
253 struct batadv_header header; 280 struct batadv_header header;
254 uint8_t reserved; 281 uint8_t reserved;
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index fd2cdbc3ea85..a080f63b3ef6 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -28,6 +28,7 @@
28#include "bridge_loop_avoidance.h" 28#include "bridge_loop_avoidance.h"
29#include "distributed-arp-table.h" 29#include "distributed-arp-table.h"
30#include "network-coding.h" 30#include "network-coding.h"
31#include "fragmentation.h"
31 32
32static int batadv_route_unicast_packet(struct sk_buff *skb, 33static int batadv_route_unicast_packet(struct sk_buff *skb,
33 struct batadv_hard_iface *recv_if); 34 struct batadv_hard_iface *recv_if);
@@ -1013,6 +1014,64 @@ int batadv_recv_unicast_tvlv(struct sk_buff *skb,
1013 return ret; 1014 return ret;
1014} 1015}
1015 1016
1017/**
1018 * batadv_recv_frag_packet - process received fragment
1019 * @skb: the received fragment
1020 * @recv_if: interface that the skb is received on
1021 *
1022 * This function does one of the three following things: 1) Forward fragment, if
1023 * the assembled packet will exceed our MTU; 2) Buffer fragment, if we till
1024 * lack further fragments; 3) Merge fragments, if we have all needed parts.
1025 *
1026 * Return NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise.
1027 */
1028int batadv_recv_frag_packet(struct sk_buff *skb,
1029 struct batadv_hard_iface *recv_if)
1030{
1031 struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
1032 struct batadv_orig_node *orig_node_src = NULL;
1033 struct batadv_frag_packet *frag_packet;
1034 int ret = NET_RX_DROP;
1035
1036 if (batadv_check_unicast_packet(bat_priv, skb,
1037 sizeof(*frag_packet)) < 0)
1038 goto out;
1039
1040 frag_packet = (struct batadv_frag_packet *)skb->data;
1041 orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig);
1042 if (!orig_node_src)
1043 goto out;
1044
1045 /* Route the fragment if it is not for us and too big to be merged. */
1046 if (!batadv_is_my_mac(bat_priv, frag_packet->dest) &&
1047 batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) {
1048 ret = NET_RX_SUCCESS;
1049 goto out;
1050 }
1051
1052 batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX);
1053 batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, skb->len);
1054
1055 /* Add fragment to buffer and merge if possible. */
1056 if (!batadv_frag_skb_buffer(&skb, orig_node_src))
1057 goto out;
1058
1059 /* Deliver merged packet to the appropriate handler, if it was
1060 * merged
1061 */
1062 if (skb)
1063 batadv_batman_skb_recv(skb, recv_if->net_dev,
1064 &recv_if->batman_adv_ptype, NULL);
1065
1066 ret = NET_RX_SUCCESS;
1067
1068out:
1069 if (orig_node_src)
1070 batadv_orig_node_free_ref(orig_node_src);
1071
1072 return ret;
1073}
1074
1016int batadv_recv_bcast_packet(struct sk_buff *skb, 1075int batadv_recv_bcast_packet(struct sk_buff *skb,
1017 struct batadv_hard_iface *recv_if) 1076 struct batadv_hard_iface *recv_if)
1018{ 1077{
diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h
index efab583fda02..55d637a90621 100644
--- a/net/batman-adv/routing.h
+++ b/net/batman-adv/routing.h
@@ -30,6 +30,8 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
30 struct batadv_hard_iface *recv_if); 30 struct batadv_hard_iface *recv_if);
31int batadv_recv_unicast_packet(struct sk_buff *skb, 31int batadv_recv_unicast_packet(struct sk_buff *skb,
32 struct batadv_hard_iface *recv_if); 32 struct batadv_hard_iface *recv_if);
33int batadv_recv_frag_packet(struct sk_buff *skb,
34 struct batadv_hard_iface *iface);
33int batadv_recv_bcast_packet(struct sk_buff *skb, 35int batadv_recv_bcast_packet(struct sk_buff *skb,
34 struct batadv_hard_iface *recv_if); 36 struct batadv_hard_iface *recv_if);
35int batadv_recv_tt_query(struct sk_buff *skb, 37int batadv_recv_tt_query(struct sk_buff *skb,
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 504d0bbddc48..dd189e6bf05a 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -758,6 +758,10 @@ static const struct {
758 { "mgmt_tx_bytes" }, 758 { "mgmt_tx_bytes" },
759 { "mgmt_rx" }, 759 { "mgmt_rx" },
760 { "mgmt_rx_bytes" }, 760 { "mgmt_rx_bytes" },
761 { "frag_rx" },
762 { "frag_rx_bytes" },
763 { "frag_fwd" },
764 { "frag_fwd_bytes" },
761 { "tt_request_tx" }, 765 { "tt_request_tx" },
762 { "tt_request_rx" }, 766 { "tt_request_rx" },
763 { "tt_response_tx" }, 767 { "tt_response_tx" },
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 795a0794ebfb..5a2cc7a9a620 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -86,6 +86,34 @@ struct batadv_hard_iface {
86}; 86};
87 87
88/** 88/**
89 * struct batadv_frag_table_entry - head in the fragment buffer table
90 * @head: head of list with fragments
91 * @lock: lock to protect the list of fragments
92 * @timestamp: time (jiffie) of last received fragment
93 * @seqno: sequence number of the fragments in the list
94 * @size: accumulated size of packets in list
95 */
96struct batadv_frag_table_entry {
97 struct hlist_head head;
98 spinlock_t lock; /* protects head */
99 unsigned long timestamp;
100 uint16_t seqno;
101 uint16_t size;
102};
103
104/**
105 * struct batadv_frag_list_entry - entry in a list of fragments
106 * @list: list node information
107 * @skb: fragment
108 * @no: fragment number in the set
109 */
110struct batadv_frag_list_entry {
111 struct hlist_node list;
112 struct sk_buff *skb;
113 uint8_t no;
114};
115
116/**
89 * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh 117 * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh
90 * @orig: originator ethernet address 118 * @orig: originator ethernet address
91 * @primary_addr: hosts primary interface address 119 * @primary_addr: hosts primary interface address
@@ -128,6 +156,7 @@ struct batadv_hard_iface {
128 * @out_coding_list: list of nodes that can hear this orig 156 * @out_coding_list: list of nodes that can hear this orig
129 * @in_coding_list_lock: protects in_coding_list 157 * @in_coding_list_lock: protects in_coding_list
130 * @out_coding_list_lock: protects out_coding_list 158 * @out_coding_list_lock: protects out_coding_list
159 * @fragments: array with heads for fragment chains
131 */ 160 */
132struct batadv_orig_node { 161struct batadv_orig_node {
133 uint8_t orig[ETH_ALEN]; 162 uint8_t orig[ETH_ALEN];
@@ -174,6 +203,7 @@ struct batadv_orig_node {
174 spinlock_t in_coding_list_lock; /* Protects in_coding_list */ 203 spinlock_t in_coding_list_lock; /* Protects in_coding_list */
175 spinlock_t out_coding_list_lock; /* Protects out_coding_list */ 204 spinlock_t out_coding_list_lock; /* Protects out_coding_list */
176#endif 205#endif
206 struct batadv_frag_table_entry fragments[BATADV_FRAG_BUFFER_COUNT];
177}; 207};
178 208
179/** 209/**
@@ -270,6 +300,10 @@ struct batadv_bcast_duplist_entry {
270 * @BATADV_CNT_MGMT_TX_BYTES: transmitted routing protocol traffic bytes counter 300 * @BATADV_CNT_MGMT_TX_BYTES: transmitted routing protocol traffic bytes counter
271 * @BATADV_CNT_MGMT_RX: received routing protocol traffic packet counter 301 * @BATADV_CNT_MGMT_RX: received routing protocol traffic packet counter
272 * @BATADV_CNT_MGMT_RX_BYTES: received routing protocol traffic bytes counter 302 * @BATADV_CNT_MGMT_RX_BYTES: received routing protocol traffic bytes counter
303 * @BATADV_CNT_FRAG_RX: received fragment traffic packet counter
304 * @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter
305 * @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter
306 * @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter
273 * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter 307 * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter
274 * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter 308 * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter
275 * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter 309 * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter
@@ -307,6 +341,10 @@ enum batadv_counters {
307 BATADV_CNT_MGMT_TX_BYTES, 341 BATADV_CNT_MGMT_TX_BYTES,
308 BATADV_CNT_MGMT_RX, 342 BATADV_CNT_MGMT_RX,
309 BATADV_CNT_MGMT_RX_BYTES, 343 BATADV_CNT_MGMT_RX_BYTES,
344 BATADV_CNT_FRAG_RX,
345 BATADV_CNT_FRAG_RX_BYTES,
346 BATADV_CNT_FRAG_FWD,
347 BATADV_CNT_FRAG_FWD_BYTES,
310 BATADV_CNT_TT_REQUEST_TX, 348 BATADV_CNT_TT_REQUEST_TX,
311 BATADV_CNT_TT_REQUEST_RX, 349 BATADV_CNT_TT_REQUEST_RX,
312 BATADV_CNT_TT_RESPONSE_TX, 350 BATADV_CNT_TT_RESPONSE_TX,