Release 4.11 net/sctp/sm_make_chunk.c
/* 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.
*
* This SCTP implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This SCTP implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
* 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>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <crypto/hash.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/random.h> /* for get_random_bytes */
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
static struct sctp_chunk *sctp_make_control(const struct sctp_association *asoc,
__u8 type, __u8 flags, int paylen,
gfp_t gfp);
static struct sctp_chunk *sctp_make_data(const struct sctp_association *asoc,
__u8 flags, int paylen, gfp_t gfp);
static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc,
__u8 type, __u8 flags, int paylen,
gfp_t gfp);
static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const struct sctp_chunk *init_chunk,
int *cookie_len,
const __u8 *raw_addrs, int addrs_len);
static int sctp_process_param(struct sctp_association *asoc,
union sctp_params param,
const union sctp_addr *peer_addr,
gfp_t gfp);
static void *sctp_addto_param(struct sctp_chunk *chunk, int len,
const void *data);
static void *sctp_addto_chunk_fixed(struct sctp_chunk *, int len,
const void *data);
/* Control chunk destructor */
static void sctp_control_release_owner(struct sk_buff *skb)
{
/*TODO: do memory release */
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 12 | 100.00% | 1 | 100.00% |
Total | 12 | 100.00% | 1 | 100.00% |
static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
{
struct sctp_association *asoc = chunk->asoc;
struct sk_buff *skb = chunk->skb;
/* 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.
*/
skb->sk = asoc ? asoc->base.sk : NULL;
skb->destructor = sctp_control_release_owner;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Vlad Yasevich | 50 | 100.00% | 1 | 100.00% |
Total | 50 | 100.00% | 1 | 100.00% |
/* What was the inbound interface for this chunk? */
int sctp_chunk_iif(const struct sctp_chunk *chunk)
{
struct sk_buff *skb = chunk->skb;
return SCTP_INPUT_CB(skb)->af->skb_iif(skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 25 | 75.76% | 1 | 33.33% |
Marcelo Ricardo Leitner | 7 | 21.21% | 1 | 33.33% |
Arnaldo Carvalho de Melo | 1 | 3.03% | 1 | 33.33% |
Total | 33 | 100.00% | 3 | 100.00% |
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 2: The ECN capable field is reserved for future use of
* Explicit Congestion Notification.
*/
static const struct sctp_paramhdr ecap_param = {
SCTP_PARAM_ECN_CAPABLE,
cpu_to_be16(sizeof(struct sctp_paramhdr)),
};
static const struct 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.
*/
void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
size_t paylen)
{
sctp_errhdr_t err;
__u16 len;
/* Cause code constants are now defined in network order. */
err.cause = cause_code;
len = sizeof(sctp_errhdr_t) + paylen;
err.length = htons(len);
chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 56 | 84.85% | 2 | 40.00% |
Vladislav Yasevich | 6 | 9.09% | 1 | 20.00% |
David S. Miller | 3 | 4.55% | 1 | 20.00% |
Al Viro | 1 | 1.52% | 1 | 20.00% |
Total | 66 | 100.00% | 5 | 100.00% |
/* A helper to initialize an op error inside a
* provided chunk, as most cause codes will be embedded inside an
* abort chunk. Differs from sctp_init_cause in that it won't oops
* if there isn't enough space in the op error chunk
*/
static int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code,
size_t paylen)
{
sctp_errhdr_t err;
__u16 len;
/* Cause code constants are now defined in network order. */
err.cause = cause_code;
len = sizeof(sctp_errhdr_t) + paylen;
err.length = htons(len);
if (skb_tailroom(chunk->skb) < len)
return -ENOSPC;
chunk->subh.err_hdr = sctp_addto_chunk_fixed(chunk,
sizeof(sctp_errhdr_t),
&err);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Neil Horman | 83 | 97.65% | 1 | 33.33% |
Ioan Orghici | 1 | 1.18% | 1 | 33.33% |
Wei Yongjun | 1 | 1.18% | 1 | 33.33% |
Total | 85 | 100.00% | 3 | 100.00% |
/* 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(const struct sctp_association *asoc,
const struct sctp_bind_addr *bp,
gfp_t gfp, int vparam_len)
{
struct net *net = sock_net(asoc->base.sk);
struct sctp_endpoint *ep = asoc->ep;
sctp_inithdr_t init;
union sctp_params addrs;
size_t chunksize;
struct sctp_chunk *retval = NULL;
int num_types, addrs_len = 0;
struct sctp_sock *sp;
sctp_supported_addrs_param_t sat;
__be16 types[2];
sctp_adaptation_ind_param_t aiparam;
sctp_supported_ext_param_t ext_param;
int num_ext = 0;
__u8 extensions[3];
sctp_paramhdr_t *auth_chunks = NULL,
*auth_hmacs = NULL;
/* 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.
*/
retval = NULL;
/* Convert the provided bind address list to raw format. */
addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, gfp);
init.init_tag = htonl(asoc->c.my_vtag);
init.a_rwnd = htonl(asoc->rwnd);
init.num_outbound_streams = htons(asoc->c.sinit_num_ostreams);
init.num_inbound_streams = htons(asoc->c.sinit_max_instreams);
init.initial_tsn = htonl(asoc->c.initial_tsn);
/* How many address types are needed? */
sp = sctp_sk(asoc->base.sk);
num_types = sp->pf->supported_addrs(sp, types);
chunksize = sizeof(init) + addrs_len;
chunksize += SCTP_PAD4(SCTP_SAT_LEN(num_types));
chunksize += sizeof(ecap_param);
if (asoc->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 (net->sctp.addip_enable) {
extensions[num_ext] = SCTP_CID_ASCONF;
extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
num_ext += 2;
}
if (asoc->reconf_enable) {
extensions[num_ext] = SCTP_CID_RECONF;
num_ext += 1;
}
if (sp->adaptation_ind)
chunksize += sizeof(aiparam);
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 = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
if (auth_hmacs->length)
chunksize += SCTP_PAD4(ntohs(auth_hmacs->length));
else
auth_hmacs = NULL;
/* Add CHUNKS parameter length */
auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
if (auth_chunks->length)
chunksize += SCTP_PAD4(ntohs(auth_chunks->length));
else
auth_chunks = NULL;
extensions[num_ext] = SCTP_CID_AUTH;
num_ext += 1;
}
/* If we have any extensions to report, account for that */
if (num_ext)
chunksize += SCTP_PAD4(sizeof(sctp_supported_ext_param_t) +
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.]
*/
retval = sctp_make_control(asoc, SCTP_CID_INIT, 0, chunksize, gfp);
if (!retval)
goto nodata;
retval->subh.init_hdr =
sctp_addto_chunk(retval, sizeof(init), &init);
retval->param_hdr.v =
sctp_addto_chunk(retval, addrs_len, addrs.v);
/* 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);
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(sctp_supported_ext_param_t) + num_ext);
sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t),
&ext_param);
sctp_addto_param(retval, num_ext, extensions);
}
if (asoc->prsctp_enable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
if (sp->adaptation_ind) {
aiparam.param_hdr.type = SCTP_PARAM_ADAPTATION_LAYER_IND;
aiparam.param_hdr.length = htons(sizeof(aiparam));
aiparam.adaptation_ind = htonl(sp->adaptation_ind);
sctp_addto_chunk(retval, sizeof(aiparam), &aiparam);
}
/* Add SCTP-AUTH chunks to the parameter list */
if (ep->auth_enable) {
sctp_addto_chunk(retval, sizeof(asoc->c.auth_random),
asoc->c.auth_random);
if (auth_hmacs)
sctp_addto_chunk(retval, ntohs(auth_hmacs->length),
auth_hmacs);
if (auth_chunks)
sctp_addto_chunk(retval, ntohs(auth_chunks->length),
auth_chunks);
}
nodata:
kfree(addrs.v);
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 300 | 38.76% | 7 | 22.58% |
Vladislav Yasevich | 284 | 36.69% | 5 | 16.13% |
Sridhar Samudrala | 88 | 11.37% | 4 | 12.90% |
Eric W. Biedermann | 27 | 3.49% | 1 | 3.23% |
Xin Long | 21 | 2.71% | 2 | 6.45% |
malc | 14 | 1.81% | 1 | 3.23% |
Vlad Yasevich | 12 | 1.55% | 2 | 6.45% |
Wei Yongjun | 11 | 1.42% | 1 | 3.23% |
Marcelo Ricardo Leitner | 6 | 0.78% | 2 | 6.45% |
Ivan Skytte Jörgensen | 4 | 0.52% | 1 | 3.23% |
David S. Miller | 3 | 0.39% | 1 | 3.23% |
Al Viro | 2 | 0.26% | 2 | 6.45% |
Arnaldo Carvalho de Melo | 1 | 0.13% | 1 | 3.23% |
Joe Perches | 1 | 0.13% | 1 | 3.23% |
Total | 774 | 100.00% | 31 | 100.00% |
struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc,
const struct sctp_chunk *chunk,
gfp_t gfp, int unkparam_len)
{
sctp_inithdr_t initack;
struct sctp_chunk *retval;
union sctp_params addrs;
struct sctp_sock *sp;
int addrs_len;
sctp_cookie_param_t *cookie;
int cookie_len;
size_t chunksize;
sctp_adaptation_ind_param_t aiparam;
sctp_supported_ext_param_t ext_param;
int num_ext = 0;
__u8 extensions[3];
sctp_paramhdr_t *auth_chunks = NULL,
*auth_hmacs = NULL,
*auth_random = NULL;
retval = NULL;
/* Note: there may be no addresses to embed. */
addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, gfp);
initack.init_tag = htonl(asoc->c.my_vtag);
initack.a_rwnd = htonl(asoc->rwnd);
initack.num_outbound_streams = htons(asoc->c.sinit_num_ostreams);
initack.num_inbound_streams = htons(asoc->c.sinit_max_instreams);
initack.initial_tsn = htonl(asoc->c.initial_tsn);
/* 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 (asoc->peer.asconf_capable) {
extensions[num_ext] = SCTP_CID_ASCONF;
extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
num_ext += 2;
}
if (asoc->peer.reconf_capable) {
extensions[num_ext] = SCTP_CID_RECONF;
num_ext += 1;
}
if (sp->adaptation_ind)
chunksize += sizeof(aiparam);
if (asoc->peer.auth_capable) {
auth_random = (sctp_paramhdr_t *)asoc->c.auth_random;
chunksize += ntohs(auth_random->length);
auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
if (auth_hmacs->length)
chunksize += SCTP_PAD4(ntohs(auth_hmacs->length));
else
auth_hmacs = NULL;
auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
if (auth_chunks->length)
chunksize += SCTP_PAD4(ntohs(auth_chunks->length));
else
auth_chunks = NULL;
extensions[num_ext] = SCTP_CID_AUTH;
num_ext += 1;
}
if (num_ext)
chunksize += SCTP_PAD4(sizeof(sctp_supported_ext_param_t) +
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.]
*/
retval->transport = chunk->transport;
retval->subh.init_hdr =
sctp_addto_chunk(retval, sizeof(initack), &initack);
retval->param_hdr.v = sctp_addto_chunk(retval, addrs_len, addrs.v);
sctp_addto_chunk(retval, cookie_len, cookie);
if (asoc->peer.ecn_capable)
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
if (num_ext) {
ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT;
ext_param.param_hdr.length =
htons(sizeof(sctp_supported_ext_param_t) + num_ext);
sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t),
&ext_param);
sctp_addto_param(retval, num_ext, extensions);
}
if (asoc->peer.prsctp_capable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
if (sp->adaptation_ind) {
aiparam.param_hdr.type = SCTP_PARAM_ADAPTATION_LAYER_IND;
aiparam.param_hdr.length = htons(sizeof(aiparam));
aiparam.adaptation_ind = htonl(sp->adaptation_ind);
sctp_addto_chunk(retval, sizeof(aiparam), &aiparam);
}
if (asoc->peer.auth_capable) {
sctp_addto_chunk(retval, ntohs(auth_random->length),
auth_random);
if (auth_hmacs)
sctp_addto_chunk(retval, ntohs(auth_hmacs->length),
auth_hmacs);
if (auth_chunks)
sctp_addto_chunk(retval, ntohs(auth_chunks->length),
auth_chunks);
}
/* We need to remove the const qualifier at this point. */
retval->asoc = (struct sctp_association *) asoc;
nomem_chunk:
kfree(cookie);
nomem_cookie:
kfree(addrs.v);
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 308 | 40.05% | 6 | 24.00% |
Vladislav Yasevich | 300 | 39.01% | 4 | 16.00% |
Sridhar Samudrala | 70 | 9.10% | 3 | 12.00% |
malc | 31 | 4.03% | 1 | 4.00% |
Xin Long | 21 | 2.73% | 1 | 4.00% |
Wei Yongjun | 16 | 2.08% | 2 | 8.00% |
Daisy Chang | 6 | 0.78% | 1 | 4.00% |
David S. Miller | 5 | 0.65% | 1 | 4.00% |
Marcelo Ricardo Leitner | 5 | 0.65% | 2 | 8.00% |
Ivan Skytte Jörgensen | 4 | 0.52% | 1 | 4.00% |
Al Viro | 1 | 0.13% | 1 | 4.00% |
Dan Carpenter | 1 | 0.13% | 1 | 4.00% |
Vlad Yasevich | 1 | 0.13% | 1 | 4.00% |
Total | 769 | 100.00% | 25 | 100.00% |
/* 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(const struct sctp_association *asoc,
const struct sctp_chunk *chunk)
{
struct sctp_chunk *retval;
void *cookie;
int cookie_len;
cookie = asoc->peer.cookie;
cookie_len = asoc->peer.cookie_len;
/* Build a cookie echo chunk. */
retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ECHO, 0,
cookie_len, GFP_ATOMIC);
if (!retval)
goto nodata;
retval->subh.cookie_hdr =
sctp_addto_chunk(retval, cookie_len, 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 99 | 95.19% | 3 | 50.00% |
David S. Miller | 2 | 1.92% | 1 | 16.67% |
Marcelo Ricardo Leitner | 2 | 1.92% | 1 | 16.67% |
Vlad Yasevich | 1 | 0.96% | 1 | 16.67% |
Total | 104 | 100.00% | 6 | 100.00% |
/* 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(const struct sctp_association *asoc,
const struct sctp_chunk *chunk)
{
struct sctp_chunk *retval;
retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ACK, 0, 0, GFP_ATOMIC);
/* 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)
retval->transport = chunk->transport;
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 53 | 92.98% | 3 | 50.00% |
Marcelo Ricardo Leitner | 2 | 3.51% | 1 | 16.67% |
Vlad Yasevich | 1 | 1.75% | 1 | 16.67% |
David S. Miller | 1 | 1.75% | 1 | 16.67% |
Total | 57 | 100.00% | 6 | 100.00% |
/*
* 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(const struct sctp_association *asoc,
const __u32 lowest_tsn,
const struct sctp_chunk *chunk)
{
struct sctp_chunk *retval;
sctp_cwrhdr_t cwr;
cwr.lowest_tsn = htonl(lowest_tsn);
retval = sctp_make_control(asoc, SCTP_CID_ECN_CWR, 0,
sizeof(sctp_cwrhdr_t), GFP_ATOMIC);
if (!retval)
goto nodata;
retval->subh.ecn_cwr_hdr =
sctp_addto_chunk(retval, sizeof(cwr), &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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 97 | 94.17% | 3 | 50.00% |
David S. Miller | 3 | 2.91% | 1 | 16.67% |
Marcelo Ricardo Leitner | 2 | 1.94% | 1 | 16.67% |
Vlad Yasevich | 1 | 0.97% | 1 | 16.67% |
Total | 103 | 100.00% | 6 | 100.00% |
/* Make an ECNE chunk. This is a congestion experienced report. */
struct sctp_chunk *sctp_make_ecne(const struct sctp_association *asoc,
const __u32 lowest_tsn)
{
struct sctp_chunk *retval;
sctp_ecnehdr_t ecne;
ecne.lowest_tsn = htonl(lowest_tsn);
retval = sctp_make_control(asoc, SCTP_CID_ECN_ECNE, 0,
sizeof(sctp_ecnehdr_t), GFP_ATOMIC);
if (!retval)
goto nodata;
retval->subh.ecne_hdr =
sctp_addto_chunk(retval, sizeof(ecne), &ecne);
nodata:
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 79 | 94.05% | 3 | 50.00% |
Marcelo Ricardo Leitner | 2 | 2.38% | 1 | 16.67% |
David S. Miller | 2 | 2.38% | 1 | 16.67% |
Vlad Yasevich | 1 | 1.19% | 1 | 16.67% |
Total | 84 | 100.00% | 6 | 100.00% |
/* 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(struct sctp_association *asoc,
const struct sctp_sndrcvinfo *sinfo,
int data_len, __u8 flags, __u16 ssn,
gfp_t gfp)
{
struct sctp_chunk *retval;
struct sctp_datahdr dp;
int chunk_len;
/* We assign the TSN as LATE as possible, not here when
* creating the chunk.
*/
dp.tsn = 0;
dp.stream = htons(sinfo->sinfo_stream);
dp.ppid = sinfo->sinfo_ppid;
/* Set the flags for an unordered send. */
if (sinfo->sinfo_flags & SCTP_UNORDERED) {
flags |= SCTP_DATA_UNORDERED;
dp.ssn = 0;
} else
dp.ssn = htons(ssn);
chunk_len = sizeof(dp) + data_len;
retval = sctp_make_data(asoc, flags, chunk_len, gfp);
if (!retval)
goto nodata;
retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp);
memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));
nodata:
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 159 | 94.08% | 4 | 50.00% |
Marcelo Ricardo Leitner | 5 | 2.96% | 1 | 12.50% |
David S. Miller | 3 | 1.78% | 1 | 12.50% |
Vlad Yasevich | 1 | 0.59% | 1 | 12.50% |
Ivan Skytte Jörgensen | 1 | 0.59% | 1 | 12.50% |
Total | 169 | 100.00% | 8 | 100.00% |
/* 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(const struct sctp_association *asoc)
{
struct sctp_chunk *retval;
struct sctp_sackhdr sack;
int len;
__u32 ctsn;
__u16 num_gabs, num_dup_tsns;
struct sctp_association *aptr = (struct sctp_association *)asoc;
struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
struct sctp_gap_ack_block gabs[SCTP_MAX_GABS];
struct sctp_transport *trans;
memset(gabs, 0, sizeof(gabs));
ctsn = sctp_tsnmap_get_ctsn(map);
pr_debug("%s: sackCTSNAck sent:0x%x\n", __func__, ctsn);
/* How much room is needed in the chunk? */
num_gabs = sctp_tsnmap_num_gabs(map, gabs);
num_dup_tsns = sctp_tsnmap_num_dups(map);
/* Initialize the SACK header. */
sack.cum_tsn_ack = htonl(ctsn);
sack.a_rwnd = htonl(asoc->a_rwnd);
sack.num_gap_ack_blocks = htons(num_gabs);
sack.num_dup_tsns = htons(num_dup_tsns);
len = sizeof(sack)
+ sizeof(struct sctp_gap_ack_block) * num_gabs
+ sizeof(__u32) * num_dup_tsns;
/* 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;
retval->subh.sack_hdr =
sctp_addto_chunk(retval, sizeof(sack), &sack);
/* 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) {
aptr->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 (++aptr->peer.sack_generation == 0) {
list_for_each_entry(trans, &asoc->peer.transport_addr_list,
transports)
trans->sack_generation = 0;
aptr->peer.sack_generation = 1;
}
nodata:
return retval;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jon Grimm | 230 | 70.55% | 7 | 50.00% |
Neil Horman | 53 | 16.26% | 1 | 7.14% |
Vladislav Yasevich | 22 | 6.75% | 1 | 7.14% |
Michele Baldessari | 10 | 3.07% | 1 | 7.14% |
David S. Miller | 4 | 1.23% | 1 | 7.14% |
Daniel Borkmann | 4 | 1.23% | 1 | 7.14% |
Marcelo Ricardo Leitner | 2 | 0.61% | 1 | 7.14% |
Vlad Yasevich | 1 | 0.31% | 1 | 7.14% |
Total | 326 | 100.00% | 14 | 100.00% |
/* Make a SHUTDOWN chunk. */
struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc,
const struct sctp_chunk *chunk)
{
struct sctp_chunk *retval;
sctp_shutdownhdr_t shut;
__u32 ctsn;
ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
shut.cum_tsn_ack = htonl(ctsn);
retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN, 0,
sizeof(sctp_shutdownhdr_t), GFP_ATOMIC);
if (!retval)
goto nodata;
retval->subh.shutdown_hdr =
sctp_addto_chunk(retval, sizeof(shut), &shut);
if (chunk)
retval->transport = chunk