xref: /openbmc/linux/net/sctp/inqueue.c (revision 1f45f78f8e511203f03138f2ccde3d2cf90d2cbf)
160c778b2SVlad Yasevich /* SCTP kernel implementation
21da177e4SLinus Torvalds  * Copyright (c) 1999-2000 Cisco, Inc.
31da177e4SLinus Torvalds  * Copyright (c) 1999-2001 Motorola, Inc.
41da177e4SLinus Torvalds  * Copyright (c) 2002 International Business Machines, Corp.
51da177e4SLinus Torvalds  *
660c778b2SVlad Yasevich  * This file is part of the SCTP kernel implementation
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * These functions are the methods for accessing the SCTP inqueue.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * An SCTP inqueue is a queue into which you push SCTP packets
111da177e4SLinus Torvalds  * (which might be bundles or fragments of chunks) and out of which you
121da177e4SLinus Torvalds  * pop SCTP whole chunks.
131da177e4SLinus Torvalds  *
1460c778b2SVlad Yasevich  * This SCTP implementation is free software;
151da177e4SLinus Torvalds  * you can redistribute it and/or modify it under the terms of
161da177e4SLinus Torvalds  * the GNU General Public License as published by
171da177e4SLinus Torvalds  * the Free Software Foundation; either version 2, or (at your option)
181da177e4SLinus Torvalds  * any later version.
191da177e4SLinus Torvalds  *
2060c778b2SVlad Yasevich  * This SCTP implementation is distributed in the hope that it
211da177e4SLinus Torvalds  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
221da177e4SLinus Torvalds  *                 ************************
231da177e4SLinus Torvalds  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
241da177e4SLinus Torvalds  * See the GNU General Public License for more details.
251da177e4SLinus Torvalds  *
261da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
274b2f13a2SJeff Kirsher  * along with GNU CC; see the file COPYING.  If not, see
284b2f13a2SJeff Kirsher  * <http://www.gnu.org/licenses/>.
291da177e4SLinus Torvalds  *
301da177e4SLinus Torvalds  * Please send any bug reports or fixes you make to the
311da177e4SLinus Torvalds  * email address(es):
3291705c61SDaniel Borkmann  *    lksctp developers <linux-sctp@vger.kernel.org>
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  * Written or modified by:
351da177e4SLinus Torvalds  *    La Monte H.P. Yarroll <piggy@acm.org>
361da177e4SLinus Torvalds  *    Karl Knutson <karl@athena.chicago.il.us>
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds 
39145ce502SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
40145ce502SJoe Perches 
411da177e4SLinus Torvalds #include <net/sctp/sctp.h>
421da177e4SLinus Torvalds #include <net/sctp/sm.h>
431da177e4SLinus Torvalds #include <linux/interrupt.h>
445a0e3ad6STejun Heo #include <linux/slab.h>
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds /* Initialize an SCTP inqueue.  */
471da177e4SLinus Torvalds void sctp_inq_init(struct sctp_inq *queue)
481da177e4SLinus Torvalds {
4979af02c2SDavid S. Miller 	INIT_LIST_HEAD(&queue->in_chunk_list);
501da177e4SLinus Torvalds 	queue->in_progress = NULL;
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	/* Create a task for delivering data.  */
53c4028958SDavid Howells 	INIT_WORK(&queue->immediate, NULL);
541da177e4SLinus Torvalds }
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* Release the memory associated with an SCTP inqueue.  */
571da177e4SLinus Torvalds void sctp_inq_free(struct sctp_inq *queue)
581da177e4SLinus Torvalds {
5979af02c2SDavid S. Miller 	struct sctp_chunk *chunk, *tmp;
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	/* Empty the queue.  */
6279af02c2SDavid S. Miller 	list_for_each_entry_safe(chunk, tmp, &queue->in_chunk_list, list) {
6379af02c2SDavid S. Miller 		list_del_init(&chunk->list);
641da177e4SLinus Torvalds 		sctp_chunk_free(chunk);
6579af02c2SDavid S. Miller 	}
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 	/* If there is a packet which is currently being worked on,
681da177e4SLinus Torvalds 	 * free it as well.
691da177e4SLinus Torvalds 	 */
707a48f923SSridhar Samudrala 	if (queue->in_progress) {
711da177e4SLinus Torvalds 		sctp_chunk_free(queue->in_progress);
727a48f923SSridhar Samudrala 		queue->in_progress = NULL;
737a48f923SSridhar Samudrala 	}
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /* Put a new packet in an SCTP inqueue.
771da177e4SLinus Torvalds  * We assume that packet->sctp_hdr is set and in host byte order.
781da177e4SLinus Torvalds  */
79ac0b0462SSridhar Samudrala void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	/* Directly call the packet handling routine. */
82027f6e1aSVlad Yasevich 	if (chunk->rcvr->dead) {
83027f6e1aSVlad Yasevich 		sctp_chunk_free(chunk);
84027f6e1aSVlad Yasevich 		return;
85027f6e1aSVlad Yasevich 	}
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	/* We are now calling this either from the soft interrupt
881da177e4SLinus Torvalds 	 * or from the backlog processing.
891da177e4SLinus Torvalds 	 * Eventually, we should clean up inqueue to not rely
901da177e4SLinus Torvalds 	 * on the BH related data structures.
911da177e4SLinus Torvalds 	 */
92860fbbc3SEric Dumazet 	local_bh_disable();
93ac0b0462SSridhar Samudrala 	list_add_tail(&chunk->list, &q->in_chunk_list);
94196d6759SMichele Baldessari 	if (chunk->asoc)
95196d6759SMichele Baldessari 		chunk->asoc->stats.ipackets++;
96c4028958SDavid Howells 	q->immediate.func(&q->immediate);
97860fbbc3SEric Dumazet 	local_bh_enable();
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
100bbd0d598SVlad Yasevich /* Peek at the next chunk on the inqeue. */
101bbd0d598SVlad Yasevich struct sctp_chunkhdr *sctp_inq_peek(struct sctp_inq *queue)
102bbd0d598SVlad Yasevich {
103bbd0d598SVlad Yasevich 	struct sctp_chunk *chunk;
104bbd0d598SVlad Yasevich 	sctp_chunkhdr_t *ch = NULL;
105bbd0d598SVlad Yasevich 
106bbd0d598SVlad Yasevich 	chunk = queue->in_progress;
107bbd0d598SVlad Yasevich 	/* If there is no more chunks in this packet, say so */
108bbd0d598SVlad Yasevich 	if (chunk->singleton ||
109bbd0d598SVlad Yasevich 	    chunk->end_of_packet ||
110bbd0d598SVlad Yasevich 	    chunk->pdiscard)
111bbd0d598SVlad Yasevich 		    return NULL;
112bbd0d598SVlad Yasevich 
113bbd0d598SVlad Yasevich 	ch = (sctp_chunkhdr_t *)chunk->chunk_end;
114bbd0d598SVlad Yasevich 
115bbd0d598SVlad Yasevich 	return ch;
116bbd0d598SVlad Yasevich }
117bbd0d598SVlad Yasevich 
118bbd0d598SVlad Yasevich 
1191da177e4SLinus Torvalds /* Extract a chunk from an SCTP inqueue.
1201da177e4SLinus Torvalds  *
1211da177e4SLinus Torvalds  * WARNING:  If you need to put the chunk on another queue, you need to
1221da177e4SLinus Torvalds  * make a shallow copy (clone) of it.
1231da177e4SLinus Torvalds  */
1241da177e4SLinus Torvalds struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	struct sctp_chunk *chunk;
1271da177e4SLinus Torvalds 	sctp_chunkhdr_t *ch = NULL;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	/* The assumption is that we are safe to process the chunks
1301da177e4SLinus Torvalds 	 * at this time.
1311da177e4SLinus Torvalds 	 */
1321da177e4SLinus Torvalds 
1333acb50c1SMarcelo Ricardo Leitner 	chunk = queue->in_progress;
1343acb50c1SMarcelo Ricardo Leitner 	if (chunk) {
1351da177e4SLinus Torvalds 		/* There is a packet that we have been working on.
1361da177e4SLinus Torvalds 		 * Any post processing work to do before we move on?
1371da177e4SLinus Torvalds 		 */
1381da177e4SLinus Torvalds 		if (chunk->singleton ||
1391da177e4SLinus Torvalds 		    chunk->end_of_packet ||
1401da177e4SLinus Torvalds 		    chunk->pdiscard) {
14190017accSMarcelo Ricardo Leitner 			if (chunk->head_skb == chunk->skb) {
14290017accSMarcelo Ricardo Leitner 				chunk->skb = skb_shinfo(chunk->skb)->frag_list;
14390017accSMarcelo Ricardo Leitner 				goto new_skb;
14490017accSMarcelo Ricardo Leitner 			}
14590017accSMarcelo Ricardo Leitner 			if (chunk->skb->next) {
14690017accSMarcelo Ricardo Leitner 				chunk->skb = chunk->skb->next;
14790017accSMarcelo Ricardo Leitner 				goto new_skb;
14890017accSMarcelo Ricardo Leitner 			}
14990017accSMarcelo Ricardo Leitner 
15090017accSMarcelo Ricardo Leitner 			if (chunk->head_skb)
15190017accSMarcelo Ricardo Leitner 				chunk->skb = chunk->head_skb;
1521da177e4SLinus Torvalds 			sctp_chunk_free(chunk);
1531da177e4SLinus Torvalds 			chunk = queue->in_progress = NULL;
1541da177e4SLinus Torvalds 		} else {
1551da177e4SLinus Torvalds 			/* Nothing to do. Next chunk in the packet, please. */
1561da177e4SLinus Torvalds 			ch = (sctp_chunkhdr_t *) chunk->chunk_end;
1571da177e4SLinus Torvalds 			/* Force chunk->skb->data to chunk->chunk_end.  */
15826b87c78SDaniel Borkmann 			skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
15926b87c78SDaniel Borkmann 			/* We are guaranteed to pull a SCTP header. */
1601da177e4SLinus Torvalds 		}
1611da177e4SLinus Torvalds 	}
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	/* Do we need to take the next packet out of the queue to process? */
1641da177e4SLinus Torvalds 	if (!chunk) {
16579af02c2SDavid S. Miller 		struct list_head *entry;
16679af02c2SDavid S. Miller 
1673acb50c1SMarcelo Ricardo Leitner next_chunk:
1681da177e4SLinus Torvalds 		/* Is the queue empty?  */
16990017accSMarcelo Ricardo Leitner 		entry = sctp_list_dequeue(&queue->in_chunk_list);
17090017accSMarcelo Ricardo Leitner 		if (!entry)
1711da177e4SLinus Torvalds 			return NULL;
1721da177e4SLinus Torvalds 
1733acb50c1SMarcelo Ricardo Leitner 		chunk = list_entry(entry, struct sctp_chunk, list);
1741da177e4SLinus Torvalds 
1753acb50c1SMarcelo Ricardo Leitner 		/* Linearize if it's not GSO */
17690017accSMarcelo Ricardo Leitner 		if ((skb_shinfo(chunk->skb)->gso_type & SKB_GSO_SCTP) != SKB_GSO_SCTP &&
17790017accSMarcelo Ricardo Leitner 		    skb_is_nonlinear(chunk->skb)) {
1783acb50c1SMarcelo Ricardo Leitner 			if (skb_linearize(chunk->skb)) {
1793acb50c1SMarcelo Ricardo Leitner 				__SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
1803acb50c1SMarcelo Ricardo Leitner 				sctp_chunk_free(chunk);
1813acb50c1SMarcelo Ricardo Leitner 				goto next_chunk;
1823acb50c1SMarcelo Ricardo Leitner 			}
1833acb50c1SMarcelo Ricardo Leitner 
1843acb50c1SMarcelo Ricardo Leitner 			/* Update sctp_hdr as it probably changed */
1853acb50c1SMarcelo Ricardo Leitner 			chunk->sctp_hdr = sctp_hdr(chunk->skb);
1863acb50c1SMarcelo Ricardo Leitner 		}
1873acb50c1SMarcelo Ricardo Leitner 
18890017accSMarcelo Ricardo Leitner 		if ((skb_shinfo(chunk->skb)->gso_type & SKB_GSO_SCTP) == SKB_GSO_SCTP) {
18990017accSMarcelo Ricardo Leitner 			/* GSO-marked skbs but without frags, handle
19090017accSMarcelo Ricardo Leitner 			 * them normally
19190017accSMarcelo Ricardo Leitner 			 */
19290017accSMarcelo Ricardo Leitner 			if (skb_shinfo(chunk->skb)->frag_list)
19390017accSMarcelo Ricardo Leitner 				chunk->head_skb = chunk->skb;
1943acb50c1SMarcelo Ricardo Leitner 
19590017accSMarcelo Ricardo Leitner 			/* skbs with "cover letter" */
19690017accSMarcelo Ricardo Leitner 			if (chunk->head_skb && chunk->skb->data_len == chunk->skb->len)
19790017accSMarcelo Ricardo Leitner 				chunk->skb = skb_shinfo(chunk->skb)->frag_list;
19890017accSMarcelo Ricardo Leitner 
19990017accSMarcelo Ricardo Leitner 			if (WARN_ON(!chunk->skb)) {
20090017accSMarcelo Ricardo Leitner 				__SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
20190017accSMarcelo Ricardo Leitner 				sctp_chunk_free(chunk);
20290017accSMarcelo Ricardo Leitner 				goto next_chunk;
20390017accSMarcelo Ricardo Leitner 			}
20490017accSMarcelo Ricardo Leitner 		}
205486bdee0SMarcelo Ricardo Leitner 
206486bdee0SMarcelo Ricardo Leitner 		if (chunk->asoc)
207486bdee0SMarcelo Ricardo Leitner 			sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb);
20890017accSMarcelo Ricardo Leitner 
20990017accSMarcelo Ricardo Leitner 		queue->in_progress = chunk;
21090017accSMarcelo Ricardo Leitner 
21190017accSMarcelo Ricardo Leitner new_skb:
21290017accSMarcelo Ricardo Leitner 		/* This is the first chunk in the packet.  */
21390017accSMarcelo Ricardo Leitner 		ch = (sctp_chunkhdr_t *) chunk->skb->data;
21490017accSMarcelo Ricardo Leitner 		chunk->singleton = 1;
21590017accSMarcelo Ricardo Leitner 		chunk->data_accepted = 0;
21690017accSMarcelo Ricardo Leitner 		chunk->pdiscard = 0;
21790017accSMarcelo Ricardo Leitner 		chunk->auth = 0;
21890017accSMarcelo Ricardo Leitner 		chunk->has_asconf = 0;
21990017accSMarcelo Ricardo Leitner 		chunk->end_of_packet = 0;
22090017accSMarcelo Ricardo Leitner 		chunk->ecn_ce_done = 0;
221*1f45f78fSMarcelo Ricardo Leitner 		if (chunk->head_skb) {
222*1f45f78fSMarcelo Ricardo Leitner 			struct sctp_input_cb
223*1f45f78fSMarcelo Ricardo Leitner 				*cb = SCTP_INPUT_CB(chunk->skb),
224*1f45f78fSMarcelo Ricardo Leitner 				*head_cb = SCTP_INPUT_CB(chunk->head_skb);
225*1f45f78fSMarcelo Ricardo Leitner 
226*1f45f78fSMarcelo Ricardo Leitner 			cb->chunk = head_cb->chunk;
227*1f45f78fSMarcelo Ricardo Leitner 		}
2281da177e4SLinus Torvalds 	}
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	chunk->chunk_hdr = ch;
2311da177e4SLinus Torvalds 	chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
2321da177e4SLinus Torvalds 	skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
2331da177e4SLinus Torvalds 	chunk->subh.v = NULL; /* Subheader is no longer valid.  */
2341da177e4SLinus Torvalds 
23526b87c78SDaniel Borkmann 	if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
23626b87c78SDaniel Borkmann 	    skb_tail_pointer(chunk->skb)) {
2371da177e4SLinus Torvalds 		/* This is not a singleton */
2381da177e4SLinus Torvalds 		chunk->singleton = 0;
23927a884dcSArnaldo Carvalho de Melo 	} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
24026b87c78SDaniel Borkmann 		/* Discard inside state machine. */
24126b87c78SDaniel Borkmann 		chunk->pdiscard = 1;
24226b87c78SDaniel Borkmann 		chunk->chunk_end = skb_tail_pointer(chunk->skb);
2431da177e4SLinus Torvalds 	} else {
2441da177e4SLinus Torvalds 		/* We are at the end of the packet, so mark the chunk
2451da177e4SLinus Torvalds 		 * in case we need to send a SACK.
2461da177e4SLinus Torvalds 		 */
2471da177e4SLinus Torvalds 		chunk->end_of_packet = 1;
2481da177e4SLinus Torvalds 	}
2491da177e4SLinus Torvalds 
250bb33381dSDaniel Borkmann 	pr_debug("+++sctp_inq_pop+++ chunk:%p[%s], length:%d, skb->len:%d\n",
251bb33381dSDaniel Borkmann 		 chunk, sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)),
2521da177e4SLinus Torvalds 		 ntohs(chunk->chunk_hdr->length), chunk->skb->len);
253bb33381dSDaniel Borkmann 
2541da177e4SLinus Torvalds 	return chunk;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds /* Set a top-half handler.
2581da177e4SLinus Torvalds  *
2591da177e4SLinus Torvalds  * Originally, we the top-half handler was scheduled as a BH.  We now
2601da177e4SLinus Torvalds  * call the handler directly in sctp_inq_push() at a time that
2611da177e4SLinus Torvalds  * we know we are lock safe.
2621da177e4SLinus Torvalds  * The intent is that this routine will pull stuff out of the
2631da177e4SLinus Torvalds  * inqueue and process it.
2641da177e4SLinus Torvalds  */
265c4028958SDavid Howells void sctp_inq_set_th_handler(struct sctp_inq *q, work_func_t callback)
2661da177e4SLinus Torvalds {
267c4028958SDavid Howells 	INIT_WORK(&q->immediate, callback);
2681da177e4SLinus Torvalds }
269