diff options
Diffstat (limited to 'net/tipc/msg.c')
| -rw-r--r-- | net/tipc/msg.c | 78 | 
1 files changed, 48 insertions, 30 deletions
| diff --git a/net/tipc/msg.c b/net/tipc/msg.c index b61891054709..f48e5857210f 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -499,54 +499,56 @@ bool tipc_msg_make_bundle(struct sk_buff **skb,  struct tipc_msg *msg,  /**   * 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; may be replaced. + * @skb:  buffer containing message to be reversed; will be consumed   * @err:  error code to be set in message, if any - * Consumes buffer at failure + * Replaces consumed buffer with new one when successful   * Returns 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; -	struct tipc_msg ohdr; -	int dlen; +	struct tipc_msg *_hdr, *hdr; +	int hlen, dlen;  	if (skb_linearize(_skb))  		goto exit; -	hdr = buf_msg(_skb); -	dlen = min_t(uint, msg_data_sz(hdr), MAX_FORWARD_SIZE); -	if (msg_dest_droppable(hdr)) +	_hdr = buf_msg(_skb); +	dlen = min_t(uint, msg_data_sz(_hdr), MAX_FORWARD_SIZE); +	hlen = msg_hdr_sz(_hdr); + +	if (msg_dest_droppable(_hdr))  		goto exit; -	if (msg_errcode(hdr)) +	if (msg_errcode(_hdr))  		goto exit; -	/* Take a copy of original header before altering message */ -	memcpy(&ohdr, hdr, msg_hdr_sz(hdr)); - -	/* Never return SHORT header; expand by replacing buffer if necessary */ -	if (msg_short(hdr)) { -		*skb = tipc_buf_acquire(BASIC_H_SIZE + dlen, GFP_ATOMIC); -		if (!*skb) -			goto exit; -		memcpy((*skb)->data + BASIC_H_SIZE, msg_data(hdr), dlen); -		kfree_skb(_skb); -		_skb = *skb; -		hdr = buf_msg(_skb); -		memcpy(hdr, &ohdr, BASIC_H_SIZE); -		msg_set_hdr_sz(hdr, BASIC_H_SIZE); -	} +	/* Never return SHORT header */ +	if (hlen == SHORT_H_SIZE) +		hlen = BASIC_H_SIZE; + +	/* Don't return data along with SYN+, - sender has a clone */ +	if (msg_is_syn(_hdr) && err == TIPC_ERR_OVERLOAD) +		dlen = 0; + +	/* Allocate new buffer to return */ +	*skb = tipc_buf_acquire(hlen + dlen, GFP_ATOMIC); +	if (!*skb) +		goto exit; +	memcpy((*skb)->data, _skb->data, msg_hdr_sz(_hdr)); +	memcpy((*skb)->data + hlen, msg_data(_hdr), dlen); -	/* Now reverse the concerned fields */ +	/* Build reverse header in new buffer */ +	hdr = buf_msg(*skb); +	msg_set_hdr_sz(hdr, hlen);  	msg_set_errcode(hdr, err);  	msg_set_non_seq(hdr, 0); -	msg_set_origport(hdr, msg_destport(&ohdr)); -	msg_set_destport(hdr, msg_origport(&ohdr)); -	msg_set_destnode(hdr, msg_prevnode(&ohdr)); +	msg_set_origport(hdr, msg_destport(_hdr)); +	msg_set_destport(hdr, msg_origport(_hdr)); +	msg_set_destnode(hdr, msg_prevnode(_hdr));  	msg_set_prevnode(hdr, own_node);  	msg_set_orignode(hdr, own_node); -	msg_set_size(hdr, msg_hdr_sz(hdr) + dlen); -	skb_trim(_skb, msg_size(hdr)); +	msg_set_size(hdr, hlen + dlen);  	skb_orphan(_skb); +	kfree_skb(_skb);  	return true;  exit:  	kfree_skb(_skb); @@ -554,6 +556,22 @@ exit:  	return false;  } +bool tipc_msg_skb_clone(struct sk_buff_head *msg, struct sk_buff_head *cpy) +{ +	struct sk_buff *skb, *_skb; + +	skb_queue_walk(msg, skb) { +		_skb = skb_clone(skb, GFP_ATOMIC); +		if (!_skb) { +			__skb_queue_purge(cpy); +			pr_err_ratelimited("Failed to clone buffer chain\n"); +			return false; +		} +		__skb_queue_tail(cpy, _skb); +	} +	return true; +} +  /**   * tipc_msg_lookup_dest(): try to find new destination for named message   * @skb: the buffer containing the message. |