Blame view

net/sctp/ulpqueue.c 28.7 KB
47505b8bc   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
60c778b25   Vlad Yasevich   [SCTP]: Stop clai...
2
  /* SCTP kernel implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
11
   * (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).
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
   * Please send any bug reports or fixes you make to the
   * email address(es):
91705c61b   Daniel Borkmann   net: sctp: trivia...
14
   *    lksctp developers <linux-sctp@vger.kernel.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
   * Written or modified by:
   *    Jon Grimm             <jgrimm@us.ibm.com>
   *    La Monte H.P. Yarroll <piggy@acm.org>
   *    Sridhar Samudrala     <sri@us.ibm.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
  #include <linux/types.h>
  #include <linux/skbuff.h>
  #include <net/sock.h>
8465a5fcd   Neil Horman   sctp: add support...
25
  #include <net/busy_poll.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
28
29
30
  #include <net/sctp/structs.h>
  #include <net/sctp/sctp.h>
  #include <net/sctp/sm.h>
  
  /* Forward declarations for internal helpers.  */
26ac8e5fe   wangweidong   sctp: fix checkpa...
31
  static struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
8728b834b   David S. Miller   [NET]: Kill skb->...
32
  					      struct sctp_ulpevent *);
26ac8e5fe   wangweidong   sctp: fix checkpa...
33
  static struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *,
