/* Calculate the SCTP checksum of an SCTP packet. */ staticinlineint sctp_rcv_checksum(struct net *net, struct sk_buff *skb)
{ struct sctphdr *sh = sctp_hdr(skb);
__le32 cmp = sh->checksum;
__le32 val = sctp_compute_cksum(skb, 0);
if (val != cmp) { /* CRC failure, dump it. */
__SCTP_INC_STATS(net, SCTP_MIB_CHECKSUMERRORS); return -1;
} return0;
}
/* *ThisistheroutinewhichIPcallswhenreceivinganSCTPpacket.
*/ int sctp_rcv(struct sk_buff *skb)
{ struct sock *sk; struct sctp_association *asoc; struct sctp_endpoint *ep = NULL; struct sctp_ep_common *rcvr; struct sctp_transport *transport = NULL; struct sctp_chunk *chunk; union sctp_addr src; union sctp_addr dest; int family; struct sctp_af *af; struct net *net = dev_net(skb->dev); bool is_gso = skb_is_gso(skb) && skb_is_gso_sctp(skb); int dif, sdif;
if (skb->pkt_type != PACKET_HOST) goto discard_it;
__SCTP_INC_STATS(net, SCTP_MIB_INSCTPPACKS);
/* If packet is too small to contain a single chunk, let's not *wastetimeonitanymore.
*/ if (skb->len < sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) +
skb_transport_offset(skb)) goto discard_it;
/* If the packet is fragmented and we need to do crc checking, *it'sbettertojustlinearizeitotherwisecrccomputing *takeslonger.
*/ if (((!is_gso || skb_cloned(skb)) && skb_linearize(skb)) ||
!pskb_may_pull(skb, sizeof(struct sctphdr))) goto discard_it;
/* Pull up the IP header. */
__skb_pull(skb, skb_transport_offset(skb));
skb->csum_valid = 0; /* Previous value not applicable */ if (skb_csum_unnecessary(skb))
__skb_decr_checksum_unnecessary(skb); elseif (!sctp_checksum_disable &&
!is_gso &&
sctp_rcv_checksum(net, skb) < 0) goto discard_it;
skb->csum_valid = 1;
__skb_pull(skb, sizeof(struct sctphdr));
family = ipver2af(ip_hdr(skb)->version);
af = sctp_get_af_specific(family); if (unlikely(!af)) goto discard_it;
SCTP_INPUT_CB(skb)->af = af;
/* Initialize local addresses for lookups. */
af->from_skb(&src, skb, 1);
af->from_skb(&dest, skb, 0);
dif = af->skb_iif(skb);
sdif = af->skb_sdif(skb);
/* If the packet is to or from a non-unicast address, *silentlydiscardthepacket. * *ThisisnotclearlydefinedintheRFCexceptinsection *8.4-OOTBhandling.However,basedonthebook"StreamControl *TransmissionProtocol"2.1,"Itisimportanttonotethatthe *IPaddressofanSCTPtransportaddressmustbearoutable *unicastaddress.Inotherwords,IPmulticastaddressesand *IPbroadcastaddressescannotbeusedinanSCTPtransport *address."
*/ if (!af->addr_valid(&src, NULL, skb) ||
!af->addr_valid(&dest, NULL, skb)) goto discard_it;
if (!asoc)
ep = __sctp_rcv_lookup_endpoint(net, skb, &dest, &src, dif, sdif);
/* Retrieve the common input handling substructure. */
rcvr = asoc ? &asoc->base : &ep->base;
sk = rcvr->sk;
/* *RFC2960,8.4-Handle"Outoftheblue"Packets. *AnSCTPpacketiscalledan"outoftheblue"(OOTB) *packetifitiscorrectlyformed,i.e.,passedthe *receiver'schecksumcheck,butthereceiverisnot *abletoidentifytheassociationtowhichthis *packetbelongs.
*/ if (!asoc) { if (sctp_rcv_ootb(skb)) {
__SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); goto discard_release;
}
}
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family)) goto discard_release;
nf_reset_ct(skb);
if (sk_filter(sk, skb)) goto discard_release;
/* Create an SCTP packet structure. */
chunk = sctp_chunkify(skb, asoc, sk, GFP_ATOMIC); if (!chunk) goto discard_release;
SCTP_INPUT_CB(skb)->chunk = chunk;
/* Remember what endpoint is to handle this packet. */
chunk->rcvr = rcvr;
/* Remember the SCTP header. */
chunk->sctp_hdr = sctp_hdr(skb);
/* Set the source and destination addresses of the incoming chunk. */
sctp_init_addrs(chunk, &src, &dest);
/* Remember where we came from. */
chunk->transport = transport;
/* Acquire access to the sock lock. Note: We are safe from other *bottomhalvesonthislock,butausermaybeinthelocktoo, *socheckifitisbusy.
*/
bh_lock_sock(sk);
if (sk != rcvr->sk) { /* Our cached sk is different from the rcvr->sk. This is *becausemigrate()/accept()mayhavemovedtheassociation *toanewsocketandreleasedallthesockets.Sonowwe *areholdingalockontheoldsocketwhiletheusermay *bedoingsomethingwiththenewsocket.Switchourveiw *ofthecurrentsk.
*/
bh_unlock_sock(sk);
sk = rcvr->sk;
bh_lock_sock(sk);
}
discard_release: /* Release the asoc/ep ref we took in the lookup calls. */ if (transport)
sctp_transport_put(transport); else
sctp_endpoint_put(ep);
goto discard_it;
}
/* Process the backlog queue of the socket. Every skb on *thebacklogholdsarefonanassociationorendpoint. *Weholdthisrefthroughoutthestatemachinetomake *surethatthestructureweneedisstillaround.
*/ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{ struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; struct sctp_inq *inqueue = &chunk->rcvr->inqueue; struct sctp_transport *t = chunk->transport; struct sctp_ep_common *rcvr = NULL; int backloged = 0;
rcvr = chunk->rcvr;
/* If the rcvr is dead then the association or endpoint *hasbeendeletedandwecansafelydropthechunk *andrefsthatweareholding.
*/ if (rcvr->dead) {
sctp_chunk_free(chunk); goto done;
}
if (unlikely(rcvr->sk != sk)) { /* In this case, the association moved from one socket to *another.Wearecurrentlysittingonthebacklogofthe *oldsocket,soweneedtomove. *However,sincewearehereintheprocesscontextwe *needtotakemakesurethattheuserdoesn'town *thenewsocketwhenweprocessthepacket. *Ifthenewsocketisuser-owned,queuethechunktothe *backlogofthenewsocketwithoutdroppinganyrefs. *Otherwise,wecansafelypushthechunkontheinqueue.
*/
sk = rcvr->sk;
local_bh_disable();
bh_lock_sock(sk);
if (sock_owned_by_user(sk) || !sctp_newsk_ready(sk)) { if (sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf)))
sctp_chunk_free(chunk); else
backloged = 1;
} else
sctp_inq_push(inqueue, chunk);
bh_unlock_sock(sk);
local_bh_enable();
/* If the chunk was backloged again, don't drop refs */ if (backloged) return0;
} else { if (!sctp_newsk_ready(sk)) { if (!sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf))) return0;
sctp_chunk_free(chunk);
} else {
sctp_inq_push(inqueue, chunk);
}
}
done: /* Release the refs we took in sctp_add_backlog */ if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
sctp_transport_put(t); elseif (SCTP_EP_TYPE_SOCKET == rcvr->type)
sctp_endpoint_put(sctp_ep(rcvr)); else
BUG();
ret = sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf)); if (!ret) { /* Hold the assoc/ep while hanging on the backlog queue. *Thisway,weknowstructuresweneedwillnotdisappear *fromus
*/ if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
sctp_transport_hold(t); elseif (SCTP_EP_TYPE_SOCKET == rcvr->type)
sctp_endpoint_hold(sctp_ep(rcvr)); else
BUG();
} return ret;
if (!(t->param_flags & SPP_PMTUD_ENABLE)) /* We can't allow retransmitting in such case, as the *retransmissionwouldbesizedjustasbefore,andthuswe *wouldgetanothericmp,andretransmitagain.
*/ return;
/* Update transports view of the MTU. Return if no update was needed. *Ifanupdatewasn'tneeded/possible,italsodoesn'tmakesenseto *trytoretransmitnow.
*/ if (!sctp_transport_update_pmtu(t, pmtu)) return;
/* Update association pmtu. */
sctp_assoc_sync_pmtu(asoc);
/* Retransmit with the new pmtu setting. */
sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD);
}
/* Common lookup code for icmp/icmpv6 error handler. */ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb, struct sctphdr *sctphdr, struct sctp_association **app, struct sctp_transport **tpp)
{ struct sctp_init_chunk *chunkhdr, _chunkhdr; union sctp_addr saddr; union sctp_addr daddr; struct sctp_af *af; struct sock *sk = NULL; struct sctp_association *asoc; struct sctp_transport *transport = NULL;
__u32 vtag = ntohl(sctphdr->vtag); int sdif = inet_sdif(skb); int dif = inet_iif(skb);
*app = NULL; *tpp = NULL;
af = sctp_get_af_specific(family); if (unlikely(!af)) { return NULL;
}
/* Initialize local addresses for lookups. */
af->from_skb(&saddr, skb, 1);
af->from_skb(&daddr, skb, 0);
/* Look for an association that matches the incoming ICMP error *packet.
*/
asoc = __sctp_lookup_association(net, &saddr, &daddr, &transport, dif, sdif); if (!asoc) return NULL;
/* If too many ICMPs get dropped on busy *serversthisneedstobesolveddifferently.
*/ if (sock_owned_by_user(sk))
__NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS);
/* Break out if chunk length is less then minimal. */ if (!ch || ntohs(ch->length) < sizeof(_ch)) break;
ch_end = offset + SCTP_PAD4(ntohs(ch->length)); if (ch_end > skb->len) break;
/* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the *receiverMUSTsilentlydiscardtheOOTBpacketandtakeno *furtheraction.
*/ if (SCTP_CID_ABORT == ch->type) goto discard;
/* RFC 8.4, 6) If the packet contains a SHUTDOWN COMPLETE *chunk,thereceivershouldsilentlydiscardthepacket *andtakenofurtheraction.
*/ if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type) goto discard;
/* Is there an association matching the given local and peer addresses? */ bool sctp_has_association(struct net *net, constunion sctp_addr *laddr, constunion sctp_addr *paddr, int dif, int sdif)
{ struct sctp_transport *transport;
/* Walk through the chunks looking for AUTH or ASCONF chunks *tohelpusfindtheassociation.
*/
ch = (struct sctp_chunkhdr *)skb->data; do { /* Break out if chunk length is less then minimal. */ if (ntohs(ch->length) < sizeof(*ch)) break;
switch (ch->type) { case SCTP_CID_AUTH:
have_auth = chunk_num; break;
case SCTP_CID_COOKIE_ECHO: /* If a packet arrives containing an AUTH chunk as *afirstchunk,aCOOKIE-ECHOchunkasthesecond *chunk,andpossiblymorechunksafterthem,and *thereceiverdoesnothaveanSTCBforthat *packet,thenauthenticationisbasedon *thecontentsoftheCOOKIE-ECHOchunk.
*/ if (have_auth == 1 && chunk_num == 2) return NULL; break;
case SCTP_CID_ASCONF: if (have_auth || net->sctp.addip_noauth)
asoc = __sctp_rcv_asconf_lookup(
net, ch, laddr,
sctp_hdr(skb)->source,
transportp, dif, sdif); break; default: break;
}
/* *TherearecircumstanceswhenweneedtolookinsidetheSCTPpacket *forinformationtohelpusfindtheassociation.Examples *includelookinginsideofINIT/INIT-ACKchunksoraftertheAUTH *chunks.
*/ staticstruct sctp_association *__sctp_rcv_lookup_harder(struct net *net, struct sk_buff *skb, constunion sctp_addr *laddr, struct sctp_transport **transportp, int dif, int sdif)
{ struct sctp_chunkhdr *ch;
/* We do not allow GSO frames here as we need to linearize and *thencannotguaranteeframeboundaries.Thisshouldn'tbean *issueaspacketshittingthisaremostlyINITorINIT-ACKand *thosecannotbeonGSO-styleanyway.
*/ if (skb_is_gso(skb) && skb_is_gso_sctp(skb)) return NULL;
ch = (struct sctp_chunkhdr *)skb->data;
/* The code below will attempt to walk the chunk and extract *parameterinformation.Beforewedothat,weneedtoverify *thatthechunklengthdoesn'tcauseoverflow.Otherwise,we'll *walkofftheend.
*/ if (SCTP_PAD4(ntohs(ch->length)) > skb->len) return NULL;
/* If this is INIT/INIT-ACK look inside the chunk too. */ if (ch->type == SCTP_CID_INIT || ch->type == SCTP_CID_INIT_ACK) return __sctp_rcv_init_lookup(net, skb, laddr, transportp, dif, sdif);
/* Lookup an association for an inbound skb. */ staticstruct sctp_association *__sctp_rcv_lookup(struct net *net, struct sk_buff *skb, constunion sctp_addr *paddr, constunion sctp_addr *laddr, struct sctp_transport **transportp, int dif, int sdif)
{ struct sctp_association *asoc;
/* Further lookup for INIT/INIT-ACK packets. *SCTPImplementorsGuide,2.18Handlingofaddress *parameterswithintheINITorINIT-ACK.
*/
asoc = __sctp_rcv_lookup_harder(net, skb, laddr, transportp, dif, sdif); if (asoc) goto out;
if (paddr->sa.sa_family == AF_INET)
pr_debug("sctp: asoc not found for src:%pI4:%d dst:%pI4:%d\n",
&laddr->v4.sin_addr, ntohs(laddr->v4.sin_port),
&paddr->v4.sin_addr, ntohs(paddr->v4.sin_port)); else
pr_debug("sctp: asoc not found for src:%pI6:%d dst:%pI6:%d\n",
&laddr->v6.sin6_addr, ntohs(laddr->v6.sin6_port),
&paddr->v6.sin6_addr, ntohs(paddr->v6.sin6_port));
out: return asoc;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.