xref: /openbmc/linux/net/batman-adv/fragmentation.c (revision 610bfc6bc99bc83680d190ebc69359a05fc7f605)
1*610bfc6bSMartin Hundebøll /* Copyright (C) 2013 B.A.T.M.A.N. contributors:
2*610bfc6bSMartin Hundebøll  *
3*610bfc6bSMartin Hundebøll  * Martin Hundebøll <martin@hundeboll.net>
4*610bfc6bSMartin Hundebøll  *
5*610bfc6bSMartin Hundebøll  * This program is free software; you can redistribute it and/or
6*610bfc6bSMartin Hundebøll  * modify it under the terms of version 2 of the GNU General Public
7*610bfc6bSMartin Hundebøll  * License as published by the Free Software Foundation.
8*610bfc6bSMartin Hundebøll  *
9*610bfc6bSMartin Hundebøll  * This program is distributed in the hope that it will be useful, but
10*610bfc6bSMartin Hundebøll  * WITHOUT ANY WARRANTY; without even the implied warranty of
11*610bfc6bSMartin Hundebøll  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12*610bfc6bSMartin Hundebøll  * General Public License for more details.
13*610bfc6bSMartin Hundebøll  *
14*610bfc6bSMartin Hundebøll  * You should have received a copy of the GNU General Public License
15*610bfc6bSMartin Hundebøll  * along with this program; if not, write to the Free Software
16*610bfc6bSMartin Hundebøll  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17*610bfc6bSMartin Hundebøll  * 02110-1301, USA
18*610bfc6bSMartin Hundebøll  */
19*610bfc6bSMartin Hundebøll 
20*610bfc6bSMartin Hundebøll #include "main.h"
21*610bfc6bSMartin Hundebøll #include "fragmentation.h"
22*610bfc6bSMartin Hundebøll #include "send.h"
23*610bfc6bSMartin Hundebøll #include "originator.h"
24*610bfc6bSMartin Hundebøll #include "routing.h"
25*610bfc6bSMartin Hundebøll #include "hard-interface.h"
26*610bfc6bSMartin Hundebøll #include "soft-interface.h"
27*610bfc6bSMartin Hundebøll 
28*610bfc6bSMartin Hundebøll 
29*610bfc6bSMartin Hundebøll /**
30*610bfc6bSMartin Hundebøll  * batadv_frag_clear_chain - delete entries in the fragment buffer chain
31*610bfc6bSMartin Hundebøll  * @head: head of chain with entries.
32*610bfc6bSMartin Hundebøll  *
33*610bfc6bSMartin Hundebøll  * Free fragments in the passed hlist. Should be called with appropriate lock.
34*610bfc6bSMartin Hundebøll  */
35*610bfc6bSMartin Hundebøll static void batadv_frag_clear_chain(struct hlist_head *head)
36*610bfc6bSMartin Hundebøll {
37*610bfc6bSMartin Hundebøll 	struct batadv_frag_list_entry *entry;
38*610bfc6bSMartin Hundebøll 	struct hlist_node *node;
39*610bfc6bSMartin Hundebøll 
40*610bfc6bSMartin Hundebøll 	hlist_for_each_entry_safe(entry, node, head, list) {
41*610bfc6bSMartin Hundebøll 		hlist_del(&entry->list);
42*610bfc6bSMartin Hundebøll 		kfree_skb(entry->skb);
43*610bfc6bSMartin Hundebøll 		kfree(entry);
44*610bfc6bSMartin Hundebøll 	}
45*610bfc6bSMartin Hundebøll }
46*610bfc6bSMartin Hundebøll 
47*610bfc6bSMartin Hundebøll /**
48*610bfc6bSMartin Hundebøll  * batadv_frag_purge_orig - free fragments associated to an orig
49*610bfc6bSMartin Hundebøll  * @orig_node: originator to free fragments from
50*610bfc6bSMartin Hundebøll  * @check_cb: optional function to tell if an entry should be purged
51*610bfc6bSMartin Hundebøll  */
52*610bfc6bSMartin Hundebøll void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
53*610bfc6bSMartin Hundebøll 			    bool (*check_cb)(struct batadv_frag_table_entry *))
54*610bfc6bSMartin Hundebøll {
55*610bfc6bSMartin Hundebøll 	struct batadv_frag_table_entry *chain;
56*610bfc6bSMartin Hundebøll 	uint8_t i;
57*610bfc6bSMartin Hundebøll 
58*610bfc6bSMartin Hundebøll 	for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
59*610bfc6bSMartin Hundebøll 		chain = &orig_node->fragments[i];
60*610bfc6bSMartin Hundebøll 		spin_lock_bh(&orig_node->fragments[i].lock);
61*610bfc6bSMartin Hundebøll 
62*610bfc6bSMartin Hundebøll 		if (!check_cb || check_cb(chain)) {
63*610bfc6bSMartin Hundebøll 			batadv_frag_clear_chain(&orig_node->fragments[i].head);
64*610bfc6bSMartin Hundebøll 			orig_node->fragments[i].size = 0;
65*610bfc6bSMartin Hundebøll 		}
66*610bfc6bSMartin Hundebøll 
67*610bfc6bSMartin Hundebøll 		spin_unlock_bh(&orig_node->fragments[i].lock);
68*610bfc6bSMartin Hundebøll 	}
69*610bfc6bSMartin Hundebøll }
70*610bfc6bSMartin Hundebøll 
71*610bfc6bSMartin Hundebøll /**
72*610bfc6bSMartin Hundebøll  * batadv_frag_size_limit - maximum possible size of packet to be fragmented
73*610bfc6bSMartin Hundebøll  *
74*610bfc6bSMartin Hundebøll  * Returns the maximum size of payload that can be fragmented.
75*610bfc6bSMartin Hundebøll  */
76*610bfc6bSMartin Hundebøll static int batadv_frag_size_limit(void)
77*610bfc6bSMartin Hundebøll {
78*610bfc6bSMartin Hundebøll 	int limit = BATADV_FRAG_MAX_FRAG_SIZE;
79*610bfc6bSMartin Hundebøll 
80*610bfc6bSMartin Hundebøll 	limit -= sizeof(struct batadv_frag_packet);
81*610bfc6bSMartin Hundebøll 	limit *= BATADV_FRAG_MAX_FRAGMENTS;
82*610bfc6bSMartin Hundebøll 
83*610bfc6bSMartin Hundebøll 	return limit;
84*610bfc6bSMartin Hundebøll }
85*610bfc6bSMartin Hundebøll 
86*610bfc6bSMartin Hundebøll /**
87*610bfc6bSMartin Hundebøll  * batadv_frag_init_chain - check and prepare fragment chain for new fragment
88*610bfc6bSMartin Hundebøll  * @chain: chain in fragments table to init
89*610bfc6bSMartin Hundebøll  * @seqno: sequence number of the received fragment
90*610bfc6bSMartin Hundebøll  *
91*610bfc6bSMartin Hundebøll  * Make chain ready for a fragment with sequence number "seqno". Delete existing
92*610bfc6bSMartin Hundebøll  * entries if they have an "old" sequence number.
93*610bfc6bSMartin Hundebøll  *
94*610bfc6bSMartin Hundebøll  * Caller must hold chain->lock.
95*610bfc6bSMartin Hundebøll  *
96*610bfc6bSMartin Hundebøll  * Returns true if chain is empty and caller can just insert the new fragment
97*610bfc6bSMartin Hundebøll  * without searching for the right position.
98*610bfc6bSMartin Hundebøll  */
99*610bfc6bSMartin Hundebøll static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain,
100*610bfc6bSMartin Hundebøll 				   uint16_t seqno)
101*610bfc6bSMartin Hundebøll {
102*610bfc6bSMartin Hundebøll 	if (chain->seqno == seqno)
103*610bfc6bSMartin Hundebøll 		return false;
104*610bfc6bSMartin Hundebøll 
105*610bfc6bSMartin Hundebøll 	if (!hlist_empty(&chain->head))
106*610bfc6bSMartin Hundebøll 		batadv_frag_clear_chain(&chain->head);
107*610bfc6bSMartin Hundebøll 
108*610bfc6bSMartin Hundebøll 	chain->size = 0;
109*610bfc6bSMartin Hundebøll 	chain->seqno = seqno;
110*610bfc6bSMartin Hundebøll 
111*610bfc6bSMartin Hundebøll 	return true;
112*610bfc6bSMartin Hundebøll }
113*610bfc6bSMartin Hundebøll 
114*610bfc6bSMartin Hundebøll /**
115*610bfc6bSMartin Hundebøll  * batadv_frag_insert_packet - insert a fragment into a fragment chain
116*610bfc6bSMartin Hundebøll  * @orig_node: originator that the fragment was received from
117*610bfc6bSMartin Hundebøll  * @skb: skb to insert
118*610bfc6bSMartin Hundebøll  * @chain_out: list head to attach complete chains of fragments to
119*610bfc6bSMartin Hundebøll  *
120*610bfc6bSMartin Hundebøll  * Insert a new fragment into the reverse ordered chain in the right table
121*610bfc6bSMartin Hundebøll  * entry. The hash table entry is cleared if "old" fragments exist in it.
122*610bfc6bSMartin Hundebøll  *
123*610bfc6bSMartin Hundebøll  * Returns true if skb is buffered, false on error. If the chain has all the
124*610bfc6bSMartin Hundebøll  * fragments needed to merge the packet, the chain is moved to the passed head
125*610bfc6bSMartin Hundebøll  * to avoid locking the chain in the table.
126*610bfc6bSMartin Hundebøll  */
127*610bfc6bSMartin Hundebøll static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
128*610bfc6bSMartin Hundebøll 				      struct sk_buff *skb,
129*610bfc6bSMartin Hundebøll 				      struct hlist_head *chain_out)
130*610bfc6bSMartin Hundebøll {
131*610bfc6bSMartin Hundebøll 	struct batadv_frag_table_entry *chain;
132*610bfc6bSMartin Hundebøll 	struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr;
133*610bfc6bSMartin Hundebøll 	struct batadv_frag_packet *frag_packet;
134*610bfc6bSMartin Hundebøll 	uint8_t bucket;
135*610bfc6bSMartin Hundebøll 	uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet);
136*610bfc6bSMartin Hundebøll 	bool ret = false;
137*610bfc6bSMartin Hundebøll 
138*610bfc6bSMartin Hundebøll 	/* Linearize packet to avoid linearizing 16 packets in a row when doing
139*610bfc6bSMartin Hundebøll 	 * the later merge. Non-linear merge should be added to remove this
140*610bfc6bSMartin Hundebøll 	 * linearization.
141*610bfc6bSMartin Hundebøll 	 */
142*610bfc6bSMartin Hundebøll 	if (skb_linearize(skb) < 0)
143*610bfc6bSMartin Hundebøll 		goto err;
144*610bfc6bSMartin Hundebøll 
145*610bfc6bSMartin Hundebøll 	frag_packet = (struct batadv_frag_packet *)skb->data;
146*610bfc6bSMartin Hundebøll 	seqno = ntohs(frag_packet->seqno);
147*610bfc6bSMartin Hundebøll 	bucket = seqno % BATADV_FRAG_BUFFER_COUNT;
148*610bfc6bSMartin Hundebøll 
149*610bfc6bSMartin Hundebøll 	frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC);
150*610bfc6bSMartin Hundebøll 	if (!frag_entry_new)
151*610bfc6bSMartin Hundebøll 		goto err;
152*610bfc6bSMartin Hundebøll 
153*610bfc6bSMartin Hundebøll 	frag_entry_new->skb = skb;
154*610bfc6bSMartin Hundebøll 	frag_entry_new->no = frag_packet->no;
155*610bfc6bSMartin Hundebøll 
156*610bfc6bSMartin Hundebøll 	/* Select entry in the "chain table" and delete any prior fragments
157*610bfc6bSMartin Hundebøll 	 * with another sequence number. batadv_frag_init_chain() returns true,
158*610bfc6bSMartin Hundebøll 	 * if the list is empty at return.
159*610bfc6bSMartin Hundebøll 	 */
160*610bfc6bSMartin Hundebøll 	chain = &orig_node->fragments[bucket];
161*610bfc6bSMartin Hundebøll 	spin_lock_bh(&chain->lock);
162*610bfc6bSMartin Hundebøll 	if (batadv_frag_init_chain(chain, seqno)) {
163*610bfc6bSMartin Hundebøll 		hlist_add_head(&frag_entry_new->list, &chain->head);
164*610bfc6bSMartin Hundebøll 		chain->size = skb->len - hdr_size;
165*610bfc6bSMartin Hundebøll 		chain->timestamp = jiffies;
166*610bfc6bSMartin Hundebøll 		ret = true;
167*610bfc6bSMartin Hundebøll 		goto out;
168*610bfc6bSMartin Hundebøll 	}
169*610bfc6bSMartin Hundebøll 
170*610bfc6bSMartin Hundebøll 	/* Find the position for the new fragment. */
171*610bfc6bSMartin Hundebøll 	hlist_for_each_entry(frag_entry_curr, &chain->head, list) {
172*610bfc6bSMartin Hundebøll 		/* Drop packet if fragment already exists. */
173*610bfc6bSMartin Hundebøll 		if (frag_entry_curr->no == frag_entry_new->no)
174*610bfc6bSMartin Hundebøll 			goto err_unlock;
175*610bfc6bSMartin Hundebøll 
176*610bfc6bSMartin Hundebøll 		/* Order fragments from highest to lowest. */
177*610bfc6bSMartin Hundebøll 		if (frag_entry_curr->no < frag_entry_new->no) {
178*610bfc6bSMartin Hundebøll 			hlist_add_before(&frag_entry_new->list,
179*610bfc6bSMartin Hundebøll 					 &frag_entry_curr->list);
180*610bfc6bSMartin Hundebøll 			chain->size += skb->len - hdr_size;
181*610bfc6bSMartin Hundebøll 			chain->timestamp = jiffies;
182*610bfc6bSMartin Hundebøll 			ret = true;
183*610bfc6bSMartin Hundebøll 			goto out;
184*610bfc6bSMartin Hundebøll 		}
185*610bfc6bSMartin Hundebøll 	}
186*610bfc6bSMartin Hundebøll 
187*610bfc6bSMartin Hundebøll 	/* Reached the end of the list, so insert after 'frag_entry_curr'. */
188*610bfc6bSMartin Hundebøll 	if (likely(frag_entry_curr)) {
189*610bfc6bSMartin Hundebøll 		hlist_add_after(&frag_entry_curr->list, &frag_entry_new->list);
190*610bfc6bSMartin Hundebøll 		chain->size += skb->len - hdr_size;
191*610bfc6bSMartin Hundebøll 		chain->timestamp = jiffies;
192*610bfc6bSMartin Hundebøll 		ret = true;
193*610bfc6bSMartin Hundebøll 	}
194*610bfc6bSMartin Hundebøll 
195*610bfc6bSMartin Hundebøll out:
196*610bfc6bSMartin Hundebøll 	if (chain->size > batadv_frag_size_limit() ||
197*610bfc6bSMartin Hundebøll 	    ntohs(frag_packet->total_size) > batadv_frag_size_limit()) {
198*610bfc6bSMartin Hundebøll 		/* Clear chain if total size of either the list or the packet
199*610bfc6bSMartin Hundebøll 		 * exceeds the maximum size of one merged packet.
200*610bfc6bSMartin Hundebøll 		 */
201*610bfc6bSMartin Hundebøll 		batadv_frag_clear_chain(&chain->head);
202*610bfc6bSMartin Hundebøll 		chain->size = 0;
203*610bfc6bSMartin Hundebøll 	} else if (ntohs(frag_packet->total_size) == chain->size) {
204*610bfc6bSMartin Hundebøll 		/* All fragments received. Hand over chain to caller. */
205*610bfc6bSMartin Hundebøll 		hlist_move_list(&chain->head, chain_out);
206*610bfc6bSMartin Hundebøll 		chain->size = 0;
207*610bfc6bSMartin Hundebøll 	}
208*610bfc6bSMartin Hundebøll 
209*610bfc6bSMartin Hundebøll err_unlock:
210*610bfc6bSMartin Hundebøll 	spin_unlock_bh(&chain->lock);
211*610bfc6bSMartin Hundebøll 
212*610bfc6bSMartin Hundebøll err:
213*610bfc6bSMartin Hundebøll 	if (!ret)
214*610bfc6bSMartin Hundebøll 		kfree(frag_entry_new);
215*610bfc6bSMartin Hundebøll 
216*610bfc6bSMartin Hundebøll 	return ret;
217*610bfc6bSMartin Hundebøll }
218*610bfc6bSMartin Hundebøll 
219*610bfc6bSMartin Hundebøll /**
220*610bfc6bSMartin Hundebøll  * batadv_frag_merge_packets - merge a chain of fragments
221*610bfc6bSMartin Hundebøll  * @chain: head of chain with fragments
222*610bfc6bSMartin Hundebøll  * @skb: packet with total size of skb after merging
223*610bfc6bSMartin Hundebøll  *
224*610bfc6bSMartin Hundebøll  * Expand the first skb in the chain and copy the content of the remaining
225*610bfc6bSMartin Hundebøll  * skb's into the expanded one. After doing so, clear the chain.
226*610bfc6bSMartin Hundebøll  *
227*610bfc6bSMartin Hundebøll  * Returns the merged skb or NULL on error.
228*610bfc6bSMartin Hundebøll  */
229*610bfc6bSMartin Hundebøll static struct sk_buff *
230*610bfc6bSMartin Hundebøll batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb)
231*610bfc6bSMartin Hundebøll {
232*610bfc6bSMartin Hundebøll 	struct batadv_frag_packet *packet;
233*610bfc6bSMartin Hundebøll 	struct batadv_frag_list_entry *entry;
234*610bfc6bSMartin Hundebøll 	struct sk_buff *skb_out = NULL;
235*610bfc6bSMartin Hundebøll 	int size, hdr_size = sizeof(struct batadv_frag_packet);
236*610bfc6bSMartin Hundebøll 
237*610bfc6bSMartin Hundebøll 	/* Make sure incoming skb has non-bogus data. */
238*610bfc6bSMartin Hundebøll 	packet = (struct batadv_frag_packet *)skb->data;
239*610bfc6bSMartin Hundebøll 	size = ntohs(packet->total_size);
240*610bfc6bSMartin Hundebøll 	if (size > batadv_frag_size_limit())
241*610bfc6bSMartin Hundebøll 		goto free;
242*610bfc6bSMartin Hundebøll 
243*610bfc6bSMartin Hundebøll 	/* Remove first entry, as this is the destination for the rest of the
244*610bfc6bSMartin Hundebøll 	 * fragments.
245*610bfc6bSMartin Hundebøll 	 */
246*610bfc6bSMartin Hundebøll 	entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list);
247*610bfc6bSMartin Hundebøll 	hlist_del(&entry->list);
248*610bfc6bSMartin Hundebøll 	skb_out = entry->skb;
249*610bfc6bSMartin Hundebøll 	kfree(entry);
250*610bfc6bSMartin Hundebøll 
251*610bfc6bSMartin Hundebøll 	/* Make room for the rest of the fragments. */
252*610bfc6bSMartin Hundebøll 	if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) {
253*610bfc6bSMartin Hundebøll 		kfree_skb(skb_out);
254*610bfc6bSMartin Hundebøll 		skb_out = NULL;
255*610bfc6bSMartin Hundebøll 		goto free;
256*610bfc6bSMartin Hundebøll 	}
257*610bfc6bSMartin Hundebøll 
258*610bfc6bSMartin Hundebøll 	/* Move the existing MAC header to just before the payload. (Override
259*610bfc6bSMartin Hundebøll 	 * the fragment header.)
260*610bfc6bSMartin Hundebøll 	 */
261*610bfc6bSMartin Hundebøll 	skb_pull_rcsum(skb_out, hdr_size);
262*610bfc6bSMartin Hundebøll 	memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN);
263*610bfc6bSMartin Hundebøll 	skb_set_mac_header(skb_out, -ETH_HLEN);
264*610bfc6bSMartin Hundebøll 	skb_reset_network_header(skb_out);
265*610bfc6bSMartin Hundebøll 	skb_reset_transport_header(skb_out);
266*610bfc6bSMartin Hundebøll 
267*610bfc6bSMartin Hundebøll 	/* Copy the payload of the each fragment into the last skb */
268*610bfc6bSMartin Hundebøll 	hlist_for_each_entry(entry, chain, list) {
269*610bfc6bSMartin Hundebøll 		size = entry->skb->len - hdr_size;
270*610bfc6bSMartin Hundebøll 		memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size,
271*610bfc6bSMartin Hundebøll 		       size);
272*610bfc6bSMartin Hundebøll 	}
273*610bfc6bSMartin Hundebøll 
274*610bfc6bSMartin Hundebøll free:
275*610bfc6bSMartin Hundebøll 	/* Locking is not needed, because 'chain' is not part of any orig. */
276*610bfc6bSMartin Hundebøll 	batadv_frag_clear_chain(chain);
277*610bfc6bSMartin Hundebøll 	return skb_out;
278*610bfc6bSMartin Hundebøll }
279*610bfc6bSMartin Hundebøll 
280*610bfc6bSMartin Hundebøll /**
281*610bfc6bSMartin Hundebøll  * batadv_frag_skb_buffer - buffer fragment for later merge
282*610bfc6bSMartin Hundebøll  * @skb: skb to buffer
283*610bfc6bSMartin Hundebøll  * @orig_node_src: originator that the skb is received from
284*610bfc6bSMartin Hundebøll  *
285*610bfc6bSMartin Hundebøll  * Add fragment to buffer and merge fragments if possible.
286*610bfc6bSMartin Hundebøll  *
287*610bfc6bSMartin Hundebøll  * There are three possible outcomes: 1) Packet is merged: Return true and
288*610bfc6bSMartin Hundebøll  * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb
289*610bfc6bSMartin Hundebøll  * to NULL; 3) Error: Return false and leave skb as is.
290*610bfc6bSMartin Hundebøll  */
291*610bfc6bSMartin Hundebøll bool batadv_frag_skb_buffer(struct sk_buff **skb,
292*610bfc6bSMartin Hundebøll 			    struct batadv_orig_node *orig_node_src)
293*610bfc6bSMartin Hundebøll {
294*610bfc6bSMartin Hundebøll 	struct sk_buff *skb_out = NULL;
295*610bfc6bSMartin Hundebøll 	struct hlist_head head = HLIST_HEAD_INIT;
296*610bfc6bSMartin Hundebøll 	bool ret = false;
297*610bfc6bSMartin Hundebøll 
298*610bfc6bSMartin Hundebøll 	/* Add packet to buffer and table entry if merge is possible. */
299*610bfc6bSMartin Hundebøll 	if (!batadv_frag_insert_packet(orig_node_src, *skb, &head))
300*610bfc6bSMartin Hundebøll 		goto out_err;
301*610bfc6bSMartin Hundebøll 
302*610bfc6bSMartin Hundebøll 	/* Leave if more fragments are needed to merge. */
303*610bfc6bSMartin Hundebøll 	if (hlist_empty(&head))
304*610bfc6bSMartin Hundebøll 		goto out;
305*610bfc6bSMartin Hundebøll 
306*610bfc6bSMartin Hundebøll 	skb_out = batadv_frag_merge_packets(&head, *skb);
307*610bfc6bSMartin Hundebøll 	if (!skb_out)
308*610bfc6bSMartin Hundebøll 		goto out_err;
309*610bfc6bSMartin Hundebøll 
310*610bfc6bSMartin Hundebøll out:
311*610bfc6bSMartin Hundebøll 	*skb = skb_out;
312*610bfc6bSMartin Hundebøll 	ret = true;
313*610bfc6bSMartin Hundebøll out_err:
314*610bfc6bSMartin Hundebøll 	return ret;
315*610bfc6bSMartin Hundebøll }
316*610bfc6bSMartin Hundebøll 
317*610bfc6bSMartin Hundebøll /**
318*610bfc6bSMartin Hundebøll  * batadv_frag_skb_fwd - forward fragments that would exceed MTU when merged
319*610bfc6bSMartin Hundebøll  * @skb: skb to forward
320*610bfc6bSMartin Hundebøll  * @recv_if: interface that the skb is received on
321*610bfc6bSMartin Hundebøll  * @orig_node_src: originator that the skb is received from
322*610bfc6bSMartin Hundebøll  *
323*610bfc6bSMartin Hundebøll  * Look up the next-hop of the fragments payload and check if the merged packet
324*610bfc6bSMartin Hundebøll  * will exceed the MTU towards the next-hop. If so, the fragment is forwarded
325*610bfc6bSMartin Hundebøll  * without merging it.
326*610bfc6bSMartin Hundebøll  *
327*610bfc6bSMartin Hundebøll  * Returns true if the fragment is consumed/forwarded, false otherwise.
328*610bfc6bSMartin Hundebøll  */
329*610bfc6bSMartin Hundebøll bool batadv_frag_skb_fwd(struct sk_buff *skb,
330*610bfc6bSMartin Hundebøll 			 struct batadv_hard_iface *recv_if,
331*610bfc6bSMartin Hundebøll 			 struct batadv_orig_node *orig_node_src)
332*610bfc6bSMartin Hundebøll {
333*610bfc6bSMartin Hundebøll 	struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
334*610bfc6bSMartin Hundebøll 	struct batadv_orig_node *orig_node_dst = NULL;
335*610bfc6bSMartin Hundebøll 	struct batadv_neigh_node *neigh_node = NULL;
336*610bfc6bSMartin Hundebøll 	struct batadv_frag_packet *packet;
337*610bfc6bSMartin Hundebøll 	uint16_t total_size;
338*610bfc6bSMartin Hundebøll 	bool ret = false;
339*610bfc6bSMartin Hundebøll 
340*610bfc6bSMartin Hundebøll 	packet = (struct batadv_frag_packet *)skb->data;
341*610bfc6bSMartin Hundebøll 	orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest);
342*610bfc6bSMartin Hundebøll 	if (!orig_node_dst)
343*610bfc6bSMartin Hundebøll 		goto out;
344*610bfc6bSMartin Hundebøll 
345*610bfc6bSMartin Hundebøll 	neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if);
346*610bfc6bSMartin Hundebøll 	if (!neigh_node)
347*610bfc6bSMartin Hundebøll 		goto out;
348*610bfc6bSMartin Hundebøll 
349*610bfc6bSMartin Hundebøll 	/* Forward the fragment, if the merged packet would be too big to
350*610bfc6bSMartin Hundebøll 	 * be assembled.
351*610bfc6bSMartin Hundebøll 	 */
352*610bfc6bSMartin Hundebøll 	total_size = ntohs(packet->total_size);
353*610bfc6bSMartin Hundebøll 	if (total_size > neigh_node->if_incoming->net_dev->mtu) {
354*610bfc6bSMartin Hundebøll 		batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD);
355*610bfc6bSMartin Hundebøll 		batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES,
356*610bfc6bSMartin Hundebøll 				   skb->len + ETH_HLEN);
357*610bfc6bSMartin Hundebøll 
358*610bfc6bSMartin Hundebøll 		packet->header.ttl--;
359*610bfc6bSMartin Hundebøll 		batadv_send_skb_packet(skb, neigh_node->if_incoming,
360*610bfc6bSMartin Hundebøll 				       neigh_node->addr);
361*610bfc6bSMartin Hundebøll 		ret = true;
362*610bfc6bSMartin Hundebøll 	}
363*610bfc6bSMartin Hundebøll 
364*610bfc6bSMartin Hundebøll out:
365*610bfc6bSMartin Hundebøll 	if (orig_node_dst)
366*610bfc6bSMartin Hundebøll 		batadv_orig_node_free_ref(orig_node_dst);
367*610bfc6bSMartin Hundebøll 	if (neigh_node)
368*610bfc6bSMartin Hundebøll 		batadv_neigh_node_free_ref(neigh_node);
369*610bfc6bSMartin Hundebøll 	return ret;
370*610bfc6bSMartin Hundebøll }
371