diff options
author | Sridhar Samudrala <sri@us.ibm.com> | 2006-07-21 17:48:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-07-21 17:48:50 -0400 |
commit | ad8fec1720e000ba2384de6408076a60fc92a981 (patch) | |
tree | 52fd2f7af583b95db6db369c6b3ea3d6adc26d69 | |
parent | cfdeef3282705a4b872d3559c4e7d2561251363c (diff) |
[SCTP]: Verify all the paths to a peer via heartbeat before using them.
This patch implements Path Initialization procedure as described in
Sec 2.36 of RFC4460.
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/sctp/structs.h | 4 | ||||
-rw-r--r-- | include/net/sctp/user.h | 9 | ||||
-rw-r--r-- | net/sctp/associola.c | 27 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 9 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 4 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 12 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 5 | ||||
-rw-r--r-- | net/sctp/transport.c | 9 |
8 files changed, 60 insertions, 19 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 5f69158c1006..268f2e19ccbb 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -445,6 +445,7 @@ typedef struct sctp_sender_hb_info { | |||
445 | struct sctp_paramhdr param_hdr; | 445 | struct sctp_paramhdr param_hdr; |
446 | union sctp_addr daddr; | 446 | union sctp_addr daddr; |
447 | unsigned long sent_at; | 447 | unsigned long sent_at; |
448 | __u64 hb_nonce; | ||
448 | } __attribute__((packed)) sctp_sender_hb_info_t; | 449 | } __attribute__((packed)) sctp_sender_hb_info_t; |
449 | 450 | ||
450 | /* | 451 | /* |
@@ -984,6 +985,9 @@ struct sctp_transport { | |||
984 | */ | 985 | */ |
985 | char cacc_saw_newack; | 986 | char cacc_saw_newack; |
986 | } cacc; | 987 | } cacc; |
988 | |||
989 | /* 64-bit random number sent with heartbeat. */ | ||
990 | __u64 hb_nonce; | ||
987 | }; | 991 | }; |
988 | 992 | ||
989 | struct sctp_transport *sctp_transport_new(const union sctp_addr *, | 993 | struct sctp_transport *sctp_transport_new(const union sctp_addr *, |
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 8a6bef6f91eb..1b7aae6cdd82 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h | |||
@@ -560,9 +560,18 @@ struct sctp_paddrinfo { | |||
560 | } __attribute__((packed, aligned(4))); | 560 | } __attribute__((packed, aligned(4))); |
561 | 561 | ||
562 | /* Peer addresses's state. */ | 562 | /* Peer addresses's state. */ |
563 | /* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x] | ||
564 | * calls. | ||
565 | * UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters. | ||
566 | * Not yet confirmed by a heartbeat and not available for data | ||
567 | * transfers. | ||
568 | * ACTIVE : Peer address confirmed, active and available for data transfers. | ||
569 | * INACTIVE: Peer address inactive and not available for data transfers. | ||
570 | */ | ||
563 | enum sctp_spinfo_state { | 571 | enum sctp_spinfo_state { |
564 | SCTP_INACTIVE, | 572 | SCTP_INACTIVE, |
565 | SCTP_ACTIVE, | 573 | SCTP_ACTIVE, |
574 | SCTP_UNCONFIRMED, | ||
566 | SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ | 575 | SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ |
567 | }; | 576 | }; |
568 | 577 | ||
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 9d05e13e92f6..27329ce9c311 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c | |||
@@ -441,7 +441,8 @@ void sctp_assoc_set_primary(struct sctp_association *asoc, | |||
441 | /* If the primary path is changing, assume that the | 441 | /* If the primary path is changing, assume that the |
442 | * user wants to use this new path. | 442 | * user wants to use this new path. |
443 | */ | 443 | */ |
444 | if (transport->state != SCTP_INACTIVE) | 444 | if ((transport->state == SCTP_ACTIVE) || |
445 | (transport->state == SCTP_UNKNOWN)) | ||
445 | asoc->peer.active_path = transport; | 446 | asoc->peer.active_path = transport; |
446 | 447 | ||
447 | /* | 448 | /* |
@@ -532,11 +533,11 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, | |||
532 | port = addr->v4.sin_port; | 533 | port = addr->v4.sin_port; |
533 | 534 | ||
534 | SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ", | 535 | SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ", |
535 | " port: %d state:%s\n", | 536 | " port: %d state:%d\n", |
536 | asoc, | 537 | asoc, |
537 | addr, | 538 | addr, |
538 | addr->v4.sin_port, | 539 | addr->v4.sin_port, |
539 | peer_state == SCTP_UNKNOWN?"UNKNOWN":"ACTIVE"); | 540 | peer_state); |
540 | 541 | ||
541 | /* Set the port if it has not been set yet. */ | 542 | /* Set the port if it has not been set yet. */ |
542 | if (0 == asoc->peer.port) | 543 | if (0 == asoc->peer.port) |
@@ -545,9 +546,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, | |||
545 | /* Check to see if this is a duplicate. */ | 546 | /* Check to see if this is a duplicate. */ |
546 | peer = sctp_assoc_lookup_paddr(asoc, addr); | 547 | peer = sctp_assoc_lookup_paddr(asoc, addr); |
547 | if (peer) { | 548 | if (peer) { |
548 | if (peer_state == SCTP_ACTIVE && | 549 | if (peer->state == SCTP_UNKNOWN) { |
549 | peer->state == SCTP_UNKNOWN) | 550 | if (peer_state == SCTP_ACTIVE) |
550 | peer->state = SCTP_ACTIVE; | 551 | peer->state = SCTP_ACTIVE; |
552 | if (peer_state == SCTP_UNCONFIRMED) | ||
553 | peer->state = SCTP_UNCONFIRMED; | ||
554 | } | ||
551 | return peer; | 555 | return peer; |
552 | } | 556 | } |
553 | 557 | ||
@@ -739,7 +743,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
739 | list_for_each(pos, &asoc->peer.transport_addr_list) { | 743 | list_for_each(pos, &asoc->peer.transport_addr_list) { |
740 | t = list_entry(pos, struct sctp_transport, transports); | 744 | t = list_entry(pos, struct sctp_transport, transports); |
741 | 745 | ||
742 | if (t->state == SCTP_INACTIVE) | 746 | if ((t->state == SCTP_INACTIVE) || |
747 | (t->state == SCTP_UNCONFIRMED)) | ||
743 | continue; | 748 | continue; |
744 | if (!first || t->last_time_heard > first->last_time_heard) { | 749 | if (!first || t->last_time_heard > first->last_time_heard) { |
745 | second = first; | 750 | second = first; |
@@ -759,7 +764,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
759 | * [If the primary is active but not most recent, bump the most | 764 | * [If the primary is active but not most recent, bump the most |
760 | * recently used transport.] | 765 | * recently used transport.] |
761 | */ | 766 | */ |
762 | if (asoc->peer.primary_path->state != SCTP_INACTIVE && | 767 | if (((asoc->peer.primary_path->state == SCTP_ACTIVE) || |
768 | (asoc->peer.primary_path->state == SCTP_UNKNOWN)) && | ||
763 | first != asoc->peer.primary_path) { | 769 | first != asoc->peer.primary_path) { |
764 | second = first; | 770 | second = first; |
765 | first = asoc->peer.primary_path; | 771 | first = asoc->peer.primary_path; |
@@ -1054,7 +1060,7 @@ void sctp_assoc_update(struct sctp_association *asoc, | |||
1054 | transports); | 1060 | transports); |
1055 | if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr)) | 1061 | if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr)) |
1056 | sctp_assoc_add_peer(asoc, &trans->ipaddr, | 1062 | sctp_assoc_add_peer(asoc, &trans->ipaddr, |
1057 | GFP_ATOMIC, SCTP_ACTIVE); | 1063 | GFP_ATOMIC, trans->state); |
1058 | } | 1064 | } |
1059 | 1065 | ||
1060 | asoc->ctsn_ack_point = asoc->next_tsn - 1; | 1066 | asoc->ctsn_ack_point = asoc->next_tsn - 1; |
@@ -1094,7 +1100,8 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) | |||
1094 | 1100 | ||
1095 | /* Try to find an active transport. */ | 1101 | /* Try to find an active transport. */ |
1096 | 1102 | ||
1097 | if (t->state != SCTP_INACTIVE) { | 1103 | if ((t->state == SCTP_ACTIVE) || |
1104 | (t->state == SCTP_UNKNOWN)) { | ||
1098 | break; | 1105 | break; |
1099 | } else { | 1106 | } else { |
1100 | /* Keep track of the next transport in case | 1107 | /* Keep track of the next transport in case |
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index e5faa351aaad..30b710c54e64 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c | |||
@@ -691,7 +691,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) | |||
691 | 691 | ||
692 | if (!new_transport) { | 692 | if (!new_transport) { |
693 | new_transport = asoc->peer.active_path; | 693 | new_transport = asoc->peer.active_path; |
694 | } else if (new_transport->state == SCTP_INACTIVE) { | 694 | } else if ((new_transport->state == SCTP_INACTIVE) || |
695 | (new_transport->state == SCTP_UNCONFIRMED)) { | ||
695 | /* If the chunk is Heartbeat or Heartbeat Ack, | 696 | /* If the chunk is Heartbeat or Heartbeat Ack, |
696 | * send it to chunk->transport, even if it's | 697 | * send it to chunk->transport, even if it's |
697 | * inactive. | 698 | * inactive. |
@@ -848,7 +849,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) | |||
848 | */ | 849 | */ |
849 | new_transport = chunk->transport; | 850 | new_transport = chunk->transport; |
850 | if (!new_transport || | 851 | if (!new_transport || |
851 | new_transport->state == SCTP_INACTIVE) | 852 | ((new_transport->state == SCTP_INACTIVE) || |
853 | (new_transport->state == SCTP_UNCONFIRMED))) | ||
852 | new_transport = asoc->peer.active_path; | 854 | new_transport = asoc->peer.active_path; |
853 | 855 | ||
854 | /* Change packets if necessary. */ | 856 | /* Change packets if necessary. */ |
@@ -1464,7 +1466,8 @@ static void sctp_check_transmitted(struct sctp_outq *q, | |||
1464 | /* Mark the destination transport address as | 1466 | /* Mark the destination transport address as |
1465 | * active if it is not so marked. | 1467 | * active if it is not so marked. |
1466 | */ | 1468 | */ |
1467 | if (transport->state == SCTP_INACTIVE) { | 1469 | if ((transport->state == SCTP_INACTIVE) || |
1470 | (transport->state == SCTP_UNCONFIRMED)) { | ||
1468 | sctp_assoc_control_transport( | 1471 | sctp_assoc_control_transport( |
1469 | transport->asoc, | 1472 | transport->asoc, |
1470 | transport, | 1473 | transport, |
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 2a8773691695..8134e8b1cdca 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c | |||
@@ -2017,7 +2017,7 @@ static int sctp_process_param(struct sctp_association *asoc, | |||
2017 | af->from_addr_param(&addr, param.addr, asoc->peer.port, 0); | 2017 | af->from_addr_param(&addr, param.addr, asoc->peer.port, 0); |
2018 | scope = sctp_scope(peer_addr); | 2018 | scope = sctp_scope(peer_addr); |
2019 | if (sctp_in_scope(&addr, scope)) | 2019 | if (sctp_in_scope(&addr, scope)) |
2020 | if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_ACTIVE)) | 2020 | if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED)) |
2021 | return 0; | 2021 | return 0; |
2022 | break; | 2022 | break; |
2023 | 2023 | ||
@@ -2418,7 +2418,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc, | |||
2418 | * Due to Resource Shortage'. | 2418 | * Due to Resource Shortage'. |
2419 | */ | 2419 | */ |
2420 | 2420 | ||
2421 | peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_ACTIVE); | 2421 | peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_UNCONFIRMED); |
2422 | if (!peer) | 2422 | if (!peer) |
2423 | return SCTP_ERROR_RSRC_LOW; | 2423 | return SCTP_ERROR_RSRC_LOW; |
2424 | 2424 | ||
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index c5beb2ad7ef7..9c10bdec1afe 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c | |||
@@ -430,7 +430,11 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, | |||
430 | /* The check for association's overall error counter exceeding the | 430 | /* The check for association's overall error counter exceeding the |
431 | * threshold is done in the state function. | 431 | * threshold is done in the state function. |
432 | */ | 432 | */ |
433 | asoc->overall_error_count++; | 433 | /* When probing UNCONFIRMED addresses, the association overall |
434 | * error count is NOT incremented | ||
435 | */ | ||
436 | if (transport->state != SCTP_UNCONFIRMED) | ||
437 | asoc->overall_error_count++; | ||
434 | 438 | ||
435 | if (transport->state != SCTP_INACTIVE && | 439 | if (transport->state != SCTP_INACTIVE && |
436 | (transport->error_count++ >= transport->pathmaxrxt)) { | 440 | (transport->error_count++ >= transport->pathmaxrxt)) { |
@@ -610,7 +614,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, | |||
610 | /* Mark the destination transport address as active if it is not so | 614 | /* Mark the destination transport address as active if it is not so |
611 | * marked. | 615 | * marked. |
612 | */ | 616 | */ |
613 | if (t->state == SCTP_INACTIVE) | 617 | if ((t->state == SCTP_INACTIVE) || (t->state == SCTP_UNCONFIRMED)) |
614 | sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, | 618 | sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, |
615 | SCTP_HEARTBEAT_SUCCESS); | 619 | SCTP_HEARTBEAT_SUCCESS); |
616 | 620 | ||
@@ -620,6 +624,10 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, | |||
620 | */ | 624 | */ |
621 | hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; | 625 | hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; |
622 | sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); | 626 | sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); |
627 | |||
628 | /* Update the heartbeat timer. */ | ||
629 | if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) | ||
630 | sctp_transport_hold(t); | ||
623 | } | 631 | } |
624 | 632 | ||
625 | /* Helper function to do a transport reset at the expiry of the hearbeat | 633 | /* Helper function to do a transport reset at the expiry of the hearbeat |
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9e58144f4851..e8498dce5335 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c | |||
@@ -846,6 +846,7 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, | |||
846 | hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); | 846 | hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); |
847 | hbinfo.daddr = transport->ipaddr; | 847 | hbinfo.daddr = transport->ipaddr; |
848 | hbinfo.sent_at = jiffies; | 848 | hbinfo.sent_at = jiffies; |
849 | hbinfo.hb_nonce = transport->hb_nonce; | ||
849 | 850 | ||
850 | /* Send a heartbeat to our peer. */ | 851 | /* Send a heartbeat to our peer. */ |
851 | paylen = sizeof(sctp_sender_hb_info_t); | 852 | paylen = sizeof(sctp_sender_hb_info_t); |
@@ -1048,6 +1049,10 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, | |||
1048 | return SCTP_DISPOSITION_DISCARD; | 1049 | return SCTP_DISPOSITION_DISCARD; |
1049 | } | 1050 | } |
1050 | 1051 | ||
1052 | /* Validate the 64-bit random nonce. */ | ||
1053 | if (hbinfo->hb_nonce != link->hb_nonce) | ||
1054 | return SCTP_DISPOSITION_DISCARD; | ||
1055 | |||
1051 | max_interval = link->hbinterval + link->rto; | 1056 | max_interval = link->hbinterval + link->rto; |
1052 | 1057 | ||
1053 | /* Check if the timestamp looks valid. */ | 1058 | /* Check if the timestamp looks valid. */ |
diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 160f62ad1cc5..2763aa93de1a 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c | |||
@@ -49,6 +49,7 @@ | |||
49 | */ | 49 | */ |
50 | 50 | ||
51 | #include <linux/types.h> | 51 | #include <linux/types.h> |
52 | #include <linux/random.h> | ||
52 | #include <net/sctp/sctp.h> | 53 | #include <net/sctp/sctp.h> |
53 | #include <net/sctp/sm.h> | 54 | #include <net/sctp/sm.h> |
54 | 55 | ||
@@ -85,7 +86,6 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, | |||
85 | 86 | ||
86 | peer->init_sent_count = 0; | 87 | peer->init_sent_count = 0; |
87 | 88 | ||
88 | peer->state = SCTP_ACTIVE; | ||
89 | peer->param_flags = SPP_HB_DISABLE | | 89 | peer->param_flags = SPP_HB_DISABLE | |
90 | SPP_PMTUD_ENABLE | | 90 | SPP_PMTUD_ENABLE | |
91 | SPP_SACKDELAY_ENABLE; | 91 | SPP_SACKDELAY_ENABLE; |
@@ -109,6 +109,9 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, | |||
109 | peer->hb_timer.function = sctp_generate_heartbeat_event; | 109 | peer->hb_timer.function = sctp_generate_heartbeat_event; |
110 | peer->hb_timer.data = (unsigned long)peer; | 110 | peer->hb_timer.data = (unsigned long)peer; |
111 | 111 | ||
112 | /* Initialize the 64-bit random nonce sent with heartbeat. */ | ||
113 | get_random_bytes(&peer->hb_nonce, sizeof(peer->hb_nonce)); | ||
114 | |||
112 | atomic_set(&peer->refcnt, 1); | 115 | atomic_set(&peer->refcnt, 1); |
113 | peer->dead = 0; | 116 | peer->dead = 0; |
114 | 117 | ||
@@ -517,7 +520,9 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport, | |||
517 | unsigned long sctp_transport_timeout(struct sctp_transport *t) | 520 | unsigned long sctp_transport_timeout(struct sctp_transport *t) |
518 | { | 521 | { |
519 | unsigned long timeout; | 522 | unsigned long timeout; |
520 | timeout = t->hbinterval + t->rto + sctp_jitter(t->rto); | 523 | timeout = t->rto + sctp_jitter(t->rto); |
524 | if (t->state != SCTP_UNCONFIRMED) | ||
525 | timeout += t->hbinterval; | ||
521 | timeout += jiffies; | 526 | timeout += jiffies; |
522 | return timeout; | 527 | return timeout; |
523 | } | 528 | } |