chunk.c 10.1 KB
Newer Older
1
/* SCTP kernel implementation
Linus Torvalds's avatar
Linus Torvalds committed
2
3
 * (C) Copyright IBM Corp. 2003, 2004
 *
4
 * This file is part of the SCTP kernel implementation
Linus Torvalds's avatar
Linus Torvalds committed
5
 *
6
 * This file contains the code relating the chunk abstraction.
Linus Torvalds's avatar
Linus Torvalds committed
7
 *
8
 * This SCTP implementation is free software;
Linus Torvalds's avatar
Linus Torvalds committed
9
10
11
12
13
 * you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
14
 * This SCTP implementation is distributed in the hope that it
Linus Torvalds's avatar
Linus Torvalds committed
15
16
17
18
19
20
 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
 *                 ************************
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
21
22
 * along with GNU CC; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/>.
Linus Torvalds's avatar
Linus Torvalds committed
23
24
25
 *
 * Please send any bug reports or fixes you make to the
 * email address(es):
26
 *    lksctp developers <linux-sctp@vger.kernel.org>
Linus Torvalds's avatar
Linus Torvalds committed
27
28
29
30
31
32
 *
 * Written or modified by:
 *    Jon Grimm             <jgrimm@us.ibm.com>
 *    Sridhar Samudrala     <sri@us.ibm.com>
 */

33
34
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

Linus Torvalds's avatar
Linus Torvalds committed
35
36
37
38
39
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/skbuff.h>
40
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <net/sock.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>

/* This file is mostly in anticipation of future work, but initially
 * populate with fragment tracking for an outbound message.
 */

/* Initialize datamsg from memory. */
static void sctp_datamsg_init(struct sctp_datamsg *msg)
{
	atomic_set(&msg->refcnt, 1);
	msg->send_failed = 0;
	msg->send_error = 0;
	msg->can_abandon = 0;
56
	msg->can_delay = 1;
Linus Torvalds's avatar
Linus Torvalds committed
57
58
59
60
61
	msg->expires_at = 0;
	INIT_LIST_HEAD(&msg->chunks);
}

/* Allocate and initialize datamsg. */
62
static struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
Linus Torvalds's avatar
Linus Torvalds committed
63
64
65
{
	struct sctp_datamsg *msg;
	msg = kmalloc(sizeof(struct sctp_datamsg), gfp);
66
	if (msg) {
Linus Torvalds's avatar
Linus Torvalds committed
67
		sctp_datamsg_init(msg);
68
69
		SCTP_DBG_OBJCNT_INC(datamsg);
	}
Linus Torvalds's avatar
Linus Torvalds committed
70
71
72
	return msg;
}

73
74
75
76
77
78
79
80
81
82
83
84
85
void sctp_datamsg_free(struct sctp_datamsg *msg)
{
	struct sctp_chunk *chunk;

	/* This doesn't have to be a _safe vairant because
	 * sctp_chunk_free() only drops the refs.
	 */
	list_for_each_entry(chunk, &msg->chunks, frag_list)
		sctp_chunk_free(chunk);

	sctp_datamsg_put(msg);
}

Linus Torvalds's avatar
Linus Torvalds committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* Final destructruction of datamsg memory. */
static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
{
	struct list_head *pos, *temp;
	struct sctp_chunk *chunk;
	struct sctp_sock *sp;
	struct sctp_ulpevent *ev;
	struct sctp_association *asoc = NULL;
	int error = 0, notify;

	/* If we failed, we may need to notify. */
	notify = msg->send_failed ? -1 : 0;

	/* Release all references. */
	list_for_each_safe(pos, temp, &msg->chunks) {
		list_del_init(pos);
		chunk = list_entry(pos, struct sctp_chunk, frag_list);
		/* Check whether we _really_ need to notify. */
		if (notify < 0) {
			asoc = chunk->asoc;
			if (msg->send_error)
				error = msg->send_error;
			else
				error = asoc->outqueue.error;

			sp = sctp_sk(asoc->base.sk);
			notify = sctp_ulpevent_type_enabled(SCTP_SEND_FAILED,
							    &sp->subscribe);
		}

		/* Generate a SEND FAILED event only if enabled. */
		if (notify > 0) {
			int sent;
			if (chunk->has_tsn)
				sent = SCTP_DATA_SENT;
			else
				sent = SCTP_DATA_UNSENT;

			ev = sctp_ulpevent_make_send_failed(asoc, chunk, sent,
							    error, GFP_ATOMIC);
			if (ev)
				sctp_ulpq_tail_event(&asoc->ulpq, ev);
		}

		sctp_chunk_put(chunk);
	}

	SCTP_DBG_OBJCNT_DEC(datamsg);
	kfree(msg);
}

/* Hold a reference. */
static void sctp_datamsg_hold(struct sctp_datamsg *msg)
{
	atomic_inc(&msg->refcnt);
}

/* Release a reference. */
void sctp_datamsg_put(struct sctp_datamsg *msg)
{
	if (atomic_dec_and_test(&msg->refcnt))
		sctp_datamsg_destroy(msg);
}

