Blame view

net/sctp/ulpqueue.c 29.2 KB
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
1
  /* SCTP kernel implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
   * (C) Copyright IBM Corp. 2001, 2004
   * Copyright (c) 1999-2000 Cisco, Inc.
   * Copyright (c) 1999-2001 Motorola, Inc.
   * Copyright (c) 2001 Intel Corp.
   * Copyright (c) 2001 Nokia, Inc.
   * Copyright (c) 2001 La Monte H.P. Yarroll
   *
   * This abstraction carries sctp events to the ULP (sockets).
   *
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
11
   * This SCTP implementation is free software;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
16
   * 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...
17
   * This SCTP implementation is distributed in the hope that it
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
   * 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>
   *    La Monte H.P. Yarroll <piggy@acm.org>
   *    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.
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
43
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
48
49
50
51
52
  #include <linux/types.h>
  #include <linux/skbuff.h>
  #include <net/sock.h>
  #include <net/sctp/structs.h>
  #include <net/sctp/sctp.h>
  #include <net/sctp/sm.h>
  
  /* Forward declarations for internal helpers.  */
  static struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
8728b834b   David S. Miller   [NET]: Kill skb->...
53
  					      struct sctp_ulpevent *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  static struct sctp_ulpevent * sctp_ulpq_order(struct sctp_ulpq *,
8728b834b   David S. Miller   [NET]: Kill skb->...
55
  					      struct sctp_ulpevent *);
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
56
  static void sctp_ulpq_reasm_drain(struct sctp_ulpq *ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
62
63
64
65
66
67
68
69
  
  /* 1st Level Abstractions */
  
  /* Initialize a ULP queue from a block of memory.  */
  struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq,
  				 struct sctp_association *asoc)
  {
  	memset(ulpq, 0, sizeof(struct sctp_ulpq));
  
  	ulpq->asoc = asoc;
  	skb_queue_head_init(&ulpq->reasm);
  	skb_queue_head_init(&ulpq->lobby);
  	ulpq->pd_mode  = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
  
  	return ulpq;
  }
  
  
  /* Flush the reassembly and ordering queues.  */
