/* * net/tipc/msg.c: TIPC message header routines * * Copyright (c) 2000-2006, 2014-2015, Ericsson AB * Copyright (c) 2005, 2010-2011, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE.
*/
/** * tipc_buf_acquire - creates a TIPC message buffer * @size: message size (including TIPC header) * @gfp: memory allocation flags * * Return: a new buffer with data pointers set to the specified size. * * NOTE: * Headroom is reserved to allow prepending of a data link header. * There may also be unrequested tailroom present at the buffer's end.
*/ struct sk_buff *tipc_buf_acquire(u32 size, gfp_t gfp)
{ struct sk_buff *skb;
/* tipc_buf_append(): Append a buffer to the fragment list of another buffer * @*headbuf: in: NULL for first frag, otherwise value returned from prev call * out: set when successful non-complete reassembly, otherwise NULL * @*buf: in: the buffer to append. Always defined * out: head buf after successful complete reassembly, otherwise NULL * Returns 1 when reassembly complete, otherwise 0
*/ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
{ struct sk_buff *head = *headbuf; struct sk_buff *frag = *buf; struct sk_buff *tail = NULL; struct tipc_msg *msg;
u32 fragid; int delta; bool headstolen;
/** * tipc_msg_append(): Append data to tail of an existing buffer queue * @_hdr: header to be used * @m: the data to be appended * @mss: max allowable size of buffer * @dlen: size of data to be appended * @txq: queue to append to * * Return: the number of 1k blocks appended or errno value
*/ int tipc_msg_append(struct tipc_msg *_hdr, struct msghdr *m, int dlen, int mss, struct sk_buff_head *txq)
{ struct sk_buff *skb; int accounted, total, curr; int mlen, cpy, rem = dlen; struct tipc_msg *hdr;
do { if (!skb || skb->len >= mss) {
skb = tipc_buf_acquire(mss, GFP_KERNEL); if (unlikely(!skb)) return -ENOMEM;
skb_orphan(skb);
skb_trim(skb, MIN_H_SIZE);
hdr = buf_msg(skb);
skb_copy_to_linear_data(skb, _hdr, MIN_H_SIZE);
msg_set_hdr_sz(hdr, MIN_H_SIZE);
msg_set_size(hdr, MIN_H_SIZE);
__skb_queue_tail(txq, skb);
total += 1;
}
hdr = buf_msg(skb);
curr = msg_blocks(hdr);
mlen = msg_size(hdr);
cpy = min_t(size_t, rem, mss - mlen); if (cpy != copy_from_iter(skb->data + mlen, cpy, &m->msg_iter)) return -EFAULT;
msg_set_size(hdr, mlen + cpy);
skb_put(skb, cpy);
rem -= cpy;
total += msg_blocks(hdr) - curr;
} while (rem > 0); return total - accounted;
}
/* tipc_msg_validate - validate basic format of received message * * This routine ensures a TIPC message has an acceptable header, and at least * as much data as the header indicates it should. The routine also ensures * that the entire message header is stored in the main fragment of the message * buffer, to simplify future access to message header fields. * * Note: Having extra info present in the message header or data areas is OK. * TIPC will ignore the excess, under the assumption that it is optional info * introduced by a later release of the protocol.
*/ bool tipc_msg_validate(struct sk_buff **_skb)
{ struct sk_buff *skb = *_skb; struct tipc_msg *hdr; int msz, hsz;
/* Ensure that flow control ratio condition is satisfied */ if (unlikely(skb->truesize / buf_roundup_len(skb) >= 4)) {
skb = skb_copy_expand(skb, BUF_HEADROOM, 0, GFP_ATOMIC); if (!skb) returnfalse;
kfree_skb(*_skb);
*_skb = skb;
}
if (unlikely(TIPC_SKB_CB(skb)->validated)) returntrue;
if (unlikely(!pskb_may_pull(skb, MIN_H_SIZE))) returnfalse;
hsz = msg_hdr_sz(buf_msg(skb)); if (unlikely(hsz < MIN_H_SIZE) || (hsz > MAX_H_SIZE)) returnfalse; if (unlikely(!pskb_may_pull(skb, hsz))) returnfalse;
hdr = buf_msg(skb); if (unlikely(msg_version(hdr) != TIPC_VERSION)) returnfalse;
msz = msg_size(hdr); if (unlikely(msz < hsz)) returnfalse; if (unlikely((msz - hsz) > TIPC_MAX_USER_MSG_SIZE)) returnfalse; if (unlikely(skb->len < msz)) returnfalse;
TIPC_SKB_CB(skb)->validated = 1; returntrue;
}
/** * tipc_msg_fragment - build a fragment skb list for TIPC message * * @skb: TIPC message skb * @hdr: internal msg header to be put on the top of the fragments * @pktmax: max size of a fragment incl. the header * @frags: returned fragment skb list * * Return: 0 if the fragmentation is successful, otherwise: -EINVAL * or -ENOMEM
*/ int tipc_msg_fragment(struct sk_buff *skb, conststruct tipc_msg *hdr, int pktmax, struct sk_buff_head *frags)
{ int pktno, nof_fragms, dsz, dmax, eat; struct tipc_msg *_hdr; struct sk_buff *_skb;
u8 *data;
/* Non-linear buffer? */ if (skb_linearize(skb)) return -ENOMEM;
data = (u8 *)skb->data;
dsz = msg_size(buf_msg(skb));
dmax = pktmax - INT_H_SIZE; if (dsz <= dmax || !dmax) return -EINVAL;
nof_fragms = dsz / dmax + 1; for (pktno = 1; pktno <= nof_fragms; pktno++) { if (pktno < nof_fragms)
eat = dmax; else
eat = dsz % dmax; /* Allocate a new fragment */
_skb = tipc_buf_acquire(INT_H_SIZE + eat, GFP_ATOMIC); if (!_skb) goto error;
skb_orphan(_skb);
__skb_queue_tail(frags, _skb); /* Copy header & data to the fragment */
skb_copy_to_linear_data(_skb, hdr, INT_H_SIZE);
skb_copy_to_linear_data_offset(_skb, INT_H_SIZE, data, eat);
data += eat; /* Update the fragment's header */
_hdr = buf_msg(_skb);
msg_set_fragm_no(_hdr, pktno);
msg_set_nof_fragms(_hdr, nof_fragms);
msg_set_size(_hdr, INT_H_SIZE + eat);
} return 0;
/** * tipc_msg_build - create buffer chain containing specified header and data * @mhdr: Message header, to be prepended to data * @m: User message * @offset: buffer offset for fragmented messages (FIXME) * @dsz: Total length of user data * @pktmax: Max packet size that can be used * @list: Buffer or chain of buffers to be returned to caller * * Note that the recursive call we are making here is safe, since it can * logically go only one further level down. * * Return: message data size or errno: -ENOMEM, -EFAULT
*/ int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, int dsz, int pktmax, struct sk_buff_head *list)
{ int mhsz = msg_hdr_sz(mhdr); struct tipc_msg pkthdr; int msz = mhsz + dsz; int pktrem = pktmax; struct sk_buff *skb; int drem = dsz; int pktno = 1; char *pktpos; int pktsz; int rc;
msg_set_size(mhdr, msz);
/* No fragmentation needed? */ if (likely(msz <= pktmax)) {
skb = tipc_buf_acquire(msz, GFP_KERNEL);
/* Fall back to smaller MTU if node local message */ if (unlikely(!skb)) { if (pktmax != MAX_MSG_SIZE) return -ENOMEM;
rc = tipc_msg_build(mhdr, m, offset, dsz,
one_page_mtu, list); if (rc != dsz) return rc; if (tipc_msg_assemble(list)) return dsz; return -ENOMEM;
}
skb_orphan(skb);
__skb_queue_tail(list, skb);
skb_copy_to_linear_data(skb, mhdr, mhsz);
pktpos = skb->data + mhsz; if (copy_from_iter_full(pktpos, dsz, &m->msg_iter)) return dsz;
rc = -EFAULT; goto error;
}
/** * tipc_msg_bundle - Append contents of a buffer to tail of an existing one * @bskb: the bundle buffer to append to * @msg: message to be appended * @max: max allowable size for the bundle buffer * * Return: "true" if bundling has been performed, otherwise "false"
*/ staticbool tipc_msg_bundle(struct sk_buff *bskb, struct tipc_msg *msg,
u32 max)
{ struct tipc_msg *bmsg = buf_msg(bskb);
u32 msz, bsz, offset, pad;
/** * tipc_msg_try_bundle - Try to bundle a new message to the last one * @tskb: the last/target message to which the new one will be appended * @skb: the new message skb pointer * @mss: max message size (header inclusive) * @dnode: destination node for the message * @new_bundle: if this call made a new bundle or not * * Return: "true" if the new message skb is potential for bundling this time or * later, in the case a bundling has been done this time, the skb is consumed * (the skb pointer = NULL). * Otherwise, "false" if the skb cannot be bundled at all.
*/ bool tipc_msg_try_bundle(struct sk_buff *tskb, struct sk_buff **skb, u32 mss,
u32 dnode, bool *new_bundle)
{ struct tipc_msg *msg, *inner, *outer;
u32 tsz;
/* First, check if the new buffer is suitable for bundling */
msg = buf_msg(*skb); if (msg_user(msg) == MSG_FRAGMENTER) returnfalse; if (msg_user(msg) == TUNNEL_PROTOCOL) returnfalse; if (msg_user(msg) == BCAST_PROTOCOL) returnfalse; if (mss <= INT_H_SIZE + msg_size(msg)) returnfalse;
/* Ok, but the last/target buffer can be empty? */ if (unlikely(!tskb)) returntrue;
/* Is it a bundle already? Try to bundle the new message to it */ if (msg_user(buf_msg(tskb)) == MSG_BUNDLER) {
*new_bundle = false; goto bundle;
}
/* Make a new bundle of the two messages if possible */
tsz = msg_size(buf_msg(tskb)); if (unlikely(mss < BUF_ALIGN(INT_H_SIZE + tsz) + msg_size(msg))) returntrue; if (unlikely(pskb_expand_head(tskb, INT_H_SIZE, mss - tsz - INT_H_SIZE,
GFP_ATOMIC))) returntrue;
inner = buf_msg(tskb);
skb_push(tskb, INT_H_SIZE);
outer = buf_msg(tskb);
tipc_msg_init(msg_prevnode(inner), outer, MSG_BUNDLER, 0, INT_H_SIZE,
dnode);
msg_set_importance(outer, msg_importance(inner));
msg_set_size(outer, INT_H_SIZE + tsz);
msg_set_msgcnt(outer, 1);
*new_bundle = true;
/** * tipc_msg_extract(): extract bundled inner packet from buffer * @skb: buffer to be extracted from. * @iskb: extracted inner buffer, to be returned * @pos: position in outer message of msg to be extracted. * Returns position of next msg. * Consumes outer buffer when last packet extracted * Return: true when there is an extracted buffer, otherwise false
*/ bool tipc_msg_extract(struct sk_buff *skb, struct sk_buff **iskb, int *pos)
{ struct tipc_msg *hdr, *ihdr; int imsz;
*iskb = NULL; if (unlikely(skb_linearize(skb))) goto none;
/** * tipc_msg_reverse(): swap source and destination addresses and add error code * @own_node: originating node id for reversed message * @skb: buffer containing message to be reversed; will be consumed * @err: error code to be set in message, if any * Replaces consumed buffer with new one when successful * Return: true if success, otherwise false
*/ bool tipc_msg_reverse(u32 own_node, struct sk_buff **skb, int err)
{ struct sk_buff *_skb = *skb; struct tipc_msg *_hdr, *hdr; int hlen, dlen;
/** * tipc_msg_lookup_dest(): try to find new destination for named message * @net: pointer to associated network namespace * @skb: the buffer containing the message. * @err: error code to be used by caller if lookup fails * Does not consume buffer * Return: true if a destination is found, false otherwise
*/ bool tipc_msg_lookup_dest(struct net *net, struct sk_buff *skb, int *err)
{ struct tipc_msg *msg = buf_msg(skb);
u32 scope = msg_lookup_scope(msg);
u32 self = tipc_own_addr(net);
u32 inst = msg_nameinst(msg); struct tipc_socket_addr sk; struct tipc_uaddr ua;
if (!msg_isdata(msg)) returnfalse; if (!msg_named(msg)) returnfalse; if (msg_errcode(msg)) returnfalse;
*err = TIPC_ERR_NO_NAME; if (skb_linearize(skb)) returnfalse;
msg = buf_msg(skb); if (msg_reroute_cnt(msg)) returnfalse;
tipc_uaddr(&ua, TIPC_SERVICE_RANGE, scope,
msg_nametype(msg), inst, inst);
sk.node = tipc_scope2node(net, scope); if (!tipc_nametbl_lookup_anycast(net, &ua, &sk)) returnfalse;
msg_incr_reroute_cnt(msg); if (sk.node != self)
msg_set_prevnode(msg, self);
msg_set_destnode(msg, sk.node);
msg_set_destport(msg, sk.ref);
*err = TIPC_OK;
returntrue;
}
/* tipc_msg_assemble() - assemble chain of fragments into one message
*/ bool tipc_msg_assemble(struct sk_buff_head *list)
{ struct sk_buff *skb, *tmp = NULL;
if (skb_queue_len(list) == 1) returntrue;
while ((skb = __skb_dequeue(list))) {
skb->next = NULL; if (tipc_buf_append(&tmp, &skb)) {
__skb_queue_tail(list, skb); returntrue;
} if (!tmp) break;
}
__skb_queue_purge(list);
__skb_queue_head_init(list);
pr_warn("Failed do assemble buffer\n"); returnfalse;
}
/* tipc_msg_reassemble() - clone a buffer chain of fragments and * reassemble the clones into one message
*/ bool tipc_msg_reassemble(struct sk_buff_head *list, struct sk_buff_head *rcvq)
{ struct sk_buff *skb, *_skb; struct sk_buff *frag = NULL; struct sk_buff *head = NULL; int hdr_len;
/* Copy header if single buffer */ if (skb_queue_len(list) == 1) {
skb = skb_peek(list);
hdr_len = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb));
_skb = __pskb_copy(skb, hdr_len, GFP_ATOMIC); if (!_skb) returnfalse;
__skb_queue_tail(rcvq, _skb); returntrue;
}
/* Clone all fragments and reassemble */
skb_queue_walk(list, skb) {
frag = skb_clone(skb, GFP_ATOMIC); if (!frag) goto error;
frag->next = NULL; if (tipc_buf_append(&head, &frag)) break; if (!head) goto error;
}
__skb_queue_tail(rcvq, frag); returntrue;
error:
pr_warn("Failed do clone local mcast rcv buffer\n");
kfree_skb(head); returnfalse;
}
/* tipc_skb_queue_sorted(); sort pkt into list according to sequence number * @list: list to be appended to * @seqno: sequence number of buffer to add * @skb: buffer to add
*/ bool __tipc_skb_queue_sorted(struct sk_buff_head *list, u16 seqno, struct sk_buff *skb)
{ struct sk_buff *_skb, *tmp;
if (skb_queue_empty(list) || less(seqno, buf_seqno(skb_peek(list)))) {
__skb_queue_head(list, skb); returntrue;
}
if (more(seqno, buf_seqno(skb_peek_tail(list)))) {
__skb_queue_tail(list, skb); returntrue;
}
void tipc_skb_reject(struct net *net, int err, struct sk_buff *skb, struct sk_buff_head *xmitq)
{ if (tipc_msg_reverse(tipc_own_addr(net), &skb, err))
__skb_queue_tail(xmitq, skb);
}
Messung V0.5
[ Original von:0.17Diese Quellcodebibliothek enthält Beispiele in vielen Programmiersprachen.
Man kann per Verzeichnistruktur darin navigieren.
Der Code wird farblich markiert angezeigt.
Datei übertragen
]