struct sctp_paramhdr param_hdr;
union sctp_addr daddr;
unsigned long sent_at;
+ __u64 hb_nonce;
} __attribute__((packed)) sctp_sender_hb_info_t;
/*
*/
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 *,
} __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 */
};
/* 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;
/*
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)
/* 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;
}
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;
* [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;
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;
/* 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
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.
*/
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. */
/* 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,
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;
* 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;
/* 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)) {
/* 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);
*/
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
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);
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. */
*/
#include <linux/types.h>
+#include <linux/random.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
peer->init_sent_count = 0;
- peer->state = SCTP_ACTIVE;
peer->param_flags = SPP_HB_DISABLE |
SPP_PMTUD_ENABLE |
SPP_SACKDELAY_ENABLE;
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;
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;
}