Skip to content

Commit 8a07eb0

Browse files
micchiedavem330
authored andcommitted
sctp: Add ASCONF operation on the single-homed host
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 <[email protected]> Signed-off-by: YOSHIFUJI Hideaki <[email protected]> Acked-by: Wei Yongjun <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7dc04d7 commit 8a07eb0

File tree

6 files changed

+101
-7
lines changed

6 files changed

+101
-7
lines changed

include/net/sctp/structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,8 @@ struct sctp_association {
19131913
* after reaching 4294967295.
19141914
*/
19151915
__u32 addip_serial;
1916+
union sctp_addr *asconf_addr_del_pending;
1917+
int src_out_of_asoc_ok;
19161918

19171919
/* SCTP AUTH: list of the endpoint shared keys. These
19181920
* keys are provided out of band by the user applicaton

net/sctp/associola.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
280280
asoc->peer.asconf_capable = 0;
281281
if (sctp_addip_noauth)
282282
asoc->peer.asconf_capable = 1;
283+
asoc->asconf_addr_del_pending = NULL;
284+
asoc->src_out_of_asoc_ok = 0;
283285

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

447449
sctp_asconf_queue_teardown(asoc);
448450

451+
/* Free pending address space being deleted */
452+
if (asoc->asconf_addr_del_pending != NULL)
453+
kfree(asoc->asconf_addr_del_pending);
454+
449455
/* AUTH - Free the endpoint shared keys */
450456
sctp_auth_destroy_keys(&asoc->endpoint_shared_keys);
451457

net/sctp/outqueue.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
754754
*/
755755

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

759769
/* Pick the right transport to use. */
@@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
881891
}
882892
}
883893

894+
if (q->asoc->src_out_of_asoc_ok)
895+
goto sctp_flush_out;
896+
884897
/* Is it OK to send data chunks? */
885898
switch (asoc->state) {
886899
case SCTP_STATE_COOKIE_ECHOED:

net/sctp/protocol.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,9 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
503503
sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port));
504504
rcu_read_lock();
505505
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
506-
if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
506+
if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) ||
507+
(laddr->state != SCTP_ADDR_SRC &&
508+
!asoc->src_out_of_asoc_ok))
507509
continue;
508510
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
509511
goto out_unlock;

net/sctp/sm_make_chunk.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,6 +2768,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
27682768
int addr_param_len = 0;
27692769
int totallen = 0;
27702770
int i;
2771+
int del_pickup = 0;
27712772

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

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

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

28032811
addr_buf += af->sockaddr_len;
28042812
}
2813+
if (flags == SCTP_PARAM_ADD_IP && del_pickup) {
2814+
addr = asoc->asconf_addr_del_pending;
2815+
af = sctp_get_af_specific(addr->v4.sin_family);
2816+
addr_param_len = af->to_addr_param(addr, &addr_param);
2817+
param.param_hdr.type = SCTP_PARAM_DEL_IP;
2818+
param.param_hdr.length = htons(paramlen + addr_param_len);
2819+
param.crr_id = i;
2820+
2821+
sctp_addto_chunk(retval, paramlen, &param);
2822+
sctp_addto_chunk(retval, addr_param_len, &addr_param);
2823+
}
28052824
return retval;
28062825
}
28072826

@@ -3224,6 +3243,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
32243243
case SCTP_PARAM_DEL_IP:
32253244
local_bh_disable();
32263245
sctp_del_bind_addr(bp, &addr);
3246+
if (asoc->asconf_addr_del_pending != NULL &&
3247+
sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
3248+
kfree(asoc->asconf_addr_del_pending);
3249+
asoc->asconf_addr_del_pending = NULL;
3250+
}
32273251
local_bh_enable();
32283252
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
32293253
transports) {
@@ -3381,6 +3405,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
33813405
asconf_len -= length;
33823406
}
33833407

3408+
if (no_err && asoc->src_out_of_asoc_ok)
3409+
asoc->src_out_of_asoc_ok = 0;
3410+
33843411
/* Free the cached last sent asconf chunk. */
33853412
list_del_init(&asconf->transmitted_list);
33863413
sctp_chunk_free(asconf);

net/sctp/socket.c

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
583583
goto out;
584584
}
585585

586-
retval = sctp_send_asconf(asoc, chunk);
587-
if (retval)
588-
goto out;
589-
590586
/* Add the new addresses to the bind address list with
591587
* use_as_src set to 0.
592588
*/
@@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
599595
SCTP_ADDR_NEW, GFP_ATOMIC);
600596
addr_buf += af->sockaddr_len;
601597
}
598+
if (asoc->src_out_of_asoc_ok) {
599+
struct sctp_transport *trans;
600+
601+
list_for_each_entry(trans,
602+
&asoc->peer.transport_addr_list, transports) {
603+
/* Clear the source and route cache */
604+
dst_release(trans->dst);
605+
trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
606+
2*asoc->pathmtu, 4380));
607+
trans->ssthresh = asoc->peer.i.a_rwnd;
608+
trans->rto = asoc->rto_initial;
609+
trans->rtt = trans->srtt = trans->rttvar = 0;
610+
sctp_transport_route(trans, NULL,
611+
sctp_sk(asoc->base.sk));
612+
}
613+
}
614+
retval = sctp_send_asconf(asoc, chunk);
602615
}
603616

604617
out:
@@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
715728
struct sctp_sockaddr_entry *saddr;
716729
int i;
717730
int retval = 0;
731+
int stored = 0;
718732

733+
chunk = NULL;
719734
if (!sctp_addip_enable)
720735
return retval;
721736

@@ -766,8 +781,33 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
766781
bp = &asoc->base.bind_addr;
767782
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
768783
addrcnt, sp);
769-
if (!laddr)
770-
continue;
784+
if ((laddr == NULL) && (addrcnt == 1)) {
785+
if (asoc->asconf_addr_del_pending)
786+
continue;
787+
asoc->asconf_addr_del_pending =
788+
kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
789+
asoc->asconf_addr_del_pending->sa.sa_family =
790+
addrs->sa_family;
791+
asoc->asconf_addr_del_pending->v4.sin_port =
792+
htons(bp->port);
793+
if (addrs->sa_family == AF_INET) {
794+
struct sockaddr_in *sin;
795+
796+
sin = (struct sockaddr_in *)addrs;
797+
asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
798+
} else if (addrs->sa_family == AF_INET6) {
799+
struct sockaddr_in6 *sin6;
800+
801+
sin6 = (struct sockaddr_in6 *)addrs;
802+
ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr);
803+
}
804+
SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
805+
" at %p\n", asoc, asoc->asconf_addr_del_pending,
806+
asoc->asconf_addr_del_pending);
807+
asoc->src_out_of_asoc_ok = 1;
808+
stored = 1;
809+
goto skip_mkasconf;
810+
}
771811

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

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

849+
if (stored)
850+
/* We don't need to transmit ASCONF */
851+
continue;
808852
retval = sctp_send_asconf(asoc, chunk);
809853
}
810854
out:

0 commit comments

Comments
 (0)