diff options
Diffstat (limited to 'net/sctp')
-rw-r--r-- | net/sctp/associola.c | 6 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 13 | ||||
-rw-r--r-- | net/sctp/protocol.c | 4 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 27 | ||||
-rw-r--r-- | net/sctp/socket.c | 56 |
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, ¶m); | ||
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 | ||
604 | out: | 617 | out: |
@@ -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 | ||
823 | skip_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 | } |
810 | out: | 854 | out: |