diff options
-rw-r--r-- | Documentation/networking/ip-sysctl.txt | 14 | ||||
-rw-r--r-- | include/net/sctp/constants.h | 1 | ||||
-rw-r--r-- | include/net/sctp/structs.h | 20 | ||||
-rw-r--r-- | include/net/sctp/user.h | 11 | ||||
-rw-r--r-- | net/sctp/associola.c | 37 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 6 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 33 | ||||
-rw-r--r-- | net/sctp/socket.c | 101 | ||||
-rw-r--r-- | net/sctp/sysctl.c | 9 | ||||
-rw-r--r-- | net/sctp/transport.c | 4 |
10 files changed, 221 insertions, 15 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 5f3ef7f7fcec..406a5226220d 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt | |||
@@ -1440,6 +1440,20 @@ path_max_retrans - INTEGER | |||
1440 | 1440 | ||
1441 | Default: 5 | 1441 | Default: 5 |
1442 | 1442 | ||
1443 | pf_retrans - INTEGER | ||
1444 | The number of retransmissions that will be attempted on a given path | ||
1445 | before traffic is redirected to an alternate transport (should one | ||
1446 | exist). Note this is distinct from path_max_retrans, as a path that | ||
1447 | passes the pf_retrans threshold can still be used. Its only | ||
1448 | deprioritized when a transmission path is selected by the stack. This | ||
1449 | setting is primarily used to enable fast failover mechanisms without | ||
1450 | having to reduce path_max_retrans to a very low value. See: | ||
1451 | http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt | ||
1452 | for details. Note also that a value of pf_retrans > path_max_retrans | ||
1453 | disables this feature | ||
1454 | |||
1455 | Default: 0 | ||
1456 | |||
1443 | rto_initial - INTEGER | 1457 | rto_initial - INTEGER |
1444 | The initial round trip timeout value in milliseconds that will be used | 1458 | The initial round trip timeout value in milliseconds that will be used |
1445 | in calculating round trip times. This is the initial time interval | 1459 | in calculating round trip times. This is the initial time interval |
diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 942b864f6135..d053d2e99876 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h | |||
@@ -334,6 +334,7 @@ typedef enum { | |||
334 | typedef enum { | 334 | typedef enum { |
335 | SCTP_TRANSPORT_UP, | 335 | SCTP_TRANSPORT_UP, |
336 | SCTP_TRANSPORT_DOWN, | 336 | SCTP_TRANSPORT_DOWN, |
337 | SCTP_TRANSPORT_PF, | ||
337 | } sctp_transport_cmd_t; | 338 | } sctp_transport_cmd_t; |
338 | 339 | ||
339 | /* These are the address scopes defined mainly for IPv4 addresses | 340 | /* These are the address scopes defined mainly for IPv4 addresses |
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 536e439ddf1d..fc5e60016e37 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -161,6 +161,12 @@ extern struct sctp_globals { | |||
161 | int max_retrans_path; | 161 | int max_retrans_path; |
162 | int max_retrans_init; | 162 | int max_retrans_init; |
163 | 163 | ||
164 | /* Potentially-Failed.Max.Retrans sysctl value | ||
165 | * taken from: | ||
166 | * http://tools.ietf.org/html/draft-nishida-tsvwg-sctp-failover-05 | ||
167 | */ | ||
168 | int pf_retrans; | ||
169 | |||
164 | /* | 170 | /* |
165 | * Policy for preforming sctp/socket accounting | 171 | * Policy for preforming sctp/socket accounting |
166 | * 0 - do socket level accounting, all assocs share sk_sndbuf | 172 | * 0 - do socket level accounting, all assocs share sk_sndbuf |
@@ -258,6 +264,7 @@ extern struct sctp_globals { | |||
258 | #define sctp_sndbuf_policy (sctp_globals.sndbuf_policy) | 264 | #define sctp_sndbuf_policy (sctp_globals.sndbuf_policy) |
259 | #define sctp_rcvbuf_policy (sctp_globals.rcvbuf_policy) | 265 | #define sctp_rcvbuf_policy (sctp_globals.rcvbuf_policy) |
260 | #define sctp_max_retrans_path (sctp_globals.max_retrans_path) | 266 | #define sctp_max_retrans_path (sctp_globals.max_retrans_path) |
267 | #define sctp_pf_retrans (sctp_globals.pf_retrans) | ||
261 | #define sctp_max_retrans_init (sctp_globals.max_retrans_init) | 268 | #define sctp_max_retrans_init (sctp_globals.max_retrans_init) |
262 | #define sctp_sack_timeout (sctp_globals.sack_timeout) | 269 | #define sctp_sack_timeout (sctp_globals.sack_timeout) |
263 | #define sctp_hb_interval (sctp_globals.hb_interval) | 270 | #define sctp_hb_interval (sctp_globals.hb_interval) |
@@ -990,10 +997,15 @@ struct sctp_transport { | |||
990 | 997 | ||
991 | /* This is the max_retrans value for the transport and will | 998 | /* This is the max_retrans value for the transport and will |
992 | * be initialized from the assocs value. This can be changed | 999 | * be initialized from the assocs value. This can be changed |
993 | * using SCTP_SET_PEER_ADDR_PARAMS socket option. | 1000 | * using the SCTP_SET_PEER_ADDR_PARAMS socket option. |
994 | */ | 1001 | */ |
995 | __u16 pathmaxrxt; | 1002 | __u16 pathmaxrxt; |
996 | 1003 | ||
1004 | /* This is the partially failed retrans value for the transport | ||
1005 | * and will be initialized from the assocs value. This can be changed | ||
1006 | * using the SCTP_PEER_ADDR_THLDS socket option | ||
1007 | */ | ||
1008 | int pf_retrans; | ||
997 | /* PMTU : The current known path MTU. */ | 1009 | /* PMTU : The current known path MTU. */ |
998 | __u32 pathmtu; | 1010 | __u32 pathmtu; |
999 | 1011 | ||
@@ -1664,6 +1676,12 @@ struct sctp_association { | |||
1664 | */ | 1676 | */ |
1665 | int max_retrans; | 1677 | int max_retrans; |
1666 | 1678 | ||
1679 | /* This is the partially failed retrans value for the transport | ||
1680 | * and will be initialized from the assocs value. This can be | ||
1681 | * changed using the SCTP_PEER_ADDR_THLDS socket option | ||
1682 | */ | ||
1683 | int pf_retrans; | ||
1684 | |||
1667 | /* Maximum number of times the endpoint will retransmit INIT */ | 1685 | /* Maximum number of times the endpoint will retransmit INIT */ |
1668 | __u16 max_init_attempts; | 1686 | __u16 max_init_attempts; |
1669 | 1687 | ||
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 0842ef00b2fe..1b02d7ad453b 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h | |||
@@ -93,6 +93,7 @@ typedef __s32 sctp_assoc_t; | |||
93 | #define SCTP_GET_ASSOC_NUMBER 28 /* Read only */ | 93 | #define SCTP_GET_ASSOC_NUMBER 28 /* Read only */ |
94 | #define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ | 94 | #define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */ |
95 | #define SCTP_AUTO_ASCONF 30 | 95 | #define SCTP_AUTO_ASCONF 30 |
96 | #define SCTP_PEER_ADDR_THLDS 31 | ||
96 | 97 | ||
97 | /* Internal Socket Options. Some of the sctp library functions are | 98 | /* Internal Socket Options. Some of the sctp library functions are |
98 | * implemented using these socket options. | 99 | * implemented using these socket options. |
@@ -649,6 +650,7 @@ struct sctp_paddrinfo { | |||
649 | */ | 650 | */ |
650 | enum sctp_spinfo_state { | 651 | enum sctp_spinfo_state { |
651 | SCTP_INACTIVE, | 652 | SCTP_INACTIVE, |
653 | SCTP_PF, | ||
652 | SCTP_ACTIVE, | 654 | SCTP_ACTIVE, |
653 | SCTP_UNCONFIRMED, | 655 | SCTP_UNCONFIRMED, |
654 | SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ | 656 | SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ |
@@ -741,4 +743,13 @@ typedef struct { | |||
741 | int sd; | 743 | int sd; |
742 | } sctp_peeloff_arg_t; | 744 | } sctp_peeloff_arg_t; |
743 | 745 | ||
746 | /* | ||
747 | * Peer Address Thresholds socket option | ||
748 | */ | ||
749 | struct sctp_paddrthlds { | ||
750 | sctp_assoc_t spt_assoc_id; | ||
751 | struct sockaddr_storage spt_address; | ||
752 | __u16 spt_pathmaxrxt; | ||
753 | __u16 spt_pathpfthld; | ||
754 | }; | ||
744 | #endif /* __net_sctp_user_h__ */ | 755 | #endif /* __net_sctp_user_h__ */ |
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 8cf348e62e74..ebaef3ed6065 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c | |||
@@ -124,6 +124,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a | |||
124 | * socket values. | 124 | * socket values. |
125 | */ | 125 | */ |
126 | asoc->max_retrans = sp->assocparams.sasoc_asocmaxrxt; | 126 | asoc->max_retrans = sp->assocparams.sasoc_asocmaxrxt; |
127 | asoc->pf_retrans = sctp_pf_retrans; | ||
128 | |||
127 | asoc->rto_initial = msecs_to_jiffies(sp->rtoinfo.srto_initial); | 129 | asoc->rto_initial = msecs_to_jiffies(sp->rtoinfo.srto_initial); |
128 | asoc->rto_max = msecs_to_jiffies(sp->rtoinfo.srto_max); | 130 | asoc->rto_max = msecs_to_jiffies(sp->rtoinfo.srto_max); |
129 | asoc->rto_min = msecs_to_jiffies(sp->rtoinfo.srto_min); | 131 | asoc->rto_min = msecs_to_jiffies(sp->rtoinfo.srto_min); |
@@ -686,6 +688,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, | |||
686 | /* Set the path max_retrans. */ | 688 | /* Set the path max_retrans. */ |
687 | peer->pathmaxrxt = asoc->pathmaxrxt; | 689 | peer->pathmaxrxt = asoc->pathmaxrxt; |
688 | 690 | ||
691 | /* And the partial failure retrnas threshold */ | ||
692 | peer->pf_retrans = asoc->pf_retrans; | ||
693 | |||
689 | /* Initialize the peer's SACK delay timeout based on the | 694 | /* Initialize the peer's SACK delay timeout based on the |
690 | * association configured value. | 695 | * association configured value. |
691 | */ | 696 | */ |
@@ -841,6 +846,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
841 | struct sctp_ulpevent *event; | 846 | struct sctp_ulpevent *event; |
842 | struct sockaddr_storage addr; | 847 | struct sockaddr_storage addr; |
843 | int spc_state = 0; | 848 | int spc_state = 0; |
849 | bool ulp_notify = true; | ||
844 | 850 | ||
845 | /* Record the transition on the transport. */ | 851 | /* Record the transition on the transport. */ |
846 | switch (command) { | 852 | switch (command) { |
@@ -854,6 +860,14 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
854 | spc_state = SCTP_ADDR_CONFIRMED; | 860 | spc_state = SCTP_ADDR_CONFIRMED; |
855 | else | 861 | else |
856 | spc_state = SCTP_ADDR_AVAILABLE; | 862 | spc_state = SCTP_ADDR_AVAILABLE; |
863 | /* Don't inform ULP about transition from PF to | ||
864 | * active state and set cwnd to 1, see SCTP | ||
865 | * Quick failover draft section 5.1, point 5 | ||
866 | */ | ||
867 | if (transport->state == SCTP_PF) { | ||
868 | ulp_notify = false; | ||
869 | transport->cwnd = 1; | ||
870 | } | ||
857 | transport->state = SCTP_ACTIVE; | 871 | transport->state = SCTP_ACTIVE; |
858 | break; | 872 | break; |
859 | 873 | ||
@@ -872,6 +886,11 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
872 | spc_state = SCTP_ADDR_UNREACHABLE; | 886 | spc_state = SCTP_ADDR_UNREACHABLE; |
873 | break; | 887 | break; |
874 | 888 | ||
889 | case SCTP_TRANSPORT_PF: | ||
890 | transport->state = SCTP_PF; | ||
891 | ulp_notify = false; | ||
892 | break; | ||
893 | |||
875 | default: | 894 | default: |
876 | return; | 895 | return; |
877 | } | 896 | } |
@@ -879,12 +898,15 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
879 | /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the | 898 | /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the |
880 | * user. | 899 | * user. |
881 | */ | 900 | */ |
882 | memset(&addr, 0, sizeof(struct sockaddr_storage)); | 901 | if (ulp_notify) { |
883 | memcpy(&addr, &transport->ipaddr, transport->af_specific->sockaddr_len); | 902 | memset(&addr, 0, sizeof(struct sockaddr_storage)); |
884 | event = sctp_ulpevent_make_peer_addr_change(asoc, &addr, | 903 | memcpy(&addr, &transport->ipaddr, |
885 | 0, spc_state, error, GFP_ATOMIC); | 904 | transport->af_specific->sockaddr_len); |
886 | if (event) | 905 | event = sctp_ulpevent_make_peer_addr_change(asoc, &addr, |
887 | sctp_ulpq_tail_event(&asoc->ulpq, event); | 906 | 0, spc_state, error, GFP_ATOMIC); |
907 | if (event) | ||
908 | sctp_ulpq_tail_event(&asoc->ulpq, event); | ||
909 | } | ||
888 | 910 | ||
889 | /* Select new active and retran paths. */ | 911 | /* Select new active and retran paths. */ |
890 | 912 | ||
@@ -900,7 +922,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
900 | transports) { | 922 | transports) { |
901 | 923 | ||
902 | if ((t->state == SCTP_INACTIVE) || | 924 | if ((t->state == SCTP_INACTIVE) || |
903 | (t->state == SCTP_UNCONFIRMED)) | 925 | (t->state == SCTP_UNCONFIRMED) || |
926 | (t->state == SCTP_PF)) | ||
904 | continue; | 927 | continue; |
905 | if (!first || t->last_time_heard > first->last_time_heard) { | 928 | if (!first || t->last_time_heard > first->last_time_heard) { |
906 | second = first; | 929 | second = first; |
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index a0fa19f5650c..e7aa177c9522 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c | |||
@@ -792,7 +792,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) | |||
792 | if (!new_transport) | 792 | if (!new_transport) |
793 | new_transport = asoc->peer.active_path; | 793 | new_transport = asoc->peer.active_path; |
794 | } else if ((new_transport->state == SCTP_INACTIVE) || | 794 | } else if ((new_transport->state == SCTP_INACTIVE) || |
795 | (new_transport->state == SCTP_UNCONFIRMED)) { | 795 | (new_transport->state == SCTP_UNCONFIRMED) || |
796 | (new_transport->state == SCTP_PF)) { | ||
796 | /* If the chunk is Heartbeat or Heartbeat Ack, | 797 | /* If the chunk is Heartbeat or Heartbeat Ack, |
797 | * send it to chunk->transport, even if it's | 798 | * send it to chunk->transport, even if it's |
798 | * inactive. | 799 | * inactive. |
@@ -987,7 +988,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) | |||
987 | new_transport = chunk->transport; | 988 | new_transport = chunk->transport; |
988 | if (!new_transport || | 989 | if (!new_transport || |
989 | ((new_transport->state == SCTP_INACTIVE) || | 990 | ((new_transport->state == SCTP_INACTIVE) || |
990 | (new_transport->state == SCTP_UNCONFIRMED))) | 991 | (new_transport->state == SCTP_UNCONFIRMED) || |
992 | (new_transport->state == SCTP_PF))) | ||
991 | new_transport = asoc->peer.active_path; | 993 | new_transport = asoc->peer.active_path; |
992 | if (new_transport->state == SCTP_UNCONFIRMED) | 994 | if (new_transport->state == SCTP_UNCONFIRMED) |
993 | continue; | 995 | continue; |
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 8716da1a8592..fe99628e1257 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c | |||
@@ -76,6 +76,8 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, | |||
76 | sctp_cmd_seq_t *commands, | 76 | sctp_cmd_seq_t *commands, |
77 | gfp_t gfp); | 77 | gfp_t gfp); |
78 | 78 | ||
79 | static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds, | ||
80 | struct sctp_transport *t); | ||
79 | /******************************************************************** | 81 | /******************************************************************** |
80 | * Helper functions | 82 | * Helper functions |
81 | ********************************************************************/ | 83 | ********************************************************************/ |
@@ -470,7 +472,8 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { | |||
470 | * notification SHOULD be sent to the upper layer. | 472 | * notification SHOULD be sent to the upper layer. |
471 | * | 473 | * |
472 | */ | 474 | */ |
473 | static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, | 475 | static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands, |
476 | struct sctp_association *asoc, | ||
474 | struct sctp_transport *transport, | 477 | struct sctp_transport *transport, |
475 | int is_hb) | 478 | int is_hb) |
476 | { | 479 | { |
@@ -495,6 +498,23 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, | |||
495 | transport->error_count++; | 498 | transport->error_count++; |
496 | } | 499 | } |
497 | 500 | ||
501 | /* If the transport error count is greater than the pf_retrans | ||
502 | * threshold, and less than pathmaxrtx, then mark this transport | ||
503 | * as Partially Failed, ee SCTP Quick Failover Draft, secon 5.1, | ||
504 | * point 1 | ||
505 | */ | ||
506 | if ((transport->state != SCTP_PF) && | ||
507 | (asoc->pf_retrans < transport->pathmaxrxt) && | ||
508 | (transport->error_count > asoc->pf_retrans)) { | ||
509 | |||
510 | sctp_assoc_control_transport(asoc, transport, | ||
511 | SCTP_TRANSPORT_PF, | ||
512 | 0); | ||
513 | |||
514 | /* Update the hb timer to resend a heartbeat every rto */ | ||
515 | sctp_cmd_hb_timer_update(commands, transport); | ||
516 | } | ||
517 | |||
498 | if (transport->state != SCTP_INACTIVE && | 518 | if (transport->state != SCTP_INACTIVE && |
499 | (transport->error_count > transport->pathmaxrxt)) { | 519 | (transport->error_count > transport->pathmaxrxt)) { |
500 | SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p", | 520 | SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p", |
@@ -699,6 +719,10 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, | |||
699 | SCTP_HEARTBEAT_SUCCESS); | 719 | SCTP_HEARTBEAT_SUCCESS); |
700 | } | 720 | } |
701 | 721 | ||
722 | if (t->state == SCTP_PF) | ||
723 | sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, | ||
724 | SCTP_HEARTBEAT_SUCCESS); | ||
725 | |||
702 | /* The receiver of the HEARTBEAT ACK should also perform an | 726 | /* The receiver of the HEARTBEAT ACK should also perform an |
703 | * RTT measurement for that destination transport address | 727 | * RTT measurement for that destination transport address |
704 | * using the time value carried in the HEARTBEAT ACK chunk. | 728 | * using the time value carried in the HEARTBEAT ACK chunk. |
@@ -1565,8 +1589,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, | |||
1565 | 1589 | ||
1566 | case SCTP_CMD_STRIKE: | 1590 | case SCTP_CMD_STRIKE: |
1567 | /* Mark one strike against a transport. */ | 1591 | /* Mark one strike against a transport. */ |
1568 | sctp_do_8_2_transport_strike(asoc, cmd->obj.transport, | 1592 | sctp_do_8_2_transport_strike(commands, asoc, |
1569 | 0); | 1593 | cmd->obj.transport, 0); |
1570 | break; | 1594 | break; |
1571 | 1595 | ||
1572 | case SCTP_CMD_TRANSPORT_IDLE: | 1596 | case SCTP_CMD_TRANSPORT_IDLE: |
@@ -1576,7 +1600,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, | |||
1576 | 1600 | ||
1577 | case SCTP_CMD_TRANSPORT_HB_SENT: | 1601 | case SCTP_CMD_TRANSPORT_HB_SENT: |
1578 | t = cmd->obj.transport; | 1602 | t = cmd->obj.transport; |
1579 | sctp_do_8_2_transport_strike(asoc, t, 1); | 1603 | sctp_do_8_2_transport_strike(commands, asoc, |
1604 | t, 1); | ||
1580 | t->hb_sent = 1; | 1605 | t->hb_sent = 1; |
1581 | break; | 1606 | break; |
1582 | 1607 | ||
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 5d488cdcf679..5e259817a7f3 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -3478,6 +3478,56 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, | |||
3478 | } | 3478 | } |
3479 | 3479 | ||
3480 | 3480 | ||
3481 | /* | ||
3482 | * SCTP_PEER_ADDR_THLDS | ||
3483 | * | ||
3484 | * This option allows us to alter the partially failed threshold for one or all | ||
3485 | * transports in an association. See Section 6.1 of: | ||
3486 | * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt | ||
3487 | */ | ||
3488 | static int sctp_setsockopt_paddr_thresholds(struct sock *sk, | ||
3489 | char __user *optval, | ||
3490 | unsigned int optlen) | ||
3491 | { | ||
3492 | struct sctp_paddrthlds val; | ||
3493 | struct sctp_transport *trans; | ||
3494 | struct sctp_association *asoc; | ||
3495 | |||
3496 | if (optlen < sizeof(struct sctp_paddrthlds)) | ||
3497 | return -EINVAL; | ||
3498 | if (copy_from_user(&val, (struct sctp_paddrthlds __user *)optval, | ||
3499 | sizeof(struct sctp_paddrthlds))) | ||
3500 | return -EFAULT; | ||
3501 | |||
3502 | |||
3503 | if (sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) { | ||
3504 | asoc = sctp_id2assoc(sk, val.spt_assoc_id); | ||
3505 | if (!asoc) | ||
3506 | return -ENOENT; | ||
3507 | list_for_each_entry(trans, &asoc->peer.transport_addr_list, | ||
3508 | transports) { | ||
3509 | if (val.spt_pathmaxrxt) | ||
3510 | trans->pathmaxrxt = val.spt_pathmaxrxt; | ||
3511 | trans->pf_retrans = val.spt_pathpfthld; | ||
3512 | } | ||
3513 | |||
3514 | if (val.spt_pathmaxrxt) | ||
3515 | asoc->pathmaxrxt = val.spt_pathmaxrxt; | ||
3516 | asoc->pf_retrans = val.spt_pathpfthld; | ||
3517 | } else { | ||
3518 | trans = sctp_addr_id2transport(sk, &val.spt_address, | ||
3519 | val.spt_assoc_id); | ||
3520 | if (!trans) | ||
3521 | return -ENOENT; | ||
3522 | |||
3523 | if (val.spt_pathmaxrxt) | ||
3524 | trans->pathmaxrxt = val.spt_pathmaxrxt; | ||
3525 | trans->pf_retrans = val.spt_pathpfthld; | ||
3526 | } | ||
3527 | |||
3528 | return 0; | ||
3529 | } | ||
3530 | |||
3481 | /* API 6.2 setsockopt(), getsockopt() | 3531 | /* API 6.2 setsockopt(), getsockopt() |
3482 | * | 3532 | * |
3483 | * Applications use setsockopt() and getsockopt() to set or retrieve | 3533 | * Applications use setsockopt() and getsockopt() to set or retrieve |
@@ -3627,6 +3677,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, | |||
3627 | case SCTP_AUTO_ASCONF: | 3677 | case SCTP_AUTO_ASCONF: |
3628 | retval = sctp_setsockopt_auto_asconf(sk, optval, optlen); | 3678 | retval = sctp_setsockopt_auto_asconf(sk, optval, optlen); |
3629 | break; | 3679 | break; |
3680 | case SCTP_PEER_ADDR_THLDS: | ||
3681 | retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen); | ||
3682 | break; | ||
3630 | default: | 3683 | default: |
3631 | retval = -ENOPROTOOPT; | 3684 | retval = -ENOPROTOOPT; |
3632 | break; | 3685 | break; |
@@ -5498,6 +5551,51 @@ static int sctp_getsockopt_assoc_ids(struct sock *sk, int len, | |||
5498 | return 0; | 5551 | return 0; |
5499 | } | 5552 | } |
5500 | 5553 | ||
5554 | /* | ||
5555 | * SCTP_PEER_ADDR_THLDS | ||
5556 | * | ||
5557 | * This option allows us to fetch the partially failed threshold for one or all | ||
5558 | * transports in an association. See Section 6.1 of: | ||
5559 | * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt | ||
5560 | */ | ||
5561 | static int sctp_getsockopt_paddr_thresholds(struct sock *sk, | ||
5562 | char __user *optval, | ||
5563 | int len, | ||
5564 | int __user *optlen) | ||
5565 | { | ||
5566 | struct sctp_paddrthlds val; | ||
5567 | struct sctp_transport *trans; | ||
5568 | struct sctp_association *asoc; | ||
5569 | |||
5570 | if (len < sizeof(struct sctp_paddrthlds)) | ||
5571 | return -EINVAL; | ||
5572 | len = sizeof(struct sctp_paddrthlds); | ||
5573 | if (copy_from_user(&val, (struct sctp_paddrthlds __user *)optval, len)) | ||
5574 | return -EFAULT; | ||
5575 | |||
5576 | if (sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) { | ||
5577 | asoc = sctp_id2assoc(sk, val.spt_assoc_id); | ||
5578 | if (!asoc) | ||
5579 | return -ENOENT; | ||
5580 | |||
5581 | val.spt_pathpfthld = asoc->pf_retrans; | ||
5582 | val.spt_pathmaxrxt = asoc->pathmaxrxt; | ||
5583 | } else { | ||
5584 | trans = sctp_addr_id2transport(sk, &val.spt_address, | ||
5585 | val.spt_assoc_id); | ||
5586 | if (!trans) | ||
5587 | return -ENOENT; | ||
5588 | |||
5589 | val.spt_pathmaxrxt = trans->pathmaxrxt; | ||
5590 | val.spt_pathpfthld = trans->pf_retrans; | ||
5591 | } | ||
5592 | |||
5593 | if (put_user(len, optlen) || copy_to_user(optval, &val, len)) | ||
5594 | return -EFAULT; | ||
5595 | |||
5596 | return 0; | ||
5597 | } | ||
5598 | |||
5501 | SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, | 5599 | SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, |
5502 | char __user *optval, int __user *optlen) | 5600 | char __user *optval, int __user *optlen) |
5503 | { | 5601 | { |
@@ -5636,6 +5734,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, | |||
5636 | case SCTP_AUTO_ASCONF: | 5734 | case SCTP_AUTO_ASCONF: |
5637 | retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen); | 5735 | retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen); |
5638 | break; | 5736 | break; |
5737 | case SCTP_PEER_ADDR_THLDS: | ||
5738 | retval = sctp_getsockopt_paddr_thresholds(sk, optval, len, optlen); | ||
5739 | break; | ||
5639 | default: | 5740 | default: |
5640 | retval = -ENOPROTOOPT; | 5741 | retval = -ENOPROTOOPT; |
5641 | break; | 5742 | break; |
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index e5fe639c89e7..2b2bfe933ff1 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c | |||
@@ -141,6 +141,15 @@ static ctl_table sctp_table[] = { | |||
141 | .extra2 = &int_max | 141 | .extra2 = &int_max |
142 | }, | 142 | }, |
143 | { | 143 | { |
144 | .procname = "pf_retrans", | ||
145 | .data = &sctp_pf_retrans, | ||
146 | .maxlen = sizeof(int), | ||
147 | .mode = 0644, | ||
148 | .proc_handler = proc_dointvec_minmax, | ||
149 | .extra1 = &zero, | ||
150 | .extra2 = &int_max | ||
151 | }, | ||
152 | { | ||
144 | .procname = "max_init_retransmits", | 153 | .procname = "max_init_retransmits", |
145 | .data = &sctp_max_retrans_init, | 154 | .data = &sctp_max_retrans_init, |
146 | .maxlen = sizeof(int), | 155 | .maxlen = sizeof(int), |
diff --git a/net/sctp/transport.c b/net/sctp/transport.c index a6b7ee9ce28a..d1c652ed2f3d 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c | |||
@@ -87,6 +87,7 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, | |||
87 | 87 | ||
88 | /* Initialize the default path max_retrans. */ | 88 | /* Initialize the default path max_retrans. */ |
89 | peer->pathmaxrxt = sctp_max_retrans_path; | 89 | peer->pathmaxrxt = sctp_max_retrans_path; |
90 | peer->pf_retrans = sctp_pf_retrans; | ||
90 | 91 | ||
91 | INIT_LIST_HEAD(&peer->transmitted); | 92 | INIT_LIST_HEAD(&peer->transmitted); |
92 | INIT_LIST_HEAD(&peer->send_ready); | 93 | INIT_LIST_HEAD(&peer->send_ready); |
@@ -595,7 +596,8 @@ unsigned long sctp_transport_timeout(struct sctp_transport *t) | |||
595 | { | 596 | { |
596 | unsigned long timeout; | 597 | unsigned long timeout; |
597 | timeout = t->rto + sctp_jitter(t->rto); | 598 | timeout = t->rto + sctp_jitter(t->rto); |
598 | if (t->state != SCTP_UNCONFIRMED) | 599 | if ((t->state != SCTP_UNCONFIRMED) && |
600 | (t->state != SCTP_PF)) | ||
599 | timeout += t->hbinterval; | 601 | timeout += t->hbinterval; |
600 | timeout += jiffies; | 602 | timeout += jiffies; |
601 | return timeout; | 603 | return timeout; |