162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* SCTP kernel implementation 362306a36Sopenharmony_ci * Copyright (c) 1999-2000 Cisco, Inc. 462306a36Sopenharmony_ci * Copyright (c) 1999-2001 Motorola, Inc. 562306a36Sopenharmony_ci * Copyright (c) 2002 International Business Machines, Corp. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is part of the SCTP kernel implementation 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * These functions are the methods for accessing the SCTP inqueue. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * An SCTP inqueue is a queue into which you push SCTP packets 1262306a36Sopenharmony_ci * (which might be bundles or fragments of chunks) and out of which you 1362306a36Sopenharmony_ci * pop SCTP whole chunks. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Please send any bug reports or fixes you make to the 1662306a36Sopenharmony_ci * email address(es): 1762306a36Sopenharmony_ci * lksctp developers <linux-sctp@vger.kernel.org> 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Written or modified by: 2062306a36Sopenharmony_ci * La Monte H.P. Yarroll <piggy@acm.org> 2162306a36Sopenharmony_ci * Karl Knutson <karl@athena.chicago.il.us> 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <net/sctp/sctp.h> 2762306a36Sopenharmony_ci#include <net/sctp/sm.h> 2862306a36Sopenharmony_ci#include <linux/interrupt.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Initialize an SCTP inqueue. */ 3262306a36Sopenharmony_civoid sctp_inq_init(struct sctp_inq *queue) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci INIT_LIST_HEAD(&queue->in_chunk_list); 3562306a36Sopenharmony_ci queue->in_progress = NULL; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Create a task for delivering data. */ 3862306a36Sopenharmony_ci INIT_WORK(&queue->immediate, NULL); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Release the memory associated with an SCTP inqueue. */ 4262306a36Sopenharmony_civoid sctp_inq_free(struct sctp_inq *queue) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct sctp_chunk *chunk, *tmp; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Empty the queue. */ 4762306a36Sopenharmony_ci list_for_each_entry_safe(chunk, tmp, &queue->in_chunk_list, list) { 4862306a36Sopenharmony_ci list_del_init(&chunk->list); 4962306a36Sopenharmony_ci sctp_chunk_free(chunk); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* If there is a packet which is currently being worked on, 5362306a36Sopenharmony_ci * free it as well. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci if (queue->in_progress) { 5662306a36Sopenharmony_ci sctp_chunk_free(queue->in_progress); 5762306a36Sopenharmony_ci queue->in_progress = NULL; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Put a new packet in an SCTP inqueue. 6262306a36Sopenharmony_ci * We assume that packet->sctp_hdr is set and in host byte order. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_civoid sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci /* Directly call the packet handling routine. */ 6762306a36Sopenharmony_ci if (chunk->rcvr->dead) { 6862306a36Sopenharmony_ci sctp_chunk_free(chunk); 6962306a36Sopenharmony_ci return; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* We are now calling this either from the soft interrupt 7362306a36Sopenharmony_ci * or from the backlog processing. 7462306a36Sopenharmony_ci * Eventually, we should clean up inqueue to not rely 7562306a36Sopenharmony_ci * on the BH related data structures. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci list_add_tail(&chunk->list, &q->in_chunk_list); 7862306a36Sopenharmony_ci if (chunk->asoc) 7962306a36Sopenharmony_ci chunk->asoc->stats.ipackets++; 8062306a36Sopenharmony_ci q->immediate.func(&q->immediate); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Peek at the next chunk on the inqeue. */ 8462306a36Sopenharmony_cistruct sctp_chunkhdr *sctp_inq_peek(struct sctp_inq *queue) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct sctp_chunk *chunk; 8762306a36Sopenharmony_ci struct sctp_chunkhdr *ch = NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci chunk = queue->in_progress; 9062306a36Sopenharmony_ci /* If there is no more chunks in this packet, say so */ 9162306a36Sopenharmony_ci if (chunk->singleton || 9262306a36Sopenharmony_ci chunk->end_of_packet || 9362306a36Sopenharmony_ci chunk->pdiscard) 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ch = (struct sctp_chunkhdr *)chunk->chunk_end; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return ch; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* Extract a chunk from an SCTP inqueue. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * WARNING: If you need to put the chunk on another queue, you need to 10562306a36Sopenharmony_ci * make a shallow copy (clone) of it. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistruct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct sctp_chunk *chunk; 11062306a36Sopenharmony_ci struct sctp_chunkhdr *ch = NULL; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* The assumption is that we are safe to process the chunks 11362306a36Sopenharmony_ci * at this time. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci chunk = queue->in_progress; 11762306a36Sopenharmony_ci if (chunk) { 11862306a36Sopenharmony_ci /* There is a packet that we have been working on. 11962306a36Sopenharmony_ci * Any post processing work to do before we move on? 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (chunk->singleton || 12262306a36Sopenharmony_ci chunk->end_of_packet || 12362306a36Sopenharmony_ci chunk->pdiscard) { 12462306a36Sopenharmony_ci if (chunk->head_skb == chunk->skb) { 12562306a36Sopenharmony_ci chunk->skb = skb_shinfo(chunk->skb)->frag_list; 12662306a36Sopenharmony_ci goto new_skb; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci if (chunk->skb->next) { 12962306a36Sopenharmony_ci chunk->skb = chunk->skb->next; 13062306a36Sopenharmony_ci goto new_skb; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (chunk->head_skb) 13462306a36Sopenharmony_ci chunk->skb = chunk->head_skb; 13562306a36Sopenharmony_ci sctp_chunk_free(chunk); 13662306a36Sopenharmony_ci chunk = queue->in_progress = NULL; 13762306a36Sopenharmony_ci } else { 13862306a36Sopenharmony_ci /* Nothing to do. Next chunk in the packet, please. */ 13962306a36Sopenharmony_ci ch = (struct sctp_chunkhdr *)chunk->chunk_end; 14062306a36Sopenharmony_ci /* Force chunk->skb->data to chunk->chunk_end. */ 14162306a36Sopenharmony_ci skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data); 14262306a36Sopenharmony_ci /* We are guaranteed to pull a SCTP header. */ 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Do we need to take the next packet out of the queue to process? */ 14762306a36Sopenharmony_ci if (!chunk) { 14862306a36Sopenharmony_ci struct list_head *entry; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cinext_chunk: 15162306a36Sopenharmony_ci /* Is the queue empty? */ 15262306a36Sopenharmony_ci entry = sctp_list_dequeue(&queue->in_chunk_list); 15362306a36Sopenharmony_ci if (!entry) 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci chunk = list_entry(entry, struct sctp_chunk, list); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (skb_is_gso(chunk->skb) && skb_is_gso_sctp(chunk->skb)) { 15962306a36Sopenharmony_ci /* GSO-marked skbs but without frags, handle 16062306a36Sopenharmony_ci * them normally 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci if (skb_shinfo(chunk->skb)->frag_list) 16362306a36Sopenharmony_ci chunk->head_skb = chunk->skb; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* skbs with "cover letter" */ 16662306a36Sopenharmony_ci if (chunk->head_skb && chunk->skb->data_len == chunk->skb->len) 16762306a36Sopenharmony_ci chunk->skb = skb_shinfo(chunk->skb)->frag_list; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (WARN_ON(!chunk->skb)) { 17062306a36Sopenharmony_ci __SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS); 17162306a36Sopenharmony_ci sctp_chunk_free(chunk); 17262306a36Sopenharmony_ci goto next_chunk; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (chunk->asoc) 17762306a36Sopenharmony_ci sock_rps_save_rxhash(chunk->asoc->base.sk, chunk->skb); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci queue->in_progress = chunk; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cinew_skb: 18262306a36Sopenharmony_ci /* This is the first chunk in the packet. */ 18362306a36Sopenharmony_ci ch = (struct sctp_chunkhdr *)chunk->skb->data; 18462306a36Sopenharmony_ci chunk->singleton = 1; 18562306a36Sopenharmony_ci chunk->data_accepted = 0; 18662306a36Sopenharmony_ci chunk->pdiscard = 0; 18762306a36Sopenharmony_ci chunk->auth = 0; 18862306a36Sopenharmony_ci chunk->has_asconf = 0; 18962306a36Sopenharmony_ci chunk->end_of_packet = 0; 19062306a36Sopenharmony_ci if (chunk->head_skb) { 19162306a36Sopenharmony_ci struct sctp_input_cb 19262306a36Sopenharmony_ci *cb = SCTP_INPUT_CB(chunk->skb), 19362306a36Sopenharmony_ci *head_cb = SCTP_INPUT_CB(chunk->head_skb); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci cb->chunk = head_cb->chunk; 19662306a36Sopenharmony_ci cb->af = head_cb->af; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci chunk->chunk_hdr = ch; 20162306a36Sopenharmony_ci chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length)); 20262306a36Sopenharmony_ci skb_pull(chunk->skb, sizeof(*ch)); 20362306a36Sopenharmony_ci chunk->subh.v = NULL; /* Subheader is no longer valid. */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) { 20662306a36Sopenharmony_ci /* This is not a singleton */ 20762306a36Sopenharmony_ci chunk->singleton = 0; 20862306a36Sopenharmony_ci } else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) { 20962306a36Sopenharmony_ci /* Discard inside state machine. */ 21062306a36Sopenharmony_ci chunk->pdiscard = 1; 21162306a36Sopenharmony_ci chunk->chunk_end = skb_tail_pointer(chunk->skb); 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci /* We are at the end of the packet, so mark the chunk 21462306a36Sopenharmony_ci * in case we need to send a SACK. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci chunk->end_of_packet = 1; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci pr_debug("+++sctp_inq_pop+++ chunk:%p[%s], length:%d, skb->len:%d\n", 22062306a36Sopenharmony_ci chunk, sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)), 22162306a36Sopenharmony_ci ntohs(chunk->chunk_hdr->length), chunk->skb->len); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return chunk; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* Set a top-half handler. 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * Originally, we the top-half handler was scheduled as a BH. We now 22962306a36Sopenharmony_ci * call the handler directly in sctp_inq_push() at a time that 23062306a36Sopenharmony_ci * we know we are lock safe. 23162306a36Sopenharmony_ci * The intent is that this routine will pull stuff out of the 23262306a36Sopenharmony_ci * inqueue and process it. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_civoid sctp_inq_set_th_handler(struct sctp_inq *q, work_func_t callback) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci INIT_WORK(&q->immediate, callback); 23762306a36Sopenharmony_ci} 238