162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * net/tipc/msg.c: TIPC message header routines 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2000-2006, 2014-2015, Ericsson AB 562306a36Sopenharmony_ci * Copyright (c) 2005, 2010-2011, Wind River Systems 662306a36Sopenharmony_ci * All rights reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 962306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1262306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1362306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1462306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1562306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1662306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its 1762306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived from 1862306a36Sopenharmony_ci * this software without specific prior written permission. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 2162306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free 2262306a36Sopenharmony_ci * Software Foundation. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2562306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2662306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2762306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 2862306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3462306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <net/sock.h> 3862306a36Sopenharmony_ci#include "core.h" 3962306a36Sopenharmony_ci#include "msg.h" 4062306a36Sopenharmony_ci#include "addr.h" 4162306a36Sopenharmony_ci#include "name_table.h" 4262306a36Sopenharmony_ci#include "crypto.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define BUF_ALIGN(x) ALIGN(x, 4) 4562306a36Sopenharmony_ci#define MAX_FORWARD_SIZE 1024 4662306a36Sopenharmony_ci#ifdef CONFIG_TIPC_CRYPTO 4762306a36Sopenharmony_ci#define BUF_HEADROOM ALIGN(((LL_MAX_HEADER + 48) + EHDR_MAX_SIZE), 16) 4862306a36Sopenharmony_ci#define BUF_OVERHEAD (BUF_HEADROOM + TIPC_AES_GCM_TAG_SIZE) 4962306a36Sopenharmony_ci#else 5062306a36Sopenharmony_ci#define BUF_HEADROOM (LL_MAX_HEADER + 48) 5162306a36Sopenharmony_ci#define BUF_OVERHEAD BUF_HEADROOM 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciconst int one_page_mtu = PAGE_SIZE - SKB_DATA_ALIGN(BUF_OVERHEAD) - 5562306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/** 5862306a36Sopenharmony_ci * tipc_buf_acquire - creates a TIPC message buffer 5962306a36Sopenharmony_ci * @size: message size (including TIPC header) 6062306a36Sopenharmony_ci * @gfp: memory allocation flags 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Return: a new buffer with data pointers set to the specified size. 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * NOTE: 6562306a36Sopenharmony_ci * Headroom is reserved to allow prepending of a data link header. 6662306a36Sopenharmony_ci * There may also be unrequested tailroom present at the buffer's end. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistruct sk_buff *tipc_buf_acquire(u32 size, gfp_t gfp) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct sk_buff *skb; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci skb = alloc_skb_fclone(BUF_OVERHEAD + size, gfp); 7362306a36Sopenharmony_ci if (skb) { 7462306a36Sopenharmony_ci skb_reserve(skb, BUF_HEADROOM); 7562306a36Sopenharmony_ci skb_put(skb, size); 7662306a36Sopenharmony_ci skb->next = NULL; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci return skb; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_civoid tipc_msg_init(u32 own_node, struct tipc_msg *m, u32 user, u32 type, 8262306a36Sopenharmony_ci u32 hsize, u32 dnode) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci memset(m, 0, hsize); 8562306a36Sopenharmony_ci msg_set_version(m); 8662306a36Sopenharmony_ci msg_set_user(m, user); 8762306a36Sopenharmony_ci msg_set_hdr_sz(m, hsize); 8862306a36Sopenharmony_ci msg_set_size(m, hsize); 8962306a36Sopenharmony_ci msg_set_prevnode(m, own_node); 9062306a36Sopenharmony_ci msg_set_type(m, type); 9162306a36Sopenharmony_ci if (hsize > SHORT_H_SIZE) { 9262306a36Sopenharmony_ci msg_set_orignode(m, own_node); 9362306a36Sopenharmony_ci msg_set_destnode(m, dnode); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct sk_buff *tipc_msg_create(uint user, uint type, 9862306a36Sopenharmony_ci uint hdr_sz, uint data_sz, u32 dnode, 9962306a36Sopenharmony_ci u32 onode, u32 dport, u32 oport, int errcode) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct tipc_msg *msg; 10262306a36Sopenharmony_ci struct sk_buff *buf; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci buf = tipc_buf_acquire(hdr_sz + data_sz, GFP_ATOMIC); 10562306a36Sopenharmony_ci if (unlikely(!buf)) 10662306a36Sopenharmony_ci return NULL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci msg = buf_msg(buf); 10962306a36Sopenharmony_ci tipc_msg_init(onode, msg, user, type, hdr_sz, dnode); 11062306a36Sopenharmony_ci msg_set_size(msg, hdr_sz + data_sz); 11162306a36Sopenharmony_ci msg_set_origport(msg, oport); 11262306a36Sopenharmony_ci msg_set_destport(msg, dport); 11362306a36Sopenharmony_ci msg_set_errcode(msg, errcode); 11462306a36Sopenharmony_ci return buf; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* tipc_buf_append(): Append a buffer to the fragment list of another buffer 11862306a36Sopenharmony_ci * @*headbuf: in: NULL for first frag, otherwise value returned from prev call 11962306a36Sopenharmony_ci * out: set when successful non-complete reassembly, otherwise NULL 12062306a36Sopenharmony_ci * @*buf: in: the buffer to append. Always defined 12162306a36Sopenharmony_ci * out: head buf after successful complete reassembly, otherwise NULL 12262306a36Sopenharmony_ci * Returns 1 when reassembly complete, otherwise 0 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ciint tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct sk_buff *head = *headbuf; 12762306a36Sopenharmony_ci struct sk_buff *frag = *buf; 12862306a36Sopenharmony_ci struct sk_buff *tail = NULL; 12962306a36Sopenharmony_ci struct tipc_msg *msg; 13062306a36Sopenharmony_ci u32 fragid; 13162306a36Sopenharmony_ci int delta; 13262306a36Sopenharmony_ci bool headstolen; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!frag) 13562306a36Sopenharmony_ci goto err; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci msg = buf_msg(frag); 13862306a36Sopenharmony_ci fragid = msg_type(msg); 13962306a36Sopenharmony_ci frag->next = NULL; 14062306a36Sopenharmony_ci skb_pull(frag, msg_hdr_sz(msg)); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (fragid == FIRST_FRAGMENT) { 14362306a36Sopenharmony_ci if (unlikely(head)) 14462306a36Sopenharmony_ci goto err; 14562306a36Sopenharmony_ci *buf = NULL; 14662306a36Sopenharmony_ci if (skb_has_frag_list(frag) && __skb_linearize(frag)) 14762306a36Sopenharmony_ci goto err; 14862306a36Sopenharmony_ci frag = skb_unshare(frag, GFP_ATOMIC); 14962306a36Sopenharmony_ci if (unlikely(!frag)) 15062306a36Sopenharmony_ci goto err; 15162306a36Sopenharmony_ci head = *headbuf = frag; 15262306a36Sopenharmony_ci TIPC_SKB_CB(head)->tail = NULL; 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!head) 15762306a36Sopenharmony_ci goto err; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (skb_try_coalesce(head, frag, &headstolen, &delta)) { 16062306a36Sopenharmony_ci kfree_skb_partial(frag, headstolen); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci tail = TIPC_SKB_CB(head)->tail; 16362306a36Sopenharmony_ci if (!skb_has_frag_list(head)) 16462306a36Sopenharmony_ci skb_shinfo(head)->frag_list = frag; 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci tail->next = frag; 16762306a36Sopenharmony_ci head->truesize += frag->truesize; 16862306a36Sopenharmony_ci head->data_len += frag->len; 16962306a36Sopenharmony_ci head->len += frag->len; 17062306a36Sopenharmony_ci TIPC_SKB_CB(head)->tail = frag; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (fragid == LAST_FRAGMENT) { 17462306a36Sopenharmony_ci TIPC_SKB_CB(head)->validated = 0; 17562306a36Sopenharmony_ci if (unlikely(!tipc_msg_validate(&head))) 17662306a36Sopenharmony_ci goto err; 17762306a36Sopenharmony_ci *buf = head; 17862306a36Sopenharmony_ci TIPC_SKB_CB(head)->tail = NULL; 17962306a36Sopenharmony_ci *headbuf = NULL; 18062306a36Sopenharmony_ci return 1; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci *buf = NULL; 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_cierr: 18562306a36Sopenharmony_ci kfree_skb(*buf); 18662306a36Sopenharmony_ci kfree_skb(*headbuf); 18762306a36Sopenharmony_ci *buf = *headbuf = NULL; 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * tipc_msg_append(): Append data to tail of an existing buffer queue 19362306a36Sopenharmony_ci * @_hdr: header to be used 19462306a36Sopenharmony_ci * @m: the data to be appended 19562306a36Sopenharmony_ci * @mss: max allowable size of buffer 19662306a36Sopenharmony_ci * @dlen: size of data to be appended 19762306a36Sopenharmony_ci * @txq: queue to append to 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Return: the number of 1k blocks appended or errno value 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ciint tipc_msg_append(struct tipc_msg *_hdr, struct msghdr *m, int dlen, 20262306a36Sopenharmony_ci int mss, struct sk_buff_head *txq) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct sk_buff *skb; 20562306a36Sopenharmony_ci int accounted, total, curr; 20662306a36Sopenharmony_ci int mlen, cpy, rem = dlen; 20762306a36Sopenharmony_ci struct tipc_msg *hdr; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci skb = skb_peek_tail(txq); 21062306a36Sopenharmony_ci accounted = skb ? msg_blocks(buf_msg(skb)) : 0; 21162306a36Sopenharmony_ci total = accounted; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci do { 21462306a36Sopenharmony_ci if (!skb || skb->len >= mss) { 21562306a36Sopenharmony_ci skb = tipc_buf_acquire(mss, GFP_KERNEL); 21662306a36Sopenharmony_ci if (unlikely(!skb)) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci skb_orphan(skb); 21962306a36Sopenharmony_ci skb_trim(skb, MIN_H_SIZE); 22062306a36Sopenharmony_ci hdr = buf_msg(skb); 22162306a36Sopenharmony_ci skb_copy_to_linear_data(skb, _hdr, MIN_H_SIZE); 22262306a36Sopenharmony_ci msg_set_hdr_sz(hdr, MIN_H_SIZE); 22362306a36Sopenharmony_ci msg_set_size(hdr, MIN_H_SIZE); 22462306a36Sopenharmony_ci __skb_queue_tail(txq, skb); 22562306a36Sopenharmony_ci total += 1; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci hdr = buf_msg(skb); 22862306a36Sopenharmony_ci curr = msg_blocks(hdr); 22962306a36Sopenharmony_ci mlen = msg_size(hdr); 23062306a36Sopenharmony_ci cpy = min_t(size_t, rem, mss - mlen); 23162306a36Sopenharmony_ci if (cpy != copy_from_iter(skb->data + mlen, cpy, &m->msg_iter)) 23262306a36Sopenharmony_ci return -EFAULT; 23362306a36Sopenharmony_ci msg_set_size(hdr, mlen + cpy); 23462306a36Sopenharmony_ci skb_put(skb, cpy); 23562306a36Sopenharmony_ci rem -= cpy; 23662306a36Sopenharmony_ci total += msg_blocks(hdr) - curr; 23762306a36Sopenharmony_ci } while (rem > 0); 23862306a36Sopenharmony_ci return total - accounted; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* tipc_msg_validate - validate basic format of received message 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * This routine ensures a TIPC message has an acceptable header, and at least 24462306a36Sopenharmony_ci * as much data as the header indicates it should. The routine also ensures 24562306a36Sopenharmony_ci * that the entire message header is stored in the main fragment of the message 24662306a36Sopenharmony_ci * buffer, to simplify future access to message header fields. 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * Note: Having extra info present in the message header or data areas is OK. 24962306a36Sopenharmony_ci * TIPC will ignore the excess, under the assumption that it is optional info 25062306a36Sopenharmony_ci * introduced by a later release of the protocol. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cibool tipc_msg_validate(struct sk_buff **_skb) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct sk_buff *skb = *_skb; 25562306a36Sopenharmony_ci struct tipc_msg *hdr; 25662306a36Sopenharmony_ci int msz, hsz; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Ensure that flow control ratio condition is satisfied */ 25962306a36Sopenharmony_ci if (unlikely(skb->truesize / buf_roundup_len(skb) >= 4)) { 26062306a36Sopenharmony_ci skb = skb_copy_expand(skb, BUF_HEADROOM, 0, GFP_ATOMIC); 26162306a36Sopenharmony_ci if (!skb) 26262306a36Sopenharmony_ci return false; 26362306a36Sopenharmony_ci kfree_skb(*_skb); 26462306a36Sopenharmony_ci *_skb = skb; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (unlikely(TIPC_SKB_CB(skb)->validated)) 26862306a36Sopenharmony_ci return true; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, MIN_H_SIZE))) 27162306a36Sopenharmony_ci return false; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci hsz = msg_hdr_sz(buf_msg(skb)); 27462306a36Sopenharmony_ci if (unlikely(hsz < MIN_H_SIZE) || (hsz > MAX_H_SIZE)) 27562306a36Sopenharmony_ci return false; 27662306a36Sopenharmony_ci if (unlikely(!pskb_may_pull(skb, hsz))) 27762306a36Sopenharmony_ci return false; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci hdr = buf_msg(skb); 28062306a36Sopenharmony_ci if (unlikely(msg_version(hdr) != TIPC_VERSION)) 28162306a36Sopenharmony_ci return false; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci msz = msg_size(hdr); 28462306a36Sopenharmony_ci if (unlikely(msz < hsz)) 28562306a36Sopenharmony_ci return false; 28662306a36Sopenharmony_ci if (unlikely((msz - hsz) > TIPC_MAX_USER_MSG_SIZE)) 28762306a36Sopenharmony_ci return false; 28862306a36Sopenharmony_ci if (unlikely(skb->len < msz)) 28962306a36Sopenharmony_ci return false; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci TIPC_SKB_CB(skb)->validated = 1; 29262306a36Sopenharmony_ci return true; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/** 29662306a36Sopenharmony_ci * tipc_msg_fragment - build a fragment skb list for TIPC message 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * @skb: TIPC message skb 29962306a36Sopenharmony_ci * @hdr: internal msg header to be put on the top of the fragments 30062306a36Sopenharmony_ci * @pktmax: max size of a fragment incl. the header 30162306a36Sopenharmony_ci * @frags: returned fragment skb list 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * Return: 0 if the fragmentation is successful, otherwise: -EINVAL 30462306a36Sopenharmony_ci * or -ENOMEM 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ciint tipc_msg_fragment(struct sk_buff *skb, const struct tipc_msg *hdr, 30762306a36Sopenharmony_ci int pktmax, struct sk_buff_head *frags) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int pktno, nof_fragms, dsz, dmax, eat; 31062306a36Sopenharmony_ci struct tipc_msg *_hdr; 31162306a36Sopenharmony_ci struct sk_buff *_skb; 31262306a36Sopenharmony_ci u8 *data; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Non-linear buffer? */ 31562306a36Sopenharmony_ci if (skb_linearize(skb)) 31662306a36Sopenharmony_ci return -ENOMEM; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci data = (u8 *)skb->data; 31962306a36Sopenharmony_ci dsz = msg_size(buf_msg(skb)); 32062306a36Sopenharmony_ci dmax = pktmax - INT_H_SIZE; 32162306a36Sopenharmony_ci if (dsz <= dmax || !dmax) 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci nof_fragms = dsz / dmax + 1; 32562306a36Sopenharmony_ci for (pktno = 1; pktno <= nof_fragms; pktno++) { 32662306a36Sopenharmony_ci if (pktno < nof_fragms) 32762306a36Sopenharmony_ci eat = dmax; 32862306a36Sopenharmony_ci else 32962306a36Sopenharmony_ci eat = dsz % dmax; 33062306a36Sopenharmony_ci /* Allocate a new fragment */ 33162306a36Sopenharmony_ci _skb = tipc_buf_acquire(INT_H_SIZE + eat, GFP_ATOMIC); 33262306a36Sopenharmony_ci if (!_skb) 33362306a36Sopenharmony_ci goto error; 33462306a36Sopenharmony_ci skb_orphan(_skb); 33562306a36Sopenharmony_ci __skb_queue_tail(frags, _skb); 33662306a36Sopenharmony_ci /* Copy header & data to the fragment */ 33762306a36Sopenharmony_ci skb_copy_to_linear_data(_skb, hdr, INT_H_SIZE); 33862306a36Sopenharmony_ci skb_copy_to_linear_data_offset(_skb, INT_H_SIZE, data, eat); 33962306a36Sopenharmony_ci data += eat; 34062306a36Sopenharmony_ci /* Update the fragment's header */ 34162306a36Sopenharmony_ci _hdr = buf_msg(_skb); 34262306a36Sopenharmony_ci msg_set_fragm_no(_hdr, pktno); 34362306a36Sopenharmony_ci msg_set_nof_fragms(_hdr, nof_fragms); 34462306a36Sopenharmony_ci msg_set_size(_hdr, INT_H_SIZE + eat); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cierror: 34962306a36Sopenharmony_ci __skb_queue_purge(frags); 35062306a36Sopenharmony_ci __skb_queue_head_init(frags); 35162306a36Sopenharmony_ci return -ENOMEM; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * tipc_msg_build - create buffer chain containing specified header and data 35662306a36Sopenharmony_ci * @mhdr: Message header, to be prepended to data 35762306a36Sopenharmony_ci * @m: User message 35862306a36Sopenharmony_ci * @offset: buffer offset for fragmented messages (FIXME) 35962306a36Sopenharmony_ci * @dsz: Total length of user data 36062306a36Sopenharmony_ci * @pktmax: Max packet size that can be used 36162306a36Sopenharmony_ci * @list: Buffer or chain of buffers to be returned to caller 36262306a36Sopenharmony_ci * 36362306a36Sopenharmony_ci * Note that the recursive call we are making here is safe, since it can 36462306a36Sopenharmony_ci * logically go only one further level down. 36562306a36Sopenharmony_ci * 36662306a36Sopenharmony_ci * Return: message data size or errno: -ENOMEM, -EFAULT 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ciint tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, 36962306a36Sopenharmony_ci int dsz, int pktmax, struct sk_buff_head *list) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci int mhsz = msg_hdr_sz(mhdr); 37262306a36Sopenharmony_ci struct tipc_msg pkthdr; 37362306a36Sopenharmony_ci int msz = mhsz + dsz; 37462306a36Sopenharmony_ci int pktrem = pktmax; 37562306a36Sopenharmony_ci struct sk_buff *skb; 37662306a36Sopenharmony_ci int drem = dsz; 37762306a36Sopenharmony_ci int pktno = 1; 37862306a36Sopenharmony_ci char *pktpos; 37962306a36Sopenharmony_ci int pktsz; 38062306a36Sopenharmony_ci int rc; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci msg_set_size(mhdr, msz); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* No fragmentation needed? */ 38562306a36Sopenharmony_ci if (likely(msz <= pktmax)) { 38662306a36Sopenharmony_ci skb = tipc_buf_acquire(msz, GFP_KERNEL); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Fall back to smaller MTU if node local message */ 38962306a36Sopenharmony_ci if (unlikely(!skb)) { 39062306a36Sopenharmony_ci if (pktmax != MAX_MSG_SIZE) 39162306a36Sopenharmony_ci return -ENOMEM; 39262306a36Sopenharmony_ci rc = tipc_msg_build(mhdr, m, offset, dsz, 39362306a36Sopenharmony_ci one_page_mtu, list); 39462306a36Sopenharmony_ci if (rc != dsz) 39562306a36Sopenharmony_ci return rc; 39662306a36Sopenharmony_ci if (tipc_msg_assemble(list)) 39762306a36Sopenharmony_ci return dsz; 39862306a36Sopenharmony_ci return -ENOMEM; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci skb_orphan(skb); 40162306a36Sopenharmony_ci __skb_queue_tail(list, skb); 40262306a36Sopenharmony_ci skb_copy_to_linear_data(skb, mhdr, mhsz); 40362306a36Sopenharmony_ci pktpos = skb->data + mhsz; 40462306a36Sopenharmony_ci if (copy_from_iter_full(pktpos, dsz, &m->msg_iter)) 40562306a36Sopenharmony_ci return dsz; 40662306a36Sopenharmony_ci rc = -EFAULT; 40762306a36Sopenharmony_ci goto error; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Prepare reusable fragment header */ 41162306a36Sopenharmony_ci tipc_msg_init(msg_prevnode(mhdr), &pkthdr, MSG_FRAGMENTER, 41262306a36Sopenharmony_ci FIRST_FRAGMENT, INT_H_SIZE, msg_destnode(mhdr)); 41362306a36Sopenharmony_ci msg_set_size(&pkthdr, pktmax); 41462306a36Sopenharmony_ci msg_set_fragm_no(&pkthdr, pktno); 41562306a36Sopenharmony_ci msg_set_importance(&pkthdr, msg_importance(mhdr)); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Prepare first fragment */ 41862306a36Sopenharmony_ci skb = tipc_buf_acquire(pktmax, GFP_KERNEL); 41962306a36Sopenharmony_ci if (!skb) 42062306a36Sopenharmony_ci return -ENOMEM; 42162306a36Sopenharmony_ci skb_orphan(skb); 42262306a36Sopenharmony_ci __skb_queue_tail(list, skb); 42362306a36Sopenharmony_ci pktpos = skb->data; 42462306a36Sopenharmony_ci skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE); 42562306a36Sopenharmony_ci pktpos += INT_H_SIZE; 42662306a36Sopenharmony_ci pktrem -= INT_H_SIZE; 42762306a36Sopenharmony_ci skb_copy_to_linear_data_offset(skb, INT_H_SIZE, mhdr, mhsz); 42862306a36Sopenharmony_ci pktpos += mhsz; 42962306a36Sopenharmony_ci pktrem -= mhsz; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci do { 43262306a36Sopenharmony_ci if (drem < pktrem) 43362306a36Sopenharmony_ci pktrem = drem; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (!copy_from_iter_full(pktpos, pktrem, &m->msg_iter)) { 43662306a36Sopenharmony_ci rc = -EFAULT; 43762306a36Sopenharmony_ci goto error; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci drem -= pktrem; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (!drem) 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Prepare new fragment: */ 44562306a36Sopenharmony_ci if (drem < (pktmax - INT_H_SIZE)) 44662306a36Sopenharmony_ci pktsz = drem + INT_H_SIZE; 44762306a36Sopenharmony_ci else 44862306a36Sopenharmony_ci pktsz = pktmax; 44962306a36Sopenharmony_ci skb = tipc_buf_acquire(pktsz, GFP_KERNEL); 45062306a36Sopenharmony_ci if (!skb) { 45162306a36Sopenharmony_ci rc = -ENOMEM; 45262306a36Sopenharmony_ci goto error; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci skb_orphan(skb); 45562306a36Sopenharmony_ci __skb_queue_tail(list, skb); 45662306a36Sopenharmony_ci msg_set_type(&pkthdr, FRAGMENT); 45762306a36Sopenharmony_ci msg_set_size(&pkthdr, pktsz); 45862306a36Sopenharmony_ci msg_set_fragm_no(&pkthdr, ++pktno); 45962306a36Sopenharmony_ci skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE); 46062306a36Sopenharmony_ci pktpos = skb->data + INT_H_SIZE; 46162306a36Sopenharmony_ci pktrem = pktsz - INT_H_SIZE; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci } while (1); 46462306a36Sopenharmony_ci msg_set_type(buf_msg(skb), LAST_FRAGMENT); 46562306a36Sopenharmony_ci return dsz; 46662306a36Sopenharmony_cierror: 46762306a36Sopenharmony_ci __skb_queue_purge(list); 46862306a36Sopenharmony_ci __skb_queue_head_init(list); 46962306a36Sopenharmony_ci return rc; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/** 47362306a36Sopenharmony_ci * tipc_msg_bundle - Append contents of a buffer to tail of an existing one 47462306a36Sopenharmony_ci * @bskb: the bundle buffer to append to 47562306a36Sopenharmony_ci * @msg: message to be appended 47662306a36Sopenharmony_ci * @max: max allowable size for the bundle buffer 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * Return: "true" if bundling has been performed, otherwise "false" 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_cistatic bool tipc_msg_bundle(struct sk_buff *bskb, struct tipc_msg *msg, 48162306a36Sopenharmony_ci u32 max) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct tipc_msg *bmsg = buf_msg(bskb); 48462306a36Sopenharmony_ci u32 msz, bsz, offset, pad; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci msz = msg_size(msg); 48762306a36Sopenharmony_ci bsz = msg_size(bmsg); 48862306a36Sopenharmony_ci offset = BUF_ALIGN(bsz); 48962306a36Sopenharmony_ci pad = offset - bsz; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (unlikely(skb_tailroom(bskb) < (pad + msz))) 49262306a36Sopenharmony_ci return false; 49362306a36Sopenharmony_ci if (unlikely(max < (offset + msz))) 49462306a36Sopenharmony_ci return false; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci skb_put(bskb, pad + msz); 49762306a36Sopenharmony_ci skb_copy_to_linear_data_offset(bskb, offset, msg, msz); 49862306a36Sopenharmony_ci msg_set_size(bmsg, offset + msz); 49962306a36Sopenharmony_ci msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1); 50062306a36Sopenharmony_ci return true; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/** 50462306a36Sopenharmony_ci * tipc_msg_try_bundle - Try to bundle a new message to the last one 50562306a36Sopenharmony_ci * @tskb: the last/target message to which the new one will be appended 50662306a36Sopenharmony_ci * @skb: the new message skb pointer 50762306a36Sopenharmony_ci * @mss: max message size (header inclusive) 50862306a36Sopenharmony_ci * @dnode: destination node for the message 50962306a36Sopenharmony_ci * @new_bundle: if this call made a new bundle or not 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * Return: "true" if the new message skb is potential for bundling this time or 51262306a36Sopenharmony_ci * later, in the case a bundling has been done this time, the skb is consumed 51362306a36Sopenharmony_ci * (the skb pointer = NULL). 51462306a36Sopenharmony_ci * Otherwise, "false" if the skb cannot be bundled at all. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_cibool tipc_msg_try_bundle(struct sk_buff *tskb, struct sk_buff **skb, u32 mss, 51762306a36Sopenharmony_ci u32 dnode, bool *new_bundle) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct tipc_msg *msg, *inner, *outer; 52062306a36Sopenharmony_ci u32 tsz; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* First, check if the new buffer is suitable for bundling */ 52362306a36Sopenharmony_ci msg = buf_msg(*skb); 52462306a36Sopenharmony_ci if (msg_user(msg) == MSG_FRAGMENTER) 52562306a36Sopenharmony_ci return false; 52662306a36Sopenharmony_ci if (msg_user(msg) == TUNNEL_PROTOCOL) 52762306a36Sopenharmony_ci return false; 52862306a36Sopenharmony_ci if (msg_user(msg) == BCAST_PROTOCOL) 52962306a36Sopenharmony_ci return false; 53062306a36Sopenharmony_ci if (mss <= INT_H_SIZE + msg_size(msg)) 53162306a36Sopenharmony_ci return false; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Ok, but the last/target buffer can be empty? */ 53462306a36Sopenharmony_ci if (unlikely(!tskb)) 53562306a36Sopenharmony_ci return true; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Is it a bundle already? Try to bundle the new message to it */ 53862306a36Sopenharmony_ci if (msg_user(buf_msg(tskb)) == MSG_BUNDLER) { 53962306a36Sopenharmony_ci *new_bundle = false; 54062306a36Sopenharmony_ci goto bundle; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* Make a new bundle of the two messages if possible */ 54462306a36Sopenharmony_ci tsz = msg_size(buf_msg(tskb)); 54562306a36Sopenharmony_ci if (unlikely(mss < BUF_ALIGN(INT_H_SIZE + tsz) + msg_size(msg))) 54662306a36Sopenharmony_ci return true; 54762306a36Sopenharmony_ci if (unlikely(pskb_expand_head(tskb, INT_H_SIZE, mss - tsz - INT_H_SIZE, 54862306a36Sopenharmony_ci GFP_ATOMIC))) 54962306a36Sopenharmony_ci return true; 55062306a36Sopenharmony_ci inner = buf_msg(tskb); 55162306a36Sopenharmony_ci skb_push(tskb, INT_H_SIZE); 55262306a36Sopenharmony_ci outer = buf_msg(tskb); 55362306a36Sopenharmony_ci tipc_msg_init(msg_prevnode(inner), outer, MSG_BUNDLER, 0, INT_H_SIZE, 55462306a36Sopenharmony_ci dnode); 55562306a36Sopenharmony_ci msg_set_importance(outer, msg_importance(inner)); 55662306a36Sopenharmony_ci msg_set_size(outer, INT_H_SIZE + tsz); 55762306a36Sopenharmony_ci msg_set_msgcnt(outer, 1); 55862306a36Sopenharmony_ci *new_bundle = true; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cibundle: 56162306a36Sopenharmony_ci if (likely(tipc_msg_bundle(tskb, msg, mss))) { 56262306a36Sopenharmony_ci consume_skb(*skb); 56362306a36Sopenharmony_ci *skb = NULL; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci return true; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci/** 56962306a36Sopenharmony_ci * tipc_msg_extract(): extract bundled inner packet from buffer 57062306a36Sopenharmony_ci * @skb: buffer to be extracted from. 57162306a36Sopenharmony_ci * @iskb: extracted inner buffer, to be returned 57262306a36Sopenharmony_ci * @pos: position in outer message of msg to be extracted. 57362306a36Sopenharmony_ci * Returns position of next msg. 57462306a36Sopenharmony_ci * Consumes outer buffer when last packet extracted 57562306a36Sopenharmony_ci * Return: true when there is an extracted buffer, otherwise false 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_cibool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct tipc_msg *hdr, *ihdr; 58062306a36Sopenharmony_ci int imsz; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci *iskb = NULL; 58362306a36Sopenharmony_ci if (unlikely(skb_linearize(skb))) 58462306a36Sopenharmony_ci goto none; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci hdr = buf_msg(skb); 58762306a36Sopenharmony_ci if (unlikely(*pos > (msg_data_sz(hdr) - MIN_H_SIZE))) 58862306a36Sopenharmony_ci goto none; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ihdr = (struct tipc_msg *)(msg_data(hdr) + *pos); 59162306a36Sopenharmony_ci imsz = msg_size(ihdr); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if ((*pos + imsz) > msg_data_sz(hdr)) 59462306a36Sopenharmony_ci goto none; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci *iskb = tipc_buf_acquire(imsz, GFP_ATOMIC); 59762306a36Sopenharmony_ci if (!*iskb) 59862306a36Sopenharmony_ci goto none; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci skb_copy_to_linear_data(*iskb, ihdr, imsz); 60162306a36Sopenharmony_ci if (unlikely(!tipc_msg_validate(iskb))) 60262306a36Sopenharmony_ci goto none; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci *pos += BUF_ALIGN(imsz); 60562306a36Sopenharmony_ci return true; 60662306a36Sopenharmony_cinone: 60762306a36Sopenharmony_ci kfree_skb(skb); 60862306a36Sopenharmony_ci kfree_skb(*iskb); 60962306a36Sopenharmony_ci *iskb = NULL; 61062306a36Sopenharmony_ci return false; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/** 61462306a36Sopenharmony_ci * tipc_msg_reverse(): swap source and destination addresses and add error code 61562306a36Sopenharmony_ci * @own_node: originating node id for reversed message 61662306a36Sopenharmony_ci * @skb: buffer containing message to be reversed; will be consumed 61762306a36Sopenharmony_ci * @err: error code to be set in message, if any 61862306a36Sopenharmony_ci * Replaces consumed buffer with new one when successful 61962306a36Sopenharmony_ci * Return: true if success, otherwise false 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cibool tipc_msg_reverse(u32 own_node, struct sk_buff **skb, int err) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct sk_buff *_skb = *skb; 62462306a36Sopenharmony_ci struct tipc_msg *_hdr, *hdr; 62562306a36Sopenharmony_ci int hlen, dlen; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (skb_linearize(_skb)) 62862306a36Sopenharmony_ci goto exit; 62962306a36Sopenharmony_ci _hdr = buf_msg(_skb); 63062306a36Sopenharmony_ci dlen = min_t(uint, msg_data_sz(_hdr), MAX_FORWARD_SIZE); 63162306a36Sopenharmony_ci hlen = msg_hdr_sz(_hdr); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (msg_dest_droppable(_hdr)) 63462306a36Sopenharmony_ci goto exit; 63562306a36Sopenharmony_ci if (msg_errcode(_hdr)) 63662306a36Sopenharmony_ci goto exit; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* Never return SHORT header */ 63962306a36Sopenharmony_ci if (hlen == SHORT_H_SIZE) 64062306a36Sopenharmony_ci hlen = BASIC_H_SIZE; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* Don't return data along with SYN+, - sender has a clone */ 64362306a36Sopenharmony_ci if (msg_is_syn(_hdr) && err == TIPC_ERR_OVERLOAD) 64462306a36Sopenharmony_ci dlen = 0; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Allocate new buffer to return */ 64762306a36Sopenharmony_ci *skb = tipc_buf_acquire(hlen + dlen, GFP_ATOMIC); 64862306a36Sopenharmony_ci if (!*skb) 64962306a36Sopenharmony_ci goto exit; 65062306a36Sopenharmony_ci memcpy((*skb)->data, _skb->data, msg_hdr_sz(_hdr)); 65162306a36Sopenharmony_ci memcpy((*skb)->data + hlen, msg_data(_hdr), dlen); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Build reverse header in new buffer */ 65462306a36Sopenharmony_ci hdr = buf_msg(*skb); 65562306a36Sopenharmony_ci msg_set_hdr_sz(hdr, hlen); 65662306a36Sopenharmony_ci msg_set_errcode(hdr, err); 65762306a36Sopenharmony_ci msg_set_non_seq(hdr, 0); 65862306a36Sopenharmony_ci msg_set_origport(hdr, msg_destport(_hdr)); 65962306a36Sopenharmony_ci msg_set_destport(hdr, msg_origport(_hdr)); 66062306a36Sopenharmony_ci msg_set_destnode(hdr, msg_prevnode(_hdr)); 66162306a36Sopenharmony_ci msg_set_prevnode(hdr, own_node); 66262306a36Sopenharmony_ci msg_set_orignode(hdr, own_node); 66362306a36Sopenharmony_ci msg_set_size(hdr, hlen + dlen); 66462306a36Sopenharmony_ci skb_orphan(_skb); 66562306a36Sopenharmony_ci kfree_skb(_skb); 66662306a36Sopenharmony_ci return true; 66762306a36Sopenharmony_ciexit: 66862306a36Sopenharmony_ci kfree_skb(_skb); 66962306a36Sopenharmony_ci *skb = NULL; 67062306a36Sopenharmony_ci return false; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cibool tipc_msg_skb_clone(struct sk_buff_head *msg, struct sk_buff_head *cpy) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct sk_buff *skb, *_skb; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci skb_queue_walk(msg, skb) { 67862306a36Sopenharmony_ci _skb = skb_clone(skb, GFP_ATOMIC); 67962306a36Sopenharmony_ci if (!_skb) { 68062306a36Sopenharmony_ci __skb_queue_purge(cpy); 68162306a36Sopenharmony_ci pr_err_ratelimited("Failed to clone buffer chain\n"); 68262306a36Sopenharmony_ci return false; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci __skb_queue_tail(cpy, _skb); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci return true; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci/** 69062306a36Sopenharmony_ci * tipc_msg_lookup_dest(): try to find new destination for named message 69162306a36Sopenharmony_ci * @net: pointer to associated network namespace 69262306a36Sopenharmony_ci * @skb: the buffer containing the message. 69362306a36Sopenharmony_ci * @err: error code to be used by caller if lookup fails 69462306a36Sopenharmony_ci * Does not consume buffer 69562306a36Sopenharmony_ci * Return: true if a destination is found, false otherwise 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_cibool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct tipc_msg *msg = buf_msg(skb); 70062306a36Sopenharmony_ci u32 scope = msg_lookup_scope(msg); 70162306a36Sopenharmony_ci u32 self = tipc_own_addr(net); 70262306a36Sopenharmony_ci u32 inst = msg_nameinst(msg); 70362306a36Sopenharmony_ci struct tipc_socket_addr sk; 70462306a36Sopenharmony_ci struct tipc_uaddr ua; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (!msg_isdata(msg)) 70762306a36Sopenharmony_ci return false; 70862306a36Sopenharmony_ci if (!msg_named(msg)) 70962306a36Sopenharmony_ci return false; 71062306a36Sopenharmony_ci if (msg_errcode(msg)) 71162306a36Sopenharmony_ci return false; 71262306a36Sopenharmony_ci *err = TIPC_ERR_NO_NAME; 71362306a36Sopenharmony_ci if (skb_linearize(skb)) 71462306a36Sopenharmony_ci return false; 71562306a36Sopenharmony_ci msg = buf_msg(skb); 71662306a36Sopenharmony_ci if (msg_reroute_cnt(msg)) 71762306a36Sopenharmony_ci return false; 71862306a36Sopenharmony_ci tipc_uaddr(&ua, TIPC_SERVICE_RANGE, scope, 71962306a36Sopenharmony_ci msg_nametype(msg), inst, inst); 72062306a36Sopenharmony_ci sk.node = tipc_scope2node(net, scope); 72162306a36Sopenharmony_ci if (!tipc_nametbl_lookup_anycast(net, &ua, &sk)) 72262306a36Sopenharmony_ci return false; 72362306a36Sopenharmony_ci msg_incr_reroute_cnt(msg); 72462306a36Sopenharmony_ci if (sk.node != self) 72562306a36Sopenharmony_ci msg_set_prevnode(msg, self); 72662306a36Sopenharmony_ci msg_set_destnode(msg, sk.node); 72762306a36Sopenharmony_ci msg_set_destport(msg, sk.ref); 72862306a36Sopenharmony_ci *err = TIPC_OK; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return true; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci/* tipc_msg_assemble() - assemble chain of fragments into one message 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_cibool tipc_msg_assemble(struct sk_buff_head *list) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct sk_buff *skb, *tmp = NULL; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (skb_queue_len(list) == 1) 74062306a36Sopenharmony_ci return true; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci while ((skb = __skb_dequeue(list))) { 74362306a36Sopenharmony_ci skb->next = NULL; 74462306a36Sopenharmony_ci if (tipc_buf_append(&tmp, &skb)) { 74562306a36Sopenharmony_ci __skb_queue_tail(list, skb); 74662306a36Sopenharmony_ci return true; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci if (!tmp) 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci __skb_queue_purge(list); 75262306a36Sopenharmony_ci __skb_queue_head_init(list); 75362306a36Sopenharmony_ci pr_warn("Failed do assemble buffer\n"); 75462306a36Sopenharmony_ci return false; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/* tipc_msg_reassemble() - clone a buffer chain of fragments and 75862306a36Sopenharmony_ci * reassemble the clones into one message 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_cibool tipc_msg_reassemble(struct sk_buff_head *list, struct sk_buff_head *rcvq) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct sk_buff *skb, *_skb; 76362306a36Sopenharmony_ci struct sk_buff *frag = NULL; 76462306a36Sopenharmony_ci struct sk_buff *head = NULL; 76562306a36Sopenharmony_ci int hdr_len; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* Copy header if single buffer */ 76862306a36Sopenharmony_ci if (skb_queue_len(list) == 1) { 76962306a36Sopenharmony_ci skb = skb_peek(list); 77062306a36Sopenharmony_ci hdr_len = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb)); 77162306a36Sopenharmony_ci _skb = __pskb_copy(skb, hdr_len, GFP_ATOMIC); 77262306a36Sopenharmony_ci if (!_skb) 77362306a36Sopenharmony_ci return false; 77462306a36Sopenharmony_ci __skb_queue_tail(rcvq, _skb); 77562306a36Sopenharmony_ci return true; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Clone all fragments and reassemble */ 77962306a36Sopenharmony_ci skb_queue_walk(list, skb) { 78062306a36Sopenharmony_ci frag = skb_clone(skb, GFP_ATOMIC); 78162306a36Sopenharmony_ci if (!frag) 78262306a36Sopenharmony_ci goto error; 78362306a36Sopenharmony_ci frag->next = NULL; 78462306a36Sopenharmony_ci if (tipc_buf_append(&head, &frag)) 78562306a36Sopenharmony_ci break; 78662306a36Sopenharmony_ci if (!head) 78762306a36Sopenharmony_ci goto error; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci __skb_queue_tail(rcvq, frag); 79062306a36Sopenharmony_ci return true; 79162306a36Sopenharmony_cierror: 79262306a36Sopenharmony_ci pr_warn("Failed do clone local mcast rcv buffer\n"); 79362306a36Sopenharmony_ci kfree_skb(head); 79462306a36Sopenharmony_ci return false; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cibool tipc_msg_pskb_copy(u32 dst, struct sk_buff_head *msg, 79862306a36Sopenharmony_ci struct sk_buff_head *cpy) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct sk_buff *skb, *_skb; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci skb_queue_walk(msg, skb) { 80362306a36Sopenharmony_ci _skb = pskb_copy(skb, GFP_ATOMIC); 80462306a36Sopenharmony_ci if (!_skb) { 80562306a36Sopenharmony_ci __skb_queue_purge(cpy); 80662306a36Sopenharmony_ci return false; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci msg_set_destnode(buf_msg(_skb), dst); 80962306a36Sopenharmony_ci __skb_queue_tail(cpy, _skb); 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci return true; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci/* tipc_skb_queue_sorted(); sort pkt into list according to sequence number 81562306a36Sopenharmony_ci * @list: list to be appended to 81662306a36Sopenharmony_ci * @seqno: sequence number of buffer to add 81762306a36Sopenharmony_ci * @skb: buffer to add 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_cibool __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, 82062306a36Sopenharmony_ci struct sk_buff *skb) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct sk_buff *_skb, *tmp; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (skb_queue_empty(list) || less(seqno, buf_seqno(skb_peek(list)))) { 82562306a36Sopenharmony_ci __skb_queue_head(list, skb); 82662306a36Sopenharmony_ci return true; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (more(seqno, buf_seqno(skb_peek_tail(list)))) { 83062306a36Sopenharmony_ci __skb_queue_tail(list, skb); 83162306a36Sopenharmony_ci return true; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci skb_queue_walk_safe(list, _skb, tmp) { 83562306a36Sopenharmony_ci if (more(seqno, buf_seqno(_skb))) 83662306a36Sopenharmony_ci continue; 83762306a36Sopenharmony_ci if (seqno == buf_seqno(_skb)) 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci __skb_queue_before(list, _skb, skb); 84062306a36Sopenharmony_ci return true; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci kfree_skb(skb); 84362306a36Sopenharmony_ci return false; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_civoid tipc_skb_reject(struct net *net, int err, struct sk_buff *skb, 84762306a36Sopenharmony_ci struct sk_buff_head *xmitq) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci if (tipc_msg_reverse(tipc_own_addr(net), &skb, err)) 85062306a36Sopenharmony_ci __skb_queue_tail(xmitq, skb); 85162306a36Sopenharmony_ci} 852