// SPDX-License-Identifier: GPL-2.0-or-later /* SCTP kernel implementation * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2002 Intel Corp. * * This file is part of the SCTP kernel implementation * * These functions work with the state functions in sctp_sm_statefuns.c * to implement the state operations. These functions implement the * steps which require modifying existing data structures. * * Please send any bug reports or fixes you make to the * email address(es): * lksctp developers <linux-sctp@vger.kernel.org> * * Written or modified by: * La Monte H.P. Yarroll <piggy@acm.org> * Karl Knutson <karl@athena.chicago.il.us> * C. Robin <chris@hundredacre.ac.uk> * Jon Grimm <jgrimm@us.ibm.com> * Xingang Guo <xingang.guo@intel.com> * Dajiang Zhang <dajiang.zhang@nokia.com> * Sridhar Samudrala <sri@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com> * Ardelle Fan <ardelle.fan@intel.com> * Kevin Gao <kevin.gao@intel.com>
*/
/* refcnt == 2 and !list_empty mean after this release, it's * not being used anywhere, and it's time to notify userland * that this shkey can be freed if it's been deactivated.
*/ if (shkey->deactivated && !list_empty(&shkey->key_list) &&
refcount_read(&shkey->refcnt) == 2) { struct sctp_ulpevent *ev;
ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id,
SCTP_AUTH_FREE_KEY,
GFP_KERNEL); if (ev)
asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
}
sctp_auth_shkey_release(chunk->shkey);
}
}
/* TODO: properly account for control chunks. * To do it right we'll need: * 1) endpoint if association isn't known. * 2) proper memory accounting. * * For now don't do anything for now.
*/ if (chunk->auth) {
chunk->shkey = asoc->shkey;
sctp_auth_shkey_hold(chunk->shkey);
}
skb->sk = asoc ? asoc->base.sk : NULL;
skb_shinfo(skb)->destructor_arg = chunk;
skb->destructor = sctp_control_release_owner;
}
/* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 2: The ECN capable field is reserved for future use of * Explicit Congestion Notification.
*/ staticconststruct sctp_paramhdr ecap_param = {
SCTP_PARAM_ECN_CAPABLE,
cpu_to_be16(sizeof(struct sctp_paramhdr)),
}; staticconststruct sctp_paramhdr prsctp_param = {
SCTP_PARAM_FWD_TSN_SUPPORT,
cpu_to_be16(sizeof(struct sctp_paramhdr)),
};
/* A helper to initialize an op error inside a provided chunk, as most * cause codes will be embedded inside an abort chunk.
*/ int sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
size_t paylen)
{ struct sctp_errhdr err;
__u16 len;
/* Cause code constants are now defined in network order. */
err.cause = cause_code;
len = sizeof(err) + paylen;
err.length = htons(len);
if (skb_tailroom(chunk->skb) < len) return -ENOSPC;
/* 3.3.2 Initiation (INIT) (1) * * This chunk is used to initiate a SCTP association between two * endpoints. The format of the INIT chunk is shown below: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type = 1 | Chunk Flags | Chunk Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Initiate Tag | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Advertised Receiver Window Credit (a_rwnd) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Number of Outbound Streams | Number of Inbound Streams | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Initial TSN | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * \ \ * / Optional/Variable-Length Parameters / * \ \ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * * The INIT chunk contains the following parameters. Unless otherwise * noted, each parameter MUST only be included once in the INIT chunk. * * Fixed Parameters Status * ---------------------------------------------- * Initiate Tag Mandatory * Advertised Receiver Window Credit Mandatory * Number of Outbound Streams Mandatory * Number of Inbound Streams Mandatory * Initial TSN Mandatory * * Variable Parameters Status Type Value * ------------------------------------------------------------- * IPv4 Address (Note 1) Optional 5 * IPv6 Address (Note 1) Optional 6 * Cookie Preservative Optional 9 * Reserved for ECN Capable (Note 2) Optional 32768 (0x8000) * Host Name Address (Note 3) Optional 11 * Supported Address Types (Note 4) Optional 12
*/ struct sctp_chunk *sctp_make_init(conststruct sctp_association *asoc, conststruct sctp_bind_addr *bp,
gfp_t gfp, int vparam_len)
{ struct sctp_supported_ext_param ext_param; struct sctp_adaptation_ind_param aiparam; struct sctp_paramhdr *auth_chunks = NULL; struct sctp_paramhdr *auth_hmacs = NULL; struct sctp_supported_addrs_param sat; struct sctp_endpoint *ep = asoc->ep; struct sctp_chunk *retval = NULL; int num_types, addrs_len = 0; struct sctp_inithdr init; union sctp_params addrs; struct sctp_sock *sp;
__u8 extensions[5];
size_t chunksize;
__be16 types[2]; int num_ext = 0;
/* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 1: The INIT chunks can contain multiple addresses that * can be IPv4 and/or IPv6 in any combination.
*/
/* Convert the provided bind address list to raw format. */
addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, gfp);
if (asoc->ep->ecn_enable)
chunksize += sizeof(ecap_param);
if (asoc->ep->prsctp_enable)
chunksize += sizeof(prsctp_param);
/* ADDIP: Section 4.2.7: * An implementation supporting this extension [ADDIP] MUST list * the ASCONF,the ASCONF-ACK, and the AUTH chunks in its INIT and * INIT-ACK parameters.
*/ if (asoc->ep->asconf_enable) {
extensions[num_ext] = SCTP_CID_ASCONF;
extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
num_ext += 2;
}
if (asoc->ep->reconf_enable) {
extensions[num_ext] = SCTP_CID_RECONF;
num_ext += 1;
}
if (sp->adaptation_ind)
chunksize += sizeof(aiparam);
if (asoc->ep->intl_enable) {
extensions[num_ext] = SCTP_CID_I_DATA;
num_ext += 1;
}
chunksize += vparam_len;
/* Account for AUTH related parameters */ if (ep->auth_enable) { /* Add random parameter length*/
chunksize += sizeof(asoc->c.auth_random);
/* Add HMACS parameter length if any were defined */
auth_hmacs = (struct sctp_paramhdr *)asoc->c.auth_hmacs; if (auth_hmacs->length)
chunksize += SCTP_PAD4(ntohs(auth_hmacs->length)); else
auth_hmacs = NULL;
/* If we have any extensions to report, account for that */ if (num_ext)
chunksize += SCTP_PAD4(sizeof(ext_param) + num_ext);
/* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 3: An INIT chunk MUST NOT contain more than one Host * Name address parameter. Moreover, the sender of the INIT * MUST NOT combine any other address types with the Host Name * address in the INIT. The receiver of INIT MUST ignore any * other address types if the Host Name address parameter is * present in the received INIT chunk. * * PLEASE DO NOT FIXME [This version does not support Host Name.]
*/
/* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 4: This parameter, when present, specifies all the * address types the sending endpoint can support. The absence * of this parameter indicates that the sending endpoint can * support any address type.
*/
sat.param_hdr.type = SCTP_PARAM_SUPPORTED_ADDRESS_TYPES;
sat.param_hdr.length = htons(SCTP_SAT_LEN(num_types));
sctp_addto_chunk(retval, sizeof(sat), &sat);
sctp_addto_chunk(retval, num_types * sizeof(__u16), &types);
if (asoc->ep->ecn_enable)
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
/* Add the supported extensions parameter. Be nice and add this * fist before addiding the parameters for the extensions themselves
*/ if (num_ext) {
ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT;
ext_param.param_hdr.length = htons(sizeof(ext_param) + num_ext);
sctp_addto_chunk(retval, sizeof(ext_param), &ext_param);
sctp_addto_param(retval, num_ext, extensions);
}
if (asoc->ep->prsctp_enable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
/* FIXME: We really ought to build the cookie right * into the packet instead of allocating more fresh memory.
*/
cookie = sctp_pack_cookie(asoc->ep, asoc, chunk, &cookie_len,
addrs.v, addrs_len); if (!cookie) goto nomem_cookie;
/* Calculate the total size of allocation, include the reserved * space for reporting unknown parameters if it is specified.
*/
sp = sctp_sk(asoc->base.sk);
chunksize = sizeof(initack) + addrs_len + cookie_len + unkparam_len;
/* Tell peer that we'll do ECN only if peer advertised such cap. */ if (asoc->peer.ecn_capable)
chunksize += sizeof(ecap_param);
if (asoc->peer.prsctp_capable)
chunksize += sizeof(prsctp_param);
if (num_ext)
chunksize += SCTP_PAD4(sizeof(ext_param) + num_ext);
/* Now allocate and fill out the chunk. */
retval = sctp_make_control(asoc, SCTP_CID_INIT_ACK, 0, chunksize, gfp); if (!retval) goto nomem_chunk;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it received the DATA or control chunk * to which it is replying. * * [INIT ACK back to where the INIT came from.]
*/ if (chunk->transport)
retval->transport =
sctp_assoc_lookup_paddr(asoc,
&chunk->transport->ipaddr);
/* 3.3.11 Cookie Echo (COOKIE ECHO) (10): * * This chunk is used only during the initialization of an association. * It is sent by the initiator of an association to its peer to complete * the initialization process. This chunk MUST precede any DATA chunk * sent within the association, but MAY be bundled with one or more DATA * chunks in the same packet. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type = 10 |Chunk Flags | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * / Cookie / * \ \ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Chunk Flags: 8 bit * * Set to zero on transmit and ignored on receipt. * * Length: 16 bits (unsigned integer) * * Set to the size of the chunk in bytes, including the 4 bytes of * the chunk header and the size of the Cookie. * * Cookie: variable size * * This field must contain the exact cookie received in the * State Cookie parameter from the previous INIT ACK. * * An implementation SHOULD make the cookie as small as possible * to insure interoperability.
*/ struct sctp_chunk *sctp_make_cookie_echo(conststruct sctp_association *asoc, conststruct sctp_chunk *chunk)
{ struct sctp_chunk *retval; int cookie_len; void *cookie;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [COOKIE ECHO back to where the INIT ACK came from.]
*/ if (chunk)
retval->transport = chunk->transport;
nodata: return retval;
}
/* 3.3.12 Cookie Acknowledgement (COOKIE ACK) (11): * * This chunk is used only during the initialization of an * association. It is used to acknowledge the receipt of a COOKIE * ECHO chunk. This chunk MUST precede any DATA or SACK chunk sent * within the association, but MAY be bundled with one or more DATA * chunks or SACK chunk in the same SCTP packet. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type = 11 |Chunk Flags | Length = 4 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Chunk Flags: 8 bits * * Set to zero on transmit and ignored on receipt.
*/ struct sctp_chunk *sctp_make_cookie_ack(conststruct sctp_association *asoc, conststruct sctp_chunk *chunk)
{ struct sctp_chunk *retval;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [COOKIE ACK back to where the COOKIE ECHO came from.]
*/ if (retval && chunk && chunk->transport)
retval->transport =
sctp_assoc_lookup_paddr(asoc,
&chunk->transport->ipaddr);
return retval;
}
/* * Appendix A: Explicit Congestion Notification: * CWR: * * RFC 2481 details a specific bit for a sender to send in the header of * its next outbound TCP segment to indicate to its peer that it has * reduced its congestion window. This is termed the CWR bit. For * SCTP the same indication is made by including the CWR chunk. * This chunk contains one data element, i.e. the TSN number that * was sent in the ECNE chunk. This element represents the lowest * TSN number in the datagram that was originally marked with the * CE bit. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Chunk Type=13 | Flags=00000000| Chunk Length = 8 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Lowest TSN Number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Note: The CWR is considered a Control chunk.
*/ struct sctp_chunk *sctp_make_cwr(conststruct sctp_association *asoc, const __u32 lowest_tsn, conststruct sctp_chunk *chunk)
{ struct sctp_chunk *retval; struct sctp_cwrhdr cwr;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [Report a reduced congestion window back to where the ECNE * came from.]
*/ if (chunk)
retval->transport = chunk->transport;
nodata: return retval;
}
/* Make an ECNE chunk. This is a congestion experienced report. */ struct sctp_chunk *sctp_make_ecne(conststruct sctp_association *asoc, const __u32 lowest_tsn)
{ struct sctp_chunk *retval; struct sctp_ecnehdr ecne;
/* Make a DATA chunk for the given association from the provided * parameters. However, do not populate the data payload.
*/ struct sctp_chunk *sctp_make_datafrag_empty(conststruct sctp_association *asoc, conststruct sctp_sndrcvinfo *sinfo, int len, __u8 flags, gfp_t gfp)
{ struct sctp_chunk *retval; struct sctp_datahdr dp;
/* We assign the TSN as LATE as possible, not here when * creating the chunk.
*/
memset(&dp, 0, sizeof(dp));
dp.ppid = sinfo->sinfo_ppid;
dp.stream = htons(sinfo->sinfo_stream);
/* Set the flags for an unordered send. */ if (sinfo->sinfo_flags & SCTP_UNORDERED)
flags |= SCTP_DATA_UNORDERED;
/* Create a selective ackowledgement (SACK) for the given * association. This reports on which TSN's we've seen to date, * including duplicates and gaps.
*/ struct sctp_chunk *sctp_make_sack(struct sctp_association *asoc)
{ struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sctp_gap_ack_block gabs[SCTP_MAX_GABS];
__u16 num_gabs, num_dup_tsns; struct sctp_transport *trans; struct sctp_chunk *retval; struct sctp_sackhdr sack;
__u32 ctsn; int len;
/* Create the chunk. */
retval = sctp_make_control(asoc, SCTP_CID_SACK, 0, len, GFP_ATOMIC); if (!retval) goto nodata;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, etc.) to the same destination transport * address from which it received the DATA or control chunk to * which it is replying. This rule should also be followed if * the endpoint is bundling DATA chunks together with the * reply chunk. * * However, when acknowledging multiple DATA chunks received * in packets from different source addresses in a single * SACK, the SACK chunk may be transmitted to one of the * destination transport addresses from which the DATA or * control chunks being acknowledged were received. * * [BUG: We do not implement the following paragraph. * Perhaps we should remember the last transport we used for a * SACK and avoid that (if possible) if we have seen any * duplicates. --piggy] * * When a receiver of a duplicate DATA chunk sends a SACK to a * multi- homed endpoint it MAY be beneficial to vary the * destination address and not use the source address of the * DATA chunk. The reason being that receiving a duplicate * from a multi-homed endpoint might indicate that the return * path (as specified in the source address of the DATA chunk) * for the SACK is broken. * * [Send to the address from which we last received a DATA chunk.]
*/
retval->transport = asoc->peer.last_data_from;
/* Add the gap ack block information. */ if (num_gabs)
sctp_addto_chunk(retval, sizeof(__u32) * num_gabs,
gabs);
/* Add the duplicate TSN information. */ if (num_dup_tsns) {
asoc->stats.idupchunks += num_dup_tsns;
sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
sctp_tsnmap_get_dups(map));
} /* Once we have a sack generated, check to see what our sack * generation is, if its 0, reset the transports to 0, and reset * the association generation to 1 * * The idea is that zero is never used as a valid generation for the * association so no transport will match after a wrap event like this, * Until the next sack
*/ if (++asoc->peer.sack_generation == 0) {
list_for_each_entry(trans, &asoc->peer.transport_addr_list,
transports)
trans->sack_generation = 0;
asoc->peer.sack_generation = 1;
}
nodata: return retval;
}
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [ACK back to where the SHUTDOWN came from.]
*/ if (retval && chunk)
retval->transport = chunk->transport;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [Report SHUTDOWN COMPLETE back to where the SHUTDOWN ACK * came from.]
*/ if (retval && chunk)
retval->transport = chunk->transport;
return retval;
}
/* Create an ABORT. Note that we set the T bit if we have no * association, except when responding to an INIT (sctpimpguide 2.41).
*/ struct sctp_chunk *sctp_make_abort(conststruct sctp_association *asoc, conststruct sctp_chunk *chunk, const size_t hint)
{ struct sctp_chunk *retval;
__u8 flags = 0;
/* Set the T-bit if we have no association and 'chunk' is not * an INIT (vtag will be reflected).
*/ if (!asoc) { if (chunk && chunk->chunk_hdr &&
chunk->chunk_hdr->type == SCTP_CID_INIT)
flags = 0; else
flags = SCTP_CHUNK_FLAG_T;
}
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [ABORT back to where the offender came from.]
*/ if (retval && chunk)
retval->transport = chunk->transport;
return retval;
}
/* Helper to create ABORT with a NO_USER_DATA error. */ struct sctp_chunk *sctp_make_abort_no_data( conststruct sctp_association *asoc, conststruct sctp_chunk *chunk,
__u32 tsn)
{ struct sctp_chunk *retval;
__be32 payload;
/* Put the tsn back into network byte order. */
payload = htonl(tsn);
sctp_init_cause(retval, SCTP_ERROR_NO_DATA, sizeof(payload));
sctp_addto_chunk(retval, sizeof(payload), (constvoid *)&payload);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [ABORT back to where the offender came from.]
*/ if (chunk)
retval->transport = chunk->transport;
no_mem: return retval;
}
/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ struct sctp_chunk *sctp_make_abort_user(conststruct sctp_association *asoc, struct msghdr *msg,
size_t paylen)
{ struct sctp_chunk *retval; void *payload = NULL; int err;
/* Append bytes to the end of a parameter. Will panic if chunk is not big * enough.
*/ staticvoid *sctp_addto_param(struct sctp_chunk *chunk, int len, constvoid *data)
{ int chunklen = ntohs(chunk->chunk_hdr->length); void *target;
target = skb_put(chunk->skb, len);
if (data)
memcpy(target, data, len); else
memset(target, 0, len);
/* Cast away the 'const', as this is just telling the chunk * what transport it belongs to.
*/
retval->transport = (struct sctp_transport *) transport;
retval->subh.hbs_hdr = sctp_addto_chunk(retval, sizeof(hbinfo),
&hbinfo);
retval->pmtu_probe = !!probe_size;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, * etc.) to the same destination transport * address from which it * received the DATA or control chunk * to which it is replying. * * [HBACK back to where the HEARTBEAT came from.]
*/ if (chunk)
retval->transport = chunk->transport;
/* Create an Operation Error chunk with the specified space reserved. * This routine can be used for containing multiple causes in the chunk.
*/ staticstruct sctp_chunk *sctp_make_op_error_space( conststruct sctp_association *asoc, conststruct sctp_chunk *chunk,
size_t size)
{ struct sctp_chunk *retval;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints * * An endpoint SHOULD transmit reply chunks (e.g., SACK, * HEARTBEAT ACK, etc.) to the same destination transport * address from which it received the DATA or control chunk * to which it is replying. *
*/ if (chunk)
retval->transport = chunk->transport;
nodata: return retval;
}
/* Create an Operation Error chunk of a fixed size, specifically, * min(asoc->pathmtu, SCTP_DEFAULT_MAXSEGMENT) - overheads. * This is a helper function to allocate an error chunk for those * invalid parameter codes in which we may not want to report all the * errors, if the incoming chunk is large. If it can't fit in a single * packet, we ignore it.
*/ staticinlinestruct sctp_chunk *sctp_make_op_error_limited( conststruct sctp_association *asoc, conststruct sctp_chunk *chunk)
{
size_t size = SCTP_DEFAULT_MAXSEGMENT; struct sctp_sock *sp = NULL;
/* Adjust the chunk header to include the empty MAC */
retval->chunk_hdr->length =
htons(ntohs(retval->chunk_hdr->length) + hmac_desc->hmac_len);
retval->chunk_end = skb_tail_pointer(retval->skb);
/* Turn an skb into a chunk. * FIXME: Eventually move the structure directly inside the skb->cb[]. * * sctpimpguide-05.txt Section 2.8.2 * M1) Each time a new DATA chunk is transmitted * set the 'TSN.Missing.Report' count for that TSN to 0. The * 'TSN.Missing.Report' count will be used to determine missing chunks * and when to fast retransmit. *
*/ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, conststruct sctp_association *asoc, struct sock *sk, gfp_t gfp)
{ struct sctp_chunk *retval;
/* Set chunk->source and dest based on the IP header in chunk->skb. */ void sctp_init_addrs(struct sctp_chunk *chunk, union sctp_addr *src, union sctp_addr *dest)
{
memcpy(&chunk->source, src, sizeof(union sctp_addr));
memcpy(&chunk->dest, dest, sizeof(union sctp_addr));
}
/* Extract the source address from a chunk. */ constunion sctp_addr *sctp_source(conststruct sctp_chunk *chunk)
{ /* If we have a known transport, use that. */ if (chunk->transport) { return &chunk->transport->ipaddr;
} else { /* Otherwise, extract it from the IP header. */ return &chunk->source;
}
}
/* Create a new chunk, setting the type and flags headers from the * arguments, reserving enough space for a 'paylen' byte payload.
*/ staticstruct sctp_chunk *_sctp_make_chunk(conststruct sctp_association *asoc,
__u8 type, __u8 flags, int paylen,
gfp_t gfp)
{ struct sctp_chunkhdr *chunk_hdr; struct sctp_chunk *retval; struct sk_buff *skb; struct sock *sk; int chunklen;
/* Possibly, free the chunk. */ void sctp_chunk_free(struct sctp_chunk *chunk)
{ /* Release our reference on the message tracker. */ if (chunk->msg)
sctp_datamsg_put(chunk->msg);
sctp_chunk_put(chunk);
}
/* Grab a reference to the chunk. */ void sctp_chunk_hold(struct sctp_chunk *ch)
{
refcount_inc(&ch->refcnt);
}
/* Release a reference to the chunk. */ void sctp_chunk_put(struct sctp_chunk *ch)
{ if (refcount_dec_and_test(&ch->refcnt))
sctp_chunk_destroy(ch);
}
/* Append bytes to the end of a chunk. Will panic if chunk is not big * enough.
*/ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, constvoid *data)
{ int chunklen = ntohs(chunk->chunk_hdr->length); int padlen = SCTP_PAD4(chunklen) - chunklen; void *target;
/* Append bytes from user space to the end of a chunk. Will panic if * chunk is not big enough. * Returns a kernel err value.
*/ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len, struct iov_iter *from)
{ void *target;
/* Make room in chunk for data. */
target = skb_put(chunk->skb, len);
/* Copy data (whole iovec) into chunk */ if (!copy_from_iter_full(target, len, from)) return -EFAULT;
/* Helper function to assign a TSN if needed. This assumes that both * the data_hdr and association have already been assigned.
*/ void sctp_chunk_assign_ssn(struct sctp_chunk *chunk)
{ struct sctp_stream *stream; struct sctp_chunk *lchunk; struct sctp_datamsg *msg;
__u16 ssn, sid;
if (chunk->has_ssn) return;
/* All fragments will be on the same stream */
sid = ntohs(chunk->subh.data_hdr->stream);
stream = &chunk->asoc->stream;
/* Now assign the sequence number to the entire message. * All fragments must have the same stream sequence number.
*/
msg = chunk->msg;
list_for_each_entry(lchunk, &msg->chunks, frag_list) { if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
ssn = 0;
} else { if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
ssn = sctp_ssn_next(stream, out, sid); else
ssn = sctp_ssn_peek(stream, out, sid);
}
/* Helper function to assign a TSN if needed. This assumes that both * the data_hdr and association have already been assigned.
*/ void sctp_chunk_assign_tsn(struct sctp_chunk *chunk)
{ if (!chunk->has_tsn) { /* This is the last possible instant to * assign a TSN.
*/
chunk->subh.data_hdr->tsn =
htonl(sctp_association_get_next_tsn(chunk->asoc));
chunk->has_tsn = 1;
}
}
/* Create a CLOSED association to use with an incoming packet. */ struct sctp_association *sctp_make_temp_asoc(conststruct sctp_endpoint *ep, struct sctp_chunk *chunk,
gfp_t gfp)
{ struct sctp_association *asoc; enum sctp_scope scope; struct sk_buff *skb;
/* Create the bare association. */
scope = sctp_scope(sctp_source(chunk));
asoc = sctp_association_new(ep, ep->base.sk, scope, gfp); if (!asoc) goto nodata;
asoc->temp = 1;
skb = chunk->skb; /* Create an entry for the source address of the packet. */
SCTP_INPUT_CB(skb)->af->from_skb(&asoc->c.peer_addr, skb, 1);
nodata: return asoc;
}
/* Build a cookie representing asoc. * This INCLUDES the param header needed to put the cookie in the INIT ACK.
*/ staticstruct sctp_cookie_param *sctp_pack_cookie( conststruct sctp_endpoint *ep, conststruct sctp_association *asoc, conststruct sctp_chunk *init_chunk, int *cookie_len, const __u8 *raw_addrs, int addrs_len)
{ struct sctp_signed_cookie *cookie; struct sctp_cookie_param *retval; int headersize, bodysize;
/* Header size is static data prior to the actual cookie, including * any padding.
*/
headersize = sizeof(struct sctp_paramhdr) +
(sizeof(struct sctp_signed_cookie) - sizeof(struct sctp_cookie));
bodysize = sizeof(struct sctp_cookie)
+ ntohs(init_chunk->chunk_hdr->length) + addrs_len;
/* Pad out the cookie to a multiple to make the signature * functions simpler to write.
*/ if (bodysize % SCTP_COOKIE_MULTIPLE)
bodysize += SCTP_COOKIE_MULTIPLE
- (bodysize % SCTP_COOKIE_MULTIPLE);
*cookie_len = headersize + bodysize;
/* Clear this memory since we are sending this data structure * out on the network.
*/
retval = kzalloc(*cookie_len, GFP_ATOMIC); if (!retval) goto nodata;
/* Set up the parameter header. */
retval->p.type = SCTP_PARAM_STATE_COOKIE;
retval->p.length = htons(*cookie_len);
/* Copy the cookie part of the association itself. */
cookie->c = asoc->c; /* Save the raw address list length in the cookie. */
cookie->c.raw_addr_list_len = addrs_len;
/* Header size is static data prior to the actual cookie, including * any padding.
*/
headersize = sizeof(struct sctp_chunkhdr) +
(sizeof(struct sctp_signed_cookie) - sizeof(struct sctp_cookie));
bodysize = ntohs(chunk->chunk_hdr->length) - headersize;
fixed_size = headersize + sizeof(struct sctp_cookie);
/* Verify that the chunk looks like it even has a cookie. * There must be enough room for our cookie and our peer's * INIT chunk.
*/
len = ntohs(chunk->chunk_hdr->length); if (len < fixed_size + sizeof(struct sctp_chunkhdr)) goto malformed;
/* Verify that the cookie has been padded out. */ if (bodysize % SCTP_COOKIE_MULTIPLE) goto malformed;
/* Process the cookie. */
cookie = chunk->subh.cookie_hdr;
bear_cookie = &cookie->c;
if (!sctp_sk(ep->base.sk)->hmac) goto no_hmac;
/* Check the signature. */
{ struct crypto_shash *tfm = sctp_sk(ep->base.sk)->hmac; int err;
no_hmac: /* IG Section 2.35.2: * 3) Compare the port numbers and the verification tag contained * within the COOKIE ECHO chunk to the actual port numbers and the * verification tag within the SCTP common header of the received * packet. If these values do not match the packet MUST be silently * discarded,
*/ if (ntohl(chunk->sctp_hdr->vtag) != bear_cookie->my_vtag) {
*error = -SCTP_IERROR_BAD_TAG; goto fail;
}
/* Check to see if the cookie is stale. If there is already * an association, there is no need to check cookie's expiration * for init collision case of lost COOKIE ACK. * If skb has been timestamped, then use the stamp, otherwise * use current time. This introduces a small possibility that * a cookie may be considered expired, but this would only slow * down the new association establishment instead of every packet.
*/ if (sock_flag(ep->base.sk, SOCK_TIMESTAMP))
kt = skb_get_ktime(skb); else
kt = ktime_get_real();
if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration));
__be32 n = htonl(usecs);
/* * Section 3.3.10.3 Stale Cookie Error (3) * * Cause of error * --------------- * Stale Cookie Error: Indicates the receipt of a valid State * Cookie that has expired.
*/
*errp = sctp_make_op_error(asoc, chunk,
SCTP_ERROR_STALE_COOKIE, &n, sizeof(n), 0); if (*errp)
*error = -SCTP_IERROR_STALE_COOKIE; else
*error = -SCTP_IERROR_NOMEM;
goto fail;
}
/* Make a new base association. */
scope = sctp_scope(sctp_source(chunk));
retval = sctp_association_new(ep, ep->base.sk, scope, gfp); if (!retval) {
*error = -SCTP_IERROR_NOMEM; goto fail;
}
/* Set up our peer's port number. */
retval->peer.port = ntohs(chunk->sctp_hdr->source);
/* Populate the association from the cookie. */
memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie));
/* Report an Invalid Mandatory Parameter. */ staticint sctp_process_inv_mandatory(conststruct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_chunk **errp)
{ /* Invalid Mandatory Parameter Error has no payload. */
if (!*errp)
*errp = sctp_make_op_error_space(asoc, chunk, 0);
if (*errp)
sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, 0);
/* Stop processing this chunk. */ return 0;
}
staticint sctp_process_inv_paramlength(conststruct sctp_association *asoc, struct sctp_paramhdr *param, conststruct sctp_chunk *chunk, struct sctp_chunk **errp)
{ /* This is a fatal error. Any accumulated non-fatal errors are * not reported.
*/ if (*errp)
sctp_chunk_free(*errp);
/* Create an error chunk and fill it in with our payload. */
*errp = sctp_make_violation_paramlen(asoc, chunk, param);
return 0;
}
/* Do not attempt to handle the HOST_NAME parm. However, do * send back an indicator to the peer.
*/ staticint sctp_process_hn_param(conststruct sctp_association *asoc, union sctp_params param, struct sctp_chunk *chunk, struct sctp_chunk **errp)
{
__u16 len = ntohs(param.p->length);
/* Processing of the HOST_NAME parameter will generate an * ABORT. If we've accumulated any non-fatal errors, they * would be unrecognized parameters and we should not include * them in the ABORT.
*/ if (*errp)
sctp_chunk_free(*errp);
staticint sctp_verify_ext_param(struct net *net, conststruct sctp_endpoint *ep, union sctp_params param)
{
__u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); int have_asconf = 0; int have_auth = 0; int i;
for (i = 0; i < num_ext; i++) { switch (param.ext->chunks[i]) { case SCTP_CID_AUTH:
have_auth = 1; break; case SCTP_CID_ASCONF: case SCTP_CID_ASCONF_ACK:
have_asconf = 1; break;
}
}
/* ADD-IP Security: The draft requires us to ABORT or ignore the * INIT/INIT-ACK if ADD-IP is listed, but AUTH is not. Do this * only if ADD-IP is turned on and we are not backward-compatible * mode.
*/ if (net->sctp.addip_noauth) return 1;
if (ep->asconf_enable && !have_auth && have_asconf) return 0;
return 1;
}
staticvoid sctp_process_ext_param(struct sctp_association *asoc, union sctp_params param)
{
__u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); int i;
for (i = 0; i < num_ext; i++) { switch (param.ext->chunks[i]) { case SCTP_CID_RECONF: if (asoc->ep->reconf_enable)
asoc->peer.reconf_capable = 1; break; case SCTP_CID_FWD_TSN: if (asoc->ep->prsctp_enable)
asoc->peer.prsctp_capable = 1; break; case SCTP_CID_AUTH: /* if the peer reports AUTH, assume that he * supports AUTH.
*/ if (asoc->ep->auth_enable)
asoc->peer.auth_capable = 1; break; case SCTP_CID_ASCONF: case SCTP_CID_ASCONF_ACK: if (asoc->ep->asconf_enable)
asoc->peer.asconf_capable = 1; break; case SCTP_CID_I_DATA: if (asoc->ep->intl_enable)
asoc->peer.intl_capable = 1; break; default: break;
}
}
}
/* RFC 3.2.1 & the Implementers Guide 2.2. * * The Parameter Types are encoded such that the * highest-order two bits specify the action that must be * taken if the processing endpoint does not recognize the * Parameter Type. * * 00 - Stop processing this parameter; do not process any further * parameters within this chunk * * 01 - Stop processing this parameter, do not process any further * parameters within this chunk, and report the unrecognized * parameter in an 'Unrecognized Parameter' ERROR chunk. * * 10 - Skip this parameter and continue processing. * * 11 - Skip this parameter and continue processing but * report the unrecognized parameter in an * 'Unrecognized Parameter' ERROR chunk. * * Return value: * SCTP_IERROR_NO_ERROR - continue with the chunk * SCTP_IERROR_ERROR - stop and report an error. * SCTP_IERROR_NOMEME - out of memory.
*/ staticenum sctp_ierror sctp_process_unk_param( conststruct sctp_association *asoc, union sctp_params param, struct sctp_chunk *chunk, struct sctp_chunk **errp)
{ int retval = SCTP_IERROR_NO_ERROR;
switch (param.p->type & SCTP_PARAM_ACTION_MASK) { case SCTP_PARAM_ACTION_DISCARD:
retval = SCTP_IERROR_ERROR; break; case SCTP_PARAM_ACTION_SKIP: break; case SCTP_PARAM_ACTION_DISCARD_ERR:
retval = SCTP_IERROR_ERROR;
fallthrough; case SCTP_PARAM_ACTION_SKIP_ERR: /* Make an ERROR chunk, preparing enough room for * returning multiple unknown parameters.
*/ if (!*errp) {
*errp = sctp_make_op_error_limited(asoc, chunk); if (!*errp) { /* If there is no memory for generating the * ERROR report as specified, an ABORT will be * triggered to the peer and the association * won't be established.
*/
retval = SCTP_IERROR_NOMEM; break;
}
}
/* Verify variable length parameters * Return values: * SCTP_IERROR_ABORT - trigger an ABORT * SCTP_IERROR_NOMEM - out of memory (abort) * SCTP_IERROR_ERROR - stop processing, trigger an ERROR * SCTP_IERROR_NO_ERROR - continue with the chunk
*/ staticenum sctp_ierror sctp_verify_param(struct net *net, conststruct sctp_endpoint *ep, conststruct sctp_association *asoc, union sctp_params param, enum sctp_cid cid, struct sctp_chunk *chunk, struct sctp_chunk **err_chunk)
{ struct sctp_hmac_algo_param *hmacs; int retval = SCTP_IERROR_NO_ERROR;
__u16 n_elt, id = 0; int i;
/* FIXME - This routine is not looking at each parameter per the * chunk type, i.e., unrecognized parameters should be further * identified based on the chunk id.
*/
switch (param.p->type) { case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV6_ADDRESS: case SCTP_PARAM_COOKIE_PRESERVATIVE: case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: case SCTP_PARAM_STATE_COOKIE: case SCTP_PARAM_HEARTBEAT_INFO: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_ECN_CAPABLE: case SCTP_PARAM_ADAPTATION_LAYER_IND: break;
case SCTP_PARAM_SUPPORTED_EXT: if (!sctp_verify_ext_param(net, ep, param)) return SCTP_IERROR_ABORT; break;
case SCTP_PARAM_SET_PRIMARY: if (!ep->asconf_enable) goto unhandled;
case SCTP_PARAM_HOST_NAME_ADDRESS: /* This param has been Deprecated, send ABORT. */
sctp_process_hn_param(asoc, param, chunk, err_chunk);
retval = SCTP_IERROR_ABORT; break;
case SCTP_PARAM_FWD_TSN_SUPPORT: if (ep->prsctp_enable) break; goto unhandled;
case SCTP_PARAM_RANDOM: if (!ep->auth_enable) goto unhandled;
/* SCTP-AUTH: Secion 6.1 * If the random number is not 32 byte long the association * MUST be aborted. The ABORT chunk SHOULD contain the error * cause 'Protocol Violation'.
*/ if (SCTP_AUTH_RANDOM_LENGTH != ntohs(param.p->length) - sizeof(struct sctp_paramhdr)) {
sctp_process_inv_paramlength(asoc, param.p,
chunk, err_chunk);
retval = SCTP_IERROR_ABORT;
} break;
case SCTP_PARAM_CHUNKS: if (!ep->auth_enable) goto unhandled;
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.