/* Assign a chunk to this datamsg. */
static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chunk)
{
	sctp_datamsg_hold(msg);
	chunk->msg = msg;
}


/* A data chunk can have a maximum payload of (2^16 - 20).  Break
 * down any such message into smaller chunks.  Opportunistically, fragment
 * the chunks down to the current MTU constraints.  We may get refragmented
 * later if the PMTU changes, but it is _much better_ to fragment immediately
 * with a reasonable guess than always doing our fragmentation on the
 * soft-interrupt.
 */
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
					    struct sctp_sndrcvinfo *sinfo,
167
					    struct iov_iter *from)
Linus Torvalds's avatar
Linus Torvalds committed
168
169
170
{
	int max, whole, i, offset, over, err;
	int len, first_len;
171
	int max_data;
Linus Torvalds's avatar
Linus Torvalds committed
172
173
174
	struct sctp_chunk *chunk;
	struct sctp_datamsg *msg;
	struct list_head *pos, *temp;
175
	size_t msg_len = iov_iter_count(from);
Linus Torvalds's avatar
Linus Torvalds committed
176
177
178
179
	__u8 frag;

	msg = sctp_datamsg_new(GFP_KERNEL);
	if (!msg)
180
		return ERR_PTR(-ENOMEM);
Linus Torvalds's avatar
Linus Torvalds committed
181
182
183
184
185
186
187
188
189

	/* Note: Calculate this outside of the loop, so that all fragments
	 * have the same expiration.
	 */
	if (sinfo->sinfo_timetolive) {
		/* sinfo_timetolive is in milliseconds */
		msg->expires_at = jiffies +
				    msecs_to_jiffies(sinfo->sinfo_timetolive);
		msg->can_abandon = 1;
190
191
192

		pr_debug("%s: msg:%p expires_at:%ld jiffies:%ld\n", __func__,
			 msg, msg->expires_at, jiffies);
Linus Torvalds's avatar
Linus Torvalds committed
193
194
	}

195
196
197
	/* This is the biggest possible DATA chunk that can fit into
	 * the packet
	 */
198
	max_data = (asoc->pathmtu -
199
		sctp_sk(asoc->base.sk)->pf->af->net_header_len -
200
		sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk)) & ~3;
Linus Torvalds's avatar
Linus Torvalds committed
201

202
	max = asoc->frag_point;
203
	/* If the the peer requested that we authenticate DATA chunks
204
	 * we need to account for bundling of the AUTH chunks along with
205
206
207
208
209
210
	 * DATA.
	 */
	if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) {
		struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc);

		if (hmac_desc)
211
212
			max_data -= SCTP_PAD4(sizeof(sctp_auth_chunk_t) +
					      hmac_desc->hmac_len);
213
214
	}

215
216
217
218
	/* Now, check if we need to reduce our max */
	if (max > max_data)
		max = max_data;

Linus Torvalds's avatar
Linus Torvalds committed
219
220
221
	whole = 0;
	first_len = max;

222
223
224
225
226
227
228
229
230
231
	/* Check to see if we have a pending SACK and try to let it be bundled
	 * with this message.  Do this if we don't have any data queued already.
	 * To check that, look at out_qlen and retransmit list.
	 * NOTE: we will not reduce to account for SACK, if the message would
	 * not have been fragmented.
	 */
	if (timer_pending(&asoc->timers[SCTP_EVENT_TIMEOUT_SACK]) &&
	    asoc->outqueue.out_qlen == 0 &&
	    list_empty(&asoc->outqueue.retransmit) &&
	    msg_len > max)
232
		max_data -= SCTP_PAD4(sizeof(sctp_sack_chunk_t));
233

Linus Torvalds's avatar
Linus Torvalds committed
234
	/* Encourage Cookie-ECHO bundling. */
235
	if (asoc->state < SCTP_STATE_COOKIE_ECHOED)
236
		max_data -= SCTP_ARBITRARY_COOKIE_ECHO_LEN;
Linus Torvalds's avatar
Linus Torvalds committed
237

238
239
240
	/* Now that we adjusted completely, reset first_len */
	if (first_len > max_data)
		first_len = max_data;
241
242
243
244
245

	/* Account for a different sized first fragment */
	if (msg_len >= first_len) {
		msg_len -= first_len;
		whole = 1;
246
		msg->can_delay = 0;
Linus Torvalds's avatar
Linus Torvalds committed
247
248
249
250
251
252
253
254
	}

	/* How many full sized?  How many bytes leftover? */
	whole += msg_len / max;
	over = msg_len % max;
	offset = 0;

	if ((whole > 1) || (whole && over))
255
		SCTP_INC_STATS(sock_net(asoc->base.sk), SCTP_MIB_FRAGUSRMSGS);
Linus Torvalds's avatar
Linus Torvalds committed
256
257

	/* Create chunks for all the full sized DATA chunks. */
