Commit f0f06257 authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP] ADDIP: Handle ERROR chunk in response to an ASCONF chunk.

Disable sending any further ASCONF chunks and stop its T-4 timer if
the peer responds to an ASCONF with an ERROR chunk.
parent fd1a5cba
/* 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;
......
......@@ -1393,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,
......
/* 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;
......
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