xref: /openbmc/linux/net/tipc/msg.c (revision e0d77d0f38aa60ca61b3ce6e60d64fad2aa0853d)
1b97bf3fdSPer Liden /*
2b97bf3fdSPer Liden  * net/tipc/msg.c: TIPC message header routines
3b97bf3fdSPer Liden  *
4cf2157f8SJon Paul Maloy  * Copyright (c) 2000-2006, 2014-2015, Ericsson AB
5741de3e9SAllan Stephens  * Copyright (c) 2005, 2010-2011, Wind River Systems
6b97bf3fdSPer Liden  * All rights reserved.
7b97bf3fdSPer Liden  *
8b97bf3fdSPer Liden  * Redistribution and use in source and binary forms, with or without
9b97bf3fdSPer Liden  * modification, are permitted provided that the following conditions are met:
10b97bf3fdSPer Liden  *
119ea1fd3cSPer Liden  * 1. Redistributions of source code must retain the above copyright
129ea1fd3cSPer Liden  *    notice, this list of conditions and the following disclaimer.
139ea1fd3cSPer Liden  * 2. Redistributions in binary form must reproduce the above copyright
149ea1fd3cSPer Liden  *    notice, this list of conditions and the following disclaimer in the
159ea1fd3cSPer Liden  *    documentation and/or other materials provided with the distribution.
169ea1fd3cSPer Liden  * 3. Neither the names of the copyright holders nor the names of its
179ea1fd3cSPer Liden  *    contributors may be used to endorse or promote products derived from
189ea1fd3cSPer Liden  *    this software without specific prior written permission.
199ea1fd3cSPer Liden  *
209ea1fd3cSPer Liden  * Alternatively, this software may be distributed under the terms of the
219ea1fd3cSPer Liden  * GNU General Public License ("GPL") version 2 as published by the Free
229ea1fd3cSPer Liden  * Software Foundation.
23b97bf3fdSPer Liden  *
24b97bf3fdSPer Liden  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25b97bf3fdSPer Liden  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26b97bf3fdSPer Liden  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27b97bf3fdSPer Liden  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28b97bf3fdSPer Liden  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29b97bf3fdSPer Liden  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30b97bf3fdSPer Liden  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31b97bf3fdSPer Liden  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32b97bf3fdSPer Liden  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33b97bf3fdSPer Liden  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34b97bf3fdSPer Liden  * POSSIBILITY OF SUCH DAMAGE.
35b97bf3fdSPer Liden  */
36b97bf3fdSPer Liden 
37c93d3baaSYing Xue #include <net/sock.h>
38b97bf3fdSPer Liden #include "core.h"
39b97bf3fdSPer Liden #include "msg.h"
405a379074SJon Paul Maloy #include "addr.h"
415a379074SJon Paul Maloy #include "name_table.h"
42fc1b6d6dSTuong Lien #include "crypto.h"
43b97bf3fdSPer Liden 
44d4cfb7feSMenglong Dong #define BUF_ALIGN(x) ALIGN(x, 4)
458db1bae3SJon Paul Maloy #define MAX_FORWARD_SIZE 1024
46fc1b6d6dSTuong Lien #ifdef CONFIG_TIPC_CRYPTO
47fc1b6d6dSTuong Lien #define BUF_HEADROOM ALIGN(((LL_MAX_HEADER + 48) + EHDR_MAX_SIZE), 16)
480c6de0c9SMenglong Dong #define BUF_OVERHEAD (BUF_HEADROOM + TIPC_AES_GCM_TAG_SIZE)
49fc1b6d6dSTuong Lien #else
5027777daaSJon Paul Maloy #define BUF_HEADROOM (LL_MAX_HEADER + 48)
510c6de0c9SMenglong Dong #define BUF_OVERHEAD BUF_HEADROOM
52fc1b6d6dSTuong Lien #endif
538db1bae3SJon Paul Maloy 
540c6de0c9SMenglong Dong const int one_page_mtu = PAGE_SIZE - SKB_DATA_ALIGN(BUF_OVERHEAD) -
550c6de0c9SMenglong Dong 			 SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
560c6de0c9SMenglong Dong 
57859fc7c0SYing Xue /**
58859fc7c0SYing Xue  * tipc_buf_acquire - creates a TIPC message buffer
59859fc7c0SYing Xue  * @size: message size (including TIPC header)
605fcb7d47SRandy Dunlap  * @gfp: memory allocation flags
61859fc7c0SYing Xue  *
62637b77fdSRandy Dunlap  * Return: a new buffer with data pointers set to the specified size.
63859fc7c0SYing Xue  *
645fcb7d47SRandy Dunlap  * NOTE:
655fcb7d47SRandy Dunlap  * Headroom is reserved to allow prepending of a data link header.
66859fc7c0SYing Xue  * There may also be unrequested tailroom present at the buffer's end.
67859fc7c0SYing Xue  */
tipc_buf_acquire(u32 size,gfp_t gfp)6857d5f64dSParthasarathy Bhuvaragan struct sk_buff *tipc_buf_acquire(u32 size, gfp_t gfp)
69859fc7c0SYing Xue {
70859fc7c0SYing Xue 	struct sk_buff *skb;
71859fc7c0SYing Xue 
720c6de0c9SMenglong Dong 	skb = alloc_skb_fclone(BUF_OVERHEAD + size, gfp);
73859fc7c0SYing Xue 	if (skb) {
74859fc7c0SYing Xue 		skb_reserve(skb, BUF_HEADROOM);
75859fc7c0SYing Xue 		skb_put(skb, size);
76859fc7c0SYing Xue 		skb->next = NULL;
77859fc7c0SYing Xue 	}
78859fc7c0SYing Xue 	return skb;
79859fc7c0SYing Xue }
80859fc7c0SYing Xue 
tipc_msg_init(u32 own_node,struct tipc_msg * m,u32 user,u32 type,u32 hsize,u32 dnode)81c5898636SJon Paul Maloy void tipc_msg_init(u32 own_node, struct tipc_msg *m, u32 user, u32 type,
82c5898636SJon Paul Maloy 		   u32 hsize, u32 dnode)
8323461e83SAllan Stephens {
8423461e83SAllan Stephens 	memset(m, 0, hsize);
8523461e83SAllan Stephens 	msg_set_version(m);
8623461e83SAllan Stephens 	msg_set_user(m, user);
8723461e83SAllan Stephens 	msg_set_hdr_sz(m, hsize);
8823461e83SAllan Stephens 	msg_set_size(m, hsize);
89c5898636SJon Paul Maloy 	msg_set_prevnode(m, own_node);
9023461e83SAllan Stephens 	msg_set_type(m, type);
911dd0bd2bSJon Paul Maloy 	if (hsize > SHORT_H_SIZE) {
92c5898636SJon Paul Maloy 		msg_set_orignode(m, own_node);
93c5898636SJon Paul Maloy 		msg_set_destnode(m, dnode);
9423461e83SAllan Stephens 	}
951dd0bd2bSJon Paul Maloy }
961dd0bd2bSJon Paul Maloy 
tipc_msg_create(uint user,uint type,uint hdr_sz,uint data_sz,u32 dnode,u32 onode,u32 dport,u32 oport,int errcode)97c5898636SJon Paul Maloy struct sk_buff *tipc_msg_create(uint user, uint type,
9834747539SYing Xue 				uint hdr_sz, uint data_sz, u32 dnode,
9934747539SYing Xue 				u32 onode, u32 dport, u32 oport, int errcode)
1001dd0bd2bSJon Paul Maloy {
1011dd0bd2bSJon Paul Maloy 	struct tipc_msg *msg;
1021dd0bd2bSJon Paul Maloy 	struct sk_buff *buf;
1031dd0bd2bSJon Paul Maloy 
10457d5f64dSParthasarathy Bhuvaragan 	buf = tipc_buf_acquire(hdr_sz + data_sz, GFP_ATOMIC);
1051dd0bd2bSJon Paul Maloy 	if (unlikely(!buf))
1061dd0bd2bSJon Paul Maloy 		return NULL;
1071dd0bd2bSJon Paul Maloy 
1081dd0bd2bSJon Paul Maloy 	msg = buf_msg(buf);
109c5898636SJon Paul Maloy 	tipc_msg_init(onode, msg, user, type, hdr_sz, dnode);
1101dd0bd2bSJon Paul Maloy 	msg_set_size(msg, hdr_sz + data_sz);
1111dd0bd2bSJon Paul Maloy 	msg_set_origport(msg, oport);
1121dd0bd2bSJon Paul Maloy 	msg_set_destport(msg, dport);
1131dd0bd2bSJon Paul Maloy 	msg_set_errcode(msg, errcode);
1141dd0bd2bSJon Paul Maloy 	return buf;
1151dd0bd2bSJon Paul Maloy }
11623461e83SAllan Stephens 
11737e22164SJon Paul Maloy /* tipc_buf_append(): Append a buffer to the fragment list of another buffer
11829322d0dSJon Paul Maloy  * @*headbuf: in:  NULL for first frag, otherwise value returned from prev call
11929322d0dSJon Paul Maloy  *            out: set when successful non-complete reassembly, otherwise NULL
12029322d0dSJon Paul Maloy  * @*buf:     in:  the buffer to append. Always defined
121b2ad5e5fSstephen hemminger  *            out: head buf after successful complete reassembly, otherwise NULL
12229322d0dSJon Paul Maloy  * Returns 1 when reassembly complete, otherwise 0
12337e22164SJon Paul Maloy  */
tipc_buf_append(struct sk_buff ** headbuf,struct sk_buff ** buf)12437e22164SJon Paul Maloy int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
12537e22164SJon Paul Maloy {
12637e22164SJon Paul Maloy 	struct sk_buff *head = *headbuf;
12737e22164SJon Paul Maloy 	struct sk_buff *frag = *buf;
12845c8b7b1SJon Paul Maloy 	struct sk_buff *tail = NULL;
12913e9b997SJon Paul Maloy 	struct tipc_msg *msg;
13013e9b997SJon Paul Maloy 	u32 fragid;
13137e22164SJon Paul Maloy 	int delta;
13213e9b997SJon Paul Maloy 	bool headstolen;
13337e22164SJon Paul Maloy 
13413e9b997SJon Paul Maloy 	if (!frag)
13513e9b997SJon Paul Maloy 		goto err;
13613e9b997SJon Paul Maloy 
13713e9b997SJon Paul Maloy 	msg = buf_msg(frag);
13813e9b997SJon Paul Maloy 	fragid = msg_type(msg);
13913e9b997SJon Paul Maloy 	frag->next = NULL;
14037e22164SJon Paul Maloy 	skb_pull(frag, msg_hdr_sz(msg));
14137e22164SJon Paul Maloy 
14237e22164SJon Paul Maloy 	if (fragid == FIRST_FRAGMENT) {
14313e9b997SJon Paul Maloy 		if (unlikely(head))
14413e9b997SJon Paul Maloy 			goto err;
145b7df21cfSXin Long 		if (skb_has_frag_list(frag) && __skb_linearize(frag))
146b7df21cfSXin Long 			goto err;
147614c5a5aSXin Long 		*buf = NULL;
148ceb1eb2fSTung Nguyen 		frag = skb_unshare(frag, GFP_ATOMIC);
149ff48b622SXin Long 		if (unlikely(!frag))
15013e9b997SJon Paul Maloy 			goto err;
15137e22164SJon Paul Maloy 		head = *headbuf = frag;
15245c8b7b1SJon Paul Maloy 		TIPC_SKB_CB(head)->tail = NULL;
15337e22164SJon Paul Maloy 		return 0;
15437e22164SJon Paul Maloy 	}
15513e9b997SJon Paul Maloy 
15637e22164SJon Paul Maloy 	if (!head)
15713e9b997SJon Paul Maloy 		goto err;
15813e9b997SJon Paul Maloy 
159*ffd4917cSPaolo Abeni 	/* Either the input skb ownership is transferred to headskb
160*ffd4917cSPaolo Abeni 	 * or the input skb is freed, clear the reference to avoid
161*ffd4917cSPaolo Abeni 	 * bad access on error path.
162*ffd4917cSPaolo Abeni 	 */
163*ffd4917cSPaolo Abeni 	*buf = NULL;
16437e22164SJon Paul Maloy 	if (skb_try_coalesce(head, frag, &headstolen, &delta)) {
16537e22164SJon Paul Maloy 		kfree_skb_partial(frag, headstolen);
16637e22164SJon Paul Maloy 	} else {
16713e9b997SJon Paul Maloy 		tail = TIPC_SKB_CB(head)->tail;
16837e22164SJon Paul Maloy 		if (!skb_has_frag_list(head))
16937e22164SJon Paul Maloy 			skb_shinfo(head)->frag_list = frag;
17037e22164SJon Paul Maloy 		else
17137e22164SJon Paul Maloy 			tail->next = frag;
17237e22164SJon Paul Maloy 		head->truesize += frag->truesize;
17337e22164SJon Paul Maloy 		head->data_len += frag->len;
17437e22164SJon Paul Maloy 		head->len += frag->len;
17537e22164SJon Paul Maloy 		TIPC_SKB_CB(head)->tail = frag;
17637e22164SJon Paul Maloy 	}
17713e9b997SJon Paul Maloy 
17837e22164SJon Paul Maloy 	if (fragid == LAST_FRAGMENT) {
179fc1b6d6dSTuong Lien 		TIPC_SKB_CB(head)->validated = 0;
180d618d09aSJon Maloy 		if (unlikely(!tipc_msg_validate(&head)))
1811149557dSJon Paul Maloy 			goto err;
18237e22164SJon Paul Maloy 		*buf = head;
18337e22164SJon Paul Maloy 		TIPC_SKB_CB(head)->tail = NULL;
18437e22164SJon Paul Maloy 		*headbuf = NULL;
18537e22164SJon Paul Maloy 		return 1;
18637e22164SJon Paul Maloy 	}
18737e22164SJon Paul Maloy 	return 0;
18813e9b997SJon Paul Maloy err:
18937e22164SJon Paul Maloy 	kfree_skb(*buf);
19029322d0dSJon Paul Maloy 	kfree_skb(*headbuf);
19129322d0dSJon Paul Maloy 	*buf = *headbuf = NULL;
19237e22164SJon Paul Maloy 	return 0;
19337e22164SJon Paul Maloy }
1944f1688b2SJon Paul Maloy 
195c0bceb97SJon Maloy /**
196c0bceb97SJon Maloy  * tipc_msg_append(): Append data to tail of an existing buffer queue
197d8141208SAndrew Lunn  * @_hdr: header to be used
198c0bceb97SJon Maloy  * @m: the data to be appended
199c0bceb97SJon Maloy  * @mss: max allowable size of buffer
200c0bceb97SJon Maloy  * @dlen: size of data to be appended
201637b77fdSRandy Dunlap  * @txq: queue to append to
202637b77fdSRandy Dunlap  *
203637b77fdSRandy Dunlap  * Return: the number of 1k blocks appended or errno value
204c0bceb97SJon Maloy  */
tipc_msg_append(struct tipc_msg * _hdr,struct msghdr * m,int dlen,int mss,struct sk_buff_head * txq)205c0bceb97SJon Maloy int tipc_msg_append(struct tipc_msg *_hdr, struct msghdr *m, int dlen,
206c0bceb97SJon Maloy 		    int mss, struct sk_buff_head *txq)
207c0bceb97SJon Maloy {
2088298a419SYueHaibing 	struct sk_buff *skb;
209c0bceb97SJon Maloy 	int accounted, total, curr;
210c0bceb97SJon Maloy 	int mlen, cpy, rem = dlen;
211c0bceb97SJon Maloy 	struct tipc_msg *hdr;
212c0bceb97SJon Maloy 
213c0bceb97SJon Maloy 	skb = skb_peek_tail(txq);
214c0bceb97SJon Maloy 	accounted = skb ? msg_blocks(buf_msg(skb)) : 0;
215c0bceb97SJon Maloy 	total = accounted;
216c0bceb97SJon Maloy 
2175e9eecccSTuong Lien 	do {
218c0bceb97SJon Maloy 		if (!skb || skb->len >= mss) {
219c0bceb97SJon Maloy 			skb = tipc_buf_acquire(mss, GFP_KERNEL);
220c0bceb97SJon Maloy 			if (unlikely(!skb))
221c0bceb97SJon Maloy 				return -ENOMEM;
222c0bceb97SJon Maloy 			skb_orphan(skb);
223c0bceb97SJon Maloy 			skb_trim(skb, MIN_H_SIZE);
224c0bceb97SJon Maloy 			hdr = buf_msg(skb);
225c0bceb97SJon Maloy 			skb_copy_to_linear_data(skb, _hdr, MIN_H_SIZE);
226c0bceb97SJon Maloy 			msg_set_hdr_sz(hdr, MIN_H_SIZE);
227c0bceb97SJon Maloy 			msg_set_size(hdr, MIN_H_SIZE);
228c0bceb97SJon Maloy 			__skb_queue_tail(txq, skb);
229c0bceb97SJon Maloy 			total += 1;
230c0bceb97SJon Maloy 		}
231c0bceb97SJon Maloy 		hdr = buf_msg(skb);
232c0bceb97SJon Maloy 		curr = msg_blocks(hdr);
233c0bceb97SJon Maloy 		mlen = msg_size(hdr);
234c9aa81faSTuong Lien 		cpy = min_t(size_t, rem, mss - mlen);
235c0bceb97SJon Maloy 		if (cpy != copy_from_iter(skb->data + mlen, cpy, &m->msg_iter))
236c0bceb97SJon Maloy 			return -EFAULT;
237c0bceb97SJon Maloy 		msg_set_size(hdr, mlen + cpy);
238c0bceb97SJon Maloy 		skb_put(skb, cpy);
239c0bceb97SJon Maloy 		rem -= cpy;
240c0bceb97SJon Maloy 		total += msg_blocks(hdr) - curr;
241c9aa81faSTuong Lien 	} while (rem > 0);
242c0bceb97SJon Maloy 	return total - accounted;
243c0bceb97SJon Maloy }
244c0bceb97SJon Maloy 
245cf2157f8SJon Paul Maloy /* tipc_msg_validate - validate basic format of received message
246cf2157f8SJon Paul Maloy  *
247cf2157f8SJon Paul Maloy  * This routine ensures a TIPC message has an acceptable header, and at least
248cf2157f8SJon Paul Maloy  * as much data as the header indicates it should.  The routine also ensures
249cf2157f8SJon Paul Maloy  * that the entire message header is stored in the main fragment of the message
250cf2157f8SJon Paul Maloy  * buffer, to simplify future access to message header fields.
251cf2157f8SJon Paul Maloy  *
252cf2157f8SJon Paul Maloy  * Note: Having extra info present in the message header or data areas is OK.
253cf2157f8SJon Paul Maloy  * TIPC will ignore the excess, under the assumption that it is optional info
254cf2157f8SJon Paul Maloy  * introduced by a later release of the protocol.
255cf2157f8SJon Paul Maloy  */
tipc_msg_validate(struct sk_buff ** _skb)256d618d09aSJon Maloy bool tipc_msg_validate(struct sk_buff **_skb)
257cf2157f8SJon Paul Maloy {
258d618d09aSJon Maloy 	struct sk_buff *skb = *_skb;
259d618d09aSJon Maloy 	struct tipc_msg *hdr;
260cf2157f8SJon Paul Maloy 	int msz, hsz;
261cf2157f8SJon Paul Maloy 
262d618d09aSJon Maloy 	/* Ensure that flow control ratio condition is satisfied */
26355b3280dSHoang Le 	if (unlikely(skb->truesize / buf_roundup_len(skb) >= 4)) {
26455b3280dSHoang Le 		skb = skb_copy_expand(skb, BUF_HEADROOM, 0, GFP_ATOMIC);
265d618d09aSJon Maloy 		if (!skb)
266d618d09aSJon Maloy 			return false;
267d618d09aSJon Maloy 		kfree_skb(*_skb);
268d618d09aSJon Maloy 		*_skb = skb;
269d618d09aSJon Maloy 	}
270d618d09aSJon Maloy 
271cf2157f8SJon Paul Maloy 	if (unlikely(TIPC_SKB_CB(skb)->validated))
272cf2157f8SJon Paul Maloy 		return true;
273fc1b6d6dSTuong Lien 
274cf2157f8SJon Paul Maloy 	if (unlikely(!pskb_may_pull(skb, MIN_H_SIZE)))
275cf2157f8SJon Paul Maloy 		return false;
276cf2157f8SJon Paul Maloy 
277cf2157f8SJon Paul Maloy 	hsz = msg_hdr_sz(buf_msg(skb));
278cf2157f8SJon Paul Maloy 	if (unlikely(hsz < MIN_H_SIZE) || (hsz > MAX_H_SIZE))
279cf2157f8SJon Paul Maloy 		return false;
280cf2157f8SJon Paul Maloy 	if (unlikely(!pskb_may_pull(skb, hsz)))
281cf2157f8SJon Paul Maloy 		return false;
282cf2157f8SJon Paul Maloy 
283d618d09aSJon Maloy 	hdr = buf_msg(skb);
284d618d09aSJon Maloy 	if (unlikely(msg_version(hdr) != TIPC_VERSION))
285cf2157f8SJon Paul Maloy 		return false;
286cf2157f8SJon Paul Maloy 
287d618d09aSJon Maloy 	msz = msg_size(hdr);
288cf2157f8SJon Paul Maloy 	if (unlikely(msz < hsz))
289cf2157f8SJon Paul Maloy 		return false;
290cf2157f8SJon Paul Maloy 	if (unlikely((msz - hsz) > TIPC_MAX_USER_MSG_SIZE))
291cf2157f8SJon Paul Maloy 		return false;
292cf2157f8SJon Paul Maloy 	if (unlikely(skb->len < msz))
293cf2157f8SJon Paul Maloy 		return false;
294cf2157f8SJon Paul Maloy 
295fc1b6d6dSTuong Lien 	TIPC_SKB_CB(skb)->validated = 1;
296cf2157f8SJon Paul Maloy 	return true;
297cf2157f8SJon Paul Maloy }
298067608e9SJon Paul Maloy 
299067608e9SJon Paul Maloy /**
3002320bcdaSTuong Lien  * tipc_msg_fragment - build a fragment skb list for TIPC message
3012320bcdaSTuong Lien  *
3022320bcdaSTuong Lien  * @skb: TIPC message skb
3032320bcdaSTuong Lien  * @hdr: internal msg header to be put on the top of the fragments
3042320bcdaSTuong Lien  * @pktmax: max size of a fragment incl. the header
3052320bcdaSTuong Lien  * @frags: returned fragment skb list
3062320bcdaSTuong Lien  *
307637b77fdSRandy Dunlap  * Return: 0 if the fragmentation is successful, otherwise: -EINVAL
3082320bcdaSTuong Lien  * or -ENOMEM
3092320bcdaSTuong Lien  */
tipc_msg_fragment(struct sk_buff * skb,const struct tipc_msg * hdr,int pktmax,struct sk_buff_head * frags)3102320bcdaSTuong Lien int tipc_msg_fragment(struct sk_buff *skb, const struct tipc_msg *hdr,
3112320bcdaSTuong Lien 		      int pktmax, struct sk_buff_head *frags)
3122320bcdaSTuong Lien {
3132320bcdaSTuong Lien 	int pktno, nof_fragms, dsz, dmax, eat;
3142320bcdaSTuong Lien 	struct tipc_msg *_hdr;
3152320bcdaSTuong Lien 	struct sk_buff *_skb;
3162320bcdaSTuong Lien 	u8 *data;
3172320bcdaSTuong Lien 
3182320bcdaSTuong Lien 	/* Non-linear buffer? */
3192320bcdaSTuong Lien 	if (skb_linearize(skb))
3202320bcdaSTuong Lien 		return -ENOMEM;
3212320bcdaSTuong Lien 
3222320bcdaSTuong Lien 	data = (u8 *)skb->data;
3232320bcdaSTuong Lien 	dsz = msg_size(buf_msg(skb));
3242320bcdaSTuong Lien 	dmax = pktmax - INT_H_SIZE;
3252320bcdaSTuong Lien 	if (dsz <= dmax || !dmax)
3262320bcdaSTuong Lien 		return -EINVAL;
3272320bcdaSTuong Lien 
3282320bcdaSTuong Lien 	nof_fragms = dsz / dmax + 1;
3292320bcdaSTuong Lien 	for (pktno = 1; pktno <= nof_fragms; pktno++) {
3302320bcdaSTuong Lien 		if (pktno < nof_fragms)
3312320bcdaSTuong Lien 			eat = dmax;
3322320bcdaSTuong Lien 		else
3332320bcdaSTuong Lien 			eat = dsz % dmax;
3342320bcdaSTuong Lien 		/* Allocate a new fragment */
3352320bcdaSTuong Lien 		_skb = tipc_buf_acquire(INT_H_SIZE + eat, GFP_ATOMIC);
3362320bcdaSTuong Lien 		if (!_skb)
3372320bcdaSTuong Lien 			goto error;
3382320bcdaSTuong Lien 		skb_orphan(_skb);
3392320bcdaSTuong Lien 		__skb_queue_tail(frags, _skb);
3402320bcdaSTuong Lien 		/* Copy header & data to the fragment */
3412320bcdaSTuong Lien 		skb_copy_to_linear_data(_skb, hdr, INT_H_SIZE);
3422320bcdaSTuong Lien 		skb_copy_to_linear_data_offset(_skb, INT_H_SIZE, data, eat);
3432320bcdaSTuong Lien 		data += eat;
3442320bcdaSTuong Lien 		/* Update the fragment's header */
3452320bcdaSTuong Lien 		_hdr = buf_msg(_skb);
3462320bcdaSTuong Lien 		msg_set_fragm_no(_hdr, pktno);
3472320bcdaSTuong Lien 		msg_set_nof_fragms(_hdr, nof_fragms);
3482320bcdaSTuong Lien 		msg_set_size(_hdr, INT_H_SIZE + eat);
3492320bcdaSTuong Lien 	}
3502320bcdaSTuong Lien 	return 0;
3512320bcdaSTuong Lien 
3522320bcdaSTuong Lien error:
3532320bcdaSTuong Lien 	__skb_queue_purge(frags);
3542320bcdaSTuong Lien 	__skb_queue_head_init(frags);
3552320bcdaSTuong Lien 	return -ENOMEM;
3562320bcdaSTuong Lien }
3572320bcdaSTuong Lien 
3582320bcdaSTuong Lien /**
3599fbfb8b1SJon Paul Maloy  * tipc_msg_build - create buffer chain containing specified header and data
360067608e9SJon Paul Maloy  * @mhdr: Message header, to be prepended to data
36145dcc687SAl Viro  * @m: User message
3625fcb7d47SRandy Dunlap  * @offset: buffer offset for fragmented messages (FIXME)
363067608e9SJon Paul Maloy  * @dsz: Total length of user data
364067608e9SJon Paul Maloy  * @pktmax: Max packet size that can be used
365a6ca1094SYing Xue  * @list: Buffer or chain of buffers to be returned to caller
366a6ca1094SYing Xue  *
3674c94cc2dSJon Maloy  * Note that the recursive call we are making here is safe, since it can
3684c94cc2dSJon Maloy  * logically go only one further level down.
3694c94cc2dSJon Maloy  *
370637b77fdSRandy Dunlap  * Return: message data size or errno: -ENOMEM, -EFAULT
371067608e9SJon Paul Maloy  */
tipc_msg_build(struct tipc_msg * mhdr,struct msghdr * m,int offset,int dsz,int pktmax,struct sk_buff_head * list)3724c94cc2dSJon Maloy int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,
3734c94cc2dSJon Maloy 		   int dsz, int pktmax, struct sk_buff_head *list)
374067608e9SJon Paul Maloy {
375067608e9SJon Paul Maloy 	int mhsz = msg_hdr_sz(mhdr);
376067608e9SJon Paul Maloy 	struct tipc_msg pkthdr;
3774c94cc2dSJon Maloy 	int msz = mhsz + dsz;
3784c94cc2dSJon Maloy 	int pktrem = pktmax;
379a6ca1094SYing Xue 	struct sk_buff *skb;
3804c94cc2dSJon Maloy 	int drem = dsz;
3814c94cc2dSJon Maloy 	int pktno = 1;
382067608e9SJon Paul Maloy 	char *pktpos;
3834c94cc2dSJon Maloy 	int pktsz;
384067608e9SJon Paul Maloy 	int rc;
385a6ca1094SYing Xue 
386067608e9SJon Paul Maloy 	msg_set_size(mhdr, msz);
387067608e9SJon Paul Maloy 
388067608e9SJon Paul Maloy 	/* No fragmentation needed? */
389067608e9SJon Paul Maloy 	if (likely(msz <= pktmax)) {
39057d5f64dSParthasarathy Bhuvaragan 		skb = tipc_buf_acquire(msz, GFP_KERNEL);
3914c94cc2dSJon Maloy 
3924c94cc2dSJon Maloy 		/* Fall back to smaller MTU if node local message */
3934c94cc2dSJon Maloy 		if (unlikely(!skb)) {
3944c94cc2dSJon Maloy 			if (pktmax != MAX_MSG_SIZE)
395067608e9SJon Paul Maloy 				return -ENOMEM;
3960c6de0c9SMenglong Dong 			rc = tipc_msg_build(mhdr, m, offset, dsz,
3970c6de0c9SMenglong Dong 					    one_page_mtu, list);
3984c94cc2dSJon Maloy 			if (rc != dsz)
3994c94cc2dSJon Maloy 				return rc;
4004c94cc2dSJon Maloy 			if (tipc_msg_assemble(list))
4014c94cc2dSJon Maloy 				return dsz;
4024c94cc2dSJon Maloy 			return -ENOMEM;
4034c94cc2dSJon Maloy 		}
404c93d3baaSYing Xue 		skb_orphan(skb);
405a6ca1094SYing Xue 		__skb_queue_tail(list, skb);
406a6ca1094SYing Xue 		skb_copy_to_linear_data(skb, mhdr, mhsz);
407a6ca1094SYing Xue 		pktpos = skb->data + mhsz;
408cbbd26b8SAl Viro 		if (copy_from_iter_full(pktpos, dsz, &m->msg_iter))
409067608e9SJon Paul Maloy 			return dsz;
410067608e9SJon Paul Maloy 		rc = -EFAULT;
411067608e9SJon Paul Maloy 		goto error;
412067608e9SJon Paul Maloy 	}
413067608e9SJon Paul Maloy 
414067608e9SJon Paul Maloy 	/* Prepare reusable fragment header */
415c5898636SJon Paul Maloy 	tipc_msg_init(msg_prevnode(mhdr), &pkthdr, MSG_FRAGMENTER,
416c5898636SJon Paul Maloy 		      FIRST_FRAGMENT, INT_H_SIZE, msg_destnode(mhdr));
417067608e9SJon Paul Maloy 	msg_set_size(&pkthdr, pktmax);
418067608e9SJon Paul Maloy 	msg_set_fragm_no(&pkthdr, pktno);
419e3eea1ebSJon Paul Maloy 	msg_set_importance(&pkthdr, msg_importance(mhdr));
420067608e9SJon Paul Maloy 
421067608e9SJon Paul Maloy 	/* Prepare first fragment */
42257d5f64dSParthasarathy Bhuvaragan 	skb = tipc_buf_acquire(pktmax, GFP_KERNEL);
423a6ca1094SYing Xue 	if (!skb)
424067608e9SJon Paul Maloy 		return -ENOMEM;
425c93d3baaSYing Xue 	skb_orphan(skb);
426a6ca1094SYing Xue 	__skb_queue_tail(list, skb);
427a6ca1094SYing Xue 	pktpos = skb->data;
428a6ca1094SYing Xue 	skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE);
429067608e9SJon Paul Maloy 	pktpos += INT_H_SIZE;
430067608e9SJon Paul Maloy 	pktrem -= INT_H_SIZE;
431a6ca1094SYing Xue 	skb_copy_to_linear_data_offset(skb, INT_H_SIZE, mhdr, mhsz);
432067608e9SJon Paul Maloy 	pktpos += mhsz;
433067608e9SJon Paul Maloy 	pktrem -= mhsz;
434067608e9SJon Paul Maloy 
435067608e9SJon Paul Maloy 	do {
436067608e9SJon Paul Maloy 		if (drem < pktrem)
437067608e9SJon Paul Maloy 			pktrem = drem;
438067608e9SJon Paul Maloy 
439cbbd26b8SAl Viro 		if (!copy_from_iter_full(pktpos, pktrem, &m->msg_iter)) {
440067608e9SJon Paul Maloy 			rc = -EFAULT;
441067608e9SJon Paul Maloy 			goto error;
442067608e9SJon Paul Maloy 		}
443067608e9SJon Paul Maloy 		drem -= pktrem;
444067608e9SJon Paul Maloy 
445067608e9SJon Paul Maloy 		if (!drem)
446067608e9SJon Paul Maloy 			break;
447067608e9SJon Paul Maloy 
448067608e9SJon Paul Maloy 		/* Prepare new fragment: */
449067608e9SJon Paul Maloy 		if (drem < (pktmax - INT_H_SIZE))
450067608e9SJon Paul Maloy 			pktsz = drem + INT_H_SIZE;
451067608e9SJon Paul Maloy 		else
452067608e9SJon Paul Maloy 			pktsz = pktmax;
45357d5f64dSParthasarathy Bhuvaragan 		skb = tipc_buf_acquire(pktsz, GFP_KERNEL);
454a6ca1094SYing Xue 		if (!skb) {
455067608e9SJon Paul Maloy 			rc = -ENOMEM;
456067608e9SJon Paul Maloy 			goto error;
457067608e9SJon Paul Maloy 		}
458c93d3baaSYing Xue 		skb_orphan(skb);
459a6ca1094SYing Xue 		__skb_queue_tail(list, skb);
460067608e9SJon Paul Maloy 		msg_set_type(&pkthdr, FRAGMENT);
461067608e9SJon Paul Maloy 		msg_set_size(&pkthdr, pktsz);
462067608e9SJon Paul Maloy 		msg_set_fragm_no(&pkthdr, ++pktno);
463a6ca1094SYing Xue 		skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE);
464a6ca1094SYing Xue 		pktpos = skb->data + INT_H_SIZE;
465067608e9SJon Paul Maloy 		pktrem = pktsz - INT_H_SIZE;
466067608e9SJon Paul Maloy 
467067608e9SJon Paul Maloy 	} while (1);
468a6ca1094SYing Xue 	msg_set_type(buf_msg(skb), LAST_FRAGMENT);
469067608e9SJon Paul Maloy 	return dsz;
470067608e9SJon Paul Maloy error:
471a6ca1094SYing Xue 	__skb_queue_purge(list);
472a6ca1094SYing Xue 	__skb_queue_head_init(list);
473067608e9SJon Paul Maloy 	return rc;
474067608e9SJon Paul Maloy }
475067608e9SJon Paul Maloy 
4764f1688b2SJon Paul Maloy /**
47706e7c70cSTuong Lien  * tipc_msg_bundle - Append contents of a buffer to tail of an existing one
47806e7c70cSTuong Lien  * @bskb: the bundle buffer to append to
479dd3f9e70SJon Paul Maloy  * @msg: message to be appended
48006e7c70cSTuong Lien  * @max: max allowable size for the bundle buffer
48106e7c70cSTuong Lien  *
482637b77fdSRandy Dunlap  * Return: "true" if bundling has been performed, otherwise "false"
4834f1688b2SJon Paul Maloy  */
tipc_msg_bundle(struct sk_buff * bskb,struct tipc_msg * msg,u32 max)48406e7c70cSTuong Lien static bool tipc_msg_bundle(struct sk_buff *bskb, struct tipc_msg *msg,
48506e7c70cSTuong Lien 			    u32 max)
4864f1688b2SJon Paul Maloy {
48706e7c70cSTuong Lien 	struct tipc_msg *bmsg = buf_msg(bskb);
48806e7c70cSTuong Lien 	u32 msz, bsz, offset, pad;
4894f1688b2SJon Paul Maloy 
49006e7c70cSTuong Lien 	msz = msg_size(msg);
49105dcc5aaSJon Paul Maloy 	bsz = msg_size(bmsg);
492d4cfb7feSMenglong Dong 	offset = BUF_ALIGN(bsz);
49306e7c70cSTuong Lien 	pad = offset - bsz;
49405dcc5aaSJon Paul Maloy 
49506e7c70cSTuong Lien 	if (unlikely(skb_tailroom(bskb) < (pad + msz)))
4964f1688b2SJon Paul Maloy 		return false;
49706e7c70cSTuong Lien 	if (unlikely(max < (offset + msz)))
498f21e897eSJon Paul Maloy 		return false;
4994f1688b2SJon Paul Maloy 
50006e7c70cSTuong Lien 	skb_put(bskb, pad + msz);
50106e7c70cSTuong Lien 	skb_copy_to_linear_data_offset(bskb, offset, msg, msz);
50206e7c70cSTuong Lien 	msg_set_size(bmsg, offset + msz);
5034f1688b2SJon Paul Maloy 	msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1);
5044f1688b2SJon Paul Maloy 	return true;
5054f1688b2SJon Paul Maloy }
5064f1688b2SJon Paul Maloy 
5074f1688b2SJon Paul Maloy /**
50806e7c70cSTuong Lien  * tipc_msg_try_bundle - Try to bundle a new message to the last one
50906e7c70cSTuong Lien  * @tskb: the last/target message to which the new one will be appended
51006e7c70cSTuong Lien  * @skb: the new message skb pointer
51106e7c70cSTuong Lien  * @mss: max message size (header inclusive)
51206e7c70cSTuong Lien  * @dnode: destination node for the message
51306e7c70cSTuong Lien  * @new_bundle: if this call made a new bundle or not
51406e7c70cSTuong Lien  *
51506e7c70cSTuong Lien  * Return: "true" if the new message skb is potential for bundling this time or
51606e7c70cSTuong Lien  * later, in the case a bundling has been done this time, the skb is consumed
51706e7c70cSTuong Lien  * (the skb pointer = NULL).
51806e7c70cSTuong Lien  * Otherwise, "false" if the skb cannot be bundled at all.
51906e7c70cSTuong Lien  */
tipc_msg_try_bundle(struct sk_buff * tskb,struct sk_buff ** skb,u32 mss,u32 dnode,bool * new_bundle)52006e7c70cSTuong Lien bool tipc_msg_try_bundle(struct sk_buff *tskb, struct sk_buff **skb, u32 mss,
52106e7c70cSTuong Lien 			 u32 dnode, bool *new_bundle)
52206e7c70cSTuong Lien {
52306e7c70cSTuong Lien 	struct tipc_msg *msg, *inner, *outer;
52406e7c70cSTuong Lien 	u32 tsz;
52506e7c70cSTuong Lien 
52606e7c70cSTuong Lien 	/* First, check if the new buffer is suitable for bundling */
52706e7c70cSTuong Lien 	msg = buf_msg(*skb);
52806e7c70cSTuong Lien 	if (msg_user(msg) == MSG_FRAGMENTER)
52906e7c70cSTuong Lien 		return false;
53006e7c70cSTuong Lien 	if (msg_user(msg) == TUNNEL_PROTOCOL)
53106e7c70cSTuong Lien 		return false;
53206e7c70cSTuong Lien 	if (msg_user(msg) == BCAST_PROTOCOL)
53306e7c70cSTuong Lien 		return false;
53406e7c70cSTuong Lien 	if (mss <= INT_H_SIZE + msg_size(msg))
53506e7c70cSTuong Lien 		return false;
53606e7c70cSTuong Lien 
53706e7c70cSTuong Lien 	/* Ok, but the last/target buffer can be empty? */
53806e7c70cSTuong Lien 	if (unlikely(!tskb))
53906e7c70cSTuong Lien 		return true;
54006e7c70cSTuong Lien 
54106e7c70cSTuong Lien 	/* Is it a bundle already? Try to bundle the new message to it */
54206e7c70cSTuong Lien 	if (msg_user(buf_msg(tskb)) == MSG_BUNDLER) {
54306e7c70cSTuong Lien 		*new_bundle = false;
54406e7c70cSTuong Lien 		goto bundle;
54506e7c70cSTuong Lien 	}
54606e7c70cSTuong Lien 
54706e7c70cSTuong Lien 	/* Make a new bundle of the two messages if possible */
54806e7c70cSTuong Lien 	tsz = msg_size(buf_msg(tskb));
549d4cfb7feSMenglong Dong 	if (unlikely(mss < BUF_ALIGN(INT_H_SIZE + tsz) + msg_size(msg)))
55006e7c70cSTuong Lien 		return true;
55106e7c70cSTuong Lien 	if (unlikely(pskb_expand_head(tskb, INT_H_SIZE, mss - tsz - INT_H_SIZE,
55206e7c70cSTuong Lien 				      GFP_ATOMIC)))
55306e7c70cSTuong Lien 		return true;
55406e7c70cSTuong Lien 	inner = buf_msg(tskb);
55506e7c70cSTuong Lien 	skb_push(tskb, INT_H_SIZE);
55606e7c70cSTuong Lien 	outer = buf_msg(tskb);
55706e7c70cSTuong Lien 	tipc_msg_init(msg_prevnode(inner), outer, MSG_BUNDLER, 0, INT_H_SIZE,
55806e7c70cSTuong Lien 		      dnode);
55906e7c70cSTuong Lien 	msg_set_importance(outer, msg_importance(inner));
56006e7c70cSTuong Lien 	msg_set_size(outer, INT_H_SIZE + tsz);
56106e7c70cSTuong Lien 	msg_set_msgcnt(outer, 1);
56206e7c70cSTuong Lien 	*new_bundle = true;
56306e7c70cSTuong Lien 
56406e7c70cSTuong Lien bundle:
56506e7c70cSTuong Lien 	if (likely(tipc_msg_bundle(tskb, msg, mss))) {
56606e7c70cSTuong Lien 		consume_skb(*skb);
56706e7c70cSTuong Lien 		*skb = NULL;
56806e7c70cSTuong Lien 	}
56906e7c70cSTuong Lien 	return true;
57006e7c70cSTuong Lien }
57106e7c70cSTuong Lien 
57206e7c70cSTuong Lien /**
573c637c103SJon Paul Maloy  *  tipc_msg_extract(): extract bundled inner packet from buffer
574c1336ee4SJon Paul Maloy  *  @skb: buffer to be extracted from.
575c637c103SJon Paul Maloy  *  @iskb: extracted inner buffer, to be returned
576c1336ee4SJon Paul Maloy  *  @pos: position in outer message of msg to be extracted.
5775fcb7d47SRandy Dunlap  *  Returns position of next msg.
578c637c103SJon Paul Maloy  *  Consumes outer buffer when last packet extracted
579637b77fdSRandy Dunlap  *  Return: true when there is an extracted buffer, otherwise false
580c637c103SJon Paul Maloy  */
tipc_msg_extract(struct sk_buff * skb,struct sk_buff ** iskb,int * pos)581c637c103SJon Paul Maloy bool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos)
582c637c103SJon Paul Maloy {
583ef9be755STung Nguyen 	struct tipc_msg *hdr, *ihdr;
584ef9be755STung Nguyen 	int imsz;
585c637c103SJon Paul Maloy 
586c1336ee4SJon Paul Maloy 	*iskb = NULL;
5871149557dSJon Paul Maloy 	if (unlikely(skb_linearize(skb)))
588c637c103SJon Paul Maloy 		goto none;
589c637c103SJon Paul Maloy 
590ef9be755STung Nguyen 	hdr = buf_msg(skb);
591ef9be755STung Nguyen 	if (unlikely(*pos > (msg_data_sz(hdr) - MIN_H_SIZE)))
592c637c103SJon Paul Maloy 		goto none;
593c1336ee4SJon Paul Maloy 
594ef9be755STung Nguyen 	ihdr = (struct tipc_msg *)(msg_data(hdr) + *pos);
595ef9be755STung Nguyen 	imsz = msg_size(ihdr);
596ef9be755STung Nguyen 
597ef9be755STung Nguyen 	if ((*pos + imsz) > msg_data_sz(hdr))
598c637c103SJon Paul Maloy 		goto none;
599ef9be755STung Nguyen 
600ef9be755STung Nguyen 	*iskb = tipc_buf_acquire(imsz, GFP_ATOMIC);
601ef9be755STung Nguyen 	if (!*iskb)
602ef9be755STung Nguyen 		goto none;
603ef9be755STung Nguyen 
604ef9be755STung Nguyen 	skb_copy_to_linear_data(*iskb, ihdr, imsz);
605d618d09aSJon Maloy 	if (unlikely(!tipc_msg_validate(iskb)))
606c1336ee4SJon Paul Maloy 		goto none;
607ef9be755STung Nguyen 
608d4cfb7feSMenglong Dong 	*pos += BUF_ALIGN(imsz);
609c637c103SJon Paul Maloy 	return true;
610c637c103SJon Paul Maloy none:
611c637c103SJon Paul Maloy 	kfree_skb(skb);
612c1336ee4SJon Paul Maloy 	kfree_skb(*iskb);
613c637c103SJon Paul Maloy 	*iskb = NULL;
614c637c103SJon Paul Maloy 	return false;
615c637c103SJon Paul Maloy }
616c637c103SJon Paul Maloy 
617c637c103SJon Paul Maloy /**
6188db1bae3SJon Paul Maloy  * tipc_msg_reverse(): swap source and destination addresses and add error code
61929042e19SJon Paul Maloy  * @own_node: originating node id for reversed message
6205cbdbd1aSJon Maloy  * @skb:  buffer containing message to be reversed; will be consumed
62129042e19SJon Paul Maloy  * @err:  error code to be set in message, if any
6225cbdbd1aSJon Maloy  * Replaces consumed buffer with new one when successful
623637b77fdSRandy Dunlap  * Return: true if success, otherwise false
6248db1bae3SJon Paul Maloy  */
tipc_msg_reverse(u32 own_node,struct sk_buff ** skb,int err)625bcd3ffd4SJon Paul Maloy bool tipc_msg_reverse(u32 own_node,  struct sk_buff **skb, int err)
6268db1bae3SJon Paul Maloy {
62729042e19SJon Paul Maloy 	struct sk_buff *_skb = *skb;
6285cbdbd1aSJon Maloy 	struct tipc_msg *_hdr, *hdr;
6295cbdbd1aSJon Maloy 	int hlen, dlen;
6308db1bae3SJon Paul Maloy 
63129042e19SJon Paul Maloy 	if (skb_linearize(_skb))
6328db1bae3SJon Paul Maloy 		goto exit;
6335cbdbd1aSJon Maloy 	_hdr = buf_msg(_skb);
6345cbdbd1aSJon Maloy 	dlen = min_t(uint, msg_data_sz(_hdr), MAX_FORWARD_SIZE);
6355cbdbd1aSJon Maloy 	hlen = msg_hdr_sz(_hdr);
6365cbdbd1aSJon Maloy 
6375cbdbd1aSJon Maloy 	if (msg_dest_droppable(_hdr))
638ac0074eeSJon Paul Maloy 		goto exit;
6395cbdbd1aSJon Maloy 	if (msg_errcode(_hdr))
6408db1bae3SJon Paul Maloy 		goto exit;
64129042e19SJon Paul Maloy 
6425cbdbd1aSJon Maloy 	/* Never return SHORT header */
6435cbdbd1aSJon Maloy 	if (hlen == SHORT_H_SIZE)
6445cbdbd1aSJon Maloy 		hlen = BASIC_H_SIZE;
64529042e19SJon Paul Maloy 
64667879274STung Nguyen 	/* Don't return data along with SYN+, - sender has a clone */
64767879274STung Nguyen 	if (msg_is_syn(_hdr) && err == TIPC_ERR_OVERLOAD)
64867879274STung Nguyen 		dlen = 0;
64967879274STung Nguyen 
6505cbdbd1aSJon Maloy 	/* Allocate new buffer to return */
6515cbdbd1aSJon Maloy 	*skb = tipc_buf_acquire(hlen + dlen, GFP_ATOMIC);
65229042e19SJon Paul Maloy 	if (!*skb)
65329042e19SJon Paul Maloy 		goto exit;
6545cbdbd1aSJon Maloy 	memcpy((*skb)->data, _skb->data, msg_hdr_sz(_hdr));
6555cbdbd1aSJon Maloy 	memcpy((*skb)->data + hlen, msg_data(_hdr), dlen);
65629042e19SJon Paul Maloy 
6575cbdbd1aSJon Maloy 	/* Build reverse header in new buffer */
6585cbdbd1aSJon Maloy 	hdr = buf_msg(*skb);
6595cbdbd1aSJon Maloy 	msg_set_hdr_sz(hdr, hlen);
66029042e19SJon Paul Maloy 	msg_set_errcode(hdr, err);
66159a361bcSJon Paul Maloy 	msg_set_non_seq(hdr, 0);
6625cbdbd1aSJon Maloy 	msg_set_origport(hdr, msg_destport(_hdr));
6635cbdbd1aSJon Maloy 	msg_set_destport(hdr, msg_origport(_hdr));
6645cbdbd1aSJon Maloy 	msg_set_destnode(hdr, msg_prevnode(_hdr));
66529042e19SJon Paul Maloy 	msg_set_prevnode(hdr, own_node);
66629042e19SJon Paul Maloy 	msg_set_orignode(hdr, own_node);
6675cbdbd1aSJon Maloy 	msg_set_size(hdr, hlen + dlen);
66829042e19SJon Paul Maloy 	skb_orphan(_skb);
6695cbdbd1aSJon Maloy 	kfree_skb(_skb);
6708db1bae3SJon Paul Maloy 	return true;
6718db1bae3SJon Paul Maloy exit:
67229042e19SJon Paul Maloy 	kfree_skb(_skb);
67329042e19SJon Paul Maloy 	*skb = NULL;
6748db1bae3SJon Paul Maloy 	return false;
6758db1bae3SJon Paul Maloy }
6765a379074SJon Paul Maloy 
tipc_msg_skb_clone(struct sk_buff_head * msg,struct sk_buff_head * cpy)67767879274STung Nguyen bool tipc_msg_skb_clone(struct sk_buff_head *msg, struct sk_buff_head *cpy)
67867879274STung Nguyen {
67967879274STung Nguyen 	struct sk_buff *skb, *_skb;
68067879274STung Nguyen 
68167879274STung Nguyen 	skb_queue_walk(msg, skb) {
68267879274STung Nguyen 		_skb = skb_clone(skb, GFP_ATOMIC);
68367879274STung Nguyen 		if (!_skb) {
68467879274STung Nguyen 			__skb_queue_purge(cpy);
68567879274STung Nguyen 			pr_err_ratelimited("Failed to clone buffer chain\n");
68667879274STung Nguyen 			return false;
68767879274STung Nguyen 		}
68867879274STung Nguyen 		__skb_queue_tail(cpy, _skb);
68967879274STung Nguyen 	}
69067879274STung Nguyen 	return true;
69167879274STung Nguyen }
69267879274STung Nguyen 
6935a379074SJon Paul Maloy /**
694e3a77561SJon Paul Maloy  * tipc_msg_lookup_dest(): try to find new destination for named message
6955fcb7d47SRandy Dunlap  * @net: pointer to associated network namespace
696e3a77561SJon Paul Maloy  * @skb: the buffer containing the message.
697cda3696dSJon Paul Maloy  * @err: error code to be used by caller if lookup fails
6985a379074SJon Paul Maloy  * Does not consume buffer
699637b77fdSRandy Dunlap  * Return: true if a destination is found, false otherwise
7005a379074SJon Paul Maloy  */
tipc_msg_lookup_dest(struct net * net,struct sk_buff * skb,int * err)701cda3696dSJon Paul Maloy bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err)
7025a379074SJon Paul Maloy {
703e3a77561SJon Paul Maloy 	struct tipc_msg *msg = buf_msg(skb);
704908148bcSJon Maloy 	u32 scope = msg_lookup_scope(msg);
705908148bcSJon Maloy 	u32 self = tipc_own_addr(net);
706908148bcSJon Maloy 	u32 inst = msg_nameinst(msg);
707908148bcSJon Maloy 	struct tipc_socket_addr sk;
708908148bcSJon Maloy 	struct tipc_uaddr ua;
7095a379074SJon Paul Maloy 
710e3a77561SJon Paul Maloy 	if (!msg_isdata(msg))
711e3a77561SJon Paul Maloy 		return false;
712e3a77561SJon Paul Maloy 	if (!msg_named(msg))
713e3a77561SJon Paul Maloy 		return false;
714d482994fSJon Paul Maloy 	if (msg_errcode(msg))
715d482994fSJon Paul Maloy 		return false;
716aad06212SParthasarathy Bhuvaragan 	*err = TIPC_ERR_NO_NAME;
717e3a77561SJon Paul Maloy 	if (skb_linearize(skb))
718e3a77561SJon Paul Maloy 		return false;
7194e3ae001SErik Hugne 	msg = buf_msg(skb);
720d482994fSJon Paul Maloy 	if (msg_reroute_cnt(msg))
721e3a77561SJon Paul Maloy 		return false;
722908148bcSJon Maloy 	tipc_uaddr(&ua, TIPC_SERVICE_RANGE, scope,
723908148bcSJon Maloy 		   msg_nametype(msg), inst, inst);
724908148bcSJon Maloy 	sk.node = tipc_scope2node(net, scope);
725908148bcSJon Maloy 	if (!tipc_nametbl_lookup_anycast(net, &ua, &sk))
726e3a77561SJon Paul Maloy 		return false;
7275a379074SJon Paul Maloy 	msg_incr_reroute_cnt(msg);
728908148bcSJon Maloy 	if (sk.node != self)
729908148bcSJon Maloy 		msg_set_prevnode(msg, self);
730908148bcSJon Maloy 	msg_set_destnode(msg, sk.node);
731908148bcSJon Maloy 	msg_set_destport(msg, sk.ref);
732e3a77561SJon Paul Maloy 	*err = TIPC_OK;
733a9e2971bSJon Maloy 
734e3a77561SJon Paul Maloy 	return true;
7355a379074SJon Paul Maloy }
736078bec82SJon Paul Maloy 
7374c94cc2dSJon Maloy /* tipc_msg_assemble() - assemble chain of fragments into one message
7384c94cc2dSJon Maloy  */
tipc_msg_assemble(struct sk_buff_head * list)7394c94cc2dSJon Maloy bool tipc_msg_assemble(struct sk_buff_head *list)
7404c94cc2dSJon Maloy {
7414c94cc2dSJon Maloy 	struct sk_buff *skb, *tmp = NULL;
7424c94cc2dSJon Maloy 
7434c94cc2dSJon Maloy 	if (skb_queue_len(list) == 1)
7444c94cc2dSJon Maloy 		return true;
7454c94cc2dSJon Maloy 
7464c94cc2dSJon Maloy 	while ((skb = __skb_dequeue(list))) {
7474c94cc2dSJon Maloy 		skb->next = NULL;
7484c94cc2dSJon Maloy 		if (tipc_buf_append(&tmp, &skb)) {
7494c94cc2dSJon Maloy 			__skb_queue_tail(list, skb);
7504c94cc2dSJon Maloy 			return true;
7514c94cc2dSJon Maloy 		}
7524c94cc2dSJon Maloy 		if (!tmp)
7534c94cc2dSJon Maloy 			break;
7544c94cc2dSJon Maloy 	}
7554c94cc2dSJon Maloy 	__skb_queue_purge(list);
7564c94cc2dSJon Maloy 	__skb_queue_head_init(list);
7574c94cc2dSJon Maloy 	pr_warn("Failed do assemble buffer\n");
7584c94cc2dSJon Maloy 	return false;
7594c94cc2dSJon Maloy }
7604c94cc2dSJon Maloy 
761078bec82SJon Paul Maloy /* tipc_msg_reassemble() - clone a buffer chain of fragments and
762078bec82SJon Paul Maloy  *                         reassemble the clones into one message
763078bec82SJon Paul Maloy  */
tipc_msg_reassemble(struct sk_buff_head * list,struct sk_buff_head * rcvq)7642f566124SJon Paul Maloy bool tipc_msg_reassemble(struct sk_buff_head *list, struct sk_buff_head *rcvq)
765078bec82SJon Paul Maloy {
7662f566124SJon Paul Maloy 	struct sk_buff *skb, *_skb;
767a6ca1094SYing Xue 	struct sk_buff *frag = NULL;
768078bec82SJon Paul Maloy 	struct sk_buff *head = NULL;
7692f566124SJon Paul Maloy 	int hdr_len;
770078bec82SJon Paul Maloy 
771078bec82SJon Paul Maloy 	/* Copy header if single buffer */
772a6ca1094SYing Xue 	if (skb_queue_len(list) == 1) {
773a6ca1094SYing Xue 		skb = skb_peek(list);
7742f566124SJon Paul Maloy 		hdr_len = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb));
7752f566124SJon Paul Maloy 		_skb = __pskb_copy(skb, hdr_len, GFP_ATOMIC);
7762f566124SJon Paul Maloy 		if (!_skb)
7772f566124SJon Paul Maloy 			return false;
7782f566124SJon Paul Maloy 		__skb_queue_tail(rcvq, _skb);
7792f566124SJon Paul Maloy 		return true;
780078bec82SJon Paul Maloy 	}
781078bec82SJon Paul Maloy 
782078bec82SJon Paul Maloy 	/* Clone all fragments and reassemble */
783a6ca1094SYing Xue 	skb_queue_walk(list, skb) {
784a6ca1094SYing Xue 		frag = skb_clone(skb, GFP_ATOMIC);
785078bec82SJon Paul Maloy 		if (!frag)
786078bec82SJon Paul Maloy 			goto error;
787078bec82SJon Paul Maloy 		frag->next = NULL;
788078bec82SJon Paul Maloy 		if (tipc_buf_append(&head, &frag))
789078bec82SJon Paul Maloy 			break;
790078bec82SJon Paul Maloy 		if (!head)
791078bec82SJon Paul Maloy 			goto error;
792078bec82SJon Paul Maloy 	}
7932f566124SJon Paul Maloy 	__skb_queue_tail(rcvq, frag);
7942f566124SJon Paul Maloy 	return true;
795078bec82SJon Paul Maloy error:
796078bec82SJon Paul Maloy 	pr_warn("Failed do clone local mcast rcv buffer\n");
797078bec82SJon Paul Maloy 	kfree_skb(head);
7982f566124SJon Paul Maloy 	return false;
799078bec82SJon Paul Maloy }
8008306f99aSJon Paul Maloy 
tipc_msg_pskb_copy(u32 dst,struct sk_buff_head * msg,struct sk_buff_head * cpy)801a853e4c6SJon Paul Maloy bool tipc_msg_pskb_copy(u32 dst, struct sk_buff_head *msg,
802a853e4c6SJon Paul Maloy 			struct sk_buff_head *cpy)
803a853e4c6SJon Paul Maloy {
804a853e4c6SJon Paul Maloy 	struct sk_buff *skb, *_skb;
805a853e4c6SJon Paul Maloy 
806a853e4c6SJon Paul Maloy 	skb_queue_walk(msg, skb) {
807a853e4c6SJon Paul Maloy 		_skb = pskb_copy(skb, GFP_ATOMIC);
808a853e4c6SJon Paul Maloy 		if (!_skb) {
809a853e4c6SJon Paul Maloy 			__skb_queue_purge(cpy);
810a853e4c6SJon Paul Maloy 			return false;
811a853e4c6SJon Paul Maloy 		}
812a853e4c6SJon Paul Maloy 		msg_set_destnode(buf_msg(_skb), dst);
813a853e4c6SJon Paul Maloy 		__skb_queue_tail(cpy, _skb);
814a853e4c6SJon Paul Maloy 	}
815a853e4c6SJon Paul Maloy 	return true;
816a853e4c6SJon Paul Maloy }
817a853e4c6SJon Paul Maloy 
8188306f99aSJon Paul Maloy /* tipc_skb_queue_sorted(); sort pkt into list according to sequence number
8198306f99aSJon Paul Maloy  * @list: list to be appended to
8208306f99aSJon Paul Maloy  * @seqno: sequence number of buffer to add
8218306f99aSJon Paul Maloy  * @skb: buffer to add
8228306f99aSJon Paul Maloy  */
__tipc_skb_queue_sorted(struct sk_buff_head * list,u16 seqno,struct sk_buff * skb)82303b6fefdSTuong Lien bool __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno,
8248306f99aSJon Paul Maloy 			     struct sk_buff *skb)
8258306f99aSJon Paul Maloy {
8268306f99aSJon Paul Maloy 	struct sk_buff *_skb, *tmp;
8278306f99aSJon Paul Maloy 
8288306f99aSJon Paul Maloy 	if (skb_queue_empty(list) || less(seqno, buf_seqno(skb_peek(list)))) {
8298306f99aSJon Paul Maloy 		__skb_queue_head(list, skb);
83003b6fefdSTuong Lien 		return true;
8318306f99aSJon Paul Maloy 	}
8328306f99aSJon Paul Maloy 
8338306f99aSJon Paul Maloy 	if (more(seqno, buf_seqno(skb_peek_tail(list)))) {
8348306f99aSJon Paul Maloy 		__skb_queue_tail(list, skb);
83503b6fefdSTuong Lien 		return true;
8368306f99aSJon Paul Maloy 	}
8378306f99aSJon Paul Maloy 
8388306f99aSJon Paul Maloy 	skb_queue_walk_safe(list, _skb, tmp) {
8398306f99aSJon Paul Maloy 		if (more(seqno, buf_seqno(_skb)))
8408306f99aSJon Paul Maloy 			continue;
8418306f99aSJon Paul Maloy 		if (seqno == buf_seqno(_skb))
8428306f99aSJon Paul Maloy 			break;
8438306f99aSJon Paul Maloy 		__skb_queue_before(list, _skb, skb);
84403b6fefdSTuong Lien 		return true;
8458306f99aSJon Paul Maloy 	}
8468306f99aSJon Paul Maloy 	kfree_skb(skb);
84703b6fefdSTuong Lien 	return false;
8488306f99aSJon Paul Maloy }
84964ac5f59SJon Maloy 
tipc_skb_reject(struct net * net,int err,struct sk_buff * skb,struct sk_buff_head * xmitq)85064ac5f59SJon Maloy void tipc_skb_reject(struct net *net, int err, struct sk_buff *skb,
85164ac5f59SJon Maloy 		     struct sk_buff_head *xmitq)
85264ac5f59SJon Maloy {
85364ac5f59SJon Maloy 	if (tipc_msg_reverse(tipc_own_addr(net), &skb, err))
85464ac5f59SJon Maloy 		__skb_queue_tail(xmitq, skb);
85564ac5f59SJon Maloy }
856