Blame view

net/sctp/chunk.c 9.66 KB
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
1
  /* SCTP kernel implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
   * (C) Copyright IBM Corp. 2003, 2004
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
4
   * This file is part of the SCTP kernel implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   *
59c51591a   Michael Opdenacker   Fix occurrences o...
6
   * This file contains the code relating the chunk abstraction.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
8
   * This SCTP implementation is free software;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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.
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
14
   * This SCTP implementation is distributed in the hope that it
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
   * 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
   * along with GNU CC; see the file COPYING.  If not, write to
   * the Free Software Foundation, 59 Temple Place - Suite 330,
   * Boston, MA 02111-1307, USA.
   *
   * Please send any bug reports or fixes you make to the
   * email address(es):
   *    lksctp developers <lksctp-developers@lists.sourceforge.net>
   *
   * Or submit a bug report through the following website:
   *    http://www.sf.net/projects/lksctp
   *
   * Written or modified by:
   *    Jon Grimm             <jgrimm@us.ibm.com>
   *    Sridhar Samudrala     <sri@us.ibm.com>
   *
   * Any bugs reported given to us we will try to fix... any fixes shared will
   * be incorporated into the next SCTP release.
   */
145ce502e   Joe Perches   net/sctp: Use pr_...
39
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/net.h>
  #include <linux/inet.h>
  #include <linux/skbuff.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
45
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  #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;
0e3aef8d0   Vlad Yasevich   sctp: Tag message...
61
  	msg->can_delay = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
  	msg->expires_at = 0;
  	INIT_LIST_HEAD(&msg->chunks);
  }
  
  /* Allocate and initialize datamsg. */
dd0fc66fb   Al Viro   [PATCH] gfp flags...
67
  SCTP_STATIC struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
  {
  	struct sctp_datamsg *msg;
  	msg = kmalloc(sizeof(struct sctp_datamsg), gfp);
e8c38751b   Li Zefan   SCTP: fix wrong d...
71
  	if (msg) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  		sctp_datamsg_init(msg);
e8c38751b   Li Zefan   SCTP: fix wrong d...
73
74
  		SCTP_DBG_OBJCNT_INC(datamsg);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
  	return msg;
  }
9c5c62be2   Vlad Yasevich   sctp: Send user m...
77
78
79
80
81
82
83
84
85
86
87
88
  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);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /* 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);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  /* 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,
  					    struct msghdr *msgh, int msg_len)
  {
  	int max, whole, i, offset, over, err;
  	int len, first_len;
3e62abf92   Vlad Yasevich   sctp: Fix data se...
173
  	int max_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  	struct sctp_chunk *chunk;
  	struct sctp_datamsg *msg;
  	struct list_head *pos, *temp;
  	__u8 frag;
  
  	msg = sctp_datamsg_new(GFP_KERNEL);
  	if (!msg)
  		return NULL;
  
  	/* 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;
  		SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld
  ",
0dc47877a   Harvey Harrison   net: replace rema...
193
  				  __func__, msg, msg->expires_at, jiffies);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	}
3e62abf92   Vlad Yasevich   sctp: Fix data se...
195
196
197
198
199
200
  	/* This is the biggest possible DATA chunk that can fit into
  	 * the packet
  	 */
  	max_data = asoc->pathmtu -
  		sctp_sk(asoc->base.sk)->pf->af->net_header_len -
  		sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201

3e62abf92   Vlad Yasevich   sctp: Fix data se...
202
  	max = asoc->frag_point;
4cd57c807   Vlad Yasevich   [SCTP]: Enable th...
203
204
205
206
207
208
209
210
  	/* If the the peer requested that we authenticate DATA chunks
  	 * we need to accound for bundling of the AUTH chunks along with
  	 * DATA.
  	 */
  	if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) {
  		struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc);
  
  		if (hmac_desc)
