diff options
| author | David S. Miller <davem@nuts.ninka.net> | 2004-01-19 05:05:07 -0800 |
|---|---|---|
| committer | David S. Miller <davem@nuts.ninka.net> | 2004-01-19 05:05:07 -0800 |
| commit | bd23ad706ee39fc5e8a530380c4ccc2b1a4aba4c (patch) | |
| tree | 036fed42956901b46f094783050f807cdcfdd23d | |
| parent | d8baa0e00589d7e03585a8ecad8100f0e8531d25 (diff) | |
| parent | 3ac3019bbcb2ee28490be1513e7db100199edc6e (diff) | |
Merge http://linux-lksctp.bkbits.net/lksctp-2.5.work
into nuts.ninka.net:/disk1/davem/BK/net-2.6
| -rw-r--r-- | include/linux/sctp.h | 3 | ||||
| -rw-r--r-- | include/linux/sysctl.h | 1 | ||||
| -rw-r--r-- | include/net/sctp/constants.h | 4 | ||||
| -rw-r--r-- | include/net/sctp/sctp.h | 6 | ||||
| -rw-r--r-- | include/net/sctp/sm.h | 27 | ||||
| -rw-r--r-- | include/net/sctp/structs.h | 11 | ||||
| -rw-r--r-- | net/sctp/associola.c | 47 | ||||
| -rw-r--r-- | net/sctp/input.c | 9 | ||||
| -rw-r--r-- | net/sctp/output.c | 3 | ||||
| -rw-r--r-- | net/sctp/outqueue.c | 2 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 3 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 418 | ||||
| -rw-r--r-- | net/sctp/sm_sideeffect.c | 15 | ||||
| -rw-r--r-- | net/sctp/sm_statefuns.c | 194 | ||||
| -rw-r--r-- | net/sctp/sm_statetable.c | 14 | ||||
| -rw-r--r-- | net/sctp/socket.c | 118 | ||||
| -rw-r--r-- | net/sctp/sysctl.c | 8 |
17 files changed, 788 insertions, 95 deletions
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/linux/sysctl.h b/include/linux/sysctl.h index b8be51acff95..d271fcada7dd 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -580,6 +580,7 @@ enum { NET_SCTP_HB_INTERVAL = 10, NET_SCTP_PRESERVE_ENABLE = 11, NET_SCTP_MAX_BURST = 12, + NET_SCTP_ADDIP_ENABLE = 13, }; /* /proc/sys/net/bridge */ 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/sctp.h b/include/net/sctp/sctp.h index 7edb15d6561a..3fa80e611ba1 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -115,8 +115,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. diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2b62b1afef2a..60dea46dec21 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -268,15 +268,15 @@ 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); +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 *); @@ -431,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 bf2745654d7a..92394f3de76a 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 { @@ -1397,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; @@ -1708,6 +1717,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); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 44b30efb17a9..5c15d829abba 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -362,6 +362,14 @@ void sctp_association_free(struct sctp_association *asoc) asoc->eyecatcher = 0; + /* Free any cached ASCONF_ACK chunk. */ + 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); } @@ -525,6 +533,45 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, return peer; } +/* Delete a transport address from an association. */ +void sctp_assoc_del_peer(struct sctp_association *asoc, + const union sctp_addr *addr) +{ + struct list_head *pos; + struct list_head *temp; + struct sctp_transport *peer = NULL; + struct sctp_transport *transport; + + list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, transports); + if (sctp_cmp_addr_exact(addr, &transport->ipaddr)) { + peer = transport; + list_del(pos); + break; + } + } + + /* The address we want delete is not in the association. */ + if (!peer) + return; + + /* Get the first transport of asoc. */ + pos = asoc->peer.transport_addr_list.next; + transport = list_entry(pos, struct sctp_transport, transports); + + /* Update any entries that match the peer to be deleted. */ + if (asoc->peer.primary_path == peer) + sctp_assoc_set_primary(asoc, transport); + if (asoc->peer.active_path == peer) + asoc->peer.active_path = transport; + if (asoc->peer.retran_path == peer) + asoc->peer.retran_path = transport; + if (asoc->peer.last_data_from == peer) + asoc->peer.last_data_from = transport; + + sctp_transport_free(peer); +} + /* Lookup a transport by address. */ struct sctp_transport *sctp_assoc_lookup_paddr( const struct sctp_association *asoc, diff --git a/net/sctp/input.c b/net/sctp/input.c index cc3e9314dd91..8392d7f83d61 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -124,16 +124,16 @@ int sctp_rcv(struct sk_buff *skb) /* Pull up the IP and SCTP headers. */ __skb_pull(skb, skb->h.raw - skb->data); if (skb->len < sizeof(struct sctphdr)) - goto bad_packet; + goto discard_it; if (sctp_rcv_checksum(skb) < 0) - goto bad_packet; + goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); family = ipver2af(skb->nh.iph->version); af = sctp_get_af_specific(family); if (unlikely(!af)) - goto bad_packet; + goto discard_it; /* Initialize local addresses for lookups. */ af->from_skb(&src, skb, 1); @@ -223,9 +223,6 @@ int sctp_rcv(struct sk_buff *skb) sock_put(sk); return ret; -bad_packet: - SCTP_INC_STATS(SctpChecksumErrors); - discard_it: kfree_skb(skb); return ret; diff --git a/net/sctp/output.c b/net/sctp/output.c index 262a6aa06b49..ecd0c6edc6c0 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -1,7 +1,7 @@ /* SCTP kernel reference Implementation + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. - * Copyright (c) 2001-2003 International Business Machines, Corp. * * This file is part of the SCTP kernel reference Implementation * @@ -350,7 +350,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) */ SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) { - if (sctp_chunk_is_data(chunk)) { if (!chunk->has_tsn) { diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index dc4cb1a5a739..14816b5f8335 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -150,7 +150,7 @@ static inline int sctp_cacc_skip_3_1(struct sctp_transport *primary, if (!primary->cacc.cycling_changeover) { if (sctp_cacc_skip_3_1_d(primary, transport, count_of_newacks)) return 1; - if (sctp_cacc_skip_3_1_f(transport, count_of_newacks)); + if (sctp_cacc_skip_3_1_f(transport, count_of_newacks)) return 1; return 0; } 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_make_chunk.c b/net/sctp/sm_make_chunk.c index a340e8978154..3c4bfc535fe5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1441,6 +1441,7 @@ no_hmac: retval->next_tsn = retval->c.initial_tsn; retval->ctsn_ack_point = retval->next_tsn - 1; + retval->addip_serial = retval->c.initial_tsn; /* The INIT stuff will be done by the side effects. */ return retval; @@ -2035,7 +2036,7 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, if (!retval) return NULL; - asconf.serial = asoc->addip_serial++; + asconf.serial = htonl(asoc->addip_serial++); retval->subh.addip_hdr = sctp_addto_chunk(retval, sizeof(asconf), &asconf); @@ -2073,7 +2074,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, union sctp_addr *laddr, struct sockaddr *addrs, int addrcnt, - int flags) + __u16 flags) { sctp_addip_param_t param; struct sctp_chunk *retval; @@ -2112,7 +2113,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 = htonl(i); + param.crr_id = i; sctp_addto_chunk(retval, paramlen, ¶m); sctp_addto_chunk(retval, addr_param_len, &addr_param); @@ -2185,8 +2186,8 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, * * Create an ASCONF_ACK chunk with enough space for the parameter responses. */ -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) { sctp_addiphdr_t asconf; struct sctp_chunk *retval; @@ -2197,7 +2198,7 @@ struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, if (!retval) return NULL; - asconf.serial = serial; + asconf.serial = htonl(serial); retval->subh.addip_hdr = sctp_addto_chunk(retval, sizeof(asconf), &asconf); @@ -2205,10 +2206,407 @@ struct sctp_chunk *sctp_make_asconf_ack(struct sctp_association *asoc, return retval; } +/* Add response parameters to an ASCONF_ACK chunk. */ +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; + sctp_errhdr_t err_param; + int asconf_param_len = 0; + int err_param_len = 0; + __u16 response_type; + + if (SCTP_ERROR_NO_ERROR == err_code) { + response_type = SCTP_PARAM_SUCCESS_REPORT; + } else { + response_type = SCTP_PARAM_ERR_CAUSE; + err_param_len = sizeof(err_param); + if (asconf_param) + asconf_param_len = + ntohs(asconf_param->param_hdr.length); + } + + /* Add Success Indication or Error Cause Indication parameter. */ + ack_param.param_hdr.type = response_type; + ack_param.param_hdr.length = htons(sizeof(ack_param) + + err_param_len + + asconf_param_len); + ack_param.crr_id = crr_id; + sctp_addto_chunk(chunk, sizeof(ack_param), &ack_param); + + if (SCTP_ERROR_NO_ERROR == err_code) + return; + + /* Add Error Cause parameter. */ + err_param.cause = err_code; + err_param.length = htons(err_param_len + asconf_param_len); + sctp_addto_chunk(chunk, err_param_len, &err_param); + + /* Add the failed TLV copied from ASCONF chunk. */ + if (asconf_param) + sctp_addto_chunk(chunk, asconf_param_len, asconf_param); +} + +/* Process a asconf parameter. */ +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; + + af->from_addr_param(&addr, addr_param, asoc->peer.port, 0); + switch (asconf_param->param_hdr.type) { + case SCTP_PARAM_ADD_IP: + /* 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 + * Cause TLV set to the new error code 'Operation Refused + * Due to Resource Shortage'. + */ + + peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC); + if (!peer) + return SCTP_ERROR_RSRC_LOW; + + /* Start the heartbeat timer. */ + if (!mod_timer(&peer->hb_timer, sctp_transport_timeout(peer))) + sctp_transport_hold(peer); + break; + case SCTP_PARAM_DEL_IP: + /* ADDIP 4.3 D7) If a request is received to delete the + * last remaining IP address of a peer endpoint, the receiver + * MUST send an Error Cause TLV with the error cause set to the + * new error code 'Request to Delete Last Remaining IP Address'. + */ + pos = asoc->peer.transport_addr_list.next; + if (pos->next == &asoc->peer.transport_addr_list) + return SCTP_ERROR_DEL_LAST_IP; + + /* ADDIP 4.3 D8) If a request is received to delete an IP + * address which is also the source address of the IP packet + * which contained the ASCONF chunk, the receiver MUST reject + * this request. To reject the request the receiver MUST send + * an Error Cause TLV set to the new error code 'Request to + * Delete Source IP Address' + */ + if (sctp_cmp_addr_exact(sctp_source(asconf), &addr)) + return SCTP_ERROR_DEL_SRC_IP; + + sctp_assoc_del_peer(asoc, &addr); + break; + case SCTP_PARAM_SET_PRIMARY: + peer = sctp_assoc_lookup_paddr(asoc, &addr); + if (!peer) + return SCTP_ERROR_INV_PARAM; + + sctp_assoc_set_primary(asoc, peer); + break; + default: + return SCTP_ERROR_INV_PARAM; + break; + } + + return SCTP_ERROR_NO_ERROR; +} + +/* Process an incoming ASCONF chunk with the next expected serial no. and + * return an ASCONF_ACK chunk to be sent in response. + */ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, - struct sctp_chunk *asconf, - int vparam_len) + struct sctp_chunk *asconf) { - // FIXME: process asconf chunk - return NULL; + sctp_addiphdr_t *hdr; + union sctp_addr_param *addr_param; + sctp_addip_param_t *asconf_param; + struct sctp_chunk *asconf_ack; + + __u16 err_code; + int length = 0; + 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 addiphdr and store a pointer to address parameter. */ + length = sizeof(sctp_addiphdr_t); + 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 *)((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 + * paramters. + */ + asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 2); + if (!asconf_ack) + goto done; + + /* Process the TLVs contained within the ASCONF chunk. */ + while (chunk_len > 0) { + 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 + * considered successful if not reported. All TLVs after + * the failed response are considered unsuccessful unless + * a specific success indication is present for the parameter. + */ + if (SCTP_ERROR_NO_ERROR != err_code) + all_param_pass = 0; + + if (!all_param_pass) + sctp_add_asconf_response(asconf_ack, + asconf_param->crr_id, err_code, + asconf_param); + + /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add + * an IP address sends an 'Out of Resource' in its response, it + * MUST also fail any subsequent add or delete requests bundled + * in the ASCONF. + */ + if (SCTP_ERROR_RSRC_LOW == err_code) + goto done; + + /* Move to the next ASCONF param. */ + length = ntohs(asconf_param->param_hdr.length); + asconf_param = (sctp_addip_param_t *)((void *)asconf_param + + length); + chunk_len -= length; + } + +done: + asoc->peer.addip_serial++; + + /* If we are sending a new ASCONF_ACK hold a reference to it in assoc + * after freeing the reference to old asconf ack if any. + */ + if (asconf_ack) { + if (asoc->addip_last_asconf_ack) + sctp_chunk_free(asoc->addip_last_asconf_ack); + + sctp_chunk_hold(asconf_ack); + asoc->addip_last_asconf_ack = asconf_ack; + } + + 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_sideeffect.c b/net/sctp/sm_sideeffect.c index b2eeb5d93acd..3041eaa330c9 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1,5 +1,5 @@ /* SCTP kernel reference Implementation - * (C) Copyright IBM Corp. 2001, 2003 + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * @@ -663,10 +663,11 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, struct sock *sk = asoc->base.sk; /* If it is a non-temporary association belonging to a TCP-style - * listening socket, do not free it so that accept() can pick it - * up later. + * listening socket that is not closed, do not free it so that accept() + * can pick it up later. */ - if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING) && (!asoc->temp)) + if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING) && + (!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK)) return; sctp_unhash_established(asoc); @@ -676,8 +677,8 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, /* * ADDIP Section 4.1 ASCONF Chunk Procedures * A4) Start a T-4 RTO timer, using the RTO value of the selected - * destination address (normally the primary path; see RFC2960 - * section 6.4 for details). + * destination address (we use active path instead of primary path just + * because primary path may be inactive. */ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, @@ -685,7 +686,7 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, { struct sctp_transport *t; - t = asoc->peer.primary_path; + t = asoc->peer.active_path; asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = t->rto; chunk->transport = t; } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 0777e8e539b0..37d73a01097e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1,5 +1,5 @@ /* SCTP kernel reference Implementation - * (C) Copyright IBM Corp. 2001, 2003 + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2002 Intel Corp. @@ -46,6 +46,7 @@ * Daisy Chang <daisyc@us.ibm.com> * Ardelle Fan <ardelle.fan@intel.com> * Ryan Layer <rmlayer@us.ibm.com> + * Kevin Gao <kevin.gao@intel.com> * * Any bugs reported given to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. @@ -3066,19 +3067,57 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, return sctp_sf_shut_8_4_5(ep, NULL, type, arg, commands); } -/* - * ADDIP Section 4.2 Upon reception of an ASCONF Chunk - * When an endpoint receive an ASCONF Chunk from the remote peer - * special procedures MAY be needed to identify the association the - * ASCONF Chunk is associated with. To properly find the association - * the following procedures should be L1 to L4 and C1 to C5 - */ +/* ADDIP Section 4.2 Upon reception of an ASCONF Chunk. */ sctp_disposition_t sctp_sf_do_asconf(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 chunk + struct sctp_chunk *chunk = arg; + struct sctp_chunk *asconf_ack = NULL; + sctp_addiphdr_t *hdr; + __u32 serial; + + hdr = (sctp_addiphdr_t *)chunk->skb->data; + serial = ntohl(hdr->serial); + + /* ADDIP 4.2 C1) Compare the value of the serial number to the value + * the endpoint stored in a new association variable + * 'Peer-Serial-Number'. + */ + if (serial == asoc->peer.addip_serial + 1) { + /* ADDIP 4.2 C2) If the value found in the serial number is + * equal to the ('Peer-Serial-Number' + 1), the endpoint MUST + * do V1-V5. + */ + asconf_ack = sctp_process_asconf((struct sctp_association *) + asoc, chunk); + if (!asconf_ack) + return SCTP_DISPOSITION_NOMEM; + } else if (serial == asoc->peer.addip_serial) { + /* ADDIP 4.2 C3) If the value found in the serial number is + * equal to the value stored in the 'Peer-Serial-Number' + * IMPLEMENTATION NOTE: As an optimization a receiver may wish + * to save the last ASCONF-ACK for some predetermined period of * time and instead of re-processing the ASCONF (with the same + * serial number) it may just re-transmit the ASCONF-ACK. + */ + if (asoc->addip_last_asconf_ack) + asconf_ack = asoc->addip_last_asconf_ack; + else + return SCTP_DISPOSITION_DISCARD; + } else { + /* ADDIP 4.2 C4) Otherwise, the ASCONF Chunk is discarded since + * it must be either a stale packet or from an attacker. + */ + return SCTP_DISPOSITION_DISCARD; + } + + /* ADDIP 4.2 C5) In both cases C2 and C3 the ASCONF-ACK MUST be sent + * back to the source address contained in the IP header of the ASCONF + * being responded to. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(asconf_ack)); + return SCTP_DISPOSITION_CONSUME; } @@ -3088,12 +3127,81 @@ 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_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + 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) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + + 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; } /* @@ -4269,7 +4377,7 @@ nomem: /* * ADDIP Section 4.1 ASCONF CHunk Procedures - * If the T-4 RTO timer expires the endpoint should do B1 to B5 + * If the T4 RTO timer expires the endpoint should do B1 to B5 */ sctp_disposition_t sctp_sf_t4_timer_expire( const struct sctp_endpoint *ep, @@ -4278,7 +4386,55 @@ sctp_disposition_t sctp_sf_t4_timer_expire( void *arg, sctp_cmd_seq_t *commands) { - // FIXME: need to handle t4 expire + struct sctp_chunk *chunk = asoc->addip_last_asconf; + struct sctp_transport *transport = chunk->transport; + + /* ADDIP 4.1 B1) Increment the error counters and perform path failure + * detection on the appropriate destination address as defined in + * RFC2960 [5] section 8.1 and 8.2. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport)); + + /* Reconfig T4 timer and transport. */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T4, SCTP_CHUNK(chunk)); + + /* ADDIP 4.1 B2) Increment the association error counters and perform + * endpoint failure detection on the association as defined in + * RFC2960 [5] section 8.1 and 8.2. + * association error counter is incremented in SCTP_CMD_STRIKE. + */ + if (asoc->overall_error_count >= asoc->max_retrans) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); + SCTP_INC_STATS(SctpAborteds); + SCTP_INC_STATS(SctpCurrEstab); + return SCTP_DISPOSITION_ABORT; + } + + /* ADDIP 4.1 B3) Back-off the destination address RTO value to which + * the ASCONF chunk was sent by doubling the RTO timer value. + * This is done in SCTP_CMD_STRIKE. + */ + + /* ADDIP 4.1 B4) Re-transmit the ASCONF Chunk last sent and if possible + * choose an alternate destination address (please refer to RFC2960 + * [5] section 6.4.1). An endpoint MUST NOT add new parameters to this + * chunk, it MUST be the same (including its serial number) as the last + * ASCONF sent. + */ + sctp_chunk_hold(asoc->addip_last_asconf); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(asoc->addip_last_asconf)); + + /* ADDIP 4.1 B5) Restart the T-4 RTO timer. Note that if a different + * destination is selected, then the RTO used will be that of the new + * destination address. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO)); + return SCTP_DISPOSITION_CONSUME; } diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index c73c3a2ff9a2..224b8d2fe888 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -1,7 +1,7 @@ /* 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-2003 International Business Machines, Corp. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Nokia, Inc. * @@ -921,13 +921,15 @@ const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, if (state > SCTP_STATE_MAX) return &bug; - if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) { + if (cid >= 0 && cid <= SCTP_CID_BASE_MAX) return &chunk_event_table[cid][state]; - } - if (cid >= SCTP_CID_ADDIP_MIN && cid <= SCTP_CID_ADDIP_MAX) { - return &addip_chunk_event_table - [cid - SCTP_CID_ADDIP_MIN][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]; } return &chunk_event_table_unknown[state]; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 574efdb0cf42..4f7cbfe0e36c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1,5 +1,5 @@ /* SCTP kernel reference Implementation - * (C) Copyright IBM Corp. 2001, 2003 + * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2003 Intel Corp. @@ -100,6 +100,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 *, @@ -150,10 +152,14 @@ struct sctp_transport *sctp_addr_id2transport(struct sock *sk, { struct sctp_association *addr_asoc = NULL, *id_asoc = NULL; struct sctp_transport *transport; + union sctp_addr *laddr = (union sctp_addr *)addr; + laddr->v4.sin_port = ntohs(laddr->v4.sin_port); addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep, (union sctp_addr *)addr, &transport); + laddr->v4.sin_port = htons(laddr->v4.sin_port); + if (!addr_asoc) return NULL; @@ -300,6 +306,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. * @@ -380,6 +421,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; @@ -389,12 +433,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 @@ -409,9 +456,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) @@ -433,14 +480,10 @@ static int sctp_send_asconf_add_ip(struct sock *sk, goto out; } - retval = sctp_primitive_ASCONF(asoc, chunk); - if (retval) { - sctp_chunk_free(chunk); - goto out; - } + retval = sctp_send_asconf(asoc, chunk); - /* 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 @@ -565,6 +608,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; @@ -574,10 +620,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 @@ -594,9 +643,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) @@ -611,27 +660,23 @@ 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); - if (retval) { - sctp_chunk_free(chunk); - goto out; - } + retval = sctp_send_asconf(asoc, chunk); /* 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. */ } @@ -1920,6 +1965,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; @@ -1930,6 +1978,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; @@ -1942,15 +1996,11 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char *optval, if (!chunk) return -ENOMEM; - err = sctp_primitive_ASCONF(asoc, chunk); - if (err) { - sctp_chunk_free(chunk); - return err; - } + err = sctp_send_asconf(asoc, chunk); SCTP_DEBUG_PRINTK("We set peer primary addr primitively.\n"); - return 0; + return err; } @@ -2962,9 +3012,13 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len, if (!asoc->peer.primary_path) return -ENOTCONN; - + + asoc->peer.primary_path->ipaddr.v4.sin_port = + htons(asoc->peer.primary_path->ipaddr.v4.sin_port); memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr, sizeof(union sctp_addr)); + asoc->peer.primary_path->ipaddr.v4.sin_port = + ntohs(asoc->peer.primary_path->ipaddr.v4.sin_port); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, (union sctp_addr *)&prim.ssp_addr); 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 } }; |