258
	for (i = 0, len = first_len; i < whole; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
259
260
261
262
263
		frag = SCTP_DATA_MIDDLE_FRAG;

		if (0 == i)
			frag |= SCTP_DATA_FIRST_FRAG;

264
		if ((i == (whole - 1)) && !over) {
Linus Torvalds's avatar
Linus Torvalds committed
265
266
			frag |= SCTP_DATA_LAST_FRAG;

267
268
269
270
271
272
273
274
275
			/* The application requests to set the I-bit of the
			 * last DATA chunk of a user message when providing
			 * the user message to the SCTP implementation.
			 */
			if ((sinfo->sinfo_flags & SCTP_EOF) ||
			    (sinfo->sinfo_flags & SCTP_SACK_IMMEDIATELY))
				frag |= SCTP_DATA_SACK_IMM;
		}

276
277
		chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag,
						 0, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
278

279
280
		if (!chunk) {
			err = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
281
			goto errout;
282
283
		}

284
		err = sctp_user_addto_chunk(chunk, len, from);
Linus Torvalds's avatar
Linus Torvalds committed
285
		if (err < 0)
286
			goto errout_chunk_free;
Linus Torvalds's avatar
Linus Torvalds committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

		/* Put the chunk->skb back into the form expected by send.  */
		__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
			   - (__u8 *)chunk->skb->data);

		sctp_datamsg_assign(msg, chunk);
		list_add_tail(&chunk->frag_list, &msg->chunks);

		/* The first chunk, the first chunk was likely short
		 * to allow bundling, so reset to full size.
		 */
		if (0 == i)
			len = max;
	}

	/* .. now the leftover bytes. */
	if (over) {
		if (!whole)
			frag = SCTP_DATA_NOT_FRAG;
		else
			frag = SCTP_DATA_LAST_FRAG;

309
310
311
312
		if ((sinfo->sinfo_flags & SCTP_EOF) ||
		    (sinfo->sinfo_flags & SCTP_SACK_IMMEDIATELY))
			frag |= SCTP_DATA_SACK_IMM;

313
314
		chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag,
						 0, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
315

316
317
		if (!chunk) {
			err = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
318
			goto errout;
319
		}
Linus Torvalds's avatar
Linus Torvalds committed
320

321
		err = sctp_user_addto_chunk(chunk, over, from);
Linus Torvalds's avatar
Linus Torvalds committed
322
323
324
325
326

		/* Put the chunk->skb back into the form expected by send.  */
		__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
			   - (__u8 *)chunk->skb->data);
		if (err < 0)
327
			goto errout_chunk_free;
Linus Torvalds's avatar
Linus Torvalds committed
328
329
330
331
332
333
334

		sctp_datamsg_assign(msg, chunk);
		list_add_tail(&chunk->frag_list, &msg->chunks);
	}

	return msg;

335
336
337
errout_chunk_free:
	sctp_chunk_free(chunk);

Linus Torvalds's avatar
Linus Torvalds committed
338
339
340
341
342
343
errout:
	list_for_each_safe(pos, temp, &msg->chunks) {
		list_del_init(pos);
		chunk = list_entry(pos, struct sctp_chunk, frag_list);
		sctp_chunk_free(chunk);
	}
344
	sctp_datamsg_put(msg);
345
	return ERR_PTR(err);
Linus Torvalds's avatar
Linus Torvalds committed
346
347
348
349
350
}

/* Check whether this message has expired. */
int sctp_chunk_abandoned(struct sctp_chunk *chunk)
{
Xin Long's avatar
Xin Long committed
351
352
353
354
355
356
357
358
359
	if (!chunk->asoc->prsctp_enable ||
	    !SCTP_PR_POLICY(chunk->sinfo.sinfo_flags)) {
		struct sctp_datamsg *msg = chunk->msg;

		if (!msg->can_abandon)
			return 0;

		if (time_after(jiffies, msg->expires_at))
			return 1;
Linus Torvalds's avatar
Linus Torvalds committed
360
361

		return 0;
Xin Long's avatar
Xin Long committed
362
	}
Linus Torvalds's avatar
Linus Torvalds committed
363

Xin Long's avatar
Xin Long committed
364
365
366
367
368
369
	if (SCTP_PR_TTL_ENABLED(chunk->sinfo.sinfo_flags) &&
	    time_after(jiffies, chunk->prsctp_param)) {
		if (chunk->sent_count)
			chunk->asoc->abandoned_sent[SCTP_PR_INDEX(TTL)]++;
		else
			chunk->asoc->abandoned_unsent[SCTP_PR_INDEX(TTL)]++;
Linus Torvalds's avatar
Linus Torvalds committed
370
		return 1;
Xin Long's avatar
Xin Long committed
371
372
373
374
	} else if (SCTP_PR_RTX_ENABLED(chunk->sinfo.sinfo_flags) &&
		   chunk->sent_count > chunk->prsctp_param) {
		chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++;
		return 1;
Xin Long's avatar
Xin Long committed
375
	}
Xin Long's avatar
Xin Long committed
376
	/* PRIO policy is processed by sendmsg, not here */
Linus Torvalds's avatar
Linus Torvalds committed
377
378
379
380
381
382
383
384
385
386

	return 0;
}

/* This chunk (and consequently entire message) has failed in its sending. */
void sctp_chunk_fail(struct sctp_chunk *chunk, int error)
{
	chunk->msg->send_failed = 1;
	chunk->msg->send_error = error;
}