0b58a8114   Vlad Yasevich   [SCTP]: Clean up ...
76
  void sctp_ulpq_flush(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  {
  	struct sk_buff *skb;
  	struct sctp_ulpevent *event;
  
  	while ((skb = __skb_dequeue(&ulpq->lobby)) != NULL) {
  		event = sctp_skb2event(skb);
  		sctp_ulpevent_free(event);
  	}
  
  	while ((skb = __skb_dequeue(&ulpq->reasm)) != NULL) {
  		event = sctp_skb2event(skb);
  		sctp_ulpevent_free(event);
  	}
  
  }
  
  /* Dispose of a ulpqueue.  */
  void sctp_ulpq_free(struct sctp_ulpq *ulpq)
  {
  	sctp_ulpq_flush(ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
  }
  
  /* Process an incoming DATA chunk.  */
  int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
dd0fc66fb   Al Viro   [PATCH] gfp flags...
101
  			gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
  {
  	struct sk_buff_head temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  	struct sctp_ulpevent *event;
d003b41b8   Lee A. Roberts   sctp: fix associa...
105
  	int event_eor = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  	/* Create an event from the incoming chunk. */
  	event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp);
  	if (!event)
  		return -ENOMEM;
  
  	/* Do reassembly if needed.  */
  	event = sctp_ulpq_reasm(ulpq, event);
  
  	/* Do ordering if needed.  */
  	if ((event) && (event->msg_flags & MSG_EOR)){
  		/* Create a temporary list to collect chunks on.  */
  		skb_queue_head_init(&temp);
  		__skb_queue_tail(&temp, sctp_event2skb(event));
  
  		event = sctp_ulpq_order(ulpq, event);
  	}
8728b834b   David S. Miller   [NET]: Kill skb->...
123
124
125
  	/* Send event to the ULP.  'event' is the sctp_ulpevent for
  	 * very first SKB on the 'temp' list.
  	 */
d003b41b8   Lee A. Roberts   sctp: fix associa...
126
127
  	if (event) {
  		event_eor = (event->msg_flags & MSG_EOR) ? 1 : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  		sctp_ulpq_tail_event(ulpq, event);
d003b41b8   Lee A. Roberts   sctp: fix associa...
129
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

d003b41b8   Lee A. Roberts   sctp: fix associa...
131
  	return event_eor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
  }
  
  /* Add a new event for propagation to the ULP.  */
  /* Clear the partial delivery mode for this socket.   Note: This
   * assumes that no association is currently in partial delivery mode.
   */
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
138
  int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
  {
  	struct sctp_sock *sp = sctp_sk(sk);
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
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
167
168
169
170
  	if (atomic_dec_and_test(&sp->pd_mode)) {
  		/* This means there are no other associations in PD, so
  		 * we can go ahead and clear out the lobby in one shot
  		 */
  		if (!skb_queue_empty(&sp->pd_lobby)) {
  			struct list_head *list;
  			sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue);
  			list = (struct list_head *)&sctp_sk(sk)->pd_lobby;
  			INIT_LIST_HEAD(list);
  			return 1;
  		}
  	} else {
  		/* There are other associations in PD, so we only need to
  		 * pull stuff out of the lobby that belongs to the
  		 * associations that is exiting PD (all of its notifications
  		 * are posted here).
  		 */
  		if (!skb_queue_empty(&sp->pd_lobby) && asoc) {
  			struct sk_buff *skb, *tmp;
  			struct sctp_ulpevent *event;
  
  			sctp_skb_for_each(skb, &sp->pd_lobby, tmp) {
  				event = sctp_skb2event(skb);
  				if (event->asoc == asoc) {
  					__skb_unlink(skb, &sp->pd_lobby);
  					__skb_queue_tail(&sk->sk_receive_queue,
  							 skb);
  				}
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	}
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
172

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
  	return 0;
  }
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
175
176
177
178
179
180
181
182
  /* Set the pd_mode on the socket and ulpq */
  static void sctp_ulpq_set_pd(struct sctp_ulpq *ulpq)
  {
  	struct sctp_sock *sp = sctp_sk(ulpq->asoc->base.sk);
  
  	atomic_inc(&sp->pd_mode);
  	ulpq->pd_mode = 1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
186
  /* Clear the pd_mode and restart any pending messages waiting for delivery. */
  static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq)
  {
  	ulpq->pd_mode = 0;
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
187
  	sctp_ulpq_reasm_drain(ulpq);
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
188
  	return sctp_clear_pd(ulpq->asoc->base.sk, ulpq->asoc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  }
8728b834b   David S. Miller   [NET]: Kill skb->...
190
191
192
  /* If the SKB of 'event' is on a list, it is the first such member
   * of that list.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
  int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
  {
  	struct sock *sk = ulpq->asoc->base.sk;
8728b834b   David S. Miller   [NET]: Kill skb->...
196
197
  	struct sk_buff_head *queue, *skb_list;
  	struct sk_buff *skb = sctp_event2skb(event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  	int clear_pd = 0;
8728b834b   David S. Miller   [NET]: Kill skb->...
199
  	skb_list = (struct sk_buff_head *) skb->prev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  	/* If the socket is just going to throw this away, do not
  	 * even try to deliver it.
  	 */
  	if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
  		goto out_free;
  
  	/* Check if the user wishes to receive this event.  */
  	if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
  		goto out_free;
  
  	/* If we are in partial delivery mode, post to the lobby until
  	 * partial delivery is cleared, unless, of course _this_ is
  	 * the association the cause of the partial delivery.
  	 */
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
214
  	if (atomic_read(&sctp_sk(sk)->pd_mode) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  		queue = &sk->sk_receive_queue;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
  	} else {
  		if (ulpq->pd_mode) {
  			/* If the association is in partial delivery, we
  			 * need to finish delivering the partially processed
  			 * packet before passing any other data.  This is
  			 * because we don't truly support stream interleaving.
  			 */
  			if ((event->msg_flags & MSG_NOTIFICATION) ||
  			    (SCTP_DATA_NOT_FRAG ==
  				    (event->msg_flags & SCTP_DATA_FRAG_MASK)))
  				queue = &sctp_sk(sk)->pd_lobby;
  			else {
  				clear_pd = event->msg_flags & MSG_EOR;
  				queue = &sk->sk_receive_queue;
  			}
  		} else {
  			/*
  			 * If fragment interleave is enabled, we
25985edce   Lucas De Marchi   Fix common misspe...
234
  			 * can queue this to the receive queue instead
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
235
236
237
238
239
240
  			 * of the lobby.
  			 */
  			if (sctp_sk(sk)->frag_interleave)
  				queue = &sk->sk_receive_queue;
  			else
  				queue = &sctp_sk(sk)->pd_lobby;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  		}
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
242
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
244
245
246
  
  	/* If we are harvesting multiple skbs they will be
  	 * collected on a list.
  	 */
8728b834b   David S. Miller   [NET]: Kill skb->...
247
248
  	if (skb_list)
  		sctp_skb_list_tail(skb_list, queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  	else
8728b834b   David S. Miller   [NET]: Kill skb->...
250
  		__skb_queue_tail(queue, skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
255
256
257
258
259
260
261
262
263
  
  	/* Did we just complete partial delivery and need to get
  	 * rolling again?  Move pending data to the receive
  	 * queue.
  	 */
  	if (clear_pd)
  		sctp_ulpq_clear_pd(ulpq);
  
  	if (queue == &sk->sk_receive_queue)
  		sk->sk_data_ready(sk, 0);
  	return 1;
  
  out_free:
8728b834b   David S. Miller   [NET]: Kill skb->...
264
265
  	if (skb_list)
  		sctp_queue_purge_ulpevents(skb_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
  	else
  		sctp_ulpevent_free(event);
8728b834b   David S. Miller   [NET]: Kill skb->...
268

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
  	return 0;
  }
  
  /* 2nd Level Abstractions */
  
  /* Helper function to store chunks that need to be reassembled.  */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
275
  static void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  					 struct sctp_ulpevent *event)
  {
  	struct sk_buff *pos;
  	struct sctp_ulpevent *cevent;
  	__u32 tsn, ctsn;
  
  	tsn = event->tsn;
  
  	/* See if it belongs at the end. */
  	pos = skb_peek_tail(&ulpq->reasm);
  	if (!pos) {
  		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
  		return;
  	}
  
  	/* Short circuit just dropping it at the end. */
  	cevent = sctp_skb2event(pos);
  	ctsn = cevent->tsn;
  	if (TSN_lt(ctsn, tsn)) {
  		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
  		return;
  	}
  
  	/* Find the right place in this list. We store them by TSN.  */
  	skb_queue_walk(&ulpq->reasm, pos) {
  		cevent = sctp_skb2event(pos);
  		ctsn = cevent->tsn;
  
  		if (TSN_lt(tsn, ctsn))
  			break;
  	}
  
  	/* Insert before pos. */
43f59c893   David S. Miller   net: Remove __skb...
309
  	__skb_queue_before(&ulpq->reasm, pos, sctp_event2skb(event));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
313
314
315
316
317
318
319
  
  }
  
  /* Helper function to return an event corresponding to the reassembled
   * datagram.
   * This routine creates a re-assembled skb given the first and last skb's
   * as stored in the reassembly queue. The skb's may be non-linear if the sctp
   * payload was fragmented on the way and ip had to reassemble them.
   * We add the rest of skb's to the first skb's fraglist.
   */
b01a24078   Eric W. Biederman   sctp: Make the mi...
320
321
322
  static struct sctp_ulpevent *sctp_make_reassembled_event(struct net *net,
  	struct sk_buff_head *queue, struct sk_buff *f_frag,
  	struct sk_buff *l_frag)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
  {
  	struct sk_buff *pos;
672e7cca1   Vladislav Yasevich   [SCTP]: Prevent p...
325
  	struct sk_buff *new = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  	struct sctp_ulpevent *event;
  	struct sk_buff *pnext, *last;
  	struct sk_buff *list = skb_shinfo(f_frag)->frag_list;
  
  	/* Store the pointer to the 2nd skb */
  	if (f_frag == l_frag)
  		pos = NULL;
  	else
  		pos = f_frag->next;
  
  	/* Get the last skb in the f_frag's frag_list if present. */
  	for (last = list; list; last = list, list = list->next);
  
  	/* Add the list of remaining fragments to the first fragments
  	 * frag_list.
  	 */
  	if (last)
  		last->next = pos;
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
  	else {
  		if (skb_cloned(f_frag)) {
  			/* This is a cloned skb, we can't just modify
  			 * the frag_list.  We need a new skb to do that.
  			 * Instead of calling skb_unshare(), we'll do it
  			 * ourselves since we need to delay the free.
  			 */
  			new = skb_copy(f_frag, GFP_ATOMIC);
  			if (!new)
  				return NULL;	/* try again later */
  
  			sctp_skb_set_owner_r(new, f_frag->sk);
  
  			skb_shinfo(new)->frag_list = pos;
  		} else
  			skb_shinfo(f_frag)->frag_list = pos;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
  
  	/* Remove the first fragment from the reassembly queue.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
363
  	__skb_unlink(f_frag, queue);
672e7cca1   Vladislav Yasevich   [SCTP]: Prevent p...
364

d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
365
366
367
368
369
  	/* if we did unshare, then free the old skb and re-assign */
  	if (new) {
  		kfree_skb(f_frag);
  		f_frag = new;
  	}
672e7cca1   Vladislav Yasevich   [SCTP]: Prevent p...
370

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
372
373
374
375
376
377
378
379
  	while (pos) {
  
  		pnext = pos->next;
  
  		/* Update the len and data_len fields of the first fragment. */
  		f_frag->len += pos->len;
  		f_frag->data_len += pos->len;
  
  		/* Remove the fragment from the reassembly queue.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
380
  		__skb_unlink(pos, queue);
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
381

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
  		/* Break if we have reached the last fragment.  */
  		if (pos == l_frag)
  			break;
  		pos->next = pnext;
  		pos = pnext;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
387
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  
  	event = sctp_skb2event(f_frag);
b01a24078   Eric W. Biederman   sctp: Make the mi...
390
  	SCTP_INC_STATS(net, SCTP_MIB_REASMUSRMSGS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
  
  	return event;
  }
  
  
  /* Helper function to check if an incoming chunk has filled up the last
   * missing fragment in a SCTP datagram and return the corresponding event.
   */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
399
  static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
404
405
  {
  	struct sk_buff *pos;
  	struct sctp_ulpevent *cevent;
  	struct sk_buff *first_frag = NULL;
  	__u32 ctsn, next_tsn;
  	struct sctp_ulpevent *retval = NULL;
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
406
407
408
409
410
  	struct sk_buff *pd_first = NULL;
  	struct sk_buff *pd_last = NULL;
  	size_t pd_len = 0;
  	struct sctp_association *asoc;
  	u32 pd_point;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  
  	/* Initialized to 0 just to avoid compiler warning message.  Will
  	 * never be used with this value. It is referenced only after it
  	 * is set when we find the first fragment of a message.
  	 */
  	next_tsn = 0;
  
  	/* The chunks are held in the reasm queue sorted by TSN.
  	 * Walk through the queue sequentially and look for a sequence of
  	 * fragmented chunks that complete a datagram.
  	 * 'first_frag' and next_tsn are reset when we find a chunk which
  	 * is the first fragment of a datagram. Once these 2 fields are set
  	 * we expect to find the remaining middle fragments and the last
  	 * fragment in order. If not, first_frag is reset to NULL and we
  	 * start the next pass when we find another first fragment.
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
426
427
428
429
  	 *
  	 * There is a potential to do partial delivery if user sets
  	 * SCTP_PARTIAL_DELIVERY_POINT option. Lets count some things here
  	 * to see if can do PD.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
432
433
434
435
436
  	 */
  	skb_queue_walk(&ulpq->reasm, pos) {
  		cevent = sctp_skb2event(pos);
  		ctsn = cevent->tsn;
  
  		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
  		case SCTP_DATA_FIRST_FRAG:
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
437
438
439
440
441
442
443
444
445
446
447
448
449
  			/* If this "FIRST_FRAG" is the first
  			 * element in the queue, then count it towards
  			 * possible PD.
  			 */
  			if (pos == ulpq->reasm.next) {
  			    pd_first = pos;
  			    pd_last = pos;
  			    pd_len = pos->len;
  			} else {
  			    pd_first = NULL;
  			    pd_last = NULL;
  			    pd_len = 0;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
452
453
454
  			first_frag = pos;
  			next_tsn = ctsn + 1;
  			break;
  
  		case SCTP_DATA_MIDDLE_FRAG:
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
455
  			if ((first_frag) && (ctsn == next_tsn)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  				next_tsn++;
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
457
458
459
460
461
  				if (pd_first) {
  				    pd_last = pos;
  				    pd_len += pos->len;
  				}
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
466
467
468
469
470
  				first_frag = NULL;
  			break;
  
  		case SCTP_DATA_LAST_FRAG:
  			if (first_frag && (ctsn == next_tsn))
  				goto found;
  			else
  				first_frag = NULL;
  			break;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
471
  		}
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
472
473
474
475
476
477
478
479
480
481
482
483
  	}
  
  	asoc = ulpq->asoc;
  	if (pd_first) {
  		/* Make sure we can enter partial deliver.
  		 * We can trigger partial delivery only if framgent
  		 * interleave is set, or the socket is not already
  		 * in  partial delivery.
  		 */
  		if (!sctp_sk(asoc->base.sk)->frag_interleave &&
  		    atomic_read(&sctp_sk(asoc->base.sk)->pd_mode))
  			goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484

d49d91d79   Vlad Yasevich   [SCTP]: Implement...
485
486
487
  		cevent = sctp_skb2event(pd_first);
  		pd_point = sctp_sk(asoc->base.sk)->pd_point;
  		if (pd_point && pd_point <= pd_len) {
b01a24078   Eric W. Biederman   sctp: Make the mi...
488
489
  			retval = sctp_make_reassembled_event(sock_net(asoc->base.sk),
  							     &ulpq->reasm,
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
490
491
492
493
494
  							     pd_first,
  							     pd_last);
  			if (retval)
  				sctp_ulpq_set_pd(ulpq);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
  	}
  done:
  	return retval;
  found:
b01a24078   Eric W. Biederman   sctp: Make the mi...
499
500
  	retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
  					     &ulpq->reasm, first_frag, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
  	if (retval)
  		retval->msg_flags |= MSG_EOR;
  	goto done;
  }
  
  /* Retrieve the next set of fragments of a partial message. */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
507
  static struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
  {
  	struct sk_buff *pos, *last_frag, *first_frag;
  	struct sctp_ulpevent *cevent;
  	__u32 ctsn, next_tsn;
  	int is_last;
  	struct sctp_ulpevent *retval;
  
  	/* The chunks are held in the reasm queue sorted by TSN.
  	 * Walk through the queue sequentially and look for the first
  	 * sequence of fragmented chunks.
  	 */
  
  	if (skb_queue_empty(&ulpq->reasm))
  		return NULL;
  
  	last_frag = first_frag = NULL;
  	retval = NULL;
  	next_tsn = 0;
  	is_last = 0;
  
  	skb_queue_walk(&ulpq->reasm, pos) {
  		cevent = sctp_skb2event(pos);
  		ctsn = cevent->tsn;
  
  		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
d003b41b8   Lee A. Roberts   sctp: fix associa...
533
534
535
536
  		case SCTP_DATA_FIRST_FRAG:
  			if (!first_frag)
  				return NULL;
  			goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
541
  		case SCTP_DATA_MIDDLE_FRAG:
  			if (!first_frag) {
  				first_frag = pos;
  				next_tsn = ctsn + 1;
  				last_frag = pos;
d003b41b8   Lee A. Roberts   sctp: fix associa...
542
  			} else if (next_tsn == ctsn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
  				next_tsn++;
d003b41b8   Lee A. Roberts   sctp: fix associa...
544
545
  				last_frag = pos;
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
550
551
552
553
554
555
556
557
  				goto done;
  			break;
  		case SCTP_DATA_LAST_FRAG:
  			if (!first_frag)
  				first_frag = pos;
  			else if (ctsn != next_tsn)
  				goto done;
  			last_frag = pos;
  			is_last = 1;
  			goto done;
  		default:
  			return NULL;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
558
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
561
562
563
564
  	}
  
  	/* We have the reassembled event. There is no need to look
  	 * further.
  	 */
  done:
b01a24078   Eric W. Biederman   sctp: Make the mi...
565
566
  	retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
  					&ulpq->reasm, first_frag, last_frag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
  	if (retval && is_last)
  		retval->msg_flags |= MSG_EOR;
  
  	return retval;
  }
  
  
  /* Helper function to reassemble chunks.  Hold chunks on the reasm queue that
   * need reassembling.
   */
  static struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
  						struct sctp_ulpevent *event)
  {
  	struct sctp_ulpevent *retval = NULL;
  
  	/* Check if this is part of a fragmented message.  */
  	if (SCTP_DATA_NOT_FRAG == (event->msg_flags & SCTP_DATA_FRAG_MASK)) {
  		event->msg_flags |= MSG_EOR;
  		return event;
  	}
  
  	sctp_ulpq_store_reasm(ulpq, event);
  	if (!ulpq->pd_mode)
  		retval = sctp_ulpq_retrieve_reassembled(ulpq);
  	else {
  		__u32 ctsn, ctsnap;
  
  		/* Do not even bother unless this is the next tsn to
  		 * be delivered.
  		 */
  		ctsn = event->tsn;
  		ctsnap = sctp_tsnmap_get_ctsn(&ulpq->asoc->peer.tsn_map);
  		if (TSN_lte(ctsn, ctsnap))
  			retval = sctp_ulpq_retrieve_partial(ulpq);
  	}
  
  	return retval;
  }
  
  /* Retrieve the first part (sequential fragments) for partial delivery.  */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
607
  static struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  {
  	struct sk_buff *pos, *last_frag, *first_frag;
  	struct sctp_ulpevent *cevent;
  	__u32 ctsn, next_tsn;
  	struct sctp_ulpevent *retval;
  
  	/* The chunks are held in the reasm queue sorted by TSN.
  	 * Walk through the queue sequentially and look for a sequence of
  	 * fragmented chunks that start a datagram.
  	 */
  
  	if (skb_queue_empty(&ulpq->reasm))
  		return NULL;
  
  	last_frag = first_frag = NULL;
  	retval = NULL;
  	next_tsn = 0;
  
  	skb_queue_walk(&ulpq->reasm, pos) {
  		cevent = sctp_skb2event(pos);
  		ctsn = cevent->tsn;
  
  		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
  		case SCTP_DATA_FIRST_FRAG:
  			if (!first_frag) {
  				first_frag = pos;
  				next_tsn = ctsn + 1;
  				last_frag = pos;
  			} else
  				goto done;
  			break;
  
  		case SCTP_DATA_MIDDLE_FRAG:
  			if (!first_frag)
  				return NULL;
  			if (ctsn == next_tsn) {
  				next_tsn++;
  				last_frag = pos;
  			} else
  				goto done;
  			break;
d003b41b8   Lee A. Roberts   sctp: fix associa...
649
650
651
652
653
654
655
  
  		case SCTP_DATA_LAST_FRAG:
  			if (!first_frag)
  				return NULL;
  			else
  				goto done;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
  		default:
  			return NULL;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
658
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
661
662
663
664
  	}
  
  	/* We have the reassembled event. There is no need to look
  	 * further.
  	 */
  done:
b01a24078   Eric W. Biederman   sctp: Make the mi...
665
666
  	retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
  					&ulpq->reasm, first_frag, last_frag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
  	return retval;
  }
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
  /*
   * Flush out stale fragments from the reassembly queue when processing
   * a Forward TSN.
   *
   * RFC 3758, Section 3.6
   *
   * After receiving and processing a FORWARD TSN, the data receiver MUST
   * take cautions in updating its re-assembly queue.  The receiver MUST
   * remove any partially reassembled message, which is still missing one
   * or more TSNs earlier than or equal to the new cumulative TSN point.
   * In the event that the receiver has invoked the partial delivery API,
   * a notification SHOULD also be generated to inform the upper layer API
   * that the message being partially delivered will NOT be completed.
   */
  void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *ulpq, __u32 fwd_tsn)
  {
  	struct sk_buff *pos, *tmp;
  	struct sctp_ulpevent *event;
  	__u32 tsn;
  
  	if (skb_queue_empty(&ulpq->reasm))
  		return;
  
  	skb_queue_walk_safe(&ulpq->reasm, pos, tmp) {
  		event = sctp_skb2event(pos);
  		tsn = event->tsn;
  
  		/* Since the entire message must be abandoned by the
  		 * sender (item A3 in Section 3.5, RFC 3758), we can
  		 * free all fragments on the list that are less then
  		 * or equal to ctsn_point
  		 */
  		if (TSN_lte(tsn, fwd_tsn)) {
  			__skb_unlink(pos, &ulpq->reasm);
  			sctp_ulpevent_free(event);
  		} else
  			break;
  	}
  }
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
  /*
   * Drain the reassembly queue.  If we just cleared parted delivery, it
   * is possible that the reassembly queue will contain already reassembled
   * messages.  Retrieve any such messages and give them to the user.
   */
  static void sctp_ulpq_reasm_drain(struct sctp_ulpq *ulpq)
  {
  	struct sctp_ulpevent *event = NULL;
  	struct sk_buff_head temp;
  
  	if (skb_queue_empty(&ulpq->reasm))
  		return;
  
  	while ((event = sctp_ulpq_retrieve_reassembled(ulpq)) != NULL) {
  		/* Do ordering if needed.  */
  		if ((event) && (event->msg_flags & MSG_EOR)){
  			skb_queue_head_init(&temp);
  			__skb_queue_tail(&temp, sctp_event2skb(event));
  
  			event = sctp_ulpq_order(ulpq, event);
  		}
  
  		/* Send event to the ULP.  'event' is the
  		 * sctp_ulpevent for  very first SKB on the  temp' list.
  		 */
  		if (event)
  			sctp_ulpq_tail_event(ulpq, event);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
739
  /* Helper function to gather skbs that have possibly become
   * ordered by an an incoming chunk.
   */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
740
  static void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
  					      struct sctp_ulpevent *event)
  {
8728b834b   David S. Miller   [NET]: Kill skb->...
743
  	struct sk_buff_head *event_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
745
746
  	struct sk_buff *pos, *tmp;
  	struct sctp_ulpevent *cevent;
  	struct sctp_stream *in;
efea2c6b2   Hagen Paul Pfeifer   sctp: several dec...
747
  	__u16 sid, csid, cssn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748
749
  
  	sid = event->stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
  	in  = &ulpq->asoc->ssnmap->in;
8728b834b   David S. Miller   [NET]: Kill skb->...
751
  	event_list = (struct sk_buff_head *) sctp_event2skb(event)->prev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
  	/* We are holding the chunks by stream, by SSN.  */
  	sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
  		cevent = (struct sctp_ulpevent *) pos->cb;
  		csid = cevent->stream;
  		cssn = cevent->ssn;
  
  		/* Have we gone too far?  */
  		if (csid > sid)
  			break;
  
  		/* Have we not gone far enough?  */
  		if (csid < sid)
  			continue;
  
  		if (cssn != sctp_ssn_peek(in, sid))
  			break;
  
  		/* Found it, so mark in the ssnmap. */
  		sctp_ssn_next(in, sid);
8728b834b   David S. Miller   [NET]: Kill skb->...
771
  		__skb_unlink(pos, &ulpq->lobby);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
773
  
  		/* Attach all gathered skbs to the event.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
774
  		__skb_queue_tail(event_list, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
776
777
778
  	}
  }
  
  /* Helper function to store chunks needing ordering.  */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
779
  static void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
  					   struct sctp_ulpevent *event)
  {
  	struct sk_buff *pos;
  	struct sctp_ulpevent *cevent;
  	__u16 sid, csid;
  	__u16 ssn, cssn;
  
  	pos = skb_peek_tail(&ulpq->lobby);
  	if (!pos) {
  		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
  		return;
  	}
  
  	sid = event->stream;
  	ssn = event->ssn;
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
795

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
  	cevent = (struct sctp_ulpevent *) pos->cb;
  	csid = cevent->stream;
  	cssn = cevent->ssn;
  	if (sid > csid) {
  		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
  		return;
  	}
  
  	if ((sid == csid) && SSN_lt(cssn, ssn)) {
  		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
  		return;
  	}
  
  	/* Find the right place in this list.  We store them by
  	 * stream ID and then by SSN.
  	 */
  	skb_queue_walk(&ulpq->lobby, pos) {
  		cevent = (struct sctp_ulpevent *) pos->cb;
  		csid = cevent->stream;
  		cssn = cevent->ssn;
  
  		if (csid > sid)
  			break;
  		if (csid == sid && SSN_lt(ssn, cssn))
  			break;
  	}
  
  
  	/* Insert before pos. */
43f59c893   David S. Miller   net: Remove __skb...
825
  	__skb_queue_before(&ulpq->lobby, pos, sctp_event2skb(event));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
  }
  
  static struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
8728b834b   David S. Miller   [NET]: Kill skb->...
829
  					     struct sctp_ulpevent *event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
  {
  	__u16 sid, ssn;
  	struct sctp_stream *in;
  
  	/* Check if this message needs ordering.  */
  	if (SCTP_DATA_UNORDERED & event->msg_flags)
  		return event;
  
  	/* Note: The stream ID must be verified before this routine.  */
  	sid = event->stream;
  	ssn = event->ssn;
  	in  = &ulpq->asoc->ssnmap->in;
  
  	/* Is this the expected SSN for this stream ID?  */
  	if (ssn != sctp_ssn_peek(in, sid)) {
  		/* We've received something out of order, so find where it
  		 * needs to be placed.  We order by stream and then by SSN.
  		 */
  		sctp_ulpq_store_ordered(ulpq, event);
  		return NULL;
  	}
  
  	/* Mark that the next chunk has been found.  */
  	sctp_ssn_next(in, sid);
  
  	/* Go find any other chunks that were waiting for
  	 * ordering.
  	 */
  	sctp_ulpq_retrieve_ordered(ulpq, event);
  
  	return event;
  }
  
  /* Helper function to gather skbs that have possibly become
   * ordered by forward tsn skipping their dependencies.
   */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
866
  static void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq, __u16 sid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
869
  {
  	struct sk_buff *pos, *tmp;
  	struct sctp_ulpevent *cevent;
8728b834b   David S. Miller   [NET]: Kill skb->...
870
  	struct sctp_ulpevent *event;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
872
  	struct sctp_stream *in;
  	struct sk_buff_head temp;
c068be549   Vlad Yasevich   [SCTP]: Correctly...
873
  	struct sk_buff_head *lobby = &ulpq->lobby;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
876
877
878
  	__u16 csid, cssn;
  
  	in  = &ulpq->asoc->ssnmap->in;
  
  	/* We are holding the chunks by stream, by SSN.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
879
880
  	skb_queue_head_init(&temp);
  	event = NULL;
c068be549   Vlad Yasevich   [SCTP]: Correctly...
881
  	sctp_skb_for_each(pos, lobby, tmp) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
884
  		cevent = (struct sctp_ulpevent *) pos->cb;
  		csid = cevent->stream;
  		cssn = cevent->ssn;
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
885
886
  		/* Have we gone too far?  */
  		if (csid > sid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  			break;
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
888
889
890
891
892
  		/* Have we not gone far enough?  */
  		if (csid < sid)
  			continue;
  
  		/* see if this ssn has been marked by skipping */
c068be549   Vlad Yasevich   [SCTP]: Correctly...
893
  		if (!SSN_lt(cssn, sctp_ssn_peek(in, csid)))
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
894
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895

c068be549   Vlad Yasevich   [SCTP]: Correctly...
896
  		__skb_unlink(pos, lobby);
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
897
  		if (!event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
899
  			/* Create a temporary list to collect chunks on.  */
  			event = sctp_skb2event(pos);
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
900
901
902
  
  		/* Attach all gathered skbs to the event.  */
  		__skb_queue_tail(&temp, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903
  	}
c068be549   Vlad Yasevich   [SCTP]: Correctly...
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
  	/* If we didn't reap any data, see if the next expected SSN
  	 * is next on the queue and if so, use that.
  	 */
  	if (event == NULL && pos != (struct sk_buff *)lobby) {
  		cevent = (struct sctp_ulpevent *) pos->cb;
  		csid = cevent->stream;
  		cssn = cevent->ssn;
  
  		if (csid == sid && cssn == sctp_ssn_peek(in, csid)) {
  			sctp_ssn_next(in, csid);
  			__skb_unlink(pos, lobby);
  			__skb_queue_tail(&temp, pos);
  			event = sctp_skb2event(pos);
  		}
  	}
8728b834b   David S. Miller   [NET]: Kill skb->...
919
920
921
  	/* Send event to the ULP.  'event' is the sctp_ulpevent for
  	 * very first SKB on the 'temp' list.
  	 */
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
922
923
924
  	if (event) {
  		/* see if we have more ordered that we can deliver */
  		sctp_ulpq_retrieve_ordered(ulpq, event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
  		sctp_ulpq_tail_event(ulpq, event);
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
926
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
927
  }
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
928
929
930
  /* Skip over an SSN. This is used during the processing of
   * Forwared TSN chunk to skip over the abandoned ordered data
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
  void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
  {
  	struct sctp_stream *in;
  
  	/* Note: The stream ID must be verified before this routine.  */
  	in  = &ulpq->asoc->ssnmap->in;
  
  	/* Is this an old SSN?  If so ignore. */
  	if (SSN_lt(ssn, sctp_ssn_peek(in, sid)))
  		return;
  
  	/* Mark that we are no longer expecting this SSN or lower. */
  	sctp_ssn_skip(in, sid, ssn);
  
  	/* Go find any other chunks that were waiting for
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
946
  	 * ordering and deliver them if needed.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947
  	 */
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
948
  	sctp_ulpq_reap_ordered(ulpq, sid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
  }
16d14ef9f   Pavel Emelyanov   [SCTP]: Consolida...
950
951
  static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
  		struct sk_buff_head *list, __u16 needed)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
953
  {
  	__u16 freed = 0;
95ac7b859   Lee A. Roberts   sctp: fix associa...
954
955
  	__u32 tsn, last_tsn;
  	struct sk_buff *skb, *flist, *last;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956
957
958
959
  	struct sctp_ulpevent *event;
  	struct sctp_tsnmap *tsnmap;
  
  	tsnmap = &ulpq->asoc->peer.tsn_map;
e67f85ecd   Lee A. Roberts   sctp: fix associa...
960
  	while ((skb = skb_peek_tail(list)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
962
  		event = sctp_skb2event(skb);
  		tsn = event->tsn;
e67f85ecd   Lee A. Roberts   sctp: fix associa...
963
964
965
  		/* Don't renege below the Cumulative TSN ACK Point. */
  		if (TSN_lte(tsn, sctp_tsnmap_get_ctsn(tsnmap)))
  			break;
95ac7b859   Lee A. Roberts   sctp: fix associa...
966
967
968
969
  		/* Events in ordering queue may have multiple fragments
  		 * corresponding to additional TSNs.  Sum the total
  		 * freed space; find the last TSN.
  		 */
e67f85ecd   Lee A. Roberts   sctp: fix associa...
970
  		freed += skb_headlen(skb);
95ac7b859   Lee A. Roberts   sctp: fix associa...
971
972
973
974
975
976
977
978
979
980
981
982
  		flist = skb_shinfo(skb)->frag_list;
  		for (last = flist; flist; flist = flist->next) {
  			last = flist;
  			freed += skb_headlen(last);
  		}
  		if (last)
  			last_tsn = sctp_skb2event(last)->tsn;
  		else
  			last_tsn = tsn;
  
  		/* Unlink the event, then renege all applicable TSNs. */
  		__skb_unlink(skb, list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
983
  		sctp_ulpevent_free(event);
95ac7b859   Lee A. Roberts   sctp: fix associa...
984
985
986
987
  		while (TSN_lte(tsn, last_tsn)) {
  			sctp_tsnmap_renege(tsnmap, tsn);
  			tsn++;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
989
990
991
992
993
  		if (freed >= needed)
  			return freed;
  	}
  
  	return freed;
  }
16d14ef9f   Pavel Emelyanov   [SCTP]: Consolida...
994
995
996
997
998
  /* Renege 'needed' bytes from the ordering queue. */
  static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
  {
  	return sctp_ulpq_renege_list(ulpq, &ulpq->lobby, needed);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
1000
1001
  /* Renege 'needed' bytes from the reassembly queue. */
  static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
  {
16d14ef9f   Pavel Emelyanov   [SCTP]: Consolida...
1002
  	return sctp_ulpq_renege_list(ulpq, &ulpq->reasm, needed);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
1004
1005
1006
  }
  
  /* Partial deliver the first message as there is pressure on rwnd. */
  void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq,
dd0fc66fb   Al Viro   [PATCH] gfp flags...
1007
  				gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1008
1009
1010
  {
  	struct sctp_ulpevent *event;
  	struct sctp_association *asoc;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
1011
  	struct sctp_sock *sp;
d003b41b8   Lee A. Roberts   sctp: fix associa...
1012
1013
  	__u32 ctsn;
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
1015
  
  	asoc = ulpq->asoc;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
1016
  	sp = sctp_sk(asoc->base.sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1017

b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
1018
  	/* If the association is already in Partial Delivery mode
d003b41b8   Lee A. Roberts   sctp: fix associa...
1019
  	 * we have nothing to do.
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
1020
1021
1022
  	 */
  	if (ulpq->pd_mode)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023

d003b41b8   Lee A. Roberts   sctp: fix associa...
1024
1025
1026
1027
1028
1029
1030
1031
1032
  	/* Data must be at or below the Cumulative TSN ACK Point to
  	 * start partial delivery.
  	 */
  	skb = skb_peek(&asoc->ulpq.reasm);
  	if (skb != NULL) {
  		ctsn = sctp_skb2event(skb)->tsn;
  		if (!TSN_lte(ctsn, sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)))
  			return;
  	}
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
1033
1034
1035
1036
1037
1038
  	/* If the user enabled fragment interleave socket option,
  	 * multiple associations can enter partial delivery.
  	 * Otherwise, we can only enter partial delivery if the
  	 * socket is not in partial deliver mode.
  	 */
  	if (sp->frag_interleave || atomic_read(&sp->pd_mode) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
1040
1041
1042
1043
  		/* Is partial delivery possible?  */
  		event = sctp_ulpq_retrieve_first(ulpq);
  		/* Send event to the ULP.   */
  		if (event) {
  			sctp_ulpq_tail_event(ulpq, event);
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
1044
  			sctp_ulpq_set_pd(ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
1046
1047
1048
1049
1050
1051
  			return;
  		}
  	}
  }
  
  /* Renege some packets to make room for an incoming chunk.  */
  void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
dd0fc66fb   Al Viro   [PATCH] gfp flags...
1052
  		      gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
1054
1055
1056
1057
1058
1059
1060
1061
  {
  	struct sctp_association *asoc;
  	__u16 needed, freed;
  
  	asoc = ulpq->asoc;
  
  	if (chunk) {
  		needed = ntohs(chunk->chunk_hdr->length);
  		needed -= sizeof(sctp_data_chunk_t);
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
1062
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
  		needed = SCTP_DEFAULT_MAXWINDOW;
  
  	freed = 0;
  
  	if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
  		freed = sctp_ulpq_renege_order(ulpq, needed);
  		if (freed < needed) {
  			freed += sctp_ulpq_renege_frags(ulpq, needed - freed);
  		}
  	}
  	/* If able to free enough room, accept this chunk. */
  	if (chunk && (freed >= needed)) {
d003b41b8   Lee A. Roberts   sctp: fix associa...
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  		int retval;
  		retval = sctp_ulpq_tail_data(ulpq, chunk, gfp);
  		/*
  		 * Enter partial delivery if chunk has not been
  		 * delivered; otherwise, drain the reassembly queue.
  		 */
  		if (retval <= 0)
  			sctp_ulpq_partial_delivery(ulpq, gfp);
  		else if (retval == 1)
  			sctp_ulpq_reasm_drain(ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
  	}
3ab224be6   Hideo Aoki   [NET] CORE: Intro...
1086
  	sk_mem_reclaim(asoc->base.sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1087
1088
1089
1090
1091
1092
1093
  }
  
  
  
  /* Notify the application if an association is aborted and in
   * partial delivery mode.  Send up any pending received messages.
   */
dd0fc66fb   Al Viro   [PATCH] gfp flags...
1094
  void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
  {
  	struct sctp_ulpevent *ev = NULL;
  	struct sock *sk;
  
  	if (!ulpq->pd_mode)
  		return;
  
  	sk = ulpq->asoc->base.sk;
  	if (sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
  				       &sctp_sk(sk)->subscribe))
  		ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
  					      SCTP_PARTIAL_DELIVERY_ABORTED,
  					      gfp);
  	if (ev)
  		__skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
  
  	/* If there is data waiting, send it up the socket now. */
  	if (sctp_ulpq_clear_pd(ulpq) || ev)
  		sk->sk_data_ready(sk, 0);
  }