Commit 3e94542b authored by Sridhar Samudrala's avatar Sridhar Samudrala

Merge us.ibm.com:/home/sridhar/BK/linux-2.6.0-test6

into us.ibm.com:/home/sridhar/BK/lksctp-2.6.0-test6
parents e0618466 f51c1494
......@@ -524,113 +524,4 @@ typedef struct sctp_addip_chunk {
sctp_addiphdr_t addip_hdr;
} sctp_addip_chunk_t __attribute__((packed));
/* FIXME: Cleanup needs to continue below this line. */
/* ADDIP Section 3.1.1
*
* ASCONF-Request Correlation ID: 32 bits (unsigned integer)
*
* This is an opaque integer assigned by the sender to identify each
* request parameter. It is in host byte order and is only meaningful
* to the sender. The receiver of the ASCONF Chunk will copy this 32
* bit value into the ASCONF Correlation ID field of the
* ASCONF-ACK. The sender of the ASCONF can use this same value in the
* ASCONF-ACK to find which request the response is for.
*
* ASCONF Parameter: TLV format
*
* Each Address configuration change is represented by a TLV parameter
* as defined in Section 3.2. One or more requests may be present in
* an ASCONF Chunk.
*/
typedef struct {
__u32 correlation;
sctp_paramhdr_t p;
__u8 payload[0];
} sctpAsconfReq_t;
/* ADDIP
* 3.1.1 Address/Stream Configuration Change Chunk (ASCONF)
*
* This chunk is used to communicate to the remote endpoint one of the
* configuration change requests that MUST be acknowledged. The
* information carried in the ASCONF Chunk uses the form of a
* Tag-Length-Value (TLV), as described in "3.2.1
* Optional/Variable-length Parameter Format" in [RFC2960], for all
* variable parameters.
*/
typedef struct {
__u32 serial;
__u8 reserved[3];
__u8 addr_type;
__u32 addr[4];
sctpAsconfReq_t requests[0];
} sctpAsconf_t;
/* ADDIP
* 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
*
* ASCONF-Request Correlation ID: 32 bits (unsigned integer)
*
* This value is copied from the ASCONF Correlation ID received in the
* ASCONF Chunk. It is used by the receiver of the ASCONF-ACK to identify
* which ASCONF parameter this response is associated with.
*
* ASCONF Parameter Response : TLV format
*
* The ASCONF Parameter Response is used in the ASCONF-ACK to report
* status of ASCONF processing. By default, if a responding endpoint
* does not include any Error Cause, a success is indicated. Thus a
* sender of an ASCONF-ACK MAY indicate complete success of all TLVs in
* an ASCONF by returning only the Chunk Type, Chunk Flags, Chunk Length
* (set to 8) and the Serial Number.
*/
typedef union {
struct {
__u32 correlation;
sctp_paramhdr_t header; /* success report */
} success;
struct {
__u32 correlation;
sctp_paramhdr_t header; /* error cause indication */
sctp_paramhdr_t errcause;
uint8_t request[0]; /* original request from ASCONF */
} error;
#define __correlation success.correlation
#define __header success.header
#define __cause error.errcause
#define __request error.request
} sctpAsconfAckRsp_t;
/* ADDIP
* 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
*
* This chunk is used by the receiver of an ASCONF Chunk to
* acknowledge the reception. It carries zero or more results for any
* ASCONF Parameters that were processed by the receiver.
*/
typedef struct {
__u32 serial;
sctpAsconfAckRsp_t responses[0];
} sctpAsconfAck_t;
/*********************************************************************
* Internal structures
*
* These are data structures which never go out on the wire.
*********************************************************************/
/* What is this data structure for? The TLV isn't one--it is just a
* value. Perhaps this data structure ought to have a type--otherwise
* it is not unambigiously parseable. --piggy
*/
typedef struct {
struct list_head hook;
int length; /* length of the TLV */
/* the actually TLV to be copied into ASCONF_ACK */
sctpAsconfAckRsp_t TLV;
} sctpAsconfAckRspNode_t;
#endif /* __LINUX_SCTP_H__ */
/* SCTP kernel reference Implementation Copyright (C) 1999-2001
* Cisco, Motorola, and IBM
/* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (C) 1999-2001 Cisco, Motorola
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -88,6 +89,7 @@ typedef enum {
SCTP_CMD_PART_DELIVER, /* Partial data delivery considerations. */
SCTP_CMD_RENEGE, /* Renege data on an association. */
SCTP_CMD_SETUP_T4, /* ADDIP, setup T4 RTO timer parms. */
SCTP_CMD_PROCESS_OPERR, /* Process an ERROR chunk. */
SCTP_CMD_LAST
} sctp_verb_t;
......
......@@ -495,22 +495,19 @@ for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
#define tv_lt(s, t) \
(s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec))
/* Stolen from net/profile.h. Using it from there is more grief than
* it is worth.
*/
static inline void tv_add(const struct timeval *entered, struct timeval *leaved)
{
time_t usecs = leaved->tv_usec + entered->tv_usec;
time_t secs = leaved->tv_sec + entered->tv_sec;
if (usecs >= 1000000) {
usecs -= 1000000;
secs++;
}
leaved->tv_sec = secs;
leaved->tv_usec = usecs;
}
/* Add tv1 to tv2. */
#define TIMEVAL_ADD(tv1, tv2) \
({ \
suseconds_t usecs = (tv2).tv_usec + (tv1).tv_usec; \
time_t secs = (tv2).tv_sec + (tv1).tv_sec; \
\
if (usecs >= 1000000) { \
usecs -= 1000000; \
secs++; \
} \
(tv2).tv_sec = secs; \
(tv2).tv_usec = usecs; \
})
/* External references. */
......
......@@ -265,13 +265,19 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *,
struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
union sctp_addr *addr,
int vparam_len);
struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *,
union sctp_addr *,
struct sockaddr *,
int, int);
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_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf,
int vparam_len);
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
union sctp_addr *addr);
void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *);
......
......@@ -1085,6 +1085,10 @@ int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *,
int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *);
int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *,
struct sctp_opt *);
union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
const union sctp_addr *addrs,
int addrcnt,
struct sctp_opt *opt);
union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp,
int *addrs_len, int gfp);
int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len,
......@@ -1389,6 +1393,10 @@ struct sctp_association {
__u8 ipv4_address; /* Peer understands IPv4 addresses? */
__u8 ipv6_address; /* Peer understands IPv6 addresses? */
__u8 hostname_address;/* Peer understands DNS addresses? */
/* Does peer support ADDIP? */
__u8 asconf_capable;
struct sctp_inithdr i;
int cookie_len;
void *cookie;
......
......@@ -246,6 +246,11 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
*/
asoc->peer.sack_needed = 1;
/* Assume that the peer recongizes ASCONF until reported otherwise
* via an ERROR chunk.
*/
asoc->peer.asconf_capable = 1;
/* Create an input queue. */
sctp_inq_init(&asoc->base.inqueue);
sctp_inq_set_th_handler(&asoc->base.inqueue,
......
......@@ -324,6 +324,43 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp,
return 0;
}
/* Find the first address in the bind address list that is not present in
* the addrs packed array.
*/
union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
const union sctp_addr *addrs,
int addrcnt,
struct sctp_opt *opt)
{
struct sctp_sockaddr_entry *laddr;
union sctp_addr *addr;
void *addr_buf;
struct sctp_af *af;
struct list_head *pos;
int i;
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
addr_buf = (union sctp_addr *)addrs;
for (i = 0; i < addrcnt; i++) {
addr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
if (!af)
return NULL;
if (opt->pf->cmp_addr(&laddr->a, addr, opt))
break;
addr_buf += af->sockaddr_len;
}
if (i == addrcnt)
return &laddr->a;
}
return NULL;
}
/* Copy out addresses from the global local address list. */
static int sctp_copy_one_addr(struct sctp_bind_addr *dest,
union sctp_addr *addr,
......
/* SCTP kernel reference Implementation
* Copyright (C) IBM Corp. 2001, 2003
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 Intel Corp.
......@@ -1288,7 +1288,7 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
/* Set an expiration time for the cookie. */
do_gettimeofday(&cookie->c.expiration);
tv_add(&asoc->cookie_life, &cookie->c.expiration);
TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration);
/* Copy the peer's init packet. */
memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
......@@ -2021,11 +2021,11 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
sctp_addiphdr_t asconf;
struct sctp_chunk *retval;
int length = sizeof(asconf) + vparam_len;
union sctp_params addrparam;
union sctp_addr_param addrparam;
int addrlen;
struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family);
addrlen = af->to_addr_param(addr, (union sctp_addr_param *)&addrparam);
addrlen = af->to_addr_param(addr, &addrparam);
if (!addrlen)
return NULL;
length += addrlen;
......@@ -2045,6 +2045,83 @@ struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc,
return retval;
}
/* ADDIP
* 3.2.1 Add IP Address
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 0xC001 | Length = Variable |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ASCONF-Request Correlation ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Parameter |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* 3.2.2 Delete IP Address
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 0xC002 | Length = Variable |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | ASCONF-Request Correlation ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Address Parameter |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
union sctp_addr *laddr,
struct sockaddr *addrs,
int addrcnt,
int flags)
{
sctp_addip_param_t param;
struct sctp_chunk *retval;
union sctp_addr_param addr_param;
union sctp_addr *addr;
void *addr_buf;
struct sctp_af *af;
int paramlen = sizeof(param);
int addr_param_len = 0;
int totallen = 0;
int i;
/* Get total length of all the address parameters. */
addr_buf = addrs;
for (i = 0; i < addrcnt; i++) {
addr = (union sctp_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;
}
/* 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 = (union sctp_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);
sctp_addto_chunk(retval, paramlen, &param);
sctp_addto_chunk(retval, addr_param_len, &addr_param);
addr_buf += af->sockaddr_len;
}
return retval;
}
/* ADDIP
* 3.2.4 Set Primary IP Address
* 0 1 2 3
......@@ -2065,11 +2142,11 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
sctp_addip_param_t param;
struct sctp_chunk *retval;
int len = sizeof(param);
union sctp_params addrparam;
union sctp_addr_param addrparam;
int addrlen;
struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family);
addrlen = af->to_addr_param(addr, (union sctp_addr_param *)&addrparam);
addrlen = af->to_addr_param(addr, &addrparam);
if (!addrlen)
return NULL;
len += addrlen;
......@@ -2089,11 +2166,7 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
return retval;
}
/*
* Unpack the parameters in an ASCONF chunk into an association and
* generate ASCONF-ACK chunk.
*
* ADDIP 3.1.2 Address Configuration Acknowledgement Chunk (ASCONF-ACK)
/* ADDIP 3.1.2 Address Configuration Acknowledgement Chunk (ASCONF-ACK)
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
......@@ -2110,8 +2183,28 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
* | ASCONF Parameter Response#N |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* All the parameter respoinces will be added in this function.
* 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)
{
sctp_addiphdr_t asconf;
struct sctp_chunk *retval;
int length = sizeof(asconf) + vparam_len;
/* Create the chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_ASCONF_ACK, 0, length);
if (!retval)
return NULL;
asconf.serial = serial;
retval->subh.addip_hdr =
sctp_addto_chunk(retval, sizeof(asconf), &asconf);
return retval;
}
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf,
int vparam_len)
......
/* SCTP kernel reference Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -690,6 +690,44 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds,
chunk->transport = t;
}
/* Process an incoming Operation Error Chunk. */
static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds,
struct sctp_association *asoc,
struct sctp_chunk *chunk)
{
struct sctp_operr_chunk *operr_chunk;
struct sctp_errhdr *err_hdr;
operr_chunk = (struct sctp_operr_chunk *)chunk->chunk_hdr;
err_hdr = &operr_chunk->err_hdr;
switch (err_hdr->cause) {
case SCTP_ERROR_UNKNOWN_CHUNK:
{
struct sctp_chunkhdr *unk_chunk_hdr;
unk_chunk_hdr = (struct sctp_chunkhdr *)err_hdr->variable;
switch (unk_chunk_hdr->type) {
/* ADDIP 4.1 A9) If the peer responds to an ASCONF with an
* ERROR chunk reporting that it did not recognized the ASCONF
* chunk type, the sender of the ASCONF MUST NOT send any
* further ASCONF chunks and MUST stop its T-4 timer.
*/
case SCTP_CID_ASCONF:
asoc->peer.asconf_capable = 0;
sctp_add_cmd_sf(cmds, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
break;
default:
break;
}
break;
}
default:
break;
}
}
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
* functionality there.
......@@ -1205,6 +1243,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_setup_t4(commands, asoc, cmd->obj.ptr);
break;
case SCTP_CMD_PROCESS_OPERR:
sctp_cmd_process_operr(commands, asoc, chunk);
break;
default:
printk(KERN_WARNING "Impossible command: %u, %p\n",
cmd->verb, cmd->obj.ptr);
......
/* 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-2002 International Business Machines, Corp.
* Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2002 Nokia Corp.
*
......@@ -2864,6 +2864,9 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
sctp_ulpevent_free(ev);
goto nomem;
}
sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
SCTP_CHUNK(chunk));
}
return SCTP_DISPOSITION_CONSUME;
......
......@@ -97,6 +97,8 @@ static void sctp_wait_for_close(struct sock *sk, long timeo);
static inline int sctp_verify_addr(struct sock *, union sctp_addr *, int);
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_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk);
static void sctp_sock_migrate(struct sock *, struct sock *,
......@@ -349,6 +351,106 @@ int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
return retval;
}
/* Send an ASCONF chunk with Add IP address parameters to all the peers of the
* associations that are part of the endpoint indicating that a list of local
* addresses are added to the endpoint.
*
* If any of the addresses is already in the bind address list of the
* association, we do not send the chunk for that association. But it will not
* affect other associations.
*
* Only sctp_setsockopt_bindx() is supposed to call this function.
*/
static int sctp_send_asconf_add_ip(struct sock *sk,
struct sockaddr *addrs,
int addrcnt)
{
struct sctp_opt *sp;
struct sctp_endpoint *ep;
struct sctp_association *asoc;
struct sctp_bind_addr *bp;
struct sctp_chunk *chunk;
struct sctp_sockaddr_entry *laddr;
union sctp_addr *addr;
void *addr_buf;
struct sctp_af *af;
struct list_head *pos;
struct list_head *p;
int i;
int retval = 0;
sp = sctp_sk(sk);
ep = sp->ep;
SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",
__FUNCTION__, sk, addrs, addrcnt);
list_for_each(pos, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs);
if (!sctp_state(asoc, ESTABLISHED))
continue;
if (!asoc->peer.asconf_capable)
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
* other associations.
*/
addr_buf = addrs;
for (i = 0; i < addrcnt; i++) {
addr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
if (!af) {
retval = -EINVAL;
goto out;
}
if (sctp_assoc_lookup_laddr(asoc, addr))
break;
addr_buf += af->sockaddr_len;
}
if (i < addrcnt)
continue;
/* Use the first address in bind addr list of association as
* Address Parameter of ASCONF CHUNK.
*/
sctp_read_lock(&asoc->base.addr_lock);
bp = &asoc->base.bind_addr;
p = bp->address_list.next;
laddr = list_entry(p, struct sctp_sockaddr_entry, list);
sctp_read_unlock(&asoc->base.addr_lock);
chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
addrcnt, SCTP_PARAM_ADD_IP);
if (!chunk) {
retval = -ENOMEM;
goto out;
}
retval = sctp_primitive_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
* 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
* ASCONF_ACK is received.
*/
}
out:
return retval;
}
/* Remove a list of addresses from bind addresses list. Do not remove the
* last address.
*
......@@ -436,6 +538,106 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
return retval;
}
/* Send an ASCONF chunk with Delete IP address parameters to all the peers of
* the associations that are part of the endpoint indicating that a list of
* local addresses are removed from the endpoint.
*
* If any of the addresses is already in the bind address list of the
* association, we do not send the chunk for that association. But it will not
* affect other associations.
*
* Only sctp_setsockopt_bindx() is supposed to call this function.
*/
static int sctp_send_asconf_del_ip(struct sock *sk,
struct sockaddr *addrs,
int addrcnt)
{
struct sctp_opt *sp;
struct sctp_endpoint *ep;
struct sctp_association *asoc;
struct sctp_bind_addr *bp;
struct sctp_chunk *chunk;
union sctp_addr *laddr;
void *addr_buf;
struct sctp_af *af;
struct list_head *pos;
int i;
int retval = 0;
sp = sctp_sk(sk);
ep = sp->ep;
SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n",
__FUNCTION__, sk, addrs, addrcnt);
list_for_each(pos, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs);
if (!sctp_state(asoc, ESTABLISHED))
continue;
if (!asoc->peer.asconf_capable)
continue;
/* Check if any address in the packed array of addresses is
* not present in the bind address list of the association.
* If so, do not send the asconf chunk to its peer, but
* continue with other associations.
*/
addr_buf = addrs;
for (i = 0; i < addrcnt; i++) {
laddr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(laddr->v4.sin_family);
if (!af) {
retval = -EINVAL;
goto out;
}
if (!sctp_assoc_lookup_laddr(asoc, laddr))
break;
addr_buf += af->sockaddr_len;
}
if (i < addrcnt)
continue;
/* Find one address in the association's bind address list
* that is not in the packed array of addresses. This is to
* make sure that we do not delete all the addresses in the
* association.
*/
sctp_read_lock(&asoc->base.addr_lock);
bp = &asoc->base.bind_addr;
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
addrcnt, sp);
sctp_read_unlock(&asoc->base.addr_lock);
if (!laddr)
continue;
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;
}
/* 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
* packet is received.
*/
}
out:
return retval;
}
/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
*
* API 8.1
......@@ -564,10 +766,16 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, struct sockaddr *addrs,
switch (op) {
case SCTP_BINDX_ADD_ADDR:
err = sctp_bindx_add(sk, kaddrs, addrcnt);
if (err)
goto out;
err = sctp_send_asconf_add_ip(sk, kaddrs, addrcnt);
break;
case SCTP_BINDX_REM_ADDR:
err = sctp_bindx_rem(sk, kaddrs, addrcnt);
if (err)
goto out;
err = sctp_send_asconf_del_ip(sk, kaddrs, addrcnt);
break;
default:
......@@ -575,6 +783,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk, struct sockaddr *addrs,
break;
};
out:
kfree(kaddrs);
return err;
......@@ -2582,10 +2791,8 @@ static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
list_for_each(pos, &asoc->peer.transport_addr_list) {
cnt ++;
}
if (copy_to_user(optval, &cnt, sizeof(int)))
return -EFAULT;
return 0;
return cnt;
}
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
......@@ -2666,10 +2873,8 @@ static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
list_for_each(pos, &bp->address_list) {
cnt ++;
}
if (copy_to_user(optval, &cnt, sizeof(int)))
return -EFAULT;
return 0;
return cnt;
}
static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment