diff options
Diffstat (limited to 'net/sctp/sm_make_chunk.c')
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 1097 |
1 files changed, 574 insertions, 523 deletions
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 6110447fe51d..2c0017d058d4 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* SCTP kernel implementation * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. @@ -10,22 +11,6 @@ * 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> @@ -45,7 +30,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <crypto/hash.h> +#include <crypto/utils.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/ip.h> @@ -69,7 +54,8 @@ static struct sctp_chunk *sctp_make_data(const struct sctp_association *asoc, 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, +static struct sctp_cookie_param *sctp_pack_cookie( + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *init_chunk, int *cookie_len, @@ -80,13 +66,32 @@ static int sctp_process_param(struct sctp_association *asoc, 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 */ + struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg; + + if (chunk->shkey) { + struct sctp_shared_key *shkey = chunk->shkey; + struct sctp_association *asoc = chunk->asoc; + + /* 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); + } } static void sctp_control_set_owner_w(struct sctp_chunk *chunk) @@ -101,18 +106,15 @@ static void sctp_control_set_owner_w(struct sctp_chunk *chunk) * * 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; } -/* 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); -} - /* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 2: The ECN capable field is reserved for future use of @@ -127,46 +129,28 @@ static const struct sctp_paramhdr prsctp_param = { 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. +/* 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) +int sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, + size_t paylen) { - sctp_errhdr_t err; + struct sctp_errhdr 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); -} - -/* 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); + len = sizeof(err) + 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); + + chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(err), &err); + return 0; } + /* 3.3.2 Initiation (INIT) (1) * * This chunk is used to initiate a SCTP association between two @@ -212,32 +196,30 @@ static int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, * 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); + const struct 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_inithdr init; - union sctp_params addrs; - size_t chunksize; struct sctp_chunk *retval = NULL; int num_types, addrs_len = 0; + struct sctp_inithdr init; + union sctp_params addrs; struct sctp_sock *sp; - sctp_supported_addrs_param_t sat; + __u8 extensions[5]; + size_t chunksize; __be16 types[2]; - sctp_adaptation_ind_param_t aiparam; - sctp_supported_ext_param_t ext_param; int num_ext = 0; - __u8 extensions[4]; - struct sctp_paramhdr *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); @@ -254,9 +236,11 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, chunksize = sizeof(init) + addrs_len; chunksize += SCTP_PAD4(SCTP_SAT_LEN(num_types)); - chunksize += sizeof(ecap_param); - if (asoc->prsctp_enable) + if (asoc->ep->ecn_enable) + chunksize += sizeof(ecap_param); + + if (asoc->ep->prsctp_enable) chunksize += sizeof(prsctp_param); /* ADDIP: Section 4.2.7: @@ -264,13 +248,13 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, * the ASCONF,the ASCONF-ACK, and the AUTH chunks in its INIT and * INIT-ACK parameters. */ - if (net->sctp.addip_enable) { + if (asoc->ep->asconf_enable) { extensions[num_ext] = SCTP_CID_ASCONF; extensions[num_ext+1] = SCTP_CID_ASCONF_ACK; num_ext += 2; } - if (asoc->reconf_enable) { + if (asoc->ep->reconf_enable) { extensions[num_ext] = SCTP_CID_RECONF; num_ext += 1; } @@ -278,6 +262,11 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, 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 */ @@ -305,8 +294,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, /* If we have any extensions to report, account for that */ if (num_ext) - chunksize += SCTP_PAD4(sizeof(sctp_supported_ext_param_t) + - num_ext); + chunksize += SCTP_PAD4(sizeof(ext_param) + num_ext); /* RFC 2960 3.3.2 Initiation (INIT) (1) * @@ -341,21 +329,20 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, 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); + 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(sctp_supported_ext_param_t) + num_ext); - sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t), - &ext_param); + 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->prsctp_enable) + if (asoc->ep->prsctp_enable) sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param); if (sp->adaptation_ind) { @@ -382,26 +369,24 @@ nodata: } struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - gfp_t gfp, int unkparam_len) + const struct sctp_chunk *chunk, + gfp_t gfp, int unkparam_len) { + struct sctp_supported_ext_param ext_param; + struct sctp_adaptation_ind_param aiparam; + struct sctp_paramhdr *auth_chunks = NULL; + struct sctp_paramhdr *auth_random = NULL; + struct sctp_paramhdr *auth_hmacs = NULL; + struct sctp_chunk *retval = NULL; + struct sctp_cookie_param *cookie; struct sctp_inithdr initack; - struct sctp_chunk *retval; union sctp_params addrs; struct sctp_sock *sp; - int addrs_len; - sctp_cookie_param_t *cookie; - int cookie_len; + __u8 extensions[5]; size_t chunksize; - sctp_adaptation_ind_param_t aiparam; - sctp_supported_ext_param_t ext_param; int num_ext = 0; - __u8 extensions[4]; - struct sctp_paramhdr *auth_chunks = NULL, - *auth_hmacs = NULL, - *auth_random = NULL; - - retval = NULL; + int cookie_len; + int addrs_len; /* Note: there may be no addresses to embed. */ addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, gfp); @@ -447,6 +432,11 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, if (sp->adaptation_ind) chunksize += sizeof(aiparam); + if (asoc->peer.intl_capable) { + extensions[num_ext] = SCTP_CID_I_DATA; + num_ext += 1; + } + if (asoc->peer.auth_capable) { auth_random = (struct sctp_paramhdr *)asoc->c.auth_random; chunksize += ntohs(auth_random->length); @@ -468,8 +458,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, } if (num_ext) - chunksize += SCTP_PAD4(sizeof(sctp_supported_ext_param_t) + - 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); @@ -485,7 +474,10 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, * * [INIT ACK back to where the INIT came from.] */ - retval->transport = chunk->transport; + if (chunk->transport) + retval->transport = + sctp_assoc_lookup_paddr(asoc, + &chunk->transport->ipaddr); retval->subh.init_hdr = sctp_addto_chunk(retval, sizeof(initack), &initack); @@ -495,10 +487,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, 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); + 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->peer.prsctp_capable) @@ -567,11 +557,11 @@ nomem_cookie: * to insure interoperability. */ struct sctp_chunk *sctp_make_cookie_echo(const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; - void *cookie; int cookie_len; + void *cookie; cookie = asoc->peer.cookie; cookie_len = asoc->peer.cookie_len; @@ -619,7 +609,7 @@ nodata: * 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) + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; @@ -634,8 +624,10 @@ struct sctp_chunk *sctp_make_cookie_ack(const struct sctp_association *asoc, * * [COOKIE ACK back to where the COOKIE ECHO came from.] */ - if (retval && chunk) - retval->transport = chunk->transport; + if (retval && chunk && chunk->transport) + retval->transport = + sctp_assoc_lookup_paddr(asoc, + &chunk->transport->ipaddr); return retval; } @@ -664,15 +656,15 @@ struct sctp_chunk *sctp_make_cookie_ack(const struct sctp_association *asoc, * 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) + const __u32 lowest_tsn, + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; - sctp_cwrhdr_t cwr; + struct sctp_cwrhdr cwr; cwr.lowest_tsn = htonl(lowest_tsn); retval = sctp_make_control(asoc, SCTP_CID_ECN_CWR, 0, - sizeof(sctp_cwrhdr_t), GFP_ATOMIC); + sizeof(cwr), GFP_ATOMIC); if (!retval) goto nodata; @@ -699,14 +691,14 @@ nodata: /* 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) + const __u32 lowest_tsn) { struct sctp_chunk *retval; - sctp_ecnehdr_t ecne; + struct sctp_ecnehdr ecne; ecne.lowest_tsn = htonl(lowest_tsn); retval = sctp_make_control(asoc, SCTP_CID_ECN_ECNE, 0, - sizeof(sctp_ecnehdr_t), GFP_ATOMIC); + sizeof(ecne), GFP_ATOMIC); if (!retval) goto nodata; retval->subh.ecne_hdr = @@ -719,38 +711,31 @@ nodata: /* 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 *sctp_make_datafrag_empty(const struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + int len, __u8 flags, 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; + memset(&dp, 0, sizeof(dp)); + dp.ppid = sinfo->sinfo_ppid; dp.stream = htons(sinfo->sinfo_stream); - dp.ppid = sinfo->sinfo_ppid; /* Set the flags for an unordered send. */ - if (sinfo->sinfo_flags & SCTP_UNORDERED) { + 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); + retval = sctp_make_data(asoc, flags, sizeof(dp) + len, gfp); if (!retval) - goto nodata; + return NULL; retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp); memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo)); -nodata: return retval; } @@ -758,17 +743,16 @@ nodata: * 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 *sctp_make_sack(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]; + __u16 num_gabs, num_dup_tsns; struct sctp_transport *trans; + struct sctp_chunk *retval; + struct sctp_sackhdr sack; + __u32 ctsn; + int len; memset(gabs, 0, sizeof(gabs)); ctsn = sctp_tsnmap_get_ctsn(map); @@ -836,7 +820,7 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc) /* Add the duplicate TSN information. */ if (num_dup_tsns) { - aptr->stats.idupchunks += num_dup_tsns; + asoc->stats.idupchunks += num_dup_tsns; sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns, sctp_tsnmap_get_dups(map)); } @@ -848,11 +832,11 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc) * association so no transport will match after a wrap event like this, * Until the next sack */ - if (++aptr->peer.sack_generation == 0) { + if (++asoc->peer.sack_generation == 0) { list_for_each_entry(trans, &asoc->peer.transport_addr_list, transports) trans->sack_generation = 0; - aptr->peer.sack_generation = 1; + asoc->peer.sack_generation = 1; } nodata: return retval; @@ -862,15 +846,15 @@ nodata: struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc, const struct sctp_chunk *chunk) { + struct sctp_shutdownhdr shut; 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); + sizeof(shut), GFP_ATOMIC); if (!retval) goto nodata; @@ -884,7 +868,7 @@ nodata: } struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; @@ -907,8 +891,8 @@ struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc, } struct sctp_chunk *sctp_make_shutdown_complete( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; __u8 flags = 0; @@ -941,8 +925,8 @@ struct sctp_chunk *sctp_make_shutdown_complete( * association, except when responding to an INIT (sctpimpguide 2.41). */ struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const size_t hint) + const struct sctp_chunk *chunk, + const size_t hint) { struct sctp_chunk *retval; __u8 flags = 0; @@ -978,14 +962,15 @@ struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, /* Helper to create ABORT with a NO_USER_DATA error. */ struct sctp_chunk *sctp_make_abort_no_data( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, __u32 tsn) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + __u32 tsn) { struct sctp_chunk *retval; __be32 payload; - retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) - + sizeof(tsn)); + retval = sctp_make_abort(asoc, chunk, + sizeof(struct sctp_errhdr) + sizeof(tsn)); if (!retval) goto no_mem; @@ -1020,7 +1005,8 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, void *payload = NULL; int err; - retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen); + retval = sctp_make_abort(asoc, NULL, + sizeof(struct sctp_errhdr) + paylen); if (!retval) goto err_chunk; @@ -1058,8 +1044,8 @@ err_chunk: static void *sctp_addto_param(struct sctp_chunk *chunk, int len, const void *data) { - void *target; int chunklen = ntohs(chunk->chunk_hdr->length); + void *target; target = skb_put(chunk->skb, len); @@ -1077,16 +1063,16 @@ static void *sctp_addto_param(struct sctp_chunk *chunk, int len, /* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */ struct sctp_chunk *sctp_make_abort_violation( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const __u8 *payload, - const size_t paylen) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const __u8 *payload, + const size_t paylen) { struct sctp_chunk *retval; struct sctp_paramhdr phdr; - retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen + - sizeof(phdr)); + retval = sctp_make_abort(asoc, chunk, sizeof(struct sctp_errhdr) + + paylen + sizeof(phdr)); if (!retval) goto end; @@ -1103,14 +1089,14 @@ end: } struct sctp_chunk *sctp_make_violation_paramlen( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - struct sctp_paramhdr *param) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + struct sctp_paramhdr *param) { - struct sctp_chunk *retval; static const char error[] = "The following parameter had invalid length:"; - size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t) + + size_t payload_len = sizeof(error) + sizeof(struct sctp_errhdr) + sizeof(*param); + struct sctp_chunk *retval; retval = sctp_make_abort(asoc, chunk, payload_len); if (!retval) @@ -1126,12 +1112,12 @@ nodata: } struct sctp_chunk *sctp_make_violation_max_retrans( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { + static const char error[] = "Association exceeded its max_retrans count"; + size_t payload_len = sizeof(error) + sizeof(struct sctp_errhdr); struct sctp_chunk *retval; - static const char error[] = "Association exceeded its max_retans count"; - size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t); retval = sctp_make_abort(asoc, chunk, payload_len); if (!retval) @@ -1144,12 +1130,33 @@ nodata: return retval; } +struct sctp_chunk *sctp_make_new_encap_port(const struct sctp_association *asoc, + const struct sctp_chunk *chunk) +{ + struct sctp_new_encap_port_hdr nep; + struct sctp_chunk *retval; + + retval = sctp_make_abort(asoc, chunk, + sizeof(struct sctp_errhdr) + sizeof(nep)); + if (!retval) + goto nodata; + + sctp_init_cause(retval, SCTP_ERROR_NEW_ENCAP_PORT, sizeof(nep)); + nep.cur_port = SCTP_INPUT_CB(chunk->skb)->encap_port; + nep.new_port = chunk->transport->encap_port; + sctp_addto_chunk(retval, sizeof(nep), &nep); + +nodata: + return retval; +} + /* Make a HEARTBEAT chunk. */ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, - const struct sctp_transport *transport) + const struct sctp_transport *transport, + __u32 probe_size) { + struct sctp_sender_hb_info hbinfo = {}; struct sctp_chunk *retval; - sctp_sender_hb_info_t hbinfo; retval = sctp_make_control(asoc, SCTP_CID_HEARTBEAT, 0, sizeof(hbinfo), GFP_ATOMIC); @@ -1158,10 +1165,11 @@ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, goto nodata; hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; - hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); + hbinfo.param_hdr.length = htons(sizeof(hbinfo)); hbinfo.daddr = transport->ipaddr; hbinfo.sent_at = jiffies; hbinfo.hb_nonce = transport->hb_nonce; + hbinfo.probe_size = probe_size; /* Cast away the 'const', as this is just telling the chunk * what transport it belongs to. @@ -1169,14 +1177,16 @@ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, retval->transport = (struct sctp_transport *) transport; retval->subh.hbs_hdr = sctp_addto_chunk(retval, sizeof(hbinfo), &hbinfo); + retval->pmtu_probe = !!probe_size; nodata: return retval; } struct sctp_chunk *sctp_make_heartbeat_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const void *payload, const size_t paylen) + const struct sctp_chunk *chunk, + const void *payload, + const size_t paylen) { struct sctp_chunk *retval; @@ -1203,18 +1213,45 @@ nodata: return retval; } +/* RFC4820 3. Padding Chunk (PAD) + * 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 = 0x84 | Flags=0 | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * \ Padding Data / + * / \ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct sctp_chunk *sctp_make_pad(const struct sctp_association *asoc, int len) +{ + struct sctp_chunk *retval; + + retval = sctp_make_control(asoc, SCTP_CID_PAD, 0, len, GFP_ATOMIC); + if (!retval) + return NULL; + + skb_put_zero(retval->skb, len); + retval->chunk_hdr->length = htons(ntohs(retval->chunk_hdr->length) + len); + retval->chunk_end = skb_tail_pointer(retval->skb); + + return retval; +} + /* Create an Operation Error chunk with the specified space reserved. * This routine can be used for containing multiple causes in the chunk. */ static struct sctp_chunk *sctp_make_op_error_space( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - size_t size) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + size_t size) { struct sctp_chunk *retval; retval = sctp_make_control(asoc, SCTP_CID_ERROR, 0, - sizeof(sctp_errhdr_t) + size, GFP_ATOMIC); + sizeof(struct sctp_errhdr) + size, + GFP_ATOMIC); if (!retval) goto nodata; @@ -1233,29 +1270,35 @@ nodata: return retval; } -/* Create an Operation Error chunk of a fixed size, - * specifically, max(asoc->pathmtu, SCTP_DEFAULT_MAXSEGMENT) - * This is a helper function to allocate an error chunk for - * for those invalid parameter codes in which we may not want - * to report all the errors, if the incoming chunk is large +/* 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. */ -static inline struct sctp_chunk *sctp_make_op_error_fixed( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) +static inline struct sctp_chunk *sctp_make_op_error_limited( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { - size_t size = asoc ? asoc->pathmtu : 0; + size_t size = SCTP_DEFAULT_MAXSEGMENT; + struct sctp_sock *sp = NULL; - if (!size) - size = SCTP_DEFAULT_MAXSEGMENT; + if (asoc) { + size = min_t(size_t, size, asoc->pathmtu); + sp = sctp_sk(asoc->base.sk); + } + + size = sctp_mtu_payload(sp, size, sizeof(struct sctp_errhdr)); return sctp_make_op_error_space(asoc, chunk, size); } /* Create an Operation Error chunk. */ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - __be16 cause_code, const void *payload, - size_t paylen, size_t reserve_tail) + const struct sctp_chunk *chunk, + __be16 cause_code, const void *payload, + size_t paylen, size_t reserve_tail) { struct sctp_chunk *retval; @@ -1272,12 +1315,12 @@ nodata: return retval; } -struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) +struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc, + __u16 key_id) { - struct sctp_chunk *retval; - struct sctp_hmac *hmac_desc; struct sctp_authhdr auth_hdr; - __u8 *hmac; + const struct sctp_hmac *hmac_desc; + struct sctp_chunk *retval; /* Get the first hmac that the peer told us to use */ hmac_desc = sctp_auth_asoc_get_hmac(asoc); @@ -1285,18 +1328,18 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) return NULL; retval = sctp_make_control(asoc, SCTP_CID_AUTH, 0, - hmac_desc->hmac_len + sizeof(sctp_authhdr_t), - GFP_ATOMIC); + hmac_desc->hmac_len + sizeof(auth_hdr), + GFP_ATOMIC); if (!retval) return NULL; auth_hdr.hmac_id = htons(hmac_desc->hmac_id); - auth_hdr.shkey_id = htons(asoc->active_key_id); + auth_hdr.shkey_id = htons(key_id); - retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(sctp_authhdr_t), - &auth_hdr); + retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(auth_hdr), + &auth_hdr); - hmac = skb_put_zero(retval->skb, hmac_desc->hmac_len); + skb_put_zero(retval->skb, hmac_desc->hmac_len); /* Adjust the chunk header to include the empty MAC */ retval->chunk_hdr->length = @@ -1322,8 +1365,8 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) * */ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, - const struct sctp_association *asoc, - struct sock *sk, gfp_t gfp) + const struct sctp_association *asoc, + struct sock *sk, gfp_t gfp) { struct sctp_chunk *retval; @@ -1375,16 +1418,21 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk) * arguments, reserving enough space for a 'paylen' byte payload. */ static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc, - __u8 type, __u8 flags, int paylen, - gfp_t gfp) + __u8 type, __u8 flags, int paylen, + gfp_t gfp) { - struct sctp_chunk *retval; struct sctp_chunkhdr *chunk_hdr; + struct sctp_chunk *retval; struct sk_buff *skb; struct sock *sk; + int chunklen; + + chunklen = SCTP_PAD4(sizeof(*chunk_hdr) + paylen); + if (chunklen > SCTP_MAX_CHUNK_LEN) + goto nodata; /* No need to allocate LL here, as this is only a chunk. */ - skb = alloc_skb(SCTP_PAD4(sizeof(*chunk_hdr) + paylen), gfp); + skb = alloc_skb(chunklen, gfp); if (!skb) goto nodata; @@ -1419,6 +1467,12 @@ static struct sctp_chunk *sctp_make_data(const struct sctp_association *asoc, return _sctp_make_chunk(asoc, SCTP_CID_DATA, flags, paylen, gfp); } +struct sctp_chunk *sctp_make_idata(const struct sctp_association *asoc, + __u8 flags, int paylen, gfp_t gfp) +{ + return _sctp_make_chunk(asoc, SCTP_CID_I_DATA, flags, paylen, gfp); +} + static struct sctp_chunk *sctp_make_control(const struct sctp_association *asoc, __u8 type, __u8 flags, int paylen, gfp_t gfp) @@ -1473,9 +1527,9 @@ void sctp_chunk_put(struct sctp_chunk *ch) */ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) { - void *target; int chunklen = ntohs(chunk->chunk_hdr->length); int padlen = SCTP_PAD4(chunklen) - chunklen; + void *target; skb_put_zero(chunk->skb, padlen); target = skb_put_data(chunk->skb, data, len); @@ -1487,18 +1541,6 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) return target; } -/* Append bytes to the end of a chunk. Returns NULL if there isn't sufficient - * space in the chunk - */ -static void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, - int len, const void *data) -{ - if (skb_tailroom(chunk->skb) >= len) - return sctp_addto_chunk(chunk, len, data); - else - return NULL; -} - /* Append bytes from user space to the end of a chunk. Will panic if * chunk is not big enough. * Returns a kernel err value. @@ -1528,11 +1570,10 @@ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len, */ void sctp_chunk_assign_ssn(struct sctp_chunk *chunk) { - struct sctp_datamsg *msg; - struct sctp_chunk *lchunk; struct sctp_stream *stream; - __u16 ssn; - __u16 sid; + struct sctp_chunk *lchunk; + struct sctp_datamsg *msg; + __u16 ssn, sid; if (chunk->has_ssn) return; @@ -1577,12 +1618,12 @@ void sctp_chunk_assign_tsn(struct sctp_chunk *chunk) /* Create a CLOSED association to use with an incoming packet. */ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, - struct sctp_chunk *chunk, - gfp_t gfp) + struct sctp_chunk *chunk, + gfp_t gfp) { struct sctp_association *asoc; + enum sctp_scope scope; struct sk_buff *skb; - sctp_scope_t scope; /* Create the bare association. */ scope = sctp_scope(sctp_source(chunk)); @@ -1601,14 +1642,15 @@ nodata: /* Build a cookie representing asoc. * This INCLUDES the param header needed to put the cookie in the INIT ACK. */ -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 struct sctp_cookie_param *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) { - sctp_cookie_param_t *retval; struct sctp_signed_cookie *cookie; + struct sctp_cookie_param *retval; int headersize, bodysize; /* Header size is static data prior to the actual cookie, including @@ -1632,8 +1674,10 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, * out on the network. */ retval = kzalloc(*cookie_len, GFP_ATOMIC); - if (!retval) - goto nodata; + if (!retval) { + *cookie_len = 0; + return NULL; + } cookie = (struct sctp_signed_cookie *) retval->body; @@ -1657,54 +1701,37 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, ktime_get_real()); /* Copy the peer's init packet. */ - memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr, + memcpy(cookie + 1, init_chunk->chunk_hdr, ntohs(init_chunk->chunk_hdr->length)); /* Copy the raw local address list of the association. */ - memcpy((__u8 *)&cookie->c.peer_init[0] + + memcpy((__u8 *)(cookie + 1) + ntohs(init_chunk->chunk_hdr->length), raw_addrs, addrs_len); - if (sctp_sk(ep->base.sk)->hmac) { - SHASH_DESC_ON_STACK(desc, sctp_sk(ep->base.sk)->hmac); - int err; - - /* Sign the message. */ - desc->tfm = sctp_sk(ep->base.sk)->hmac; - desc->flags = 0; - - err = crypto_shash_setkey(desc->tfm, ep->secret_key, - sizeof(ep->secret_key)) ?: - crypto_shash_digest(desc, (u8 *)&cookie->c, bodysize, - cookie->signature); - shash_desc_zero(desc); - if (err) - goto free_cookie; + /* Sign the cookie, if cookie authentication is enabled. */ + if (sctp_sk(ep->base.sk)->cookie_auth_enable) { + static_assert(sizeof(cookie->mac) == SHA256_DIGEST_SIZE); + hmac_sha256(&ep->cookie_auth_key, (const u8 *)&cookie->c, + bodysize, cookie->mac); } return retval; - -free_cookie: - kfree(retval); -nodata: - *cookie_len = 0; - return NULL; } /* Unpack the cookie from COOKIE ECHO chunk, recreating the association. */ struct sctp_association *sctp_unpack_cookie( - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - struct sctp_chunk *chunk, gfp_t gfp, - int *error, struct sctp_chunk **errp) + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk, gfp_t gfp, + int *error, struct sctp_chunk **errp) { struct sctp_association *retval = NULL; + int headersize, bodysize, fixed_size; struct sctp_signed_cookie *cookie; + struct sk_buff *skb = chunk->skb; struct sctp_cookie *bear_cookie; - int headersize, bodysize, fixed_size; - __u8 *digest = ep->digest; + enum sctp_scope scope; unsigned int len; - sctp_scope_t scope; - struct sk_buff *skb = chunk->skb; ktime_t kt; /* Header size is static data prior to the actual cookie, including @@ -1732,35 +1759,19 @@ struct sctp_association *sctp_unpack_cookie( cookie = chunk->subh.cookie_hdr; bear_cookie = &cookie->c; - if (!sctp_sk(ep->base.sk)->hmac) - goto no_hmac; + /* Verify the cookie's MAC, if cookie authentication is enabled. */ + if (sctp_sk(ep->base.sk)->cookie_auth_enable) { + u8 mac[SHA256_DIGEST_SIZE]; - /* Check the signature. */ - { - SHASH_DESC_ON_STACK(desc, sctp_sk(ep->base.sk)->hmac); - int err; - - desc->tfm = sctp_sk(ep->base.sk)->hmac; - desc->flags = 0; - - err = crypto_shash_setkey(desc->tfm, ep->secret_key, - sizeof(ep->secret_key)) ?: - crypto_shash_digest(desc, (u8 *)bear_cookie, bodysize, - digest); - shash_desc_zero(desc); - - if (err) { - *error = -SCTP_IERROR_NOMEM; + hmac_sha256(&ep->cookie_auth_key, (const u8 *)bear_cookie, + bodysize, mac); + static_assert(sizeof(cookie->mac) == sizeof(mac)); + if (crypto_memneq(mac, cookie->mac, sizeof(mac))) { + *error = -SCTP_IERROR_BAD_SIG; goto fail; } } - if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { - *error = -SCTP_IERROR_BAD_SIG; - goto fail; - } - -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 @@ -1784,7 +1795,7 @@ no_hmac: * 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 - * that a cookie may be considered expired, but his would only slow + * 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)) @@ -1793,6 +1804,9 @@ no_hmac: 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) * @@ -1801,17 +1815,12 @@ no_hmac: * Stale Cookie Error: Indicates the receipt of a valid State * Cookie that has expired. */ - len = ntohs(chunk->chunk_hdr->length); - *errp = sctp_make_op_error_space(asoc, chunk, len); - if (*errp) { - suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration)); - __be32 n = htonl(usecs); - - sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE, - sizeof(n)); - sctp_addto_chunk(*errp, sizeof(n), &n); + *errp = sctp_make_op_error(asoc, chunk, + SCTP_ERROR_STALE_COOKIE, &n, + sizeof(n), 0); + if (*errp) *error = -SCTP_IERROR_STALE_COOKIE; - } else + else *error = -SCTP_IERROR_NOMEM; goto fail; @@ -1962,22 +1971,20 @@ static int sctp_process_hn_param(const struct sctp_association *asoc, if (*errp) sctp_chunk_free(*errp); - *errp = sctp_make_op_error_space(asoc, chunk, len); - - if (*errp) { - sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED, len); - sctp_addto_chunk(*errp, len, param.v); - } + *errp = sctp_make_op_error(asoc, chunk, SCTP_ERROR_DNS_FAILED, + param.v, len, 0); /* Stop processing this chunk. */ return 0; } -static int sctp_verify_ext_param(struct net *net, union sctp_params param) +static int sctp_verify_ext_param(struct net *net, + const struct sctp_endpoint *ep, + union sctp_params param) { __u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); - int have_auth = 0; int have_asconf = 0; + int have_auth = 0; int i; for (i = 0; i < num_ext; i++) { @@ -2000,28 +2007,26 @@ static int sctp_verify_ext_param(struct net *net, union sctp_params param) if (net->sctp.addip_noauth) return 1; - if (net->sctp.addip_enable && !have_auth && have_asconf) + if (ep->asconf_enable && !have_auth && have_asconf) return 0; return 1; } static void sctp_process_ext_param(struct sctp_association *asoc, - union sctp_params param) + union sctp_params param) { - struct net *net = sock_net(asoc->base.sk); __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->reconf_enable && - !asoc->peer.reconf_capable) + if (asoc->ep->reconf_enable) asoc->peer.reconf_capable = 1; break; case SCTP_CID_FWD_TSN: - if (asoc->prsctp_enable && !asoc->peer.prsctp_capable) + if (asoc->ep->prsctp_enable) asoc->peer.prsctp_capable = 1; break; case SCTP_CID_AUTH: @@ -2033,9 +2038,13 @@ static void sctp_process_ext_param(struct sctp_association *asoc, break; case SCTP_CID_ASCONF: case SCTP_CID_ASCONF_ACK: - if (net->sctp.addip_enable) + 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; } @@ -2067,10 +2076,11 @@ static void sctp_process_ext_param(struct sctp_association *asoc, * SCTP_IERROR_ERROR - stop and report an error. * SCTP_IERROR_NOMEME - out of memory. */ -static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, - union sctp_params param, - struct sctp_chunk *chunk, - struct sctp_chunk **errp) +static enum sctp_ierror sctp_process_unk_param( + const struct sctp_association *asoc, + union sctp_params param, + struct sctp_chunk *chunk, + struct sctp_chunk **errp) { int retval = SCTP_IERROR_NO_ERROR; @@ -2082,28 +2092,28 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, break; case SCTP_PARAM_ACTION_DISCARD_ERR: retval = SCTP_IERROR_ERROR; - /* Fall through */ + fallthrough; case SCTP_PARAM_ACTION_SKIP_ERR: /* Make an ERROR chunk, preparing enough room for * returning multiple unknown parameters. */ - if (NULL == *errp) - *errp = sctp_make_op_error_fixed(asoc, chunk); - - if (*errp) { - if (!sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM, - SCTP_PAD4(ntohs(param.p->length)))) - sctp_addto_chunk_fixed(*errp, - SCTP_PAD4(ntohs(param.p->length)), - param.v); - } else { - /* 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; + 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; + } } + + if (!sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, + ntohs(param.p->length))) + sctp_addto_chunk(*errp, ntohs(param.p->length), + param.v); break; default: break; @@ -2119,13 +2129,13 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, * SCTP_IERROR_ERROR - stop processing, trigger an ERROR * SCTP_IERROR_NO_ERROR - continue with the chunk */ -static sctp_ierror_t sctp_verify_param(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - union sctp_params param, - enum sctp_cid cid, - struct sctp_chunk *chunk, - struct sctp_chunk **err_chunk) +static enum sctp_ierror sctp_verify_param(struct net *net, + const struct sctp_endpoint *ep, + const struct 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; @@ -2150,17 +2160,24 @@ static sctp_ierror_t sctp_verify_param(struct net *net, break; case SCTP_PARAM_SUPPORTED_EXT: - if (!sctp_verify_ext_param(net, param)) + if (!sctp_verify_ext_param(net, ep, param)) return SCTP_IERROR_ABORT; break; case SCTP_PARAM_SET_PRIMARY: - if (net->sctp.addip_enable) - break; - goto fallthrough; + if (!ep->asconf_enable) + goto unhandled; + + if (ntohs(param.p->length) < sizeof(struct sctp_addip_param) + + sizeof(struct sctp_paramhdr)) { + sctp_process_inv_paramlength(asoc, param.p, + chunk, err_chunk); + retval = SCTP_IERROR_ABORT; + } + break; case SCTP_PARAM_HOST_NAME_ADDRESS: - /* Tell the peer, we won't support this param. */ + /* This param has been Deprecated, send ABORT. */ sctp_process_hn_param(asoc, param, chunk, err_chunk); retval = SCTP_IERROR_ABORT; break; @@ -2168,28 +2185,28 @@ static sctp_ierror_t sctp_verify_param(struct net *net, case SCTP_PARAM_FWD_TSN_SUPPORT: if (ep->prsctp_enable) break; - goto fallthrough; + goto unhandled; case SCTP_PARAM_RANDOM: if (!ep->auth_enable) - goto fallthrough; + 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)) { + if (SCTP_AUTH_RANDOM_LENGTH != ntohs(param.p->length) - + sizeof(struct sctp_paramhdr)) { sctp_process_inv_paramlength(asoc, param.p, - chunk, err_chunk); + chunk, err_chunk); retval = SCTP_IERROR_ABORT; } break; case SCTP_PARAM_CHUNKS: if (!ep->auth_enable) - goto fallthrough; + goto unhandled; /* SCTP-AUTH: Section 3.2 * The CHUNKS parameter MUST be included once in the INIT or @@ -2205,7 +2222,7 @@ static sctp_ierror_t sctp_verify_param(struct net *net, case SCTP_PARAM_HMAC_ALGO: if (!ep->auth_enable) - goto fallthrough; + goto unhandled; hmacs = (struct sctp_hmac_algo_param *)param.p; n_elt = (ntohs(param.p->length) - @@ -2228,7 +2245,7 @@ static sctp_ierror_t sctp_verify_param(struct net *net, retval = SCTP_IERROR_ABORT; } break; -fallthrough: +unhandled: default: pr_debug("%s: unrecognized param:%d for chunk:%d\n", __func__, ntohs(param.p->type), cid); @@ -2259,7 +2276,7 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, ntohl(peer_init->init_hdr.a_rwnd) < SCTP_DEFAULT_MINWINDOW) return sctp_process_inv_mandatory(asoc, chunk, errp); - sctp_walk_params(param, peer_init, init_hdr.params) { + sctp_walk_params(param, peer_init) { if (param.p->type == SCTP_PARAM_STATE_COOKIE) has_cookie = true; } @@ -2282,7 +2299,7 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, chunk, errp); /* Verify all the variable length parameters */ - sctp_walk_params(param, peer_init, init_hdr.params) { + sctp_walk_params(param, peer_init) { result = sctp_verify_param(net, ep, asoc, param, cid, chunk, errp); switch (result) { @@ -2309,13 +2326,11 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, const union sctp_addr *peer_addr, struct sctp_init_chunk *peer_init, gfp_t gfp) { - struct net *net = sock_net(asoc->base.sk); - union sctp_params param; struct sctp_transport *transport; struct list_head *pos, *temp; - struct sctp_af *af; + union sctp_params param; union sctp_addr addr; - char *cookie; + struct sctp_af *af; int src_match = 0; /* We must include the address that the INIT packet came from. @@ -2326,8 +2341,9 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, /* This implementation defaults to making the first transport * added as the primary transport. The source address seems to - * be a a better choice than any of the embedded addresses. + * be a better choice than any of the embedded addresses. */ + asoc->encap_port = SCTP_INPUT_CB(chunk->skb)->encap_port; if (!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) goto nomem; @@ -2335,12 +2351,14 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, src_match = 1; /* Process the initialization parameters. */ - sctp_walk_params(param, peer_init, init_hdr.params) { - if (!src_match && (param.p->type == SCTP_PARAM_IPV4_ADDRESS || - param.p->type == SCTP_PARAM_IPV6_ADDRESS)) { + sctp_walk_params(param, peer_init) { + if (!src_match && + (param.p->type == SCTP_PARAM_IPV4_ADDRESS || + param.p->type == SCTP_PARAM_IPV6_ADDRESS)) { af = sctp_get_af_specific(param_type2af(param.p->type)); - af->from_addr_param(&addr, param.addr, - chunk->sctp_hdr->source, 0); + if (!af->from_addr_param(&addr, param.addr, + chunk->sctp_hdr->source, 0)) + continue; if (sctp_cmp_addr_exact(sctp_source(chunk), &addr)) src_match = 1; } @@ -2366,8 +2384,8 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, * also give us an option to silently ignore the packet, which * is what we'll do here. */ - if (!net->sctp.addip_noauth && - (asoc->peer.asconf_capable && !asoc->peer.auth_capable)) { + if (!asoc->base.net->sctp.addip_noauth && + (asoc->peer.asconf_capable && !asoc->peer.auth_capable)) { asoc->peer.addip_disabled_mask |= (SCTP_PARAM_ADD_IP | SCTP_PARAM_DEL_IP | SCTP_PARAM_SET_PRIMARY); @@ -2420,14 +2438,6 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, /* Peer Rwnd : Current calculated value of the peer's rwnd. */ asoc->peer.rwnd = asoc->peer.i.a_rwnd; - /* Copy cookie in case we need to resend COOKIE-ECHO. */ - cookie = asoc->peer.cookie; - if (cookie) { - asoc->peer.cookie = kmemdup(cookie, asoc->peer.cookie_len, gfp); - if (!asoc->peer.cookie) - goto clean_up; - } - /* RFC 2960 7.2.1 The initial value of ssthresh MAY be arbitrarily * high (for example, implementations MAY use the size of the receiver * advertised window). @@ -2454,6 +2464,9 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, asoc->c.sinit_max_instreams, gfp)) goto clean_up; + /* Update frag_point when stream_interleave may get changed. */ + sctp_assoc_update_frag_point(asoc); + if (!asoc->temp && sctp_assoc_set_id(asoc, gfp)) goto clean_up; @@ -2499,17 +2512,16 @@ static int sctp_process_param(struct sctp_association *asoc, const union sctp_addr *peer_addr, gfp_t gfp) { - struct net *net = sock_net(asoc->base.sk); - union sctp_addr addr; - int i; - __u16 sat; - int retval = 1; - sctp_scope_t scope; - u32 stale; - struct sctp_af *af; + struct sctp_endpoint *ep = asoc->ep; union sctp_addr_param *addr_param; + struct net *net = asoc->base.net; struct sctp_transport *t; - struct sctp_endpoint *ep = asoc->ep; + enum sctp_scope scope; + union sctp_addr addr; + struct sctp_af *af; + int retval = 1, i; + u32 stale; + __u16 sat; /* We maintain all INIT parameters in network byte order all the * time. This allows us to not worry about whether the parameters @@ -2527,7 +2539,8 @@ static int sctp_process_param(struct sctp_association *asoc, break; do_addr_param: af = sctp_get_af_specific(param_type2af(param.p->type)); - af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0); + if (!af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0)) + break; scope = sctp_scope(peer_addr); if (sctp_in_scope(net, &addr, scope)) if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED)) @@ -2546,10 +2559,6 @@ do_addr_param: asoc->cookie_life = ktime_add_ms(asoc->cookie_life, stale); break; - case SCTP_PARAM_HOST_NAME_ADDRESS: - pr_debug("%s: unimplemented SCTP_HOST_NAME_ADDRESS\n", __func__); - break; - case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: /* Turn off the default values first so we'll know which * ones are really set by the peer. @@ -2581,10 +2590,6 @@ do_addr_param: asoc->peer.ipv6_address = 1; break; - case SCTP_PARAM_HOST_NAME_ADDRESS: - asoc->peer.hostname_address = 1; - break; - default: /* Just ignore anything else. */ break; } @@ -2594,7 +2599,10 @@ do_addr_param: case SCTP_PARAM_STATE_COOKIE: asoc->peer.cookie_len = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); - asoc->peer.cookie = param.cookie->body; + kfree(asoc->peer.cookie); + asoc->peer.cookie = kmemdup(param.cookie->body, asoc->peer.cookie_len, gfp); + if (!asoc->peer.cookie) + retval = 0; break; case SCTP_PARAM_HEARTBEAT_INFO: @@ -2606,29 +2614,32 @@ do_addr_param: break; case SCTP_PARAM_ECN_CAPABLE: - asoc->peer.ecn_capable = 1; - break; + if (asoc->ep->ecn_enable) { + asoc->peer.ecn_capable = 1; + break; + } + /* Fall Through */ + goto fall_through; + case SCTP_PARAM_ADAPTATION_LAYER_IND: asoc->peer.adaptation_ind = ntohl(param.aind->adaptation_ind); break; case SCTP_PARAM_SET_PRIMARY: - if (!net->sctp.addip_enable) + if (!ep->asconf_enable) goto fall_through; - addr_param = param.v + sizeof(sctp_addip_param_t); + addr_param = param.v + sizeof(struct sctp_addip_param); af = sctp_get_af_specific(param_type2af(addr_param->p.type)); - if (af == NULL) + if (!af) break; - af->from_addr_param(&addr, addr_param, - htons(asoc->peer.port), 0); + if (!af->from_addr_param(&addr, addr_param, + htons(asoc->peer.port), 0)) + break; - /* if the address is invalid, we can't process it. - * XXX: see spec for what to do. - */ if (!af->addr_valid(&addr, NULL, NULL)) break; @@ -2644,7 +2655,7 @@ do_addr_param: break; case SCTP_PARAM_FWD_TSN_SUPPORT: - if (asoc->prsctp_enable) { + if (asoc->ep->prsctp_enable) { asoc->peer.prsctp_capable = 1; break; } @@ -2656,6 +2667,7 @@ do_addr_param: goto fall_through; /* Save peer's random parameter */ + kfree(asoc->peer.peer_random); asoc->peer.peer_random = kmemdup(param.p, ntohs(param.p->length), gfp); if (!asoc->peer.peer_random) { @@ -2669,6 +2681,7 @@ do_addr_param: goto fall_through; /* Save peer's HMAC list */ + kfree(asoc->peer.peer_hmacs); asoc->peer.peer_hmacs = kmemdup(param.p, ntohs(param.p->length), gfp); if (!asoc->peer.peer_hmacs) { @@ -2684,6 +2697,7 @@ do_addr_param: if (!ep->auth_enable) goto fall_through; + kfree(asoc->peer.peer_chunks); asoc->peer.peer_chunks = kmemdup(param.p, ntohs(param.p->length), gfp); if (!asoc->peer.peer_chunks) @@ -2754,7 +2768,7 @@ static struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, union sctp_addr *addr, int vparam_len) { - sctp_addiphdr_t asconf; + struct sctp_addiphdr asconf; struct sctp_chunk *retval; int length = sizeof(asconf) + vparam_len; union sctp_addr_param addrparam; @@ -2807,22 +2821,20 @@ static struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, * */ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, - union sctp_addr *laddr, - struct sockaddr *addrs, - int addrcnt, - __be16 flags) -{ - sctp_addip_param_t param; - struct sctp_chunk *retval; - union sctp_addr_param addr_param; - union sctp_addr *addr; - void *addr_buf; - struct sctp_af *af; - int paramlen = sizeof(param); - int addr_param_len = 0; - int totallen = 0; - int i; - int del_pickup = 0; + union sctp_addr *laddr, + struct sockaddr *addrs, + int addrcnt, __be16 flags) +{ + union sctp_addr_param addr_param; + struct sctp_addip_param param; + int paramlen = sizeof(param); + struct sctp_chunk *retval; + int addr_param_len = 0; + union sctp_addr *addr; + int totallen = 0, i; + int del_pickup = 0; + struct sctp_af *af; + void *addr_buf; /* Get total length of all the address parameters. */ addr_buf = addrs; @@ -2860,7 +2872,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, addr_param_len = af->to_addr_param(addr, &addr_param); param.param_hdr.type = flags; param.param_hdr.length = htons(paramlen + addr_param_len); - param.crr_id = i; + param.crr_id = htonl(i); sctp_addto_chunk(retval, paramlen, ¶m); sctp_addto_chunk(retval, addr_param_len, &addr_param); @@ -2873,7 +2885,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, addr_param_len = af->to_addr_param(addr, &addr_param); param.param_hdr.type = SCTP_PARAM_DEL_IP; param.param_hdr.length = htons(paramlen + addr_param_len); - param.crr_id = i; + param.crr_id = htonl(i); sctp_addto_chunk(retval, paramlen, ¶m); sctp_addto_chunk(retval, addr_param_len, &addr_param); @@ -2898,12 +2910,12 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr) { - sctp_addip_param_t param; - struct sctp_chunk *retval; - int len = sizeof(param); - union sctp_addr_param addrparam; - int addrlen; - struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family); + struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family); + union sctp_addr_param addrparam; + struct sctp_addip_param param; + struct sctp_chunk *retval; + int len = sizeof(param); + int addrlen; addrlen = af->to_addr_param(addr, &addrparam); if (!addrlen) @@ -2947,9 +2959,9 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, __u32 serial, int vparam_len) { - sctp_addiphdr_t asconf; - struct sctp_chunk *retval; - int length = sizeof(asconf) + vparam_len; + struct sctp_addiphdr asconf; + struct sctp_chunk *retval; + int length = sizeof(asconf) + vparam_len; /* Create the chunk. */ retval = sctp_make_control(asoc, SCTP_CID_ASCONF_ACK, 0, length, @@ -2967,13 +2979,14 @@ static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *as /* Add response parameters to an ASCONF_ACK chunk. */ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, - __be16 err_code, sctp_addip_param_t *asconf_param) + __be16 err_code, + struct sctp_addip_param *asconf_param) { - sctp_addip_param_t ack_param; - sctp_errhdr_t err_param; - int asconf_param_len = 0; - int err_param_len = 0; - __be16 response_type; + struct sctp_addip_param ack_param; + struct sctp_errhdr err_param; + int asconf_param_len = 0; + int err_param_len = 0; + __be16 response_type; if (SCTP_ERROR_NO_ERROR == err_code) { response_type = SCTP_PARAM_SUCCESS_REPORT; @@ -3008,15 +3021,15 @@ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, /* Process a asconf parameter. */ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, - struct sctp_chunk *asconf, - sctp_addip_param_t *asconf_param) + struct sctp_chunk *asconf, + struct sctp_addip_param *asconf_param) { + union sctp_addr_param *addr_param; struct sctp_transport *peer; - struct sctp_af *af; union sctp_addr addr; - union sctp_addr_param *addr_param; + struct sctp_af *af; - addr_param = (void *)asconf_param + sizeof(sctp_addip_param_t); + addr_param = (void *)asconf_param + sizeof(*asconf_param); if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && @@ -3040,7 +3053,8 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (unlikely(!af)) return SCTP_ERROR_DNS_FAILED; - af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0); + if (!af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0)) + return SCTP_ERROR_DNS_FAILED; /* ADDIP 4.2.1 This parameter MUST NOT contain a broadcast * or multicast address. @@ -3059,6 +3073,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, if (af->is_any(&addr)) memcpy(&addr, &asconf->source, sizeof(addr)); + if (security_sctp_bind_connect(asoc->ep->base.sk, + SCTP_PARAM_ADD_IP, + (struct sockaddr *)&addr, + af->sockaddr_len)) + return SCTP_ERROR_REQ_REFUSED; + /* ADDIP 4.3 D9) If an endpoint receives an ADD IP address * request and does not have the local resources to add this * new address to the association, it MUST return an Error @@ -3123,7 +3143,13 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, * primary. */ if (af->is_any(&addr)) - memcpy(&addr.v4, sctp_source(asconf), sizeof(addr)); + memcpy(&addr, sctp_source(asconf), sizeof(addr)); + + if (security_sctp_bind_connect(asoc->ep->base.sk, + SCTP_PARAM_SET_PRIMARY, + (struct sockaddr *)&addr, + af->sockaddr_len)) + return SCTP_ERROR_REQ_REFUSED; peer = sctp_assoc_lookup_paddr(asoc, &addr); if (!peer) @@ -3141,11 +3167,12 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, struct sctp_chunk *chunk, bool addr_param_needed, struct sctp_paramhdr **errp) { - sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr; - union sctp_params param; + struct sctp_addip_chunk *addip; bool addr_param_seen = false; + union sctp_params param; - sctp_walk_params(param, addip, addip_hdr.params) { + addip = (struct sctp_addip_chunk *)chunk->chunk_hdr; + sctp_walk_params(param, addip) { size_t length = ntohs(param.p->length); *errp = param.p; @@ -3153,19 +3180,19 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, case SCTP_PARAM_ERR_CAUSE: break; case SCTP_PARAM_IPV4_ADDRESS: - if (length != sizeof(sctp_ipv4addr_param_t)) + if (length != sizeof(struct sctp_ipv4addr_param)) return false; /* ensure there is only one addr param and it's in the * beginning of addip_hdr params, or we reject it. */ - if (param.v != addip->addip_hdr.params) + if (param.v != (addip + 1)) return false; addr_param_seen = true; break; case SCTP_PARAM_IPV6_ADDRESS: - if (length != sizeof(sctp_ipv6addr_param_t)) + if (length != sizeof(struct sctp_ipv6addr_param)) return false; - if (param.v != addip->addip_hdr.params) + if (param.v != (addip + 1)) return false; addr_param_seen = true; break; @@ -3176,17 +3203,17 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, if (addr_param_needed && !addr_param_seen) return false; length = ntohs(param.addip->param_hdr.length); - if (length < sizeof(sctp_addip_param_t) + + if (length < sizeof(struct sctp_addip_param) + sizeof(**errp)) return false; break; case SCTP_PARAM_SUCCESS_REPORT: case SCTP_PARAM_ADAPTATION_LAYER_IND: - if (length != sizeof(sctp_addip_param_t)) + if (length != sizeof(struct sctp_addip_param)) return false; break; default: - /* This is unkown to us, reject! */ + /* This is unknown to us, reject! */ return false; } } @@ -3208,24 +3235,24 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *asconf) { - sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr; + union sctp_addr_param *addr_param; + struct sctp_addip_chunk *addip; + struct sctp_chunk *asconf_ack; bool all_param_pass = true; + struct sctp_addiphdr *hdr; + int length = 0, chunk_len; union sctp_params param; - sctp_addiphdr_t *hdr; - union sctp_addr_param *addr_param; - struct sctp_chunk *asconf_ack; - __be16 err_code; - int length = 0; - int chunk_len; - __u32 serial; + __be16 err_code; + __u32 serial; + addip = (struct sctp_addip_chunk *)asconf->chunk_hdr; chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(struct sctp_chunkhdr); - hdr = (sctp_addiphdr_t *)asconf->skb->data; + hdr = (struct sctp_addiphdr *)asconf->skb->data; serial = ntohl(hdr->serial); /* Skip the addiphdr and store a pointer to address parameter. */ - length = sizeof(sctp_addiphdr_t); + length = sizeof(*hdr); addr_param = (union sctp_addr_param *)(asconf->skb->data + length); chunk_len -= length; @@ -3245,8 +3272,8 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, goto done; /* Process the TLVs contained within the ASCONF chunk. */ - sctp_walk_params(param, addip, addip_hdr.params) { - /* Skip preceeding address parameters. */ + sctp_walk_params(param, addip) { + /* Skip preceding address parameters. */ if (param.p->type == SCTP_PARAM_IPV4_ADDRESS || param.p->type == SCTP_PARAM_IPV6_ADDRESS) continue; @@ -3291,20 +3318,21 @@ done: /* Process a asconf parameter that is successfully acked. */ static void sctp_asconf_param_success(struct sctp_association *asoc, - sctp_addip_param_t *asconf_param) + struct sctp_addip_param *asconf_param) { - struct sctp_af *af; - union sctp_addr addr; struct sctp_bind_addr *bp = &asoc->base.bind_addr; union sctp_addr_param *addr_param; - struct sctp_transport *transport; struct sctp_sockaddr_entry *saddr; + struct sctp_transport *transport; + union sctp_addr addr; + struct sctp_af *af; - addr_param = (void *)asconf_param + sizeof(sctp_addip_param_t); + addr_param = (void *)asconf_param + sizeof(*asconf_param); /* We have checked the packet before, so we do not check again. */ af = sctp_get_af_specific(param_type2af(addr_param->p.type)); - af->from_addr_param(&addr, addr_param, htons(bp->port), 0); + if (!af->from_addr_param(&addr, addr_param, htons(bp->port), 0)) + return; switch (asconf_param->param_hdr.type) { case SCTP_PARAM_ADD_IP: @@ -3351,14 +3379,14 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, * specific success indication is present for the parameter. */ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, - sctp_addip_param_t *asconf_param, - int no_err) + struct sctp_addip_param *asconf_param, + int no_err) { - sctp_addip_param_t *asconf_ack_param; - sctp_errhdr_t *err_param; - int length; - int asconf_ack_len; - __be16 err_code; + struct sctp_addip_param *asconf_ack_param; + struct sctp_errhdr *err_param; + int asconf_ack_len; + __be16 err_code; + int length; if (no_err) err_code = SCTP_ERROR_NO_ERROR; @@ -3371,9 +3399,9 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, /* Skip the addiphdr from the asconf_ack chunk and store a pointer to * the first asconf_ack parameter. */ - length = sizeof(sctp_addiphdr_t); - asconf_ack_param = (sctp_addip_param_t *)(asconf_ack->skb->data + - length); + length = sizeof(struct sctp_addiphdr); + asconf_ack_param = (struct sctp_addip_param *)(asconf_ack->skb->data + + length); asconf_ack_len -= length; while (asconf_ack_len > 0) { @@ -3382,7 +3410,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, case SCTP_PARAM_SUCCESS_REPORT: return SCTP_ERROR_NO_ERROR; case SCTP_PARAM_ERR_CAUSE: - length = sizeof(sctp_addip_param_t); + length = sizeof(*asconf_ack_param); err_param = (void *)asconf_ack_param + length; asconf_ack_len -= length; if (asconf_ack_len > 0) @@ -3407,20 +3435,20 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, int sctp_process_asconf_ack(struct sctp_association *asoc, struct sctp_chunk *asconf_ack) { - struct sctp_chunk *asconf = asoc->addip_last_asconf; - union sctp_addr_param *addr_param; - sctp_addip_param_t *asconf_param; - int length = 0; - int asconf_len = asconf->skb->len; - int all_param_pass = 0; - int no_err = 1; - int retval = 0; - __be16 err_code = SCTP_ERROR_NO_ERROR; + struct sctp_chunk *asconf = asoc->addip_last_asconf; + struct sctp_addip_param *asconf_param; + __be16 err_code = SCTP_ERROR_NO_ERROR; + union sctp_addr_param *addr_param; + int asconf_len = asconf->skb->len; + int all_param_pass = 0; + int length = 0; + int no_err = 1; + int retval = 0; /* Skip the chunkhdr and addiphdr from the last asconf sent and store * a pointer to address parameter. */ - length = sizeof(sctp_addip_chunk_t); + length = sizeof(struct sctp_addip_chunk); addr_param = (union sctp_addr_param *)(asconf->skb->data + length); asconf_len -= length; @@ -3436,7 +3464,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, * failures are indicated, then all request(s) are considered * successful. */ - if (asconf_ack->skb->len == sizeof(sctp_addiphdr_t)) + if (asconf_ack->skb->len == sizeof(struct sctp_addiphdr)) all_param_pass = 1; /* Process the TLVs contained in the last sent ASCONF chunk. */ @@ -3527,6 +3555,30 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, return retval; } +struct sctp_chunk *sctp_make_ifwdtsn(const struct sctp_association *asoc, + __u32 new_cum_tsn, size_t nstreams, + struct sctp_ifwdtsn_skip *skiplist) +{ + struct sctp_chunk *retval = NULL; + struct sctp_ifwdtsn_hdr ftsn_hdr; + size_t hint; + + hint = (nstreams + 1) * sizeof(__u32); + + retval = sctp_make_control(asoc, SCTP_CID_I_FWD_TSN, 0, hint, + GFP_ATOMIC); + if (!retval) + return NULL; + + ftsn_hdr.new_cum_tsn = htonl(new_cum_tsn); + retval->subh.ifwdtsn_hdr = + sctp_addto_chunk(retval, sizeof(ftsn_hdr), &ftsn_hdr); + + sctp_addto_chunk(retval, nstreams * sizeof(skiplist[0]), skiplist); + + return retval; +} + /* RE-CONFIG 3.1 (RE-CONFIG chunk) * 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 @@ -3542,9 +3594,8 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, * \ \ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -static struct sctp_chunk *sctp_make_reconf( - const struct sctp_association *asoc, - int length) +static struct sctp_chunk *sctp_make_reconf(const struct sctp_association *asoc, + int length) { struct sctp_reconf_chunk *reconf; struct sctp_chunk *retval; @@ -3555,7 +3606,7 @@ static struct sctp_chunk *sctp_make_reconf( return NULL; reconf = (struct sctp_reconf_chunk *)retval->chunk_hdr; - retval->param_hdr.v = reconf->params; + retval->param_hdr.v = (u8 *)(reconf + 1); return retval; } @@ -3595,12 +3646,12 @@ static struct sctp_chunk *sctp_make_reconf( * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct sctp_chunk *sctp_make_strreset_req( - const struct sctp_association *asoc, - __u16 stream_num, __u16 *stream_list, - bool out, bool in) + const struct sctp_association *asoc, + __u16 stream_num, __be16 *stream_list, + bool out, bool in) { + __u16 stream_len = stream_num * sizeof(__u16); struct sctp_strreset_outreq outreq; - __u16 stream_len = stream_num * 2; struct sctp_strreset_inreq inreq; struct sctp_chunk *retval; __u16 outlen, inlen; @@ -3608,7 +3659,7 @@ struct sctp_chunk *sctp_make_strreset_req( outlen = (sizeof(outreq) + stream_len) * out; inlen = (sizeof(inreq) + stream_len) * in; - retval = sctp_make_reconf(asoc, outlen + inlen); + retval = sctp_make_reconf(asoc, SCTP_PAD4(outlen) + SCTP_PAD4(inlen)); if (!retval) return NULL; @@ -3649,7 +3700,7 @@ struct sctp_chunk *sctp_make_strreset_req( * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct sctp_chunk *sctp_make_strreset_tsnreq( - const struct sctp_association *asoc) + const struct sctp_association *asoc) { struct sctp_strreset_tsnreq tsnreq; __u16 length = sizeof(tsnreq); @@ -3680,8 +3731,8 @@ struct sctp_chunk *sctp_make_strreset_tsnreq( * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct sctp_chunk *sctp_make_strreset_addstrm( - const struct sctp_association *asoc, - __u16 out, __u16 in) + const struct sctp_association *asoc, + __u16 out, __u16 in) { struct sctp_strreset_addstrm addstrm; __u16 size = sizeof(addstrm); @@ -3725,9 +3776,8 @@ struct sctp_chunk *sctp_make_strreset_addstrm( * | Result | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -struct sctp_chunk *sctp_make_strreset_resp( - const struct sctp_association *asoc, - __u32 result, __u32 sn) +struct sctp_chunk *sctp_make_strreset_resp(const struct sctp_association *asoc, + __u32 result, __u32 sn) { struct sctp_strreset_resp resp; __u16 length = sizeof(resp); @@ -3762,10 +3812,10 @@ struct sctp_chunk *sctp_make_strreset_resp( * | Receiver's Next TSN (optional) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -struct sctp_chunk *sctp_make_strreset_tsnresp( - struct sctp_association *asoc, - __u32 result, __u32 sn, - __u32 sender_tsn, __u32 receiver_tsn) +struct sctp_chunk *sctp_make_strreset_tsnresp(struct sctp_association *asoc, + __u32 result, __u32 sn, + __u32 sender_tsn, + __u32 receiver_tsn) { struct sctp_strreset_resptsn tsnresp; __u16 length = sizeof(tsnresp); @@ -3794,10 +3844,11 @@ bool sctp_verify_reconf(const struct sctp_association *asoc, { struct sctp_reconf_chunk *hdr; union sctp_params param; - __u16 last = 0, cnt = 0; + __be16 last = 0; + __u16 cnt = 0; hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; - sctp_walk_params(param, hdr, params) { + sctp_walk_params(param, hdr) { __u16 length = ntohs(param.p->length); *errp = param.p; |
