Commit 8a07eb0a50aebc8c95478d49c28c7f8419a26cef

Authored by Michio Honda
Committed by David S. Miller
1 parent 7dc04d7122

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>

Showing 6 changed files with 101 additions and 7 deletions Side-by-side Diff

include/net/sctp/structs.h
... ... @@ -1913,6 +1913,8 @@
1913 1913 * after reaching 4294967295.
1914 1914 */
1915 1915 __u32 addip_serial;
  1916 + union sctp_addr *asconf_addr_del_pending;
  1917 + int src_out_of_asoc_ok;
1916 1918  
1917 1919 /* SCTP AUTH: list of the endpoint shared keys. These
1918 1920 * keys are provided out of band by the user applicaton
net/sctp/associola.c
... ... @@ -280,6 +280,8 @@
280 280 asoc->peer.asconf_capable = 0;
281 281 if (sctp_addip_noauth)
282 282 asoc->peer.asconf_capable = 1;
  283 + asoc->asconf_addr_del_pending = NULL;
  284 + asoc->src_out_of_asoc_ok = 0;
283 285  
284 286 /* Create an input queue. */
285 287 sctp_inq_init(&asoc->base.inqueue);
... ... @@ -445,6 +447,10 @@
445 447 asoc->peer.transport_count = 0;
446 448  
447 449 sctp_asconf_queue_teardown(asoc);
  450 +
  451 + /* Free pending address space being deleted */
  452 + if (asoc->asconf_addr_del_pending != NULL)
  453 + kfree(asoc->asconf_addr_del_pending);
448 454  
449 455 /* AUTH - Free the endpoint shared keys */
450 456 sctp_auth_destroy_keys(&asoc->endpoint_shared_keys);
... ... @@ -754,6 +754,16 @@
754 754 */
755 755  
756 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 767 list_del_init(&chunk->list);
758 768  
759 769 /* Pick the right transport to use. */
... ... @@ -880,6 +890,9 @@
880 890 BUG();
881 891 }
882 892 }
  893 +
  894 + if (q->asoc->src_out_of_asoc_ok)
  895 + goto sctp_flush_out;
883 896  
884 897 /* Is it OK to send data chunks? */
885 898 switch (asoc->state) {
... ... @@ -503,7 +503,9 @@
503 503 sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port));
504 504 rcu_read_lock();
505 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 509 continue;
508 510 if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
509 511 goto out_unlock;
net/sctp/sm_make_chunk.c
... ... @@ -2768,6 +2768,7 @@
2768 2768 int addr_param_len = 0;
2769 2769 int totallen = 0;
2770 2770 int i;
  2771 + int del_pickup = 0;
2771 2772  
2772 2773 /* Get total length of all the address parameters. */
2773 2774 addr_buf = addrs;
... ... @@ -2780,6 +2781,13 @@
2780 2781 totallen += addr_param_len;
2781 2782  
2782 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 2793 /* Create an asconf chunk with the required length. */
... ... @@ -2802,6 +2810,17 @@
2802 2810  
2803 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 2824 return retval;
2806 2825 }
2807 2826  
... ... @@ -3224,6 +3243,11 @@
3224 3243 case SCTP_PARAM_DEL_IP:
3225 3244 local_bh_disable();
3226 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 3251 local_bh_enable();
3228 3252 list_for_each_entry(transport, &asoc->peer.transport_addr_list,
3229 3253 transports) {
... ... @@ -3380,6 +3404,9 @@
3380 3404 length);
3381 3405 asconf_len -= length;
3382 3406 }
  3407 +
  3408 + if (no_err && asoc->src_out_of_asoc_ok)
  3409 + asoc->src_out_of_asoc_ok = 0;
3383 3410  
3384 3411 /* Free the cached last sent asconf chunk. */
3385 3412 list_del_init(&asconf->transmitted_list);
... ... @@ -583,10 +583,6 @@
583 583 goto out;
584 584 }
585 585  
586   - retval = sctp_send_asconf(asoc, chunk);
587   - if (retval)
588   - goto out;
589   -
590 586 /* Add the new addresses to the bind address list with
591 587 * use_as_src set to 0.
592 588 */
... ... @@ -599,6 +595,23 @@
599 595 SCTP_ADDR_NEW, GFP_ATOMIC);
600 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  
604 617 out:
605 618  
... ... @@ -715,7 +728,9 @@
715 728 struct sctp_sockaddr_entry *saddr;
716 729 int i;
717 730 int retval = 0;
  731 + int stored = 0;
718 732  
  733 + chunk = NULL;
719 734 if (!sctp_addip_enable)
720 735 return retval;
721 736  
722 737  
... ... @@ -766,9 +781,34 @@
766 781 bp = &asoc->base.bind_addr;
767 782 laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
768 783 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;
771 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 + }
  811 +
772 812 /* We do not need RCU protection throughout this loop
773 813 * because this is done under a socket lock from the
774 814 * setsockopt call.
... ... @@ -780,6 +820,7 @@
780 820 goto out;
781 821 }
782 822  
  823 +skip_mkasconf:
783 824 /* Reset use_as_src flag for the addresses in the bind address
784 825 * list that are to be deleted.
785 826 */
... ... @@ -805,6 +846,9 @@
805 846 sctp_sk(asoc->base.sk));
806 847 }
807 848  
  849 + if (stored)
  850 + /* We don't need to transmit ASCONF */
  851 + continue;
808 852 retval = sctp_send_asconf(asoc, chunk);
809 853 }
810 854 out: