Commit ad8fec1720e000ba2384de6408076a60fc92a981
Committed by
David S. Miller
1 parent
cfdeef3282
Exists in
master
and in
7 other branches
[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>
Showing 8 changed files with 60 additions and 19 deletions Side-by-side Diff
include/net/sctp/structs.h
... | ... | @@ -445,6 +445,7 @@ |
445 | 445 | struct sctp_paramhdr param_hdr; |
446 | 446 | union sctp_addr daddr; |
447 | 447 | unsigned long sent_at; |
448 | + __u64 hb_nonce; | |
448 | 449 | } __attribute__((packed)) sctp_sender_hb_info_t; |
449 | 450 | |
450 | 451 | /* |
... | ... | @@ -984,6 +985,9 @@ |
984 | 985 | */ |
985 | 986 | char cacc_saw_newack; |
986 | 987 | } cacc; |
988 | + | |
989 | + /* 64-bit random number sent with heartbeat. */ | |
990 | + __u64 hb_nonce; | |
987 | 991 | }; |
988 | 992 | |
989 | 993 | struct sctp_transport *sctp_transport_new(const union sctp_addr *, |
include/net/sctp/user.h
... | ... | @@ -560,9 +560,18 @@ |
560 | 560 | } __attribute__((packed, aligned(4))); |
561 | 561 | |
562 | 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 | 571 | enum sctp_spinfo_state { |
564 | 572 | SCTP_INACTIVE, |
565 | 573 | SCTP_ACTIVE, |
574 | + SCTP_UNCONFIRMED, | |
566 | 575 | SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */ |
567 | 576 | }; |
568 | 577 |
net/sctp/associola.c
... | ... | @@ -441,7 +441,8 @@ |
441 | 441 | /* If the primary path is changing, assume that the |
442 | 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 | 446 | asoc->peer.active_path = transport; |
446 | 447 | |
447 | 448 | /* |
448 | 449 | |
... | ... | @@ -532,11 +533,11 @@ |
532 | 533 | port = addr->v4.sin_port; |
533 | 534 | |
534 | 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 | 537 | asoc, |
537 | 538 | addr, |
538 | 539 | addr->v4.sin_port, |
539 | - peer_state == SCTP_UNKNOWN?"UNKNOWN":"ACTIVE"); | |
540 | + peer_state); | |
540 | 541 | |
541 | 542 | /* Set the port if it has not been set yet. */ |
542 | 543 | if (0 == asoc->peer.port) |
... | ... | @@ -545,9 +546,12 @@ |
545 | 546 | /* Check to see if this is a duplicate. */ |
546 | 547 | peer = sctp_assoc_lookup_paddr(asoc, addr); |
547 | 548 | if (peer) { |
548 | - if (peer_state == SCTP_ACTIVE && | |
549 | - peer->state == SCTP_UNKNOWN) | |
550 | - peer->state = SCTP_ACTIVE; | |
549 | + if (peer->state == SCTP_UNKNOWN) { | |
550 | + if (peer_state == SCTP_ACTIVE) | |
551 | + peer->state = SCTP_ACTIVE; | |
552 | + if (peer_state == SCTP_UNCONFIRMED) | |
553 | + peer->state = SCTP_UNCONFIRMED; | |
554 | + } | |
551 | 555 | return peer; |
552 | 556 | } |
553 | 557 | |
... | ... | @@ -739,7 +743,8 @@ |
739 | 743 | list_for_each(pos, &asoc->peer.transport_addr_list) { |
740 | 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 | 748 | continue; |
744 | 749 | if (!first || t->last_time_heard > first->last_time_heard) { |
745 | 750 | second = first; |
... | ... | @@ -759,7 +764,8 @@ |
759 | 764 | * [If the primary is active but not most recent, bump the most |
760 | 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 | 769 | first != asoc->peer.primary_path) { |
764 | 770 | second = first; |
765 | 771 | first = asoc->peer.primary_path; |
... | ... | @@ -1054,7 +1060,7 @@ |
1054 | 1060 | transports); |
1055 | 1061 | if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr)) |
1056 | 1062 | sctp_assoc_add_peer(asoc, &trans->ipaddr, |
1057 | - GFP_ATOMIC, SCTP_ACTIVE); | |
1063 | + GFP_ATOMIC, trans->state); | |
1058 | 1064 | } |
1059 | 1065 | |
1060 | 1066 | asoc->ctsn_ack_point = asoc->next_tsn - 1; |
... | ... | @@ -1094,7 +1100,8 @@ |
1094 | 1100 | |
1095 | 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 | 1105 | break; |
1099 | 1106 | } else { |
1100 | 1107 | /* Keep track of the next transport in case |
net/sctp/outqueue.c
... | ... | @@ -691,7 +691,8 @@ |
691 | 691 | |
692 | 692 | if (!new_transport) { |
693 | 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 | 696 | /* If the chunk is Heartbeat or Heartbeat Ack, |
696 | 697 | * send it to chunk->transport, even if it's |
697 | 698 | * inactive. |
... | ... | @@ -848,7 +849,8 @@ |
848 | 849 | */ |
849 | 850 | new_transport = chunk->transport; |
850 | 851 | if (!new_transport || |
851 | - new_transport->state == SCTP_INACTIVE) | |
852 | + ((new_transport->state == SCTP_INACTIVE) || | |
853 | + (new_transport->state == SCTP_UNCONFIRMED))) | |
852 | 854 | new_transport = asoc->peer.active_path; |
853 | 855 | |
854 | 856 | /* Change packets if necessary. */ |
... | ... | @@ -1464,7 +1466,8 @@ |
1464 | 1466 | /* Mark the destination transport address as |
1465 | 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 | 1471 | sctp_assoc_control_transport( |
1469 | 1472 | transport->asoc, |
1470 | 1473 | transport, |
net/sctp/sm_make_chunk.c
... | ... | @@ -2017,7 +2017,7 @@ |
2017 | 2017 | af->from_addr_param(&addr, param.addr, asoc->peer.port, 0); |
2018 | 2018 | scope = sctp_scope(peer_addr); |
2019 | 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 | 2021 | return 0; |
2022 | 2022 | break; |
2023 | 2023 | |
... | ... | @@ -2418,7 +2418,7 @@ |
2418 | 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 | 2422 | if (!peer) |
2423 | 2423 | return SCTP_ERROR_RSRC_LOW; |
2424 | 2424 |
net/sctp/sm_sideeffect.c
... | ... | @@ -430,7 +430,11 @@ |
430 | 430 | /* The check for association's overall error counter exceeding the |
431 | 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 | 439 | if (transport->state != SCTP_INACTIVE && |
436 | 440 | (transport->error_count++ >= transport->pathmaxrxt)) { |
... | ... | @@ -610,7 +614,7 @@ |
610 | 614 | /* Mark the destination transport address as active if it is not so |
611 | 615 | * marked. |
612 | 616 | */ |
613 | - if (t->state == SCTP_INACTIVE) | |
617 | + if ((t->state == SCTP_INACTIVE) || (t->state == SCTP_UNCONFIRMED)) | |
614 | 618 | sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, |
615 | 619 | SCTP_HEARTBEAT_SUCCESS); |
616 | 620 | |
... | ... | @@ -620,6 +624,10 @@ |
620 | 624 | */ |
621 | 625 | hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; |
622 | 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 | 633 | /* Helper function to do a transport reset at the expiry of the hearbeat |
net/sctp/sm_statefuns.c
... | ... | @@ -846,6 +846,7 @@ |
846 | 846 | hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); |
847 | 847 | hbinfo.daddr = transport->ipaddr; |
848 | 848 | hbinfo.sent_at = jiffies; |
849 | + hbinfo.hb_nonce = transport->hb_nonce; | |
849 | 850 | |
850 | 851 | /* Send a heartbeat to our peer. */ |
851 | 852 | paylen = sizeof(sctp_sender_hb_info_t); |
... | ... | @@ -1047,6 +1048,10 @@ |
1047 | 1048 | } |
1048 | 1049 | return SCTP_DISPOSITION_DISCARD; |
1049 | 1050 | } |
1051 | + | |
1052 | + /* Validate the 64-bit random nonce. */ | |
1053 | + if (hbinfo->hb_nonce != link->hb_nonce) | |
1054 | + return SCTP_DISPOSITION_DISCARD; | |
1050 | 1055 | |
1051 | 1056 | max_interval = link->hbinterval + link->rto; |
1052 | 1057 |
net/sctp/transport.c
... | ... | @@ -49,6 +49,7 @@ |
49 | 49 | */ |
50 | 50 | |
51 | 51 | #include <linux/types.h> |
52 | +#include <linux/random.h> | |
52 | 53 | #include <net/sctp/sctp.h> |
53 | 54 | #include <net/sctp/sm.h> |
54 | 55 | |
... | ... | @@ -85,7 +86,6 @@ |
85 | 86 | |
86 | 87 | peer->init_sent_count = 0; |
87 | 88 | |
88 | - peer->state = SCTP_ACTIVE; | |
89 | 89 | peer->param_flags = SPP_HB_DISABLE | |
90 | 90 | SPP_PMTUD_ENABLE | |
91 | 91 | SPP_SACKDELAY_ENABLE; |
... | ... | @@ -109,6 +109,9 @@ |
109 | 109 | peer->hb_timer.function = sctp_generate_heartbeat_event; |
110 | 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 | 115 | atomic_set(&peer->refcnt, 1); |
113 | 116 | peer->dead = 0; |
114 | 117 | |
... | ... | @@ -517,7 +520,9 @@ |
517 | 520 | unsigned long sctp_transport_timeout(struct sctp_transport *t) |
518 | 521 | { |
519 | 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 | 526 | timeout += jiffies; |
522 | 527 | return timeout; |
523 | 528 | } |