/* A helper to initialize an op error inside a provided chunk, as most *causecodeswillbeembeddedinsideanabortchunk.
*/ int sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
size_t paylen)
{ struct sctp_errhdr err;
__u16 len;
/* Cause code constants are now defined in network order. */
err.cause = cause_code;
len = sizeof(err) + paylen;
err.length = htons(len);
if (skb_tailroom(chunk->skb) < len) return -ENOSPC;
if (asoc->ep->reconf_enable) {
extensions[num_ext] = SCTP_CID_RECONF;
num_ext += 1;
}
if (sp->adaptation_ind)
chunksize += sizeof(aiparam);
if (asoc->ep->intl_enable) {
extensions[num_ext] = SCTP_CID_I_DATA;
num_ext += 1;
}
chunksize += vparam_len;
/* Account for AUTH related parameters */ if (ep->auth_enable) { /* Add random parameter length*/
chunksize += sizeof(asoc->c.auth_random);
/* Add HMACS parameter length if any were defined */
auth_hmacs = (struct sctp_paramhdr *)asoc->c.auth_hmacs; if (auth_hmacs->length)
chunksize += SCTP_PAD4(ntohs(auth_hmacs->length)); else
auth_hmacs = NULL;
/* FIXME: We really ought to build the cookie right *intothepacketinsteadofallocatingmorefreshmemory.
*/
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 *spaceforreportingunknownparametersifitisspecified.
*/
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);
/* Make a DATA chunk for the given association from the provided *parameters.However,donotpopulatethedatapayload.
*/ struct sctp_chunk *sctp_make_datafrag_empty(conststruct sctp_association *asoc, conststruct sctp_sndrcvinfo *sinfo, int len, __u8 flags, gfp_t gfp)
{ struct sctp_chunk *retval; struct sctp_datahdr dp;
/* We assign the TSN as LATE as possible, not here when *creatingthechunk.
*/
memset(&dp, 0, sizeof(dp));
dp.ppid = sinfo->sinfo_ppid;
dp.stream = htons(sinfo->sinfo_stream);
/* Set the flags for an unordered send. */ if (sinfo->sinfo_flags & SCTP_UNORDERED)
flags |= SCTP_DATA_UNORDERED;
/* Create an ABORT. Note that we set the T bit if we have no *association,exceptwhenrespondingtoanINIT(sctpimpguide2.41).
*/ struct sctp_chunk *sctp_make_abort(conststruct sctp_association *asoc, conststruct sctp_chunk *chunk, const size_t hint)
{ struct sctp_chunk *retval;
__u8 flags = 0;
/* Set the T-bit if we have no association and 'chunk' is not *anINIT(vtagwillbereflected).
*/ if (!asoc) { if (chunk && chunk->chunk_hdr &&
chunk->chunk_hdr->type == SCTP_CID_INIT)
flags = 0; else
flags = SCTP_CHUNK_FLAG_T;
}
/* Put the tsn back into network byte order. */
payload = htonl(tsn);
sctp_init_cause(retval, SCTP_ERROR_NO_DATA, sizeof(payload));
sctp_addto_chunk(retval, sizeof(payload), (constvoid *)&payload);
/* Append bytes to the end of a parameter. Will panic if chunk is not big *enough.
*/ staticvoid *sctp_addto_param(struct sctp_chunk *chunk, int len, constvoid *data)
{ int chunklen = ntohs(chunk->chunk_hdr->length); void *target;
target = skb_put(chunk->skb, len);
if (data)
memcpy(target, data, len); else
memset(target, 0, len);
/* Cast away the 'const', as this is just telling the chunk *whattransportitbelongsto.
*/
retval->transport = (struct sctp_transport *) transport;
retval->subh.hbs_hdr = sctp_addto_chunk(retval, sizeof(hbinfo),
&hbinfo);
retval->pmtu_probe = !!probe_size;
/* Adjust the chunk header to include the empty MAC */
retval->chunk_hdr->length =
htons(ntohs(retval->chunk_hdr->length) + hmac_desc->hmac_len);
retval->chunk_end = skb_tail_pointer(retval->skb);
/* Set chunk->source and dest based on the IP header in chunk->skb. */ void sctp_init_addrs(struct sctp_chunk *chunk, union sctp_addr *src, union sctp_addr *dest)
{
memcpy(&chunk->source, src, sizeof(union sctp_addr));
memcpy(&chunk->dest, dest, sizeof(union sctp_addr));
}
/* Extract the source address from a chunk. */ constunion sctp_addr *sctp_source(conststruct sctp_chunk *chunk)
{ /* If we have a known transport, use that. */ if (chunk->transport) { return &chunk->transport->ipaddr;
} else { /* Otherwise, extract it from the IP header. */ return &chunk->source;
}
}
/* Create a new chunk, setting the type and flags headers from the *arguments,reservingenoughspacefora'paylen'bytepayload.
*/ staticstruct sctp_chunk *_sctp_make_chunk(conststruct sctp_association *asoc,
__u8 type, __u8 flags, int paylen,
gfp_t gfp)
{ struct sctp_chunkhdr *chunk_hdr; struct sctp_chunk *retval; struct sk_buff *skb; struct sock *sk; int chunklen;
/* Possibly, free the chunk. */ void sctp_chunk_free(struct sctp_chunk *chunk)
{ /* Release our reference on the message tracker. */ if (chunk->msg)
sctp_datamsg_put(chunk->msg);
sctp_chunk_put(chunk);
}
/* Grab a reference to the chunk. */ void sctp_chunk_hold(struct sctp_chunk *ch)
{
refcount_inc(&ch->refcnt);
}
/* Release a reference to the chunk. */ void sctp_chunk_put(struct sctp_chunk *ch)
{ if (refcount_dec_and_test(&ch->refcnt))
sctp_chunk_destroy(ch);
}
/* Append bytes to the end of a chunk. Will panic if chunk is not big *enough.
*/ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, constvoid *data)
{ int chunklen = ntohs(chunk->chunk_hdr->length); int padlen = SCTP_PAD4(chunklen) - chunklen; void *target;
/* Append bytes from user space to the end of a chunk. Will panic if *chunkisnotbigenough. *Returnsakernelerrvalue.
*/ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len, struct iov_iter *from)
{ void *target;
/* Make room in chunk for data. */
target = skb_put(chunk->skb, len);
/* Copy data (whole iovec) into chunk */ if (!copy_from_iter_full(target, len, from)) return -EFAULT;
/* Helper function to assign a TSN if needed. This assumes that both *thedata_hdrandassociationhavealreadybeenassigned.
*/ void sctp_chunk_assign_ssn(struct sctp_chunk *chunk)
{ struct sctp_stream *stream; struct sctp_chunk *lchunk; struct sctp_datamsg *msg;
__u16 ssn, sid;
if (chunk->has_ssn) return;
/* All fragments will be on the same stream */
sid = ntohs(chunk->subh.data_hdr->stream);
stream = &chunk->asoc->stream;
/* Now assign the sequence number to the entire message. *Allfragmentsmusthavethesamestreamsequencenumber.
*/
msg = chunk->msg;
list_for_each_entry(lchunk, &msg->chunks, frag_list) { if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
ssn = 0;
} else { if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
ssn = sctp_ssn_next(stream, out, sid); else
ssn = sctp_ssn_peek(stream, out, sid);
}
/* Helper function to assign a TSN if needed. This assumes that both *thedata_hdrandassociationhavealreadybeenassigned.
*/ void sctp_chunk_assign_tsn(struct sctp_chunk *chunk)
{ if (!chunk->has_tsn) { /* This is the last possible instant to *assignaTSN.
*/
chunk->subh.data_hdr->tsn =
htonl(sctp_association_get_next_tsn(chunk->asoc));
chunk->has_tsn = 1;
}
}
/* Create a CLOSED association to use with an incoming packet. */ struct sctp_association *sctp_make_temp_asoc(conststruct sctp_endpoint *ep, struct sctp_chunk *chunk,
gfp_t gfp)
{ struct sctp_association *asoc; enum sctp_scope scope; struct sk_buff *skb;
/* Create the bare association. */
scope = sctp_scope(sctp_source(chunk));
asoc = sctp_association_new(ep, ep->base.sk, scope, gfp); if (!asoc) goto nodata;
asoc->temp = 1;
skb = chunk->skb; /* Create an entry for the source address of the packet. */
SCTP_INPUT_CB(skb)->af->from_skb(&asoc->c.peer_addr, skb, 1);
nodata: return asoc;
}
/* Build a cookie representing asoc. *ThisINCLUDEStheparamheaderneededtoputthecookieintheINITACK.
*/ staticstruct sctp_cookie_param *sctp_pack_cookie( conststruct sctp_endpoint *ep, conststruct sctp_association *asoc, conststruct sctp_chunk *init_chunk, int *cookie_len, const __u8 *raw_addrs, int addrs_len)
{ struct sctp_signed_cookie *cookie; struct sctp_cookie_param *retval; int headersize, bodysize;
/* Header size is static data prior to the actual cookie, including *anypadding.
*/
headersize = sizeof(struct sctp_paramhdr) +
(sizeof(struct sctp_signed_cookie) - sizeof(struct sctp_cookie));
bodysize = sizeof(struct sctp_cookie)
+ ntohs(init_chunk->chunk_hdr->length) + addrs_len;
/* Pad out the cookie to a multiple to make the signature *functionssimplertowrite.
*/ if (bodysize % SCTP_COOKIE_MULTIPLE)
bodysize += SCTP_COOKIE_MULTIPLE
- (bodysize % SCTP_COOKIE_MULTIPLE);
*cookie_len = headersize + bodysize;
/* Clear this memory since we are sending this data structure *outonthenetwork.
*/
retval = kzalloc(*cookie_len, GFP_ATOMIC); if (!retval) goto nodata;
/* Set up the parameter header. */
retval->p.type = SCTP_PARAM_STATE_COOKIE;
retval->p.length = htons(*cookie_len);
/* Copy the cookie part of the association itself. */
cookie->c = asoc->c; /* Save the raw address list length in the cookie. */
cookie->c.raw_addr_list_len = addrs_len;
/* Header size is static data prior to the actual cookie, including *anypadding.
*/
headersize = sizeof(struct sctp_chunkhdr) +
(sizeof(struct sctp_signed_cookie) - sizeof(struct sctp_cookie));
bodysize = ntohs(chunk->chunk_hdr->length) - headersize;
fixed_size = headersize + sizeof(struct sctp_cookie);
/* Verify that the chunk looks like it even has a cookie. *Theremustbeenoughroomforourcookieandourpeer's *INITchunk.
*/
len = ntohs(chunk->chunk_hdr->length); if (len < fixed_size + sizeof(struct sctp_chunkhdr)) goto malformed;
/* Verify that the cookie has been padded out. */ if (bodysize % SCTP_COOKIE_MULTIPLE) goto malformed;
/* Process the cookie. */
cookie = chunk->subh.cookie_hdr;
bear_cookie = &cookie->c;
if (!sctp_sk(ep->base.sk)->hmac) goto no_hmac;
/* Check the signature. */
{ struct crypto_shash *tfm = sctp_sk(ep->base.sk)->hmac; int err;
/* Check to see if the cookie is stale. If there is already *anassociation,thereisnoneedtocheckcookie'sexpiration *forinitcollisioncaseoflostCOOKIEACK. *Ifskbhasbeentimestamped,thenusethestamp,otherwise *usecurrenttime.Thisintroducesasmallpossibilitythat *acookiemaybeconsideredexpired,butthiswouldonlyslow *downthenewassociationestablishmentinsteadofeverypacket.
*/ if (sock_flag(ep->base.sk, SOCK_TIMESTAMP))
kt = skb_get_ktime(skb); else
kt = ktime_get_real();
if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
suseconds_t usecs = ktime_to_us(ktime_sub(kt, bear_cookie->expiration));
__be32 n = htonl(usecs);
/* Make a new base association. */
scope = sctp_scope(sctp_source(chunk));
retval = sctp_association_new(ep, ep->base.sk, scope, gfp); if (!retval) {
*error = -SCTP_IERROR_NOMEM; goto fail;
}
/* Set up our peer's port number. */
retval->peer.port = ntohs(chunk->sctp_hdr->source);
/* Populate the association from the cookie. */
memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie));
/* Make an ERROR chunk, preparing enough room for *returningmultipleunknownparameters.
*/ if (!*errp)
*errp = sctp_make_op_error_space(asoc, chunk, len);
/* Report an Invalid Mandatory Parameter. */ staticint sctp_process_inv_mandatory(conststruct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_chunk **errp)
{ /* Invalid Mandatory Parameter Error has no payload. */
if (!*errp)
*errp = sctp_make_op_error_space(asoc, chunk, 0);
if (*errp)
sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, 0);
/* Stop processing this chunk. */ return0;
}
staticint sctp_process_inv_paramlength(conststruct sctp_association *asoc, struct sctp_paramhdr *param, conststruct sctp_chunk *chunk, struct sctp_chunk **errp)
{ /* This is a fatal error. Any accumulated non-fatal errors are *notreported.
*/ if (*errp)
sctp_chunk_free(*errp);
/* Create an error chunk and fill it in with our payload. */
*errp = sctp_make_violation_paramlen(asoc, chunk, param);
return0;
}
/* Do not attempt to handle the HOST_NAME parm. However, do *sendbackanindicatortothepeer.
*/ staticint sctp_process_hn_param(conststruct sctp_association *asoc, union sctp_params param, struct sctp_chunk *chunk, struct sctp_chunk **errp)
{
__u16 len = ntohs(param.p->length);
/* Processing of the HOST_NAME parameter will generate an *ABORT.Ifwe'veaccumulatedanynon-fatalerrors,they *wouldbeunrecognizedparametersandweshouldnotinclude *themintheABORT.
*/ if (*errp)
sctp_chunk_free(*errp);
staticint sctp_verify_ext_param(struct net *net, conststruct sctp_endpoint *ep, union sctp_params param)
{
__u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); int have_asconf = 0; int have_auth = 0; int i;
for (i = 0; i < num_ext; i++) { switch (param.ext->chunks[i]) { case SCTP_CID_AUTH:
have_auth = 1; break; case SCTP_CID_ASCONF: case SCTP_CID_ASCONF_ACK:
have_asconf = 1; break;
}
}
/* ADD-IP Security: The draft requires us to ABORT or ignore the *INIT/INIT-ACKifADD-IPislisted,butAUTHisnot.Dothis *onlyifADD-IPisturnedonandwearenotbackward-compatible *mode.
*/ if (net->sctp.addip_noauth) return1;
if (ep->asconf_enable && !have_auth && have_asconf) return0;
return1;
}
staticvoid sctp_process_ext_param(struct sctp_association *asoc, union sctp_params param)
{
__u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); int i;
for (i = 0; i < num_ext; i++) { switch (param.ext->chunks[i]) { case SCTP_CID_RECONF: if (asoc->ep->reconf_enable)
asoc->peer.reconf_capable = 1; break; case SCTP_CID_FWD_TSN: if (asoc->ep->prsctp_enable)
asoc->peer.prsctp_capable = 1; break; case SCTP_CID_AUTH: /* if the peer reports AUTH, assume that he *supportsAUTH.
*/ if (asoc->ep->auth_enable)
asoc->peer.auth_capable = 1; break; case SCTP_CID_ASCONF: case SCTP_CID_ASCONF_ACK: if (asoc->ep->asconf_enable)
asoc->peer.asconf_capable = 1; break; case SCTP_CID_I_DATA: if (asoc->ep->intl_enable)
asoc->peer.intl_capable = 1; break; default: break;
}
}
}
switch (param.p->type & SCTP_PARAM_ACTION_MASK) { case SCTP_PARAM_ACTION_DISCARD:
retval = SCTP_IERROR_ERROR; break; case SCTP_PARAM_ACTION_SKIP: break; case SCTP_PARAM_ACTION_DISCARD_ERR:
retval = SCTP_IERROR_ERROR;
fallthrough; case SCTP_PARAM_ACTION_SKIP_ERR: /* Make an ERROR chunk, preparing enough room for *returningmultipleunknownparameters.
*/ if (!*errp) {
*errp = sctp_make_op_error_limited(asoc, chunk); if (!*errp) { /* If there is no memory for generating the *ERRORreportasspecified,anABORTwillbe *triggeredtothepeerandtheassociation *won'tbeestablished.
*/
retval = SCTP_IERROR_NOMEM; break;
}
}
/* Verify variable length parameters *Returnvalues: *SCTP_IERROR_ABORT-triggeranABORT *SCTP_IERROR_NOMEM-outofmemory(abort) *SCTP_IERROR_ERROR-stopprocessing,triggeranERROR *SCTP_IERROR_NO_ERROR-continuewiththechunk
*/ staticenum sctp_ierror sctp_verify_param(struct net *net, conststruct sctp_endpoint *ep, conststruct sctp_association *asoc, union sctp_params param, enum sctp_cid cid, struct sctp_chunk *chunk, struct sctp_chunk **err_chunk)
{ struct sctp_hmac_algo_param *hmacs; int retval = SCTP_IERROR_NO_ERROR;
__u16 n_elt, id = 0; int i;
/* FIXME - This routine is not looking at each parameter per the *chunktype,i.e.,unrecognizedparametersshouldbefurther *identifiedbasedonthechunkid.
*/
switch (param.p->type) { case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV6_ADDRESS: case SCTP_PARAM_COOKIE_PRESERVATIVE: case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: case SCTP_PARAM_STATE_COOKIE: case SCTP_PARAM_HEARTBEAT_INFO: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_ECN_CAPABLE: case SCTP_PARAM_ADAPTATION_LAYER_IND: break;
case SCTP_PARAM_SUPPORTED_EXT: if (!sctp_verify_ext_param(net, ep, param)) return SCTP_IERROR_ABORT; break;
case SCTP_PARAM_SET_PRIMARY: if (!ep->asconf_enable) goto unhandled;
case SCTP_PARAM_HOST_NAME_ADDRESS: /* This param has been Deprecated, send ABORT. */
sctp_process_hn_param(asoc, param, chunk, err_chunk);
retval = SCTP_IERROR_ABORT; break;
case SCTP_PARAM_FWD_TSN_SUPPORT: if (ep->prsctp_enable) break; goto unhandled;
case SCTP_PARAM_RANDOM: if (!ep->auth_enable) goto unhandled;
/* SCTP-AUTH: Section 6.1 *TheHMACalgorithmbasedonSHA-1MUSTbesupportedand *includedintheHMAC-ALGOparameter.
*/ for (i = 0; i < n_elt; i++) {
id = ntohs(hmacs->hmac_ids[i]);
if (id == SCTP_AUTH_HMAC_ID_SHA1) break;
}
if (id != SCTP_AUTH_HMAC_ID_SHA1) {
sctp_process_inv_paramlength(asoc, param.p, chunk,
err_chunk);
retval = SCTP_IERROR_ABORT;
} break;
unhandled: default:
pr_debug("%s: unrecognized param:%d for chunk:%d\n",
__func__, ntohs(param.p->type), cid);
/* Verify the INIT packet before we process it. */ int sctp_verify_init(struct net *net, conststruct sctp_endpoint *ep, conststruct sctp_association *asoc, enum sctp_cid cid, struct sctp_init_chunk *peer_init, struct sctp_chunk *chunk, struct sctp_chunk **errp)
{ union sctp_params param; bool has_cookie = false; int result;
/* There is a possibility that a parameter length was bad and *inthatcasewewouldhavestopedwalkingtheparameters. *Thecurrentparam.pwouldpointatthebadone. *CurrentconsensusonthemailinglististogenerateaPROTOCOL *VIOLATIONerror.WebuildtheERRORchunkhereandletthenormal *errorhandlingcodebuildandsendthepacket.
*/ if (param.v != (void *)chunk->chunk_end) return sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
/* The only missing mandatory param possible today is *thestatecookieforanINIT-ACKchunk.
*/ if ((SCTP_CID_INIT_ACK == cid) && !has_cookie) return sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
chunk, errp);
/* Verify all the variable length parameters */
sctp_walk_params(param, peer_init) {
result = sctp_verify_param(net, ep, asoc, param, cid,
chunk, errp); switch (result) { case SCTP_IERROR_ABORT: case SCTP_IERROR_NOMEM: return0; case SCTP_IERROR_ERROR: return1; case SCTP_IERROR_NO_ERROR: default: break;
}
} /* for (loop through all parameters) */
return1;
}
/* Unpack the parameters in an INIT packet into an association. *Returns0onfailure,elsesuccess. *FIXME:Thisisanassociationmethod.
*/ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, constunion sctp_addr *peer_addr, struct sctp_init_chunk *peer_init, gfp_t gfp)
{ struct sctp_transport *transport; struct list_head *pos, *temp; union sctp_params param; union sctp_addr addr; struct sctp_af *af; int src_match = 0;
/* We must include the address that the INIT packet came from. *ThisistheonlyaddressthatmattersforanINITpacket. *WhenprocessingaCOOKIEECHO,weretrievethefromaddress *oftheINITfromthecookie.
*/
/* This implementation defaults to making the first transport *addedastheprimarytransport.Thesourceaddressseemsto *beabetterchoicethananyoftheembeddedaddresses.
*/
asoc->encap_port = SCTP_INPUT_CB(chunk->skb)->encap_port; if (!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) goto nomem;
if (sctp_cmp_addr_exact(sctp_source(chunk), peer_addr))
src_match = 1;
/* Process the initialization parameters. */
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)); 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;
}
if (!sctp_process_param(asoc, param, peer_addr, gfp)) goto clean_up;
}
/* source address of chunk may not match any valid address */ if (!src_match) goto clean_up;
/* AUTH: After processing the parameters, make sure that we *havealltherequiredinfotopotentiallydoauthentications.
*/ if (asoc->peer.auth_capable && (!asoc->peer.peer_random ||
!asoc->peer.peer_hmacs))
asoc->peer.auth_capable = 0;
/* In a non-backward compatible mode, if the peer claims *supportforADD-IPbutnotAUTH,theADD-IPspecstates *thatweMUSTABORTtheassociation.Section6.Thesection *alsogiveusanoptiontosilentlyignorethepacket,which *iswhatwe'lldohere.
*/ 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);
asoc->peer.asconf_capable = 0; goto clean_up;
}
/* Walk list of transports, removing transports in the UNKNOWN state. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports); if (transport->state == SCTP_UNKNOWN) {
sctp_assoc_rm_peer(asoc, transport);
}
}
/* The fixed INIT headers are always in network byte *order.
*/
asoc->peer.i.init_tag =
ntohl(peer_init->init_hdr.init_tag);
asoc->peer.i.a_rwnd =
ntohl(peer_init->init_hdr.a_rwnd);
asoc->peer.i.num_outbound_streams =
ntohs(peer_init->init_hdr.num_outbound_streams);
asoc->peer.i.num_inbound_streams =
ntohs(peer_init->init_hdr.num_inbound_streams);
asoc->peer.i.initial_tsn =
ntohl(peer_init->init_hdr.initial_tsn);
asoc->strreset_inseq = asoc->peer.i.initial_tsn;
/* Apply the upper bounds for output streams based on peer's *numberofinboundstreams.
*/ if (asoc->c.sinit_num_ostreams >
ntohs(peer_init->init_hdr.num_inbound_streams)) {
asoc->c.sinit_num_ostreams =
ntohs(peer_init->init_hdr.num_inbound_streams);
}
if (asoc->c.sinit_max_instreams >
ntohs(peer_init->init_hdr.num_outbound_streams)) {
asoc->c.sinit_max_instreams =
ntohs(peer_init->init_hdr.num_outbound_streams);
}
/* Copy Initiation tag from INIT to VT_peer in cookie. */
asoc->c.peer_vtag = asoc->peer.i.init_tag;
/* Peer Rwnd : Current calculated value of the peer's rwnd. */
asoc->peer.rwnd = asoc->peer.i.a_rwnd;
/* RFC 2960 7.2.1 The initial value of ssthresh MAY be arbitrarily *high(forexample,implementationsMAYusethesizeofthereceiver *advertisedwindow).
*/
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
transport->ssthresh = asoc->peer.i.a_rwnd;
}
/* Set up the TSN tracking pieces. */ if (!sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,
asoc->peer.i.initial_tsn, gfp)) goto clean_up;
/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number * *Thestreamsequencenumberinallthestreamsshallstart *from0whentheassociationisestablished.Also,whenthe *streamsequencenumberreachesthevalue65535thenext *streamsequencenumbershallbesetto0.
*/
if (sctp_stream_init(&asoc->stream, asoc->c.sinit_num_ostreams,
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;
clean_up: /* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports); if (transport->state != SCTP_ACTIVE)
sctp_assoc_rm_peer(asoc, transport);
}
nomem: return0;
}
/* Update asoc with the option described in param. * *RFC29603.3.2.1Optional/VariableLengthParametersinINIT * *asocistheassociationtoupdate. *paramisthevariablelengthparametertouseforupdate. *cidtellsusifthisisanINIT,INITACKorCOOKIEECHO. *IfthecurrentpacketisanINITwewanttominimizetheamountof *workwedo.Inparticular,weshouldnotbuildtransport *structuresfortheaddresses.
*/ staticint sctp_process_param(struct sctp_association *asoc, union sctp_params param, constunion sctp_addr *peer_addr,
gfp_t gfp)
{ struct sctp_endpoint *ep = asoc->ep; union sctp_addr_param *addr_param; struct net *net = asoc->base.net; struct sctp_transport *t; 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.Thisallowsustonotworryaboutwhethertheparameters *camefromafreshINIT,andINITACK,orwerestoredinacookie.
*/ switch (param.p->type) { case SCTP_PARAM_IPV6_ADDRESS: if (PF_INET6 != asoc->base.sk->sk_family) break; goto do_addr_param;
case SCTP_PARAM_IPV4_ADDRESS: /* v4 addresses are not allowed on v6-only socket */ if (ipv6_only_sock(asoc->base.sk)) break;
do_addr_param:
af = sctp_get_af_specific(param_type2af(param.p->type)); 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)) return0; break;
case SCTP_PARAM_COOKIE_PRESERVATIVE: if (!net->sctp.cookie_preserve_enable) break;
stale = ntohl(param.life->lifespan_increment);
/* Suggested Cookie Life span increment's unit is msec, *(1/1000sec).
*/
asoc->cookie_life = ktime_add_ms(asoc->cookie_life, stale); break;
case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: /* Turn off the default values first so we'll know which *onesarereallysetbythepeer.
*/
asoc->peer.ipv4_address = 0;
asoc->peer.ipv6_address = 0;
/* Assume that peer supports the address family *bywhichitsendsapacket.
*/ if (peer_addr->sa.sa_family == AF_INET6)
asoc->peer.ipv6_address = 1; elseif (peer_addr->sa.sa_family == AF_INET)
asoc->peer.ipv4_address = 1;
/* Cycle through address types; avoid divide by 0. */
sat = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); if (sat)
sat /= sizeof(__u16);
for (i = 0; i < sat; ++i) { switch (param.sat->types[i]) { case SCTP_PARAM_IPV4_ADDRESS:
asoc->peer.ipv4_address = 1; break;
case SCTP_PARAM_IPV6_ADDRESS: if (PF_INET6 == asoc->base.sk->sk_family)
asoc->peer.ipv6_address = 1; break;
af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (!af) break;
if (!af->from_addr_param(&addr, addr_param,
htons(asoc->peer.port), 0)) break;
if (!af->addr_valid(&addr, NULL, NULL)) break;
t = sctp_assoc_lookup_paddr(asoc, &addr); if (!t) break;
sctp_assoc_set_primary(asoc, t); break;
case SCTP_PARAM_SUPPORTED_EXT:
sctp_process_ext_param(asoc, param); break;
case SCTP_PARAM_FWD_TSN_SUPPORT: if (asoc->ep->prsctp_enable) {
asoc->peer.prsctp_capable = 1; break;
} /* Fall Through */ goto fall_through;
case SCTP_PARAM_RANDOM: if (!ep->auth_enable) 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) {
retval = 0; break;
} break;
case SCTP_PARAM_HMAC_ALGO: if (!ep->auth_enable) 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) {
retval = 0; break;
}
/* Set the default HMAC the peer requested*/
sctp_auth_asoc_set_default_hmac(asoc, param.hmac_algo); break;
case SCTP_PARAM_CHUNKS: 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)
retval = 0; break;
fall_through: default: /* Any unrecognized parameters should have been caught *andhandledbysctp_verify_param()whichshouldbe *calledpriortothisroutine.Simplylogtheerror *here.
*/
pr_debug("%s: ignoring param:%d for association:%p.\n",
__func__, ntohs(param.p->type), asoc); break;
}
return retval;
}
/* Select a new verification tag. */
__u32 sctp_generate_tag(conststruct sctp_endpoint *ep)
{ /* I believe that this random number generator complies with RFC1750. *Atagof0isreservedforspecialcases(e.g.INIT).
*/
__u32 x;
do {
get_random_bytes(&x, sizeof(__u32));
} while (x == 0);
return x;
}
/* Select an initial TSN to send during startup. */
__u32 sctp_generate_tsn(conststruct sctp_endpoint *ep)
{
__u32 retval;
/* ADDIP *3.2.1AddIPAddress *0123 *01234567890123456789012345678901 *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *|Type=0xC001|Length=Variable| *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *|ASCONF-RequestCorrelationID| *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *|AddressParameter| *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * *3.2.2DeleteIPAddress *0123 *01234567890123456789012345678901 *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *|Type=0xC002|Length=Variable| *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *|ASCONF-RequestCorrelationID| *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *|AddressParameter| *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *
*/ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, 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; for (i = 0; i < addrcnt; i++) {
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
addr_param_len = af->to_addr_param(addr, &addr_param);
totallen += paramlen;
totallen += addr_param_len;
addr_buf += af->sockaddr_len; if (asoc->asconf_addr_del_pending && !del_pickup) { /* reuse the parameter length from the same scope one */
totallen += paramlen;
totallen += addr_param_len;
del_pickup = 1;
pr_debug("%s: picked same-scope del_pending addr, " "totallen for all addresses is %d\n",
__func__, totallen);
}
}
/* Create an asconf chunk with the required length. */
retval = sctp_make_asconf(asoc, laddr, totallen); if (!retval) return NULL;
/* Add the address parameters to the asconf chunk. */
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
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 = htonl(i);
switch (addr_param->p.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) return SCTP_ERROR_DNS_FAILED; break; case SCTP_PARAM_IPV4_ADDRESS: if (!asoc->peer.ipv4_address) return SCTP_ERROR_DNS_FAILED; break; default: return SCTP_ERROR_DNS_FAILED;
}
af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (unlikely(!af)) return SCTP_ERROR_DNS_FAILED;
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 *ormulticastaddress. *(note:wildcardispermittedandrequiresspecialhandlingso *makesurewecheckforthat)
*/ if (!af->is_any(&addr) && !af->addr_valid(&addr, NULL, asconf->skb)) return SCTP_ERROR_DNS_FAILED;
switch (asconf_param->param_hdr.type) { case SCTP_PARAM_ADD_IP: /* Section 4.2.1: *Iftheaddress0.0.0.0or::0isprovided,thesource *addressofthepacketMUSTbeadded.
*/ 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 *requestanddoesnothavethelocalresourcestoaddthis *newaddresstotheassociation,itMUSTreturnanError *CauseTLVsettothenewerrorcode'OperationRefused *DuetoResourceShortage'.
*/
peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_UNCONFIRMED); if (!peer) return SCTP_ERROR_RSRC_LOW;
/* Start the heartbeat timer. */
sctp_transport_reset_hb_timer(peer);
asoc->new_transport = peer; break; case SCTP_PARAM_DEL_IP: /* ADDIP 4.3 D7) If a request is received to delete the *lastremainingIPaddressofapeerendpoint,thereceiver *MUSTsendanErrorCauseTLVwiththeerrorcausesettothe *newerrorcode'RequesttoDeleteLastRemainingIPAddress'.
*/ if (asoc->peer.transport_count == 1) return SCTP_ERROR_DEL_LAST_IP;
/* ADDIP 4.3 D8) If a request is received to delete an IP *addresswhichisalsothesourceaddressoftheIPpacket *whichcontainedtheASCONFchunk,thereceiverMUSTreject *thisrequest.TorejecttherequestthereceiverMUSTsend *anErrorCauseTLVsettothenewerrorcode'Requestto *DeleteSourceIPAddress'
*/ if (sctp_cmp_addr_exact(&asconf->source, &addr)) return SCTP_ERROR_DEL_SRC_IP;
/* If the address is not part of the association, the *ASCONF-ACKwithErrorCauseIndicationParameter *whichincludingcauseofUnresolvableAddressshould *besent.
*/
peer = sctp_assoc_lookup_paddr(asoc, &addr); if (!peer) return SCTP_ERROR_DNS_FAILED;
sctp_assoc_rm_peer(asoc, peer); break; case SCTP_PARAM_SET_PRIMARY: /* ADDIP Section 4.2.4 *Iftheaddress0.0.0.0or::0isprovided,thereceiver *MAYmarkthesourceaddressofthepacketasits *primary.
*/ if (af->is_any(&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) return SCTP_ERROR_DNS_FAILED;
sctp_assoc_set_primary(asoc, peer); break;
}
return SCTP_ERROR_NO_ERROR;
}
/* Verify the ASCONF packet before we process it. */ bool sctp_verify_asconf(conststruct sctp_association *asoc, struct sctp_chunk *chunk, bool addr_param_needed, struct sctp_paramhdr **errp)
{ struct sctp_addip_chunk *addip; bool addr_param_seen = false; union sctp_params param;
*errp = param.p; switch (param.p->type) { case SCTP_PARAM_ERR_CAUSE: break; case SCTP_PARAM_IPV4_ADDRESS: if (length != sizeof(struct sctp_ipv4addr_param)) returnfalse; /* ensure there is only one addr param and it's in the *beginningofaddip_hdrparams,orwerejectit.
*/ if (param.v != (addip + 1)) returnfalse;
addr_param_seen = true; break; case SCTP_PARAM_IPV6_ADDRESS: if (length != sizeof(struct sctp_ipv6addr_param)) returnfalse; if (param.v != (addip + 1)) returnfalse;
addr_param_seen = true; break; case SCTP_PARAM_ADD_IP: case SCTP_PARAM_DEL_IP: case SCTP_PARAM_SET_PRIMARY: /* In ASCONF chunks, these need to be first. */ if (addr_param_needed && !addr_param_seen) returnfalse;
length = ntohs(param.addip->param_hdr.length); if (length < sizeof(struct sctp_addip_param) + sizeof(**errp)) returnfalse; break; case SCTP_PARAM_SUCCESS_REPORT: case SCTP_PARAM_ADAPTATION_LAYER_IND: if (length != sizeof(struct sctp_addip_param)) returnfalse; break; default: /* This is unknown to us, reject! */ returnfalse;
}
}
/* Remaining sanity checks. */ if (addr_param_needed && !addr_param_seen) returnfalse; if (!addr_param_needed && addr_param_seen) returnfalse; if (param.v != chunk->chunk_end) returnfalse;
returntrue;
}
/* Process an incoming ASCONF chunk with the next expected serial no. and *returnanASCONF_ACKchunktobesentinresponse.
*/ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *asconf)
{ 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;
__be16 err_code;
__u32 serial;
/* Skip the addiphdr and store a pointer to address parameter. */
length = sizeof(*hdr);
addr_param = (union sctp_addr_param *)(asconf->skb->data + length);
chunk_len -= length;
/* Skip the address parameter and store a pointer to the first *asconfparameter.
*/
length = ntohs(addr_param->p.length);
chunk_len -= length;
/* create an ASCONF_ACK chunk. *Basedonthedefinitionsofparameters,weknowthatthesizeof *ASCONF_ACKparametersarelessthanorequaltothefourfoldofASCONF *parameters.
*/
asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 4); if (!asconf_ack) goto done;
/* Process the TLVs contained within the ASCONF chunk. */
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;
/* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add *anIPaddresssendsan'OutofResource'initsresponse,it *MUSTalsofailanysubsequentaddordeleterequestsbundled *intheASCONF.
*/ if (err_code == SCTP_ERROR_RSRC_LOW) goto done;
}
done:
asoc->peer.addip_serial++;
/* If we are sending a new ASCONF_ACK hold a reference to it in assoc *afterfreeingthereferencetooldasconfackifany.
*/ if (asconf_ack) {
sctp_chunk_hold(asconf_ack);
list_add_tail(&asconf_ack->transmitted_list,
&asoc->asconf_ack_list);
}
return asconf_ack;
}
/* Process a asconf parameter that is successfully acked. */ staticvoid sctp_asconf_param_success(struct sctp_association *asoc, struct sctp_addip_param *asconf_param)
{ struct sctp_bind_addr *bp = &asoc->base.bind_addr; union sctp_addr_param *addr_param; struct sctp_sockaddr_entry *saddr; struct sctp_transport *transport; union sctp_addr addr; struct sctp_af *af;
/* We have checked the packet before, so we do not check again. */
af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (!af->from_addr_param(&addr, addr_param, htons(bp->port), 0)) return;
switch (asconf_param->param_hdr.type) { case SCTP_PARAM_ADD_IP: /* This is always done in BH context with a socket lock *held,sothelistcannotchange.
*/
local_bh_disable();
list_for_each_entry(saddr, &bp->address_list, list) { if (sctp_cmp_addr_exact(&saddr->a, &addr))
saddr->state = SCTP_ADDR_SRC;
}
local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
sctp_transport_dst_release(transport);
} break; case SCTP_PARAM_DEL_IP:
local_bh_disable();
sctp_del_bind_addr(bp, &addr); if (asoc->asconf_addr_del_pending != NULL &&
sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
kfree(asoc->asconf_addr_del_pending);
asoc->asconf_addr_del_pending = NULL;
}
local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
sctp_transport_dst_release(transport);
} break; default: break;
}
}
/* Get the corresponding ASCONF response error code from the ASCONF_ACK chunk *forthegivenasconfparameter.Ifthereisnoresponseforthisparameter, *returntheerrorcodebasedonthethirdargument'no_err'. *ADDIP4.1 *A7)IfanerrorresponseisreceivedforaTLVparameter,allTLVswithno *responsebeforethefailedTLVareconsideredsuccessfulifnotreported. *AllTLVsafterthefailedresponseareconsideredunsuccessfulunlessa *specificsuccessindicationispresentfortheparameter.
*/ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, struct sctp_addip_param *asconf_param, int no_err)
{ 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; else
err_code = SCTP_ERROR_REQ_REFUSED;
/* Skip the addiphdr from the asconf_ack chunk and store a pointer to *thefirstasconf_ackparameter.
*/
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) { if (asconf_ack_param->crr_id == asconf_param->crr_id) { switch (asconf_ack_param->param_hdr.type) { case SCTP_PARAM_SUCCESS_REPORT: return SCTP_ERROR_NO_ERROR; case SCTP_PARAM_ERR_CAUSE:
length = sizeof(*asconf_ack_param);
err_param = (void *)asconf_ack_param + length;
asconf_ack_len -= length; if (asconf_ack_len > 0) return err_param->cause; else return SCTP_ERROR_INV_PARAM; break; default: return SCTP_ERROR_INV_PARAM;
}
}
/* Process an incoming ASCONF_ACK chunk against the cached last ASCONF chunk. */ int sctp_process_asconf_ack(struct sctp_association *asoc, struct sctp_chunk *asconf_ack)
{ 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 *apointertoaddressparameter.
*/
length = sizeof(struct sctp_addip_chunk);
addr_param = (union sctp_addr_param *)(asconf->skb->data + length);
asconf_len -= length;
/* Skip the address parameter in the last asconf sent and store a *pointertothefirstasconfparameter.
*/
length = ntohs(addr_param->p.length);
asconf_param = (void *)addr_param + length;
asconf_len -= length;
/* Process the TLVs contained in the last sent ASCONF chunk. */ while (asconf_len > 0) { if (all_param_pass)
err_code = SCTP_ERROR_NO_ERROR; else {
err_code = sctp_get_asconf_response(asconf_ack,
asconf_param,
no_err); if (no_err && (SCTP_ERROR_NO_ERROR != err_code))
no_err = 0;
}
switch (err_code) { case SCTP_ERROR_NO_ERROR:
sctp_asconf_param_success(asoc, asconf_param); break;
case SCTP_ERROR_RSRC_LOW:
retval = 1; break;
case SCTP_ERROR_UNKNOWN_PARAM: /* Disable sending this type of asconf parameter in *future.
*/
asoc->peer.addip_disabled_mask |=
asconf_param->param_hdr.type; break;
case SCTP_ERROR_REQ_REFUSED: case SCTP_ERROR_DEL_LAST_IP: case SCTP_ERROR_DEL_SRC_IP: default: break;
}
/* Skip the processed asconf parameter and move to the next *one.
*/
length = ntohs(asconf_param->param_hdr.length);
asconf_param = (void *)asconf_param + length;
asconf_len -= length;
}
if (no_err && asoc->src_out_of_asoc_ok) {
asoc->src_out_of_asoc_ok = 0;
sctp_transport_immediate_rtx(asoc->peer.primary_path);
}
/* Free the cached last sent asconf chunk. */
list_del_init(&asconf->transmitted_list);
sctp_chunk_free(asconf);
asoc->addip_last_asconf = NULL;
*errp = param.p; if (cnt++ > 2) returnfalse; switch (param.p->type) { case SCTP_PARAM_RESET_OUT_REQUEST: if (length < sizeof(struct sctp_strreset_outreq) ||
(last && last != SCTP_PARAM_RESET_RESPONSE &&
last != SCTP_PARAM_RESET_IN_REQUEST)) returnfalse; break; case SCTP_PARAM_RESET_IN_REQUEST: if (length < sizeof(struct sctp_strreset_inreq) ||
(last && last != SCTP_PARAM_RESET_OUT_REQUEST)) returnfalse; break; case SCTP_PARAM_RESET_RESPONSE: if ((length != sizeof(struct sctp_strreset_resp) &&
length != sizeof(struct sctp_strreset_resptsn)) ||
(last && last != SCTP_PARAM_RESET_RESPONSE &&
last != SCTP_PARAM_RESET_OUT_REQUEST)) returnfalse; break; case SCTP_PARAM_RESET_TSN_REQUEST: if (length != sizeof(struct sctp_strreset_tsnreq) || last) returnfalse; break; case SCTP_PARAM_RESET_ADD_IN_STREAMS: if (length != sizeof(struct sctp_strreset_addstrm) ||
(last && last != SCTP_PARAM_RESET_ADD_OUT_STREAMS)) returnfalse; break; case SCTP_PARAM_RESET_ADD_OUT_STREAMS: if (length != sizeof(struct sctp_strreset_addstrm) ||
(last && last != SCTP_PARAM_RESET_ADD_IN_STREAMS)) returnfalse; break; default: returnfalse;
}
last = param.p->type;
}
returntrue;
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.140Bemerkung:
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.