8728b834b   David S. Miller   [NET]: Kill skb->...
34
  					      struct sctp_ulpevent *);
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
35
  static void sctp_ulpq_reasm_drain(struct sctp_ulpq *ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
44
45
46
  
  /* 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);
132282386   Xin Long   sctp: add support...
47
  	skb_queue_head_init(&ulpq->reasm_uo);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  	skb_queue_head_init(&ulpq->lobby);
  	ulpq->pd_mode  = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
  
  	return ulpq;
  }
  
  
  /* Flush the reassembly and ordering queues.  */
0b58a8114   Vlad Yasevich   [SCTP]: Clean up ...
56
  void sctp_ulpq_flush(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
  {
  	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);
  	}
132282386   Xin Long   sctp: add support...
70
71
72
73
  	while ((skb = __skb_dequeue(&ulpq->reasm_uo)) != NULL) {
  		event = sctp_skb2event(skb);
  		sctp_ulpevent_free(event);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
78
79
  }
  
  /* Dispose of a ulpqueue.  */
  void sctp_ulpq_free(struct sctp_ulpq *ulpq)
  {
  	sctp_ulpq_flush(ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
  }
  
  /* Process an incoming DATA chunk.  */
  int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
dd0fc66fb   Al Viro   [PATCH] gfp flags...
84
  			gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
  {
  	struct sk_buff_head temp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  	struct sctp_ulpevent *event;
d003b41b8   Lee A. Roberts   sctp: fix associa...
88
  	int event_eor = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
  	/* Create an event from the incoming chunk. */
  	event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp);
  	if (!event)
  		return -ENOMEM;
bd4d627db   Xin Long   sctp: implement u...
94
95
  	event->ssn = ntohs(chunk->subh.data_hdr->ssn);
  	event->ppid = chunk->subh.data_hdr->ppid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
  	/* Do reassembly if needed.  */
  	event = sctp_ulpq_reasm(ulpq, event);
  
  	/* Do ordering if needed.  */
178ca044a   David Miller   sctp: Make sctp_e...
100
  	if (event) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
  		/* Create a temporary list to collect chunks on.  */
  		skb_queue_head_init(&temp);
  		__skb_queue_tail(&temp, sctp_event2skb(event));
178ca044a   David Miller   sctp: Make sctp_e...
104
105
  		if (event->msg_flags & MSG_EOR)
  			event = sctp_ulpq_order(ulpq, event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  	}
8728b834b   David S. Miller   [NET]: Kill skb->...
107
108
109
  	/* 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...
110
111
  	if (event) {
  		event_eor = (event->msg_flags & MSG_EOR) ? 1 : 0;
013b96ec6   David Miller   sctp: Pass sk_buf...
112
  		sctp_ulpq_tail_event(ulpq, &temp);
d003b41b8   Lee A. Roberts   sctp: fix associa...
113
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114

d003b41b8   Lee A. Roberts   sctp: fix associa...
115
  	return event_eor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
  }
  
  /* 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...
122
  int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
  {
  	struct sctp_sock *sp = sctp_sk(sk);
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
125
126
127
128
129
  	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)) {
311b21774   Marcelo Ricardo Leitner   sctp: simplify sk...
130
131
  			skb_queue_splice_tail_init(&sp->pd_lobby,
  						   &sk->sk_receive_queue);
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  			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
153
  	}
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
154

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
  	return 0;
  }
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
157
158
159
160
161
162
163
164
  /* 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
165
166
167
168
  /* 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...
169
  	sctp_ulpq_reasm_drain(ulpq);
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
170
  	return sctp_clear_pd(ulpq->asoc->base.sk, ulpq->asoc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  }
013b96ec6   David Miller   sctp: Pass sk_buf...
172
  int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sk_buff_head *skb_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
  {
  	struct sock *sk = ulpq->asoc->base.sk;
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
175
  	struct sctp_sock *sp = sctp_sk(sk);
013b96ec6   David Miller   sctp: Pass sk_buf...
176
177
178
  	struct sctp_ulpevent *event;
  	struct sk_buff_head *queue;
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  	int clear_pd = 0;
013b96ec6   David Miller   sctp: Pass sk_buf...
180
181
  	skb = __skb_peek(skb_list);
  	event = sctp_skb2event(skb);
8728b834b   David S. Miller   [NET]: Kill skb->...
182

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
  	/* If the socket is just going to throw this away, do not
  	 * even try to deliver it.
  	 */
a0fc6843f   Xin Long   sctp: allow deliv...
186
187
188
  	if (sk->sk_shutdown & RCV_SHUTDOWN &&
  	    (sk->sk_shutdown & SEND_SHUTDOWN ||
  	     !sctp_ulpevent_is_notification(event)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  		goto out_free;
2c8c56e15   Eric Dumazet   net: introduce SO...
190
  	if (!sctp_ulpevent_is_notification(event)) {
8465a5fcd   Neil Horman   sctp: add support...
191
  		sk_mark_napi_id(sk, skb);
2c8c56e15   Eric Dumazet   net: introduce SO...
192
193
  		sk_incoming_cpu_update(sk);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	/* Check if the user wishes to receive this event.  */
a1e3a0590   Xin Long   sctp: add subscri...
195
  	if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
197
198
199
200
201
  		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.
  	 */
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
202
  	if (atomic_read(&sp->pd_mode) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  		queue = &sk->sk_receive_queue;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
204
205
206
207
208
209
210
211
212
213
  	} 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)))
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
214
  				queue = &sp->pd_lobby;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
215
216
217
218
219
220
221
  			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...
222
  			 * can queue this to the receive queue instead
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
223
224
  			 * of the lobby.
  			 */
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
225
  			if (sp->frag_interleave)
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
226
227
  				queue = &sk->sk_receive_queue;
  			else
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
228
  				queue = &sp->pd_lobby;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  		}
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
230
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231

013b96ec6   David Miller   sctp: Pass sk_buf...
232
  	skb_queue_splice_tail_init(skb_list, queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
238
239
  
  	/* 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);
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
240
  	if (queue == &sk->sk_receive_queue && !sp->data_ready_signalled) {
7906b00f5   Marcelo Ricardo Leitner   sctp: fix missing...
241
242
  		if (!sock_owned_by_user(sk))
  			sp->data_ready_signalled = 1;
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
243
244
  		sk->sk_data_ready(sk);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
  	return 1;
  
  out_free:
8728b834b   David S. Miller   [NET]: Kill skb->...
248
249
  	if (skb_list)
  		sctp_queue_purge_ulpevents(skb_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
  	else
  		sctp_ulpevent_free(event);
8728b834b   David S. Miller   [NET]: Kill skb->...
252

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
255
256
257
258
  	return 0;
  }
  
  /* 2nd Level Abstractions */
  
  /* Helper function to store chunks that need to be reassembled.  */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
259
  static void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  					 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...
293
  	__skb_queue_before(&ulpq->reasm, pos, sctp_event2skb(event));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
298
299
300
301
302
303
  
  }
  
  /* 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.
   */
bd4d627db   Xin Long   sctp: implement u...
304
305
306
307
  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
308
309
  {
  	struct sk_buff *pos;
672e7cca1   Vladislav Yasevich   [SCTP]: Prevent p...
310
  	struct sk_buff *new = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
321
  	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. */
8d72651d8   wangweidong   sctp: fix checkpa...
322
323
  	for (last = list; list; last = list, list = list->next)
  		;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
327
328
329
  
  	/* Add the list of remaining fragments to the first fragments
  	 * frag_list.
  	 */
  	if (last)
  		last->next = pos;
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
  	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
347
348
  
  	/* Remove the first fragment from the reassembly queue.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
349
  	__skb_unlink(f_frag, queue);
672e7cca1   Vladislav Yasevich   [SCTP]: Prevent p...
350

d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
351
352
353
354
355
  	/* 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...
356

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
360
361
362
363
364
365
  	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->...
366
  		__skb_unlink(pos, queue);
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
367

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
372
  		/* Break if we have reached the last fragment.  */
  		if (pos == l_frag)
  			break;
  		pos->next = pnext;
  		pos = pnext;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
373
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
  
  	event = sctp_skb2event(f_frag);
b01a24078   Eric W. Biederman   sctp: Make the mi...
376
  	SCTP_INC_STATS(net, SCTP_MIB_REASMUSRMSGS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
383
384
  
  	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...
385
  static struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
  {
  	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...
392
393
394
395
396
  	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
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  
  	/* 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...
412
413
414
415
  	 *
  	 * 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
416
417
418
419
420
421
422
  	 */
  	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...
423
424
425
426
  			/* If this "FIRST_FRAG" is the first
  			 * element in the queue, then count it towards
  			 * possible PD.
  			 */
1181d629c   David S. Miller   sctp: Use skb_que...
427
  			if (skb_queue_is_first(&ulpq->reasm, pos)) {
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
428
429
430
431
432
433
434
435
  			    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
436
437
438
439
440
  			first_frag = pos;
  			next_tsn = ctsn + 1;
  			break;
  
  		case SCTP_DATA_MIDDLE_FRAG:
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
441
  			if ((first_frag) && (ctsn == next_tsn)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  				next_tsn++;
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
443
444
445
446
447
  				if (pd_first) {
  				    pd_last = pos;
  				    pd_len += pos->len;
  				}
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
  				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...
457
  		}
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
458
459
460
461
462
463
464
465
466
467
468
469
  	}
  
  	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
470

d49d91d79   Vlad Yasevich   [SCTP]: Implement...
471
472
473
  		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...
474
475
  			retval = sctp_make_reassembled_event(sock_net(asoc->base.sk),
  							     &ulpq->reasm,
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
476
477
478
479
480
  							     pd_first,
  							     pd_last);
  			if (retval)
  				sctp_ulpq_set_pd(ulpq);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
  	}
  done:
  	return retval;
  found:
b01a24078   Eric W. Biederman   sctp: Make the mi...
485
486
  	retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
  					     &ulpq->reasm, first_frag, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
488
489
490
491
492
  	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...
493
  static struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
  {
  	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...
519
520
521
522
  		case SCTP_DATA_FIRST_FRAG:
  			if (!first_frag)
  				return NULL;
  			goto done;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
  		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...
528
  			} else if (next_tsn == ctsn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
  				next_tsn++;
d003b41b8   Lee A. Roberts   sctp: fix associa...
530
531
  				last_frag = pos;
  			} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
534
535
536
537
538
539
540
541
542
543
  				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...
544
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
548
549
550
  	}
  
  	/* We have the reassembled event. There is no need to look
  	 * further.
  	 */
  done:
b01a24078   Eric W. Biederman   sctp: Make the mi...
551
552
  	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
553
554
555
556
557
558
559
560
561
562
563
564
565
566
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
  	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...
593
  static struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
596
597
598
599
600
601
602
603
604
605
606
607
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
  {
  	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...
635
636
637
638
639
640
641
  
  		case SCTP_DATA_LAST_FRAG:
  			if (!first_frag)
  				return NULL;
  			else
  				goto done;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
  		default:
  			return NULL;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
644
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
647
648
649
650
  	}
  
  	/* We have the reassembled event. There is no need to look
  	 * further.
  	 */
  done:
b01a24078   Eric W. Biederman   sctp: Make the mi...
651
652
  	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
653
654
  	return retval;
  }
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
655
656
657
658
659
660
661
662
663
664
665
666
667
668
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
  /*
   * 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...
694
695
696
697
698
699
700
701
  /*
   * 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;
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
702
703
704
705
706
  
  	if (skb_queue_empty(&ulpq->reasm))
  		return;
  
  	while ((event = sctp_ulpq_retrieve_reassembled(ulpq)) != NULL) {
925b93742   David Miller   sctp: Always pass...
707
708
709
710
  		struct sk_buff_head temp;
  
  		skb_queue_head_init(&temp);
  		__skb_queue_tail(&temp, sctp_event2skb(event));
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
711

925b93742   David Miller   sctp: Always pass...
712
713
  		/* Do ordering if needed.  */
  		if (event->msg_flags & MSG_EOR)
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
714
  			event = sctp_ulpq_order(ulpq, event);
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
715
716
717
718
719
  
  		/* Send event to the ULP.  'event' is the
  		 * sctp_ulpevent for  very first SKB on the  temp' list.
  		 */
  		if (event)
013b96ec6   David Miller   sctp: Pass sk_buf...
720
  			sctp_ulpq_tail_event(ulpq, &temp);
ef5d4cf2f   Vlad Yasevich   [SCTP]: Flush fra...
721
722
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
  /* Helper function to gather skbs that have possibly become
   * ordered by an an incoming chunk.
   */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
726
  static void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
  					      struct sctp_ulpevent *event)
  {
8728b834b   David S. Miller   [NET]: Kill skb->...
729
  	struct sk_buff_head *event_list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
  	struct sk_buff *pos, *tmp;
  	struct sctp_ulpevent *cevent;
a83863174   Xin Long   sctp: prepare aso...
732
  	struct sctp_stream *stream;
efea2c6b2   Hagen Paul Pfeifer   sctp: several dec...
733
  	__u16 sid, csid, cssn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
735
  
  	sid = event->stream;
cee360ab4   Xin Long   sctp: define the ...
736
  	stream  = &ulpq->asoc->stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737

8728b834b   David S. Miller   [NET]: Kill skb->...
738
  	event_list = (struct sk_buff_head *) sctp_event2skb(event)->prev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
741
742
743
744
745
746
747
748
749
750
751
  	/* 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;
a83863174   Xin Long   sctp: prepare aso...
752
  		if (cssn != sctp_ssn_peek(stream, in, sid))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
753
  			break;
a83863174   Xin Long   sctp: prepare aso...
754
755
  		/* Found it, so mark in the stream. */
  		sctp_ssn_next(stream, in, sid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756

8728b834b   David S. Miller   [NET]: Kill skb->...
757
  		__skb_unlink(pos, &ulpq->lobby);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
759
  
  		/* Attach all gathered skbs to the event.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
760
  		__skb_queue_tail(event_list, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
762
763
764
  	}
  }
  
  /* Helper function to store chunks needing ordering.  */
01f2d3849   Vlad Yasevich   [SCTP]: Kill sill...
765
  static void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
  					   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...
781

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
  	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...
811
  	__skb_queue_before(&ulpq->lobby, pos, sctp_event2skb(event));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
  }
  
  static struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
8728b834b   David S. Miller   [NET]: Kill skb->...
815
  					     struct sctp_ulpevent *event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
  {
  	__u16 sid, ssn;
a83863174   Xin Long   sctp: prepare aso...
818
  	struct sctp_stream *stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
  
  	/* Check if this message needs ordering.  */
bd4d627db   Xin Long   sctp: implement u...
821
  	if (event->msg_flags & SCTP_DATA_UNORDERED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
825
826
  		return event;
  
  	/* Note: The stream ID must be verified before this routine.  */
  	sid = event->stream;
  	ssn = event->ssn;
cee360ab4   Xin Long   sctp: define the ...
827
  	stream  = &ulpq->asoc->stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
  
  	/* Is this the expected SSN for this stream ID?  */
a83863174   Xin Long   sctp: prepare aso...
830
  	if (ssn != sctp_ssn_peek(stream, in, sid)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
834
835
836
837
838
  		/* 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.  */
a83863174   Xin Long   sctp: prepare aso...
839
  	sctp_ssn_next(stream, in, sid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
841
842
843
844
845
846
847
848
849
850
851
  
  	/* 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...
852
  static void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq, __u16 sid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853
854
855
  {
  	struct sk_buff *pos, *tmp;
  	struct sctp_ulpevent *cevent;
8728b834b   David S. Miller   [NET]: Kill skb->...
856
  	struct sctp_ulpevent *event;
a83863174   Xin Long   sctp: prepare aso...
857
  	struct sctp_stream *stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  	struct sk_buff_head temp;
c068be549   Vlad Yasevich   [SCTP]: Correctly...
859
  	struct sk_buff_head *lobby = &ulpq->lobby;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
  	__u16 csid, cssn;
cee360ab4   Xin Long   sctp: define the ...
861
  	stream = &ulpq->asoc->stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
  
  	/* We are holding the chunks by stream, by SSN.  */
8728b834b   David S. Miller   [NET]: Kill skb->...
864
865
  	skb_queue_head_init(&temp);
  	event = NULL;
c068be549   Vlad Yasevich   [SCTP]: Correctly...
866
  	sctp_skb_for_each(pos, lobby, tmp) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
869
  		cevent = (struct sctp_ulpevent *) pos->cb;
  		csid = cevent->stream;
  		cssn = cevent->ssn;
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
870
871
  		/* Have we gone too far?  */
  		if (csid > sid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
  			break;
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
873
874
875
876
877
  		/* Have we not gone far enough?  */
  		if (csid < sid)
  			continue;
  
  		/* see if this ssn has been marked by skipping */
a83863174   Xin Long   sctp: prepare aso...
878
  		if (!SSN_lt(cssn, sctp_ssn_peek(stream, in, csid)))
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
879
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880

c068be549   Vlad Yasevich   [SCTP]: Correctly...
881
  		__skb_unlink(pos, lobby);
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
882
  		if (!event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
884
  			/* Create a temporary list to collect chunks on.  */
  			event = sctp_skb2event(pos);
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
885
886
887
  
  		/* Attach all gathered skbs to the event.  */
  		__skb_queue_tail(&temp, pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
  	}
c068be549   Vlad Yasevich   [SCTP]: Correctly...
889
890
891
892
893
894
895
  	/* 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;
a83863174   Xin Long   sctp: prepare aso...
896
897
  		if (csid == sid && cssn == sctp_ssn_peek(stream, in, csid)) {
  			sctp_ssn_next(stream, in, csid);
c068be549   Vlad Yasevich   [SCTP]: Correctly...
898
899
900
901
902
  			__skb_unlink(pos, lobby);
  			__skb_queue_tail(&temp, pos);
  			event = sctp_skb2event(pos);
  		}
  	}
8728b834b   David S. Miller   [NET]: Kill skb->...
903
904
905
  	/* Send event to the ULP.  'event' is the sctp_ulpevent for
  	 * very first SKB on the 'temp' list.
  	 */
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
906
907
908
  	if (event) {
  		/* see if we have more ordered that we can deliver */
  		sctp_ulpq_retrieve_ordered(ulpq, event);
013b96ec6   David Miller   sctp: Pass sk_buf...
909
  		sctp_ulpq_tail_event(ulpq, &temp);
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
910
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
  }
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
912
913
914
  /* 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
915
916
  void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
  {
a83863174   Xin Long   sctp: prepare aso...
917
  	struct sctp_stream *stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
  
  	/* Note: The stream ID must be verified before this routine.  */
cee360ab4   Xin Long   sctp: define the ...
920
  	stream  = &ulpq->asoc->stream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
  
  	/* Is this an old SSN?  If so ignore. */
a83863174   Xin Long   sctp: prepare aso...
923
  	if (SSN_lt(ssn, sctp_ssn_peek(stream, in, sid)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
926
  		return;
  
  	/* Mark that we are no longer expecting this SSN or lower. */
a83863174   Xin Long   sctp: prepare aso...
927
  	sctp_ssn_skip(stream, in, sid, ssn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
  
  	/* Go find any other chunks that were waiting for
d808ad9ab   YOSHIFUJI Hideaki   [NET] SCTP: Fix w...
930
  	 * ordering and deliver them if needed.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
  	 */
ea2dfb373   Vlad Yasevich   SCTP: properly cl...
932
  	sctp_ulpq_reap_ordered(ulpq, sid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
  }
94014e8d8   Xin Long   sctp: implement r...
934
935
  __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq, struct sk_buff_head *list,
  			    __u16 needed)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936
937
  {
  	__u16 freed = 0;
95ac7b859   Lee A. Roberts   sctp: fix associa...
938
939
  	__u32 tsn, last_tsn;
  	struct sk_buff *skb, *flist, *last;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
941
942
943
  	struct sctp_ulpevent *event;
  	struct sctp_tsnmap *tsnmap;
  
  	tsnmap = &ulpq->asoc->peer.tsn_map;
e67f85ecd   Lee A. Roberts   sctp: fix associa...
944
  	while ((skb = skb_peek_tail(list)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
  		event = sctp_skb2event(skb);
  		tsn = event->tsn;
e67f85ecd   Lee A. Roberts   sctp: fix associa...
947
948
949
  		/* 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...
950
951
952
953
  		/* 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...
954
  		freed += skb_headlen(skb);
95ac7b859   Lee A. Roberts   sctp: fix associa...
955
956
957
958
959
960
961
962
963
964
965
966
  		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
967
  		sctp_ulpevent_free(event);
95ac7b859   Lee A. Roberts   sctp: fix associa...
968
969
970
971
  		while (TSN_lte(tsn, last_tsn)) {
  			sctp_tsnmap_renege(tsnmap, tsn);
  			tsn++;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
973
974
975
976
977
  		if (freed >= needed)
  			return freed;
  	}
  
  	return freed;
  }
16d14ef9f   Pavel Emelyanov   [SCTP]: Consolida...
978
979
980
981
982
  /* 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
983
984
985
  /* Renege 'needed' bytes from the reassembly queue. */
  static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
  {
16d14ef9f   Pavel Emelyanov   [SCTP]: Consolida...
986
  	return sctp_ulpq_renege_list(ulpq, &ulpq->reasm, needed);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987
988
989
990
  }
  
  /* 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...
991
  				gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
993
994
  {
  	struct sctp_ulpevent *event;
  	struct sctp_association *asoc;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
995
  	struct sctp_sock *sp;
d003b41b8   Lee A. Roberts   sctp: fix associa...
996
997
  	__u32 ctsn;
  	struct sk_buff *skb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998
999
  
  	asoc = ulpq->asoc;
b6e1331f3   Vlad Yasevich   [SCTP]: Implement...
1000
  	sp = sctp_sk(asoc->base.sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001

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

d003b41b8   Lee A. Roberts   sctp: fix associa...
1008
1009
1010
1011
1012
1013
1014
1015
1016
  	/* 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...
1017
1018
1019
1020
1021
1022
  	/* 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
1023
1024
1025
1026
  		/* Is partial delivery possible?  */
  		event = sctp_ulpq_retrieve_first(ulpq);
  		/* Send event to the ULP.   */
  		if (event) {
925b93742   David Miller   sctp: Always pass...
1027
1028
1029
1030
  			struct sk_buff_head temp;
  
  			skb_queue_head_init(&temp);
  			__skb_queue_tail(&temp, sctp_event2skb(event));
013b96ec6   David Miller   sctp: Pass sk_buf...
1031
  			sctp_ulpq_tail_event(ulpq, &temp);
d49d91d79   Vlad Yasevich   [SCTP]: Implement...
1032
  			sctp_ulpq_set_pd(ulpq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1033
1034
1035
1036
1037
1038
1039
  			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...
1040
  		      gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041
  {
5c468674d   Xin Long   sctp: fix the iss...
1042
1043
1044
  	struct sctp_association *asoc = ulpq->asoc;
  	__u32 freed = 0;
  	__u16 needed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045

5c468674d   Xin Long   sctp: fix the iss...
1046
1047
  	needed = ntohs(chunk->chunk_hdr->length) -
  		 sizeof(struct sctp_data_chunk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
1050
  
  	if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
  		freed = sctp_ulpq_renege_order(ulpq, needed);
5c468674d   Xin Long   sctp: fix the iss...
1051
  		if (freed < needed)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052
  			freed += sctp_ulpq_renege_frags(ulpq, needed - freed);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
1054
  	}
  	/* If able to free enough room, accept this chunk. */
9dde27de3   Xin Long   sctp: implement m...
1055
1056
  	if (sk_rmem_schedule(asoc->base.sk, chunk->skb, needed) &&
  	    freed >= needed) {
5c468674d   Xin Long   sctp: fix the iss...
1057
  		int retval = sctp_ulpq_tail_data(ulpq, chunk, gfp);
d003b41b8   Lee A. Roberts   sctp: fix associa...
1058
1059
1060
1061
1062
1063
1064
1065
  		/*
  		 * 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
1066
  	}
3ab224be6   Hideo Aoki   [NET] CORE: Intro...
1067
  	sk_mem_reclaim(asoc->base.sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1068
1069
1070
1071
1072
1073
1074
  }
  
  
  
  /* 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...
1075
  void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
  {
  	struct sctp_ulpevent *ev = NULL;
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
1078
  	struct sctp_sock *sp;
2cc0eeb67   Xin Long   sctp: define subs...
1079
  	struct sock *sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1080
1081
1082
1083
1084
  
  	if (!ulpq->pd_mode)
  		return;
  
  	sk = ulpq->asoc->base.sk;
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
1085
  	sp = sctp_sk(sk);
a1e3a0590   Xin Long   sctp: add subscri...
1086
  	if (sctp_ulpevent_type_enabled(ulpq->asoc->subscribe,
2cc0eeb67   Xin Long   sctp: define subs...
1087
  				       SCTP_PARTIAL_DELIVERY_EVENT))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
  		ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
  					      SCTP_PARTIAL_DELIVERY_ABORTED,
65f5e3578   Xin Long   sctp: implement a...
1090
  					      0, 0, 0, gfp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091
1092
1093
1094
  	if (ev)
  		__skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
  
  	/* If there is data waiting, send it up the socket now. */
0970f5b36   Marcelo Ricardo Leitner   sctp: signal sk_d...
1095
1096
1097
1098
  	if ((sctp_ulpq_clear_pd(ulpq) || ev) && !sp->data_ready_signalled) {
  		sp->data_ready_signalled = 1;
  		sk->sk_data_ready(sk);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1099
  }