3e62abf92   Vlad Yasevich   sctp: Fix data se...
211
  			max_data -= WORD_ROUND(sizeof(sctp_auth_chunk_t) +
4cd57c807   Vlad Yasevich   [SCTP]: Enable th...
212
213
  					    hmac_desc->hmac_len);
  	}
3e62abf92   Vlad Yasevich   sctp: Fix data se...
214
215
216
  	/* Now, check if we need to reduce our max */
  	if (max > max_data)
  		max = max_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
  	whole = 0;
  	first_len = max;
5d7ff261e   Vlad Yasevich   sctp: Try to enco...
219
220
221
222
223
224
225
226
227
228
229
  	/* 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)
  		max_data -= WORD_ROUND(sizeof(sctp_sack_chunk_t));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	/* Encourage Cookie-ECHO bundling. */
5d7ff261e   Vlad Yasevich   sctp: Try to enco...
231
  	if (asoc->state < SCTP_STATE_COOKIE_ECHOED)
3e62abf92   Vlad Yasevich   sctp: Fix data se...
232
  		max_data -= SCTP_ARBITRARY_COOKIE_ECHO_LEN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233

5d7ff261e   Vlad Yasevich   sctp: Try to enco...
234
235
236
  	/* Now that we adjusted completely, reset first_len */
  	if (first_len > max_data)
  		first_len = max_data;
3e62abf92   Vlad Yasevich   sctp: Fix data se...
237
238
239
240
241
  
  	/* Account for a different sized first fragment */
  	if (msg_len >= first_len) {
  		msg_len -= first_len;
  		whole = 1;
0e3aef8d0   Vlad Yasevich   sctp: Tag message...
242
  		msg->can_delay = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  	}
  
  	/* How many full sized?  How many bytes leftover? */
  	whole += msg_len / max;
  	over = msg_len % max;
  	offset = 0;
  
  	if ((whole > 1) || (whole && over))
  		SCTP_INC_STATS_USER(SCTP_MIB_FRAGUSRMSGS);
  
  	/* Create chunks for all the full sized DATA chunks. */
  	for (i=0, len=first_len; i < whole; i++) {
  		frag = SCTP_DATA_MIDDLE_FRAG;
  
  		if (0 == i)
  			frag |= SCTP_DATA_FIRST_FRAG;
b93d64717   Wei Yongjun   sctp: implement t...
259
  		if ((i == (whole - 1)) && !over) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  			frag |= SCTP_DATA_LAST_FRAG;
b93d64717   Wei Yongjun   sctp: implement t...
261
262
263
264
265
266
267
268
  			/* 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;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  		chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
  
  		if (!chunk)
  			goto errout;
  		err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov);
  		if (err < 0)
  			goto errout;
  
  		offset += len;
  
  		/* 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;
b93d64717   Wei Yongjun   sctp: implement t...
299
300
301
  		if ((sinfo->sinfo_flags & SCTP_EOF) ||
  		    (sinfo->sinfo_flags & SCTP_SACK_IMMEDIATELY))
  			frag |= SCTP_DATA_SACK_IMM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  		chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
  
  		if (!chunk)
  			goto errout;
  
  		err = sctp_user_addto_chunk(chunk, offset, over,msgh->msg_iov);
  
  		/* 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)
  			goto errout;
  
  		sctp_datamsg_assign(msg, chunk);
  		list_add_tail(&chunk->frag_list, &msg->chunks);
  	}
  
  	return msg;
  
  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);
  	}
80445cfb2   Florian Westphal   [SCTP]: Remove re...
327
  	sctp_datamsg_put(msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  	return NULL;
  }
  
  /* Check whether this message has expired. */
  int sctp_chunk_abandoned(struct sctp_chunk *chunk)
  {
  	struct sctp_datamsg *msg = chunk->msg;
  
  	if (!msg->can_abandon)
  		return 0;
  
  	if (time_after(jiffies, msg->expires_at))
  		return 1;
  
  	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;
  }