Skip to content

Commit

Permalink
[SCTP]: Verify all the paths to a peer via heartbeat before using them.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sridhar Samudrala authored and David S. Miller committed Jul 21, 2006
1 parent cfdeef3 commit ad8fec1
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 19 deletions.
4 changes: 4 additions & 0 deletions include/net/sctp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ typedef struct sctp_sender_hb_info {
struct sctp_paramhdr param_hdr;
union sctp_addr daddr;
unsigned long sent_at;
__u64 hb_nonce;
} __attribute__((packed)) sctp_sender_hb_info_t;

/*
Expand Down Expand Up @@ -984,6 +985,9 @@ struct sctp_transport {
*/
char cacc_saw_newack;
} cacc;

/* 64-bit random number sent with heartbeat. */
__u64 hb_nonce;
};

struct sctp_transport *sctp_transport_new(const union sctp_addr *,
Expand Down
9 changes: 9 additions & 0 deletions include/net/sctp/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,9 +560,18 @@ struct sctp_paddrinfo {
} __attribute__((packed, aligned(4)));

/* Peer addresses's state. */
/* UNKNOWN: Peer address passed by the upper layer in sendmsg or connect[x]
* calls.
* UNCONFIRMED: Peer address received in INIT/INIT-ACK address parameters.
* Not yet confirmed by a heartbeat and not available for data
* transfers.
* ACTIVE : Peer address confirmed, active and available for data transfers.
* INACTIVE: Peer address inactive and not available for data transfers.
*/
enum sctp_spinfo_state {
SCTP_INACTIVE,
SCTP_ACTIVE,
SCTP_UNCONFIRMED,
SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */
};

Expand Down
27 changes: 17 additions & 10 deletions net/sctp/associola.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,8 @@ void sctp_assoc_set_primary(struct sctp_association *asoc,
/* If the primary path is changing, assume that the
* user wants to use this new path.
*/
if (transport->state != SCTP_INACTIVE)
if ((transport->state == SCTP_ACTIVE) ||
(transport->state == SCTP_UNKNOWN))
asoc->peer.active_path = transport;

/*
Expand Down Expand Up @@ -532,11 +533,11 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
port = addr->v4.sin_port;

SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ",
" port: %d state:%s\n",
" port: %d state:%d\n",
asoc,
addr,
addr->v4.sin_port,
peer_state == SCTP_UNKNOWN?"UNKNOWN":"ACTIVE");
peer_state);

/* Set the port if it has not been set yet. */
if (0 == asoc->peer.port)
Expand All @@ -545,9 +546,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Check to see if this is a duplicate. */
peer = sctp_assoc_lookup_paddr(asoc, addr);
if (peer) {
if (peer_state == SCTP_ACTIVE &&
peer->state == SCTP_UNKNOWN)
peer->state = SCTP_ACTIVE;
if (peer->state == SCTP_UNKNOWN) {
if (peer_state == SCTP_ACTIVE)
peer->state = SCTP_ACTIVE;
if (peer_state == SCTP_UNCONFIRMED)
peer->state = SCTP_UNCONFIRMED;
}
return peer;
}

Expand Down Expand Up @@ -739,7 +743,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports);

if (t->state == SCTP_INACTIVE)
if ((t->state == SCTP_INACTIVE) ||
(t->state == SCTP_UNCONFIRMED))
continue;
if (!first || t->last_time_heard > first->last_time_heard) {
second = first;
Expand All @@ -759,7 +764,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
* [If the primary is active but not most recent, bump the most
* recently used transport.]
*/
if (asoc->peer.primary_path->state != SCTP_INACTIVE &&
if (((asoc->peer.primary_path->state == SCTP_ACTIVE) ||
(asoc->peer.primary_path->state == SCTP_UNKNOWN)) &&
first != asoc->peer.primary_path) {
second = first;
first = asoc->peer.primary_path;
Expand Down Expand Up @@ -1054,7 +1060,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
transports);
if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr))
sctp_assoc_add_peer(asoc, &trans->ipaddr,
GFP_ATOMIC, SCTP_ACTIVE);
GFP_ATOMIC, trans->state);
}

asoc->ctsn_ack_point = asoc->next_tsn - 1;
Expand Down Expand Up @@ -1094,7 +1100,8 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)

/* Try to find an active transport. */

