aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
authorMichio Honda <micchie@sfc.wide.ad.jp>2011-04-26 07:19:36 -0400
committerDavid S. Miller <davem@davemloft.net>2011-06-02 05:04:53 -0400
commit8a07eb0a50aebc8c95478d49c28c7f8419a26cef (patch)
treea98ab91189d1dd0781d7768c0b0d395c1919d571 /net/sctp
parent7dc04d712203eecdc1435a4cd135935c4a297be5 (diff)
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 <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>
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/associola.c6
-rw-r--r--net/sctp/outqueue.c13
-rw-r--r--net/sctp/protocol.c4
-rw-r--r--net/sctp/sm_make_chunk.c27
-rw-r--r--net/sctp/socket.c56
5 files changed, 99 insertions, 7 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 4a62888f2e43..dc16b90ddb6f 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -280,6 +280,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
280 asoc->peer.asconf_capable = 0; 280 asoc->peer.asconf_capable = 0;
281 if (sctp_addip_noauth) 281 if (sctp_addip_noauth)
282 asoc->peer.asconf_capable = 1; 282 asoc->peer.asconf_capable = 1;
283 asoc->asconf_addr_del_pending = NULL;
284 asoc->src_out_of_asoc_ok = 0;
283 285
284 /* Create an input queue. */ 286 /* Create an input queue. */
285 sctp_inq_init(&asoc->base.inqueue); 287 sctp_inq_init(&asoc->base.inqueue);
@@ -446,6 +448,10 @@ void sctp_association_free(struct sctp_association *asoc)
446 448
447 sctp_asconf_queue_teardown(asoc); 449 sctp_asconf_queue_teardown(asoc);
448 450
451 /* Free pending address space being deleted */
452 if (asoc->asconf_addr_del_pending != NULL)
453 kfree(asoc->asconf_addr_del_pending);
454
449 /* AUTH - Free the endpoint shared keys */ 455 /* AUTH - Free the endpoint shared keys */
450 sctp_auth_destroy_keys(&asoc->endpoint_shared_keys); 456 sctp_auth_destroy_keys(&asoc->endpoint_shared_keys);
451 457
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 1c88c8911dc5..edc753297a49 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
754 */ 754 */
755 755
756 list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) { 756 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
757 list_del_init(&chunk->list); 767 list_del_init(&chunk->list);
758 768
759 /* Pick the right transport to use. */ 769 /* Pick the right transport to use. */
@@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
881 } 891 }
882 } 892 }
883 893
894 if (q->asoc->src_out_of_asoc_ok)
895 goto sctp_flush_out;
896
884 /* Is it OK to send data chunks? */ 897 /* Is it OK to send data chunks? */
885 switch (asoc->state) { 898 switch (asoc->state) {
886 case SCTP_STATE_COOKIE_ECHOED: 899 case SCTP_STATE_COOKIE_ECHOED:
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 013c6136c546..af0a6b0fc9b6 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -503,7 +503,9 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
503 sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port)); 503 sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port));
504 rcu_read_lock(); 504 rcu_read_lock();
505 list_for_each_entry_rcu(laddr, &bp->address_list, list) { 505 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))
507 continue; 509 continue;
508 if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) 510 if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
509 goto out_unlock; 511 goto out_unlock;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 37406039820f..3363d3788259 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2768,6 +2768,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
2768 int addr_param_len = 0; 2768 int addr_param_len = 0;
2769 int totallen = 0; 2769 int totallen = 0;
2770 int i; 2770 int i;
2771 int del_pickup = 0;
2771 2772
2772 /* Get total length of all the address parameters. */ 2773 /* Get total length of all the address parameters. */
2773 addr_buf = addrs; 2774 addr_buf = addrs;
@@ -2780,6 +2781,13 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
2780 totallen += addr_param_len; 2781 totallen += addr_param_len;
2781 2782
2782 addr_buf += af->sockaddr_len; 2783 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 }
2783 } 2791 }
2784 2792
2785 /* Create an asconf chunk with the required length. */ 2793 /* Create an asconf chunk with the required length. */
@@ -2802,6 +2810,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
2802 2810
2803 addr_buf += af->sockaddr_len; 2811 addr_buf += af->sockaddr_len;
2804 } 2812 }
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 }
2805 return retval; 2824 return retval;
2806} 2825}
2807 2826
@@ -3224,6 +3243,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
3224 case SCTP_PARAM_DEL_IP: 3243 case SCTP_PARAM_DEL_IP:
3225 local_bh_disable(); 3244 local_bh_disable();
3226 sctp_del_bind_addr(bp, &addr); 3245 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 }
3227 local_bh_enable(); 3251 local_bh_enable();
3228 list_for_each_entry(transport, &asoc->peer.transport_addr_list, 3252 list_for_each_entry(transport, &asoc->peer.transport_addr_list,
3229 transports) { 3253 transports) {
@@ -3381,6 +3405,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
3381 asconf_len -= length; 3405 asconf_len -= length;
3382 } 3406 }
3383 3407
3408 if (no_err && asoc->src_out_of_asoc_ok)
3409 asoc->src_out_of_asoc_ok = 0;
3410
3384 /* Free the cached last sent asconf chunk. */ 3411 /* Free the cached last sent asconf chunk. */
3385 list_del_init(&asconf->transmitted_list); 3412 list_del_init(&asconf->transmitted_list);
3386 sctp_chunk_free(asconf); 3413 sctp_chunk_free(asconf);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index cc06198dc444..e7e1b142875c 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
583 goto out; 583 goto out;
584 } 584 }
585 585
586 retval = sctp_send_asconf(asoc, chunk);
587 if (retval)
588 goto out;
589
590 /* Add the new addresses to the bind address list with 586 /* Add the new addresses to the bind address list with
591 * use_as_src set to 0. 587 * use_as_src set to 0.
592 */ 588 */
@@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
599 SCTP_ADDR_NEW, GFP_ATOMIC); 595 SCTP_ADDR_NEW, GFP_ATOMIC);
600 addr_buf += af->sockaddr_len; 596 addr_buf += af->sockaddr_len;
601 } 597 }
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);
602 } 615 }
603 616
604out: 617out:
@@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
715 struct sctp_sockaddr_entry *saddr; 728 struct sctp_sockaddr_entry *saddr;
716 int i; 729 int i;
717 int retval = 0; 730 int retval = 0;
731 int stored = 0;
718 732
733 chunk = NULL;
719 if (!sctp_addip_enable) 734 if (!sctp_addip_enable)
720 return retval; 735 return retval;
721 736
@@ -766,8 +781,33 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
766 bp = &asoc->base.bind_addr; 781 bp = &asoc->base.bind_addr;
767 laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, 782 laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
768 addrcnt, sp); 783 addrcnt, sp);
769 if (!laddr) 784 if ((laddr == NULL) && (addrcnt == 1)) {
770 continue; 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 }
771 811
772 /* We do not need RCU protection throughout this loop 812 /* We do not need RCU protection throughout this loop
773 * because this is done under a socket lock from the 813 * because this is done under a socket lock from the
@@ -780,6 +820,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
780 goto out; 820 goto out;
781 } 821 }
782 822
823skip_mkasconf:
783 /* Reset use_as_src flag for the addresses in the bind address 824 /* Reset use_as_src flag for the addresses in the bind address
784 * list that are to be deleted. 825 * list that are to be deleted.
785 */ 826 */
@@ -805,6 +846,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
805 sctp_sk(asoc->base.sk)); 846 sctp_sk(asoc->base.sk));
806 } 847 }
807 848
849 if (stored)
850 /* We don't need to transmit ASCONF */
851 continue;
808 retval = sctp_send_asconf(asoc, chunk); 852 retval = sctp_send_asconf(asoc, chunk);
809 } 853 }
810out: 854out: