162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* SCTP kernel implementation
362306a36Sopenharmony_ci * (C) Copyright IBM Corp. 2003, 2004
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file is part of the SCTP kernel implementation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file contains the code relating the chunk abstraction.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Please send any bug reports or fixes you make to the
1062306a36Sopenharmony_ci * email address(es):
1162306a36Sopenharmony_ci *    lksctp developers <linux-sctp@vger.kernel.org>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Written or modified by:
1462306a36Sopenharmony_ci *    Jon Grimm             <jgrimm@us.ibm.com>
1562306a36Sopenharmony_ci *    Sridhar Samudrala     <sri@us.ibm.com>
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/net.h>
2362306a36Sopenharmony_ci#include <linux/inet.h>
2462306a36Sopenharmony_ci#include <linux/skbuff.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <net/sock.h>
2762306a36Sopenharmony_ci#include <net/sctp/sctp.h>
2862306a36Sopenharmony_ci#include <net/sctp/sm.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* This file is mostly in anticipation of future work, but initially
3162306a36Sopenharmony_ci * populate with fragment tracking for an outbound message.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Initialize datamsg from memory. */
3562306a36Sopenharmony_cistatic void sctp_datamsg_init(struct sctp_datamsg *msg)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	refcount_set(&msg->refcnt, 1);
3862306a36Sopenharmony_ci	msg->send_failed = 0;
3962306a36Sopenharmony_ci	msg->send_error = 0;
4062306a36Sopenharmony_ci	msg->can_delay = 1;
4162306a36Sopenharmony_ci	msg->abandoned = 0;
4262306a36Sopenharmony_ci	msg->expires_at = 0;
4362306a36Sopenharmony_ci	INIT_LIST_HEAD(&msg->chunks);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Allocate and initialize datamsg. */
4762306a36Sopenharmony_cistatic struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct sctp_datamsg *msg;
5062306a36Sopenharmony_ci	msg = kmalloc(sizeof(struct sctp_datamsg), gfp);
5162306a36Sopenharmony_ci	if (msg) {
5262306a36Sopenharmony_ci		sctp_datamsg_init(msg);
5362306a36Sopenharmony_ci		SCTP_DBG_OBJCNT_INC(datamsg);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci	return msg;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_civoid sctp_datamsg_free(struct sctp_datamsg *msg)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct sctp_chunk *chunk;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* This doesn't have to be a _safe vairant because
6362306a36Sopenharmony_ci	 * sctp_chunk_free() only drops the refs.
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	list_for_each_entry(chunk, &msg->chunks, frag_list)
6662306a36Sopenharmony_ci		sctp_chunk_free(chunk);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	sctp_datamsg_put(msg);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Final destructruction of datamsg memory. */
7262306a36Sopenharmony_cistatic void sctp_datamsg_destroy(struct sctp_datamsg *msg)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
7562306a36Sopenharmony_ci	struct list_head *pos, *temp;
7662306a36Sopenharmony_ci	struct sctp_chunk *chunk;
7762306a36Sopenharmony_ci	struct sctp_ulpevent *ev;
7862306a36Sopenharmony_ci	int error, sent;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Release all references. */
8162306a36Sopenharmony_ci	list_for_each_safe(pos, temp, &msg->chunks) {
8262306a36Sopenharmony_ci		list_del_init(pos);
8362306a36Sopenharmony_ci		chunk = list_entry(pos, struct sctp_chunk, frag_list);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		if (!msg->send_failed) {
8662306a36Sopenharmony_ci			sctp_chunk_put(chunk);
8762306a36Sopenharmony_ci			continue;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		asoc = chunk->asoc;
9162306a36Sopenharmony_ci		error = msg->send_error ?: asoc->outqueue.error;
9262306a36Sopenharmony_ci		sent = chunk->has_tsn ? SCTP_DATA_SENT : SCTP_DATA_UNSENT;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		if (sctp_ulpevent_type_enabled(asoc->subscribe,
9562306a36Sopenharmony_ci					       SCTP_SEND_FAILED)) {
9662306a36Sopenharmony_ci			ev = sctp_ulpevent_make_send_failed(asoc, chunk, sent,
9762306a36Sopenharmony_ci							    error, GFP_ATOMIC);
9862306a36Sopenharmony_ci			if (ev)
9962306a36Sopenharmony_ci				asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		if (sctp_ulpevent_type_enabled(asoc->subscribe,
10362306a36Sopenharmony_ci					       SCTP_SEND_FAILED_EVENT)) {
10462306a36Sopenharmony_ci			ev = sctp_ulpevent_make_send_failed_event(asoc, chunk,
10562306a36Sopenharmony_ci								  sent, error,
10662306a36Sopenharmony_ci								  GFP_ATOMIC);
10762306a36Sopenharmony_ci			if (ev)
10862306a36Sopenharmony_ci				asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		sctp_chunk_put(chunk);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	SCTP_DBG_OBJCNT_DEC(datamsg);
11562306a36Sopenharmony_ci	kfree(msg);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* Hold a reference. */
11962306a36Sopenharmony_cistatic void sctp_datamsg_hold(struct sctp_datamsg *msg)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	refcount_inc(&msg->refcnt);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Release a reference. */
12562306a36Sopenharmony_civoid sctp_datamsg_put(struct sctp_datamsg *msg)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	if (refcount_dec_and_test(&msg->refcnt))
12862306a36Sopenharmony_ci		sctp_datamsg_destroy(msg);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* Assign a chunk to this datamsg. */
13262306a36Sopenharmony_cistatic void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chunk)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	sctp_datamsg_hold(msg);
13562306a36Sopenharmony_ci	chunk->msg = msg;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* A data chunk can have a maximum payload of (2^16 - 20).  Break
14062306a36Sopenharmony_ci * down any such message into smaller chunks.  Opportunistically, fragment
14162306a36Sopenharmony_ci * the chunks down to the current MTU constraints.  We may get refragmented
14262306a36Sopenharmony_ci * later if the PMTU changes, but it is _much better_ to fragment immediately
14362306a36Sopenharmony_ci * with a reasonable guess than always doing our fragmentation on the
14462306a36Sopenharmony_ci * soft-interrupt.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistruct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
14762306a36Sopenharmony_ci					    struct sctp_sndrcvinfo *sinfo,
14862306a36Sopenharmony_ci					    struct iov_iter *from)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	size_t len, first_len, max_data, remaining;
15162306a36Sopenharmony_ci	size_t msg_len = iov_iter_count(from);
15262306a36Sopenharmony_ci	struct sctp_shared_key *shkey = NULL;
15362306a36Sopenharmony_ci	struct list_head *pos, *temp;
15462306a36Sopenharmony_ci	struct sctp_chunk *chunk;
15562306a36Sopenharmony_ci	struct sctp_datamsg *msg;
15662306a36Sopenharmony_ci	int err;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	msg = sctp_datamsg_new(GFP_KERNEL);
15962306a36Sopenharmony_ci	if (!msg)
16062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* Note: Calculate this outside of the loop, so that all fragments
16362306a36Sopenharmony_ci	 * have the same expiration.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	if (asoc->peer.prsctp_capable && sinfo->sinfo_timetolive &&
16662306a36Sopenharmony_ci	    (SCTP_PR_TTL_ENABLED(sinfo->sinfo_flags) ||
16762306a36Sopenharmony_ci	     !SCTP_PR_POLICY(sinfo->sinfo_flags)))
16862306a36Sopenharmony_ci		msg->expires_at = jiffies +
16962306a36Sopenharmony_ci				  msecs_to_jiffies(sinfo->sinfo_timetolive);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* This is the biggest possible DATA chunk that can fit into
17262306a36Sopenharmony_ci	 * the packet
17362306a36Sopenharmony_ci	 */
17462306a36Sopenharmony_ci	max_data = asoc->frag_point;
17562306a36Sopenharmony_ci	if (unlikely(!max_data)) {
17662306a36Sopenharmony_ci		max_data = sctp_min_frag_point(sctp_sk(asoc->base.sk),
17762306a36Sopenharmony_ci					       sctp_datachk_len(&asoc->stream));
17862306a36Sopenharmony_ci		pr_warn_ratelimited("%s: asoc:%p frag_point is zero, forcing max_data to default minimum (%zu)",
17962306a36Sopenharmony_ci				    __func__, asoc, max_data);
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* If the peer requested that we authenticate DATA chunks
18362306a36Sopenharmony_ci	 * we need to account for bundling of the AUTH chunks along with
18462306a36Sopenharmony_ci	 * DATA.
18562306a36Sopenharmony_ci	 */
18662306a36Sopenharmony_ci	if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) {
18762306a36Sopenharmony_ci		struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		if (hmac_desc)
19062306a36Sopenharmony_ci			max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) +
19162306a36Sopenharmony_ci					      hmac_desc->hmac_len);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		if (sinfo->sinfo_tsn &&
19462306a36Sopenharmony_ci		    sinfo->sinfo_ssn != asoc->active_key_id) {
19562306a36Sopenharmony_ci			shkey = sctp_auth_get_shkey(asoc, sinfo->sinfo_ssn);
19662306a36Sopenharmony_ci			if (!shkey) {
19762306a36Sopenharmony_ci				err = -EINVAL;
19862306a36Sopenharmony_ci				goto errout;
19962306a36Sopenharmony_ci			}
20062306a36Sopenharmony_ci		} else {
20162306a36Sopenharmony_ci			shkey = asoc->shkey;
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Set first_len and then account for possible bundles on first frag */
20662306a36Sopenharmony_ci	first_len = max_data;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Check to see if we have a pending SACK and try to let it be bundled
20962306a36Sopenharmony_ci	 * with this message.  Do this if we don't have any data queued already.
21062306a36Sopenharmony_ci	 * To check that, look at out_qlen and retransmit list.
21162306a36Sopenharmony_ci	 * NOTE: we will not reduce to account for SACK, if the message would
21262306a36Sopenharmony_ci	 * not have been fragmented.
21362306a36Sopenharmony_ci	 */
21462306a36Sopenharmony_ci	if (timer_pending(&asoc->timers[SCTP_EVENT_TIMEOUT_SACK]) &&
21562306a36Sopenharmony_ci	    asoc->outqueue.out_qlen == 0 &&
21662306a36Sopenharmony_ci	    list_empty(&asoc->outqueue.retransmit) &&
21762306a36Sopenharmony_ci	    msg_len > max_data)
21862306a36Sopenharmony_ci		first_len -= SCTP_PAD4(sizeof(struct sctp_sack_chunk));
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Encourage Cookie-ECHO bundling. */
22162306a36Sopenharmony_ci	if (asoc->state < SCTP_STATE_COOKIE_ECHOED)
22262306a36Sopenharmony_ci		first_len -= SCTP_ARBITRARY_COOKIE_ECHO_LEN;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Account for a different sized first fragment */
22562306a36Sopenharmony_ci	if (msg_len >= first_len) {
22662306a36Sopenharmony_ci		msg->can_delay = 0;
22762306a36Sopenharmony_ci		if (msg_len > first_len)
22862306a36Sopenharmony_ci			SCTP_INC_STATS(asoc->base.net,
22962306a36Sopenharmony_ci				       SCTP_MIB_FRAGUSRMSGS);
23062306a36Sopenharmony_ci	} else {
23162306a36Sopenharmony_ci		/* Which may be the only one... */
23262306a36Sopenharmony_ci		first_len = msg_len;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Create chunks for all DATA chunks. */
23662306a36Sopenharmony_ci	for (remaining = msg_len; remaining; remaining -= len) {
23762306a36Sopenharmony_ci		u8 frag = SCTP_DATA_MIDDLE_FRAG;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		if (remaining == msg_len) {
24062306a36Sopenharmony_ci			/* First frag, which may also be the last */
24162306a36Sopenharmony_ci			frag |= SCTP_DATA_FIRST_FRAG;
24262306a36Sopenharmony_ci			len = first_len;
24362306a36Sopenharmony_ci		} else {
24462306a36Sopenharmony_ci			/* Middle frags */
24562306a36Sopenharmony_ci			len = max_data;
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		if (len >= remaining) {
24962306a36Sopenharmony_ci			/* Last frag, which may also be the first */
25062306a36Sopenharmony_ci			len = remaining;
25162306a36Sopenharmony_ci			frag |= SCTP_DATA_LAST_FRAG;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci			/* The application requests to set the I-bit of the
25462306a36Sopenharmony_ci			 * last DATA chunk of a user message when providing
25562306a36Sopenharmony_ci			 * the user message to the SCTP implementation.
25662306a36Sopenharmony_ci			 */
25762306a36Sopenharmony_ci			if ((sinfo->sinfo_flags & SCTP_EOF) ||
25862306a36Sopenharmony_ci			    (sinfo->sinfo_flags & SCTP_SACK_IMMEDIATELY))
25962306a36Sopenharmony_ci				frag |= SCTP_DATA_SACK_IMM;
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		chunk = asoc->stream.si->make_datafrag(asoc, sinfo, len, frag,
26362306a36Sopenharmony_ci						       GFP_KERNEL);
26462306a36Sopenharmony_ci		if (!chunk) {
26562306a36Sopenharmony_ci			err = -ENOMEM;
26662306a36Sopenharmony_ci			goto errout;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		err = sctp_user_addto_chunk(chunk, len, from);
27062306a36Sopenharmony_ci		if (err < 0)
27162306a36Sopenharmony_ci			goto errout_chunk_free;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		chunk->shkey = shkey;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		/* Put the chunk->skb back into the form expected by send.  */
27662306a36Sopenharmony_ci		__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr -
27762306a36Sopenharmony_ci				       chunk->skb->data);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		sctp_datamsg_assign(msg, chunk);
28062306a36Sopenharmony_ci		list_add_tail(&chunk->frag_list, &msg->chunks);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return msg;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cierrout_chunk_free:
28662306a36Sopenharmony_ci	sctp_chunk_free(chunk);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cierrout:
28962306a36Sopenharmony_ci	list_for_each_safe(pos, temp, &msg->chunks) {
29062306a36Sopenharmony_ci		list_del_init(pos);
29162306a36Sopenharmony_ci		chunk = list_entry(pos, struct sctp_chunk, frag_list);
29262306a36Sopenharmony_ci		sctp_chunk_free(chunk);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	sctp_datamsg_put(msg);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return ERR_PTR(err);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/* Check whether this message has expired. */
30062306a36Sopenharmony_ciint sctp_chunk_abandoned(struct sctp_chunk *chunk)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	if (!chunk->asoc->peer.prsctp_capable)
30362306a36Sopenharmony_ci		return 0;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (chunk->msg->abandoned)
30662306a36Sopenharmony_ci		return 1;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (!chunk->has_tsn &&
30962306a36Sopenharmony_ci	    !(chunk->chunk_hdr->flags & SCTP_DATA_FIRST_FRAG))
31062306a36Sopenharmony_ci		return 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (SCTP_PR_TTL_ENABLED(chunk->sinfo.sinfo_flags) &&
31362306a36Sopenharmony_ci	    time_after(jiffies, chunk->msg->expires_at)) {
31462306a36Sopenharmony_ci		struct sctp_stream_out *streamout =
31562306a36Sopenharmony_ci			SCTP_SO(&chunk->asoc->stream,
31662306a36Sopenharmony_ci				chunk->sinfo.sinfo_stream);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		if (chunk->sent_count) {
31962306a36Sopenharmony_ci			chunk->asoc->abandoned_sent[SCTP_PR_INDEX(TTL)]++;
32062306a36Sopenharmony_ci			streamout->ext->abandoned_sent[SCTP_PR_INDEX(TTL)]++;
32162306a36Sopenharmony_ci		} else {
32262306a36Sopenharmony_ci			chunk->asoc->abandoned_unsent[SCTP_PR_INDEX(TTL)]++;
32362306a36Sopenharmony_ci			streamout->ext->abandoned_unsent[SCTP_PR_INDEX(TTL)]++;
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci		chunk->msg->abandoned = 1;
32662306a36Sopenharmony_ci		return 1;
32762306a36Sopenharmony_ci	} else if (SCTP_PR_RTX_ENABLED(chunk->sinfo.sinfo_flags) &&
32862306a36Sopenharmony_ci		   chunk->sent_count > chunk->sinfo.sinfo_timetolive) {
32962306a36Sopenharmony_ci		struct sctp_stream_out *streamout =
33062306a36Sopenharmony_ci			SCTP_SO(&chunk->asoc->stream,
33162306a36Sopenharmony_ci				chunk->sinfo.sinfo_stream);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++;
33462306a36Sopenharmony_ci		streamout->ext->abandoned_sent[SCTP_PR_INDEX(RTX)]++;
33562306a36Sopenharmony_ci		chunk->msg->abandoned = 1;
33662306a36Sopenharmony_ci		return 1;
33762306a36Sopenharmony_ci	} else if (!SCTP_PR_POLICY(chunk->sinfo.sinfo_flags) &&
33862306a36Sopenharmony_ci		   chunk->msg->expires_at &&
33962306a36Sopenharmony_ci		   time_after(jiffies, chunk->msg->expires_at)) {
34062306a36Sopenharmony_ci		chunk->msg->abandoned = 1;
34162306a36Sopenharmony_ci		return 1;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci	/* PRIO policy is processed by sendmsg, not here */
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/* This chunk (and consequently entire message) has failed in its sending. */
34962306a36Sopenharmony_civoid sctp_chunk_fail(struct sctp_chunk *chunk, int error)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	chunk->msg->send_failed = 1;
35262306a36Sopenharmony_ci	chunk->msg->send_error = error;
35362306a36Sopenharmony_ci}
354