if (t->state != SCTP_INACTIVE) {
if ((t->state == SCTP_ACTIVE) ||
(t->state == SCTP_UNKNOWN)) {
break;
} else {
/* Keep track of the next transport in case
Expand Down
9 changes: 6 additions & 3 deletions net/sctp/outqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)

if (!new_transport) {
new_transport = asoc->peer.active_path;
} else if (new_transport->state == SCTP_INACTIVE) {
} else if ((new_transport->state == SCTP_INACTIVE) ||
(new_transport->state == SCTP_UNCONFIRMED)) {
/* If the chunk is Heartbeat or Heartbeat Ack,
* send it to chunk->transport, even if it's
* inactive.
Expand Down Expand Up @@ -848,7 +849,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
new_transport = chunk->transport;
if (!new_transport ||
new_transport->state == SCTP_INACTIVE)
((new_transport->state == SCTP_INACTIVE) ||
(new_transport->state == SCTP_UNCONFIRMED)))
new_transport = asoc->peer.active_path;

/* Change packets if necessary. */
Expand Down Expand Up @@ -1464,7 +1466,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
/* Mark the destination transport address as
* active if it is not so marked.
*/
if (transport->state == SCTP_INACTIVE) {
if ((transport->state == SCTP_INACTIVE) ||
(transport->state == SCTP_UNCONFIRMED)) {
sctp_assoc_control_transport(
transport->asoc,
transport,
Expand Down
4 changes: 2 additions & 2 deletions net/sctp/sm_make_chunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -2017,7 +2017,7 @@ static int sctp_process_param(struct sctp_association *asoc,
af->from_addr_param(&addr, param.addr, asoc->peer.port, 0);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_ACTIVE))
if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED))
return 0;
break;

Expand Down Expand Up @@ -2418,7 +2418,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc,
* Due to Resource Shortage'.
*/

peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_ACTIVE);
peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_UNCONFIRMED);
if (!peer)
return SCTP_ERROR_RSRC_LOW;

Expand Down
12 changes: 10 additions & 2 deletions net/sctp/sm_sideeffect.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,11 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
/* The check for association's overall error counter exceeding the
* threshold is done in the state function.
*/
asoc->overall_error_count++;
/* When probing UNCONFIRMED addresses, the association overall
* error count is NOT incremented
*/
if (transport->state != SCTP_UNCONFIRMED)
asoc->overall_error_count++;

if (transport->state != SCTP_INACTIVE &&
(transport->error_count++ >= transport->pathmaxrxt)) {
Expand Down Expand Up @@ -610,7 +614,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
/* Mark the destination transport address as active if it is not so
* marked.
*/
if (t->state == SCTP_INACTIVE)
if ((t->state == SCTP_INACTIVE) || (t->state == SCTP_UNCONFIRMED))
sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
SCTP_HEARTBEAT_SUCCESS);

Expand All @@ -620,6 +624,10 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
*/
hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at));

/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
sctp_transport_hold(t);
}

/* Helper function to do a transport reset at the expiry of the hearbeat
Expand Down
5 changes: 5 additions & 0 deletions net/sctp/sm_statefuns.c
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,7 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep,
hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
hbinfo.daddr = transport->ipaddr;
hbinfo.sent_at = jiffies;
hbinfo.hb_nonce = transport->hb_nonce;

/* Send a heartbeat to our peer. */
paylen = sizeof(sctp_sender_hb_info_t);
Expand Down Expand Up @@ -1048,6 +1049,10 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
return SCTP_DISPOSITION_DISCARD;
}

/* Validate the 64-bit random nonce. */
if (hbinfo->hb_nonce != link->hb_nonce)
return SCTP_DISPOSITION_DISCARD;

max_interval = link->hbinterval + link->rto;

/* Check if the timestamp looks valid. */
Expand Down
9 changes: 7 additions & 2 deletions net/sctp/transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
*/

#include <linux/types.h>
#include <linux/random.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>

Expand Down Expand Up @@ -85,7 +86,6 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,

peer->init_sent_count = 0;

peer->state = SCTP_ACTIVE;
peer->param_flags = SPP_HB_DISABLE |
SPP_PMTUD_ENABLE |
SPP_SACKDELAY_ENABLE;
Expand All @@ -109,6 +109,9 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
peer->hb_timer.function = sctp_generate_heartbeat_event;
peer->hb_timer.data = (unsigned long)peer;

/* Initialize the 64-bit random nonce sent with heartbeat. */
get_random_bytes(&peer->hb_nonce, sizeof(peer->hb_nonce));

atomic_set(&peer->refcnt, 1);
peer->dead = 0;

Expand Down Expand Up @@ -517,7 +520,9 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
unsigned long sctp_transport_timeout(struct sctp_transport *t)
{
unsigned long timeout;
timeout = t->hbinterval + t->rto + sctp_jitter(t->rto);
timeout = t->rto + sctp_jitter(t->rto);
if (t->state != SCTP_UNCONFIRMED)
timeout += t->hbinterval;
timeout += jiffies;
return timeout;
}

0 comments on commit ad8fec1

Please sign in to comment.