From 1967937f84af220e81165d05a32f63f358e043d6 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Tue, 28 Oct 2003 00:12:52 -0800 Subject: [SCTP] ADDIP: Support for processing ASCONF chunks and respond with ASCONF_ACK chunks. --- include/net/sctp/constants.h | 4 +--- include/net/sctp/sm.h | 10 ++++------ include/net/sctp/structs.h | 2 ++ 3 files changed, 7 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 5e17983fffdc..f031313bb3ef 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -1,8 +1,8 @@ /* SCTP kernel reference Implementation + * (C) Copyright IBM Corp. 2001, 2003 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001 Intel Corp. - * Copyright (c) 2001-2002 International Business Machines Corp. * * This file is part of the SCTP kernel reference Implementation * @@ -75,8 +75,6 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; #define SCTP_NUM_BASE_CHUNK_TYPES (SCTP_CID_BASE_MAX + 1) #define SCTP_NUM_CHUNK_TYPES (SCTP_NUM_BASE_CHUNKTYPES + 2) -#define SCTP_CID_ADDIP_MIN SCTP_CID_ASCONF -#define SCTP_CID_ADDIP_MAX SCTP_CID_ASCONF_ACK #define SCTP_NUM_ADDIP_CHUNK_TYPES 2 /* These are the different flavours of event. */ diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2b62b1afef2a..c04ece969d46 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -268,15 +268,13 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, union sctp_addr *, struct sockaddr *, - int, int); + int, __u16); struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr); -struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, - int serial, int vparam_len); - +struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, + __u32 serial, int vparam_len); struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, - struct sctp_chunk *asconf, - int vparam_len); + struct sctp_chunk *asconf); void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index bf2745654d7a..21294a568225 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1708,6 +1708,8 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc, struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *, const union sctp_addr *address, const int gfp); +void sctp_assoc_del_peer(struct sctp_association *asoc, + const union sctp_addr *addr); void sctp_assoc_control_transport(struct sctp_association *, struct sctp_transport *, sctp_transport_cmd_t, sctp_sn_error_t); -- cgit v1.2.3 From 592e6a4dd7001ab0fd3b1641b827a49f52eb1aab Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Tue, 28 Oct 2003 01:19:26 -0800 Subject: [SCTP] ADDIP: Add sysctl parameter to enable/disable addip. --- include/linux/sysctl.h | 1 + include/net/sctp/structs.h | 4 ++++ net/sctp/protocol.c | 3 +++ net/sctp/sm_statetable.c | 10 ++++++---- net/sctp/socket.c | 6 ++++++ net/sctp/sysctl.c | 8 ++++++++ 6 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index d42981d6f954..ac6171d45c7f 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -576,6 +576,7 @@ enum { NET_SCTP_HB_INTERVAL = 10, NET_SCTP_PRESERVE_ENABLE = 11, NET_SCTP_MAX_BURST = 12, + NET_SCTP_ADDIP_ENABLE = 13, }; /* CTL_PROC names: */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 21294a568225..a2211d1692e9 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -190,6 +190,9 @@ extern struct sctp_globals { */ struct list_head local_addr_list; spinlock_t local_addr_lock; + + /* Flag to indicate if addip is enabled. */ + int addip_enable; } sctp_globals; #define sctp_rto_initial (sctp_globals.rto_initial) @@ -217,6 +220,7 @@ extern struct sctp_globals { #define sctp_port_hashtable (sctp_globals.port_hashtable) #define sctp_local_addr_list (sctp_globals.local_addr_list) #define sctp_local_addr_lock (sctp_globals.local_addr_lock) +#define sctp_addip_enable (sctp_globals.addip_enable) /* SCTP Socket type: UDP or TCP style. */ typedef enum { diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 986460afaa6b..21f862329e1e 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1115,6 +1115,9 @@ __init int sctp_init(void) "(established %d bind %d)\n", sctp_assoc_hashsize, sctp_port_hashsize); + /* Disable ADDIP by default. */ + sctp_addip_enable = 0; + sctp_sysctl_register(); INIT_LIST_HEAD(&sctp_address_families); diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index f30307040155..224b8d2fe888 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -924,11 +924,13 @@ const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) return &chunk_event_table[cid][state]; - if (cid == SCTP_CID_ASCONF) - return &addip_chunk_event_table[0][state]; + if (sctp_addip_enable) { + if (cid == SCTP_CID_ASCONF) + return &addip_chunk_event_table[0][state]; - if (cid == SCTP_CID_ASCONF_ACK) - return &addip_chunk_event_table[1][state]; + if (cid == SCTP_CID_ASCONF_ACK) + return &addip_chunk_event_table[1][state]; + } return &chunk_event_table_unknown[state]; } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 0ac8c9bf5363..e477ac9fa307 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -379,6 +379,9 @@ static int sctp_send_asconf_add_ip(struct sock *sk, int i; int retval = 0; + if (!sctp_addip_enable) + return retval; + sp = sctp_sk(sk); ep = sp->ep; @@ -564,6 +567,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, int i; int retval = 0; + if (!sctp_addip_enable) + return retval; + sp = sctp_sk(sk); ep = sp->ep; diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 417c62061621..15c5f3bfecbf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -162,6 +162,14 @@ static ctl_table sctp_table[] = { .mode = 0644, .proc_handler = &proc_dointvec }, + { + .ctl_name = NET_SCTP_ADDIP_ENABLE, + .procname = "addip_enable", + .data = &sctp_addip_enable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .ctl_name = 0 } }; -- cgit v1.2.3 From c4fdf856b13b1f130851eaaffaa2108e1e7300aa Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 13 Nov 2003 19:25:42 -0800 Subject: [SCTP] Fix overflow in the macros JIFFIES_TO_MSECS/MSECS_TO_JIFFIES when used with large values. --- include/net/sctp/sctp.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index bf33562cd101..607ce220af2c 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -116,8 +116,10 @@ #define SCTP_STATIC static #endif -#define MSECS_TO_JIFFIES(msec) (msec * HZ / 1000) -#define JIFFIES_TO_MSECS(jiff) (jiff * 1000 / HZ) +#define MSECS_TO_JIFFIES(msec) \ + (((msec / 1000) * HZ) + ((msec % 1000) * HZ) / 1000) +#define JIFFIES_TO_MSECS(jiff) \ + (((jiff / HZ) * 1000) + ((jiff % HZ) * 1000) / HZ) /* * Function declarations. -- cgit v1.2.3 From b896b82be4ae40edc91f6ad8b8d63a995f3c9771 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 20 Nov 2003 19:36:48 -0800 Subject: [SCTP] ADDIP: Support for processing incoming ASCONF_ACK chunks. --- include/linux/sctp.h | 3 +- include/net/sctp/sm.h | 17 +++ include/net/sctp/structs.h | 5 + net/sctp/associola.c | 4 + net/sctp/sm_make_chunk.c | 252 +++++++++++++++++++++++++++++++++++++++------ net/sctp/sm_statefuns.c | 75 +++++++++++++- net/sctp/socket.c | 86 +++++++++++++--- 7 files changed, 385 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index a022f9f629f6..9ddc3bd5cd81 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -439,12 +439,13 @@ typedef enum { * 0x0101 Operation Refused Due to Resource Shortage. * 0x0102 Request to Delete Source IP Address. * 0x0103 Association Aborted due to illegal ASCONF-ACK + * 0x0104 Request refused - no authorization. */ SCTP_ERROR_DEL_LAST_IP = __constant_htons(0x0100), SCTP_ERROR_RSRC_LOW = __constant_htons(0x0101), SCTP_ERROR_DEL_SRC_IP = __constant_htons(0x0102), SCTP_ERROR_ASCONF_ACK = __constant_htons(0x0103), - + SCTP_ERROR_REQ_REFUSED = __constant_htons(0x0104) } sctp_error_t; diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index c04ece969d46..60dea46dec21 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -275,6 +275,8 @@ struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, __u32 serial, int vparam_len); struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *asconf); +int sctp_process_asconf_ack(struct sctp_association *asoc, + struct sctp_chunk *asconf_ack); void sctp_chunk_assign_tsn(struct sctp_chunk *); void sctp_chunk_assign_ssn(struct sctp_chunk *); @@ -429,6 +431,21 @@ static inline int SSN_lte(__u16 s, __u16 t) return (((s) == (t)) || (((s) - (t)) & SSN_SIGN_BIT)); } +/* + * ADDIP 3.1.1 + * The valid range of Serial Number is from 0 to 4294967295 (2**32 - 1). Serial + * Numbers wrap back to 0 after reaching 4294967295. + */ +enum { + ADDIP_SERIAL_SIGN_BIT = (1<<31) +}; + +static inline int ADDIP_SERIAL_gte(__u16 s, __u16 t) +{ + return (((s) == (t)) || (((t) - (s)) & ADDIP_SERIAL_SIGN_BIT)); +} + + /* Run sctp_add_cmd() generating a BUG() if there is a failure. */ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) { diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index a2211d1692e9..92394f3de76a 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1401,6 +1401,11 @@ struct sctp_association { /* Does peer support ADDIP? */ __u8 asconf_capable; + /* This mask is used to disable sending the ASCONF chunk + * with specified parameter to peer. + */ + __u16 addip_disabled_mask; + struct sctp_inithdr i; int cookie_len; void *cookie; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index d981c6bacb0c..62545ba00503 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -365,6 +365,10 @@ void sctp_association_free(struct sctp_association *asoc) if (asoc->addip_last_asconf_ack) sctp_chunk_free(asoc->addip_last_asconf_ack); + /* Free any cached ASCONF chunk. */ + if (asoc->addip_last_asconf) + sctp_chunk_free(asoc->addip_last_asconf); + sctp_association_put(asoc); } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a152744b0ec2..3c4bfc535fe5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2207,7 +2207,7 @@ struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, } /* Add response parameters to an ASCONF_ACK chunk. */ -void sctp_add_asconf_response(struct sctp_chunk *chunk, __u32 crr_id, +static void sctp_add_asconf_response(struct sctp_chunk *chunk, __u32 crr_id, __u16 err_code, sctp_addip_param_t *asconf_param) { sctp_addip_param_t ack_param; @@ -2248,16 +2248,19 @@ void sctp_add_asconf_response(struct sctp_chunk *chunk, __u32 crr_id, } /* Process a asconf parameter. */ -__u16 sctp_process_asconf_param(struct sctp_association *asoc, - struct sctp_chunk *asconf, - sctp_addip_param_t *asconf_param, - union sctp_addr_param *addr_param) +static __u16 sctp_process_asconf_param(struct sctp_association *asoc, + struct sctp_chunk *asconf, + sctp_addip_param_t *asconf_param) { struct sctp_transport *peer; struct sctp_af *af; union sctp_addr addr; struct list_head *pos; + union sctp_addr_param *addr_param; + addr_param = (union sctp_addr_param *) + ((void *)asconf_param + sizeof(sctp_addip_param_t)); + af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); if (unlikely(!af)) return SCTP_ERROR_INV_PARAM; @@ -2330,33 +2333,29 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, __u16 err_code; int length = 0; - int chunk_len = ntohs(asconf->chunk_hdr->length); - int asconf_param_len; + int chunk_len = asconf->skb->len; __u32 serial; int all_param_pass = 1; hdr = (sctp_addiphdr_t *)asconf->skb->data; serial = ntohl(hdr->serial); - /* Skip the chunkhdr. */ - chunk_len -= sizeof(sctp_chunkhdr_t); - - /* Skip the addiphdr and store a pointer to address parameter. */ + /* Skip the addiphdr and store a pointer to address parameter. */ length = sizeof(sctp_addiphdr_t); - addr_param = (union sctp_addr_param *)skb_pull(asconf->skb, length); + addr_param = (union sctp_addr_param *)(asconf->skb->data + length); chunk_len -= length; /* Skip the address parameter and store a pointer to the first * asconf paramter. */ length = ntohs(addr_param->v4.param_hdr.length); - asconf_param = (sctp_addip_param_t *)skb_pull(asconf->skb, length); + asconf_param = (sctp_addip_param_t *)((void *)addr_param + length); chunk_len -= length; /* create an ASCONF_ACK chunk. * Based on the definitions of parameters, we know that the size of * ASCONF_ACK parameters are less than or equal to the twice of ASCONF - * paramter. + * paramters. */ asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 2); if (!asconf_ack) @@ -2364,22 +2363,8 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, /* Process the TLVs contained within the ASCONF chunk. */ while (chunk_len > 0) { - asconf_param_len = ntohs(asconf_param->param_hdr.length); - length = sizeof(sctp_addip_param_t); - - /* Unrecognized or unsupported paramter. */ - if (asconf_param_len <= length) { - sctp_add_asconf_response(asconf_ack, 0, - SCTP_ERROR_UNKNOWN_PARAM, - NULL); - goto done; - } - - addr_param = (union sctp_addr_param *)skb_pull(asconf->skb, - length); - - err_code = sctp_process_asconf_param(asoc, asconf, asconf_param, - addr_param); + err_code = sctp_process_asconf_param(asoc, asconf, + asconf_param); /* ADDIP 4.1 A7) * If an error response is received for a TLV parameter, * all TLVs with no response before the failed TLV are @@ -2404,10 +2389,10 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, goto done; /* Move to the next ASCONF param. */ - length = ntohs(addr_param->v4.param_hdr.length); - asconf_param = (sctp_addip_param_t *)skb_pull(asconf->skb, - length); - chunk_len -= asconf_param_len; + length = ntohs(asconf_param->param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)asconf_param + + length); + chunk_len -= length; } done: @@ -2426,3 +2411,202 @@ done: return asconf_ack; } + +/* Process a asconf parameter that is successfully acked. */ +static int sctp_asconf_param_success(struct sctp_association *asoc, + sctp_addip_param_t *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; + int retval = 0; + + addr_param = (union sctp_addr_param *) + ((void *)asconf_param + sizeof(sctp_addip_param_t)); + + /* We have checked the packet before, so we do not check again. */ + af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); + af->from_addr_param(&addr, addr_param, bp->port, 0); + + switch (asconf_param->param_hdr.type) { + case SCTP_PARAM_ADD_IP: + sctp_local_bh_disable(); + sctp_write_lock(&asoc->base.addr_lock); + retval = sctp_add_bind_addr(bp, &addr, GFP_ATOMIC); + sctp_write_unlock(&asoc->base.addr_lock); + sctp_local_bh_enable(); + break; + case SCTP_PARAM_DEL_IP: + sctp_local_bh_disable(); + sctp_write_lock(&asoc->base.addr_lock); + retval = sctp_del_bind_addr(bp, &addr); + sctp_write_unlock(&asoc->base.addr_lock); + sctp_local_bh_enable(); + break; + default: + break; + } + + return retval; +} + +/* Get the corresponding ASCONF response error code from the ASCONF_ACK chunk + * for the given asconf parameter. If there is no response for this parameter, + * return the error code based on the third argument 'no_err'. + * ADDIP 4.1 + * A7) If an error response is received for a TLV parameter, all TLVs with no + * response before the failed TLV are considered successful if not reported. + * All TLVs after the failed response are considered unsuccessful unless a + * specific success indication is present for the parameter. + */ +static __u16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, + sctp_addip_param_t *asconf_param, + int no_err) +{ + sctp_addip_param_t *asconf_ack_param; + sctp_errhdr_t *err_param; + int length; + int asconf_ack_len = asconf_ack->skb->len; + __u16 err_code; + + 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 + * the first asconf_ack parameter. + */ + length = sizeof(sctp_addiphdr_t); + asconf_ack_param = (sctp_addip_param_t *)(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(sctp_addip_param_t); + err_param = (sctp_errhdr_t *) + ((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; + } + } + + length = ntohs(asconf_ack_param->param_hdr.length); + asconf_ack_param = (sctp_addip_param_t *) + ((void *)asconf_ack_param + length); + asconf_ack_len -= length; + } + + return err_code; +} + +/* 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; + 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; + __u16 err_code = SCTP_ERROR_NO_ERROR; + + /* Skip the chunkhdr and addiphdr from the last asconf sent and store + * a pointer to address parameter. + */ + length = sizeof(sctp_addip_chunk_t); + 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 + * pointer to the first asconf paramter. + */ + length = ntohs(addr_param->v4.param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)addr_param + length); + asconf_len -= length; + + /* ADDIP 4.1 + * A8) If there is no response(s) to specific TLV parameter(s), and no + * failures are indicated, then all request(s) are considered + * successful. + */ + if (asconf_ack->skb->len == sizeof(sctp_addiphdr_t)) + all_param_pass = 1; + + /* 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: + retval = sctp_asconf_param_success(asoc, asconf_param); + break; + + case SCTP_ERROR_RSRC_LOW: + retval = 1; + break; + + case SCTP_ERROR_INV_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 = (sctp_addip_param_t *)((void *)asconf_param + + length); + asconf_len -= length; + } + + /* Free the cached last sent asconf chunk. */ + sctp_chunk_free(asconf); + asoc->addip_last_asconf = NULL; + + /* Send the next asconf chunk from the addip chunk queue. */ + asconf = (struct sctp_chunk *)__skb_dequeue(&asoc->addip_chunks); + if (asconf) { + /* Hold the chunk until an ASCONF_ACK is received. */ + sctp_chunk_hold(asconf); + if (sctp_primitive_ASCONF(asoc, asconf)) + sctp_chunk_free(asconf); + else + asoc->addip_last_asconf = asconf; + } + + return retval; +} diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index aee7af1d5781..fb0070f6086e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -46,6 +46,7 @@ * Daisy Chang * Ardelle Fan * Ryan Layer + * Kevin Gao * * Any bugs reported given to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. @@ -3126,12 +3127,76 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, * delete IP addresses the D0 to D13 rules should be applied: */ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, - sctp_cmd_seq_t *commands) + const struct sctp_association *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) { - // FIXME: Handle the ASCONF-ACK chunk - return SCTP_DISPOSITION_CONSUME; + struct sctp_chunk *asconf_ack = arg; + struct sctp_chunk *last_asconf = asoc->addip_last_asconf; + struct sctp_chunk *abort; + sctp_addiphdr_t *addip_hdr; + __u32 sent_serial, rcvd_serial; + + addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data; + rcvd_serial = ntohl(addip_hdr->serial); + + if (last_asconf) { + addip_hdr = (sctp_addiphdr_t *)last_asconf->subh.addip_hdr; + sent_serial = ntohl(addip_hdr->serial); + } else { + sent_serial = asoc->addip_serial - 1; + } + + /* D0) If an endpoint receives an ASCONF-ACK that is greater than or + * equal to the next serial number to be used but no ASCONF chunk is + * outstanding the endpoint MUST ABORT the association. Note that a + * sequence number is greater than if it is no more than 2^^31-1 + * larger than the current sequence number (using serial arithmetic). + */ + if (ADDIP_SERIAL_gte(rcvd_serial, sent_serial + 1) && + !(asoc->addip_last_asconf)) { + abort = sctp_make_abort(asoc, asconf_ack, + sizeof(sctp_errhdr_t)); + if (abort) { + sctp_init_cause(abort, SCTP_ERROR_ASCONF_ACK, NULL, 0); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(abort)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_ASCONF_ACK)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_ABORT; + } + + if ((rcvd_serial == sent_serial) && asoc->addip_last_asconf) { + if (!sctp_process_asconf_ack((struct sctp_association *)asoc, + asconf_ack)) + return SCTP_DISPOSITION_CONSUME; + + abort = sctp_make_abort(asoc, asconf_ack, + sizeof(sctp_errhdr_t)); + if (abort) { + sctp_init_cause(abort, SCTP_ERROR_RSRC_LOW, NULL, 0); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(abort)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_ASCONF_ACK)); + SCTP_INC_STATS(SctpAborteds); + SCTP_DEC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_ABORT; + } + + return SCTP_DISPOSITION_DISCARD; } /* diff --git a/net/sctp/socket.c b/net/sctp/socket.c index e477ac9fa307..43c7c9e293cc 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -99,6 +99,8 @@ static int sctp_bindx_add(struct sock *, struct sockaddr *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr *, int); static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int); static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int); +static int sctp_send_asconf(struct sctp_association *asoc, + struct sctp_chunk *chunk); static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_autobind(struct sock *sk); static void sctp_sock_migrate(struct sock *, struct sock *, @@ -299,6 +301,41 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) return ret; } + /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks + * + * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged + * at any one time. If a sender, after sending an ASCONF chunk, decides + * it needs to transfer another ASCONF Chunk, it MUST wait until the + * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a + * subsequent ASCONF. Note this restriction binds each side, so at any + * time two ASCONF may be in-transit on any given association (one sent + * from each endpoint). + */ +static int sctp_send_asconf(struct sctp_association *asoc, + struct sctp_chunk *chunk) +{ + int retval = 0; + + /* If there is an outstanding ASCONF chunk, queue it for later + * transmission. + */ + if (asoc->addip_last_asconf) { + __skb_queue_tail(&asoc->addip_chunks, (struct sk_buff *)chunk); + goto out; + } + + /* Hold the chunk until an ASCONF_ACK is received. */ + sctp_chunk_hold(chunk); + retval = sctp_primitive_ASCONF(asoc, chunk); + if (retval) + sctp_chunk_free(chunk); + else + asoc->addip_last_asconf = chunk; + +out: + return retval; +} + /* Add a list of addresses as bind addresses to local endpoint or * association. * @@ -391,12 +428,15 @@ static int sctp_send_asconf_add_ip(struct sock *sk, list_for_each(pos, &ep->asocs) { asoc = list_entry(pos, struct sctp_association, asocs); - if (!sctp_state(asoc, ESTABLISHED)) + if (!asoc->peer.asconf_capable) continue; - if (!asoc->peer.asconf_capable) + if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP) continue; - + + if (!sctp_state(asoc, ESTABLISHED)) + continue; + /* Check if any address in the packed array of addresses is * in the bind address list of the association. If so, * do not send the asconf chunk to its peer, but continue with @@ -411,9 +451,9 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - if (sctp_assoc_lookup_laddr(asoc, addr)) + if (sctp_assoc_lookup_laddr(asoc, addr)) break; - + addr_buf += af->sockaddr_len; } if (i < addrcnt) @@ -435,14 +475,14 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - retval = sctp_primitive_ASCONF(asoc, chunk); + retval = sctp_send_asconf(asoc, chunk); if (retval) { sctp_chunk_free(chunk); goto out; } - /* FIXME: After sending the add address ASCONF chunk, we - * cannot append the address to the association's binding + /* FIXME: After sending the add address ASCONF chunk, we + * cannot append the address to the association's binding * address list, because the new address may be used as the * source of a message sent to the peer before the ASCONF * chunk is received by the peer. So we should wait until @@ -579,10 +619,13 @@ static int sctp_send_asconf_del_ip(struct sock *sk, list_for_each(pos, &ep->asocs) { asoc = list_entry(pos, struct sctp_association, asocs); - if (!sctp_state(asoc, ESTABLISHED)) + if (!asoc->peer.asconf_capable) continue; - if (!asoc->peer.asconf_capable) + if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP) + continue; + + if (!sctp_state(asoc, ESTABLISHED)) continue; /* Check if any address in the packed array of addresses is @@ -599,9 +642,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk, goto out; } - if (!sctp_assoc_lookup_laddr(asoc, laddr)) + if (!sctp_assoc_lookup_laddr(asoc, laddr)) break; - + addr_buf += af->sockaddr_len; } if (i < addrcnt) @@ -616,18 +659,18 @@ static int sctp_send_asconf_del_ip(struct sock *sk, bp = &asoc->base.bind_addr; laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, addrcnt, sp); - sctp_read_unlock(&asoc->base.addr_lock); + sctp_read_unlock(&asoc->base.addr_lock); if (!laddr) continue; - chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, + chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, SCTP_PARAM_DEL_IP); if (!chunk) { retval = -ENOMEM; goto out; } - retval = sctp_primitive_ASCONF(asoc, chunk); + retval = sctp_send_asconf(asoc, chunk); if (retval) { sctp_chunk_free(chunk); goto out; @@ -636,7 +679,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk, /* FIXME: After sending the delete address ASCONF chunk, we * cannot remove the addresses from the association's bind * address list, because there maybe some packet send to - * the delete addresses, so we should wait until ASCONF_ACK + * the delete addresses, so we should wait until ASCONF_ACK * packet is received. */ } @@ -1925,6 +1968,9 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, sp = sctp_sk(sk); ep = sp->ep; + if (!sctp_addip_enable) + return -EPERM; + if (optlen != sizeof(struct sctp_setpeerprim)) return -EINVAL; @@ -1935,6 +1981,12 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, if (!asoc) return -EINVAL; + if (!asoc->peer.asconf_capable) + return -EPERM; + + if (asoc->peer.addip_disabled_mask & SCTP_PARAM_SET_PRIMARY) + return -EPERM; + if (!sctp_state(asoc, ESTABLISHED)) return -ENOTCONN; @@ -1947,7 +1999,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, if (!chunk) return -ENOMEM; - err = sctp_primitive_ASCONF(asoc, chunk); + err = sctp_send_asconf(asoc, chunk); if (err) { sctp_chunk_free(chunk); return err; -- cgit v1.2.3