Skip to content

Commit

Permalink
sctp: Add ASCONF operation on the single-homed host
Browse files Browse the repository at this point in the history
In this case, the SCTP association transmits an ASCONF packet
including addition of the new IP address and deletion of the old
address.  This patch implements this functionality.
In this case, the ASCONF chunk is added to the beginning of the
queue, because the other chunks cannot be transmitted in this state.

Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Acked-by: Wei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Michio Honda authored and David S. Miller committed Jun 2, 2011
1 parent 7dc04d7 commit 8a07eb0
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 7 deletions.
2 changes: 2 additions & 0 deletions include/net/sctp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1913,6 +1913,8 @@ struct sctp_association {
* after reaching 4294967295.
*/
__u32 addip_serial;
union sctp_addr *asconf_addr_del_pending;
int src_out_of_asoc_ok;

/* SCTP AUTH: list of the endpoint shared keys. These
* keys are provided out of band by the user applicaton
Expand Down
6 changes: 6 additions & 0 deletions net/sctp/associola.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->peer.asconf_capable = 0;
if (sctp_addip_noauth)
asoc->peer.asconf_capable = 1;
asoc->asconf_addr_del_pending = NULL;
asoc->src_out_of_asoc_ok = 0;

/* Create an input queue. */
sctp_inq_init(&asoc->base.inqueue);
Expand Down Expand Up @@ -446,6 +448,10 @@ void sctp_association_free(struct sctp_association *asoc)

sctp_asconf_queue_teardown(asoc);

/* Free pending address space being deleted */
if (asoc->asconf_addr_del_pending != NULL)
kfree(asoc->asconf_addr_del_pending);

/* AUTH - Free the endpoint shared keys */
sctp_auth_destroy_keys(&asoc->endpoint_shared_keys);

Expand Down
13 changes: 13 additions & 0 deletions net/sctp/outqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/

list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
/* RFC 5061, 5.3
* F1) This means that until such time as the ASCONF
* containing the add is acknowledged, the sender MUST
* NOT use the new IP address as a source for ANY SCTP
* packet except on carrying an ASCONF Chunk.
*/
if (asoc->src_out_of_asoc_ok &&
chunk->chunk_hdr->type != SCTP_CID_ASCONF)
continue;

list_del_init(&chunk->list);

/* Pick the right transport to use. */
Expand Down Expand Up @@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
}
}

if (q->asoc->src_out_of_asoc_ok)
goto sctp_flush_out;

/* Is it OK to send data chunks? */
switch (asoc->state) {
case SCTP_STATE_COOKIE_ECHOED:
Expand Down
4 changes: 3 additions & 1 deletion net/sctp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,9 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port));
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) ||
(laddr->state != SCTP_ADDR_SRC &&
!asoc->src_out_of_asoc_ok))
continue;
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
Expand Down
27 changes: 27 additions & 0 deletions net/sctp/sm_make_chunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
int addr_param_len = 0;
int totallen = 0;
int i;
int del_pickup = 0;

/* Get total length of all the address parameters. */
addr_buf = addrs;
Expand All @@ -2780,6 +2781,13 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
totallen += addr_param_len;

addr_buf += af->sockaddr_len;
if (asoc->asconf_addr_del_pending && !del_pickup) {
/* reuse the parameter length from the same scope one */
totallen += paramlen;
totallen += addr_param_len;
del_pickup = 1;
SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen);
}
}

/* Create an asconf chunk with the required length. */
Expand All @@ -2802,6 +2810,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,

addr_buf += af->sockaddr_len;
}
if (flags == SCTP_PARAM_ADD_IP && del_pickup) {
addr = asoc->asconf_addr_del_pending;
af = sctp_get_af_specific(addr->v4.sin_family);
addr_param_len = af->to_addr_param(addr, &addr_param);
param.param_hdr.type = SCTP_PARAM_DEL_IP;
param.param_hdr.length = htons(paramlen + addr_param_len);
param.crr_id = i;

sctp_addto_chunk(retval, paramlen, &param);
sctp_addto_chunk(retval, addr_param_len, &addr_param);
}
return retval;
}

Expand Down Expand Up @@ -3224,6 +3243,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
case SCTP_PARAM_DEL_IP:
local_bh_disable();
sctp_del_bind_addr(bp, &addr);
if (asoc->asconf_addr_del_pending != NULL &&
sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
kfree(asoc->asconf_addr_del_pending);
asoc->asconf_addr_del_pending = NULL;
}
local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
Expand Down Expand Up @@ -3381,6 +3405,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
asconf_len -= length;
}

if (no_err && asoc->src_out_of_asoc_ok)
asoc->src_out_of_asoc_ok = 0;

/* Free the cached last sent asconf chunk. */
list_del_init(&asconf->transmitted_list);
sctp_chunk_free(asconf);
Expand Down
56 changes: 50 additions & 6 deletions net/sctp/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
goto out;
}

retval = sctp_send_asconf(asoc, chunk);
if (retval)
goto out;

/* Add the new addresses to the bind address list with
* use_as_src set to 0.
*/
Expand All @@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
}
if (asoc->src_out_of_asoc_ok) {
struct sctp_transport *trans;

list_for_each_entry(trans,
&asoc->peer.transport_addr_list, transports) {
/* Clear the source and route cache */
dst_release(trans->dst);
trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
2*asoc->pathmtu, 4380));
trans->ssthresh = asoc->peer.i.a_rwnd;
trans->rto = asoc->rto_initial;
trans->rtt = trans->srtt = trans->rttvar = 0;
sctp_transport_route(trans, NULL,
sctp_sk(asoc->base.sk));
}
}
retval = sctp_send_asconf(asoc, chunk);
}

out:
Expand Down Expand Up @@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
struct sctp_sockaddr_entry *saddr;
int i;
int retval = 0;
int stored = 0;

chunk = NULL;
if (!sctp_addip_enable)
return retval;

Expand Down Expand Up @@ -766,8 +781,33 @@ 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);
if (!laddr)
continue;
if ((laddr == NULL) && (addrcnt == 1)) {
if (asoc->asconf_addr_del_pending)
continue;
asoc->asconf_addr_del_pending =
kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
asoc->asconf_addr_del_pending->sa.sa_family =
addrs->sa_family;
asoc->asconf_addr_del_pending->v4.sin_port =
htons(bp->port);
if (addrs->sa_family == AF_INET) {
struct sockaddr_in *sin;

sin = (struct sockaddr_in *)addrs;
asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
} else if (addrs->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6;

sin6 = (struct sockaddr_in6 *)addrs;
ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr);
}
SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
" at %p\n", asoc, asoc->asconf_addr_del_pending,
asoc->asconf_addr_del_pending);
asoc->src_out_of_asoc_ok = 1;
stored = 1;
goto skip_mkasconf;
}

/* We do not need RCU protection throughout this loop
* because this is done under a socket lock from the
Expand All @@ -780,6 +820,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
goto out;
}

skip_mkasconf:
/* Reset use_as_src flag for the addresses in the bind address
* list that are to be deleted.
*/
Expand All @@ -805,6 +846,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
sctp_sk(asoc->base.sk));
}

if (stored)
/* We don't need to transmit ASCONF */
continue;
retval = sctp_send_asconf(asoc, chunk);
}
out:
Expand Down

0 comments on commit 8a07eb0

Please sign in to comment.