[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>
This commit is contained in:
parent
cfdeef3282
commit
ad8fec1720
|
@ -445,6 +445,7 @@ typedef struct sctp_sender_hb_info {
|
||||||
struct sctp_paramhdr param_hdr;
|
struct sctp_paramhdr param_hdr;
|
||||||
union sctp_addr daddr;
|
union sctp_addr daddr;
|
||||||
unsigned long sent_at;
|
unsigned long sent_at;
|
||||||
|
__u64 hb_nonce;
|
||||||
} __attribute__((packed)) sctp_sender_hb_info_t;
|
} __attribute__((packed)) sctp_sender_hb_info_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -984,6 +985,9 @@ struct sctp_transport {
|
||||||
*/
|
*/
|
||||||
char cacc_saw_newack;
|
char cacc_saw_newack;
|
||||||
} cacc;
|
} cacc;
|
||||||
|
|
||||||
|
/* 64-bit random number sent with heartbeat. */
|
||||||
|
__u64 hb_nonce;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sctp_transport *sctp_transport_new(const union sctp_addr *,
|
struct sctp_transport *sctp_transport_new(const union sctp_addr *,
|
||||||
|
|
|
@ -560,9 +560,18 @@ struct sctp_paddrinfo {
|
||||||
} __attribute__((packed, aligned(4)));
|
} __attribute__((packed, aligned(4)));
|
||||||
|
|
||||||
/* Peer addresses's state. */
|
/* 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 {
|
enum sctp_spinfo_state {
|
||||||
SCTP_INACTIVE,
|
SCTP_INACTIVE,
|
||||||
SCTP_ACTIVE,
|
SCTP_ACTIVE,
|
||||||
|
SCTP_UNCONFIRMED,
|
||||||
SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */
|
SCTP_UNKNOWN = 0xffff /* Value used for transport state unknown */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -441,7 +441,8 @@ void sctp_assoc_set_primary(struct sctp_association *asoc,
|
||||||
/* If the primary path is changing, assume that the
|
/* If the primary path is changing, assume that the
|
||||||
* user wants to use this new path.
|
* 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;
|
asoc->peer.active_path = transport;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -532,11 +533,11 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
|
||||||
port = addr->v4.sin_port;
|
port = addr->v4.sin_port;
|
||||||
|
|
||||||
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ",
|
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ",
|
||||||
" port: %d state:%s\n",
|
" port: %d state:%d\n",
|
||||||
asoc,
|
asoc,
|
||||||
addr,
|
addr,
|
||||||
addr->v4.sin_port,
|
addr->v4.sin_port,
|
||||||
peer_state == SCTP_UNKNOWN?"UNKNOWN":"ACTIVE");
|
peer_state);
|
||||||
|
|
||||||
/* Set the port if it has not been set yet. */
|
/* Set the port if it has not been set yet. */
|
||||||
if (0 == asoc->peer.port)
|
if (0 == asoc->peer.port)
|
||||||
|
@ -545,9 +546,12 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
|
||||||
/* Check to see if this is a duplicate. */
|
/* Check to see if this is a duplicate. */
|
||||||
peer = sctp_assoc_lookup_paddr(asoc, addr);
|
peer = sctp_assoc_lookup_paddr(asoc, addr);
|
||||||
if (peer) {
|
if (peer) {
|
||||||
if (peer_state == SCTP_ACTIVE &&
|
if (peer->state == SCTP_UNKNOWN) {
|
||||||
peer->state == SCTP_UNKNOWN)
|
if (peer_state == SCTP_ACTIVE)
|
||||||
peer->state = SCTP_ACTIVE;
|
peer->state = SCTP_ACTIVE;
|
||||||
|
if (peer_state == SCTP_UNCONFIRMED)
|
||||||
|
peer->state = SCTP_UNCONFIRMED;
|
||||||
|
}
|
||||||
return peer;
|
return peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,7 +743,8 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
|
||||||
list_for_each(pos, &asoc->peer.transport_addr_list) {
|
list_for_each(pos, &asoc->peer.transport_addr_list) {
|
||||||
t = list_entry(pos, struct sctp_transport, transports);
|
t = list_entry(pos, struct sctp_transport, transports);
|
||||||
|
|
||||||
if (t->state == SCTP_INACTIVE)
|
if ((t->state == SCTP_INACTIVE) ||
|
||||||
|
(t->state == SCTP_UNCONFIRMED))
|
||||||
continue;
|
continue;
|
||||||
if (!first || t->last_time_heard > first->last_time_heard) {
|
if (!first || t->last_time_heard > first->last_time_heard) {
|
||||||
second = first;
|
second = first;
|
||||||
|
@ -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
|
* [If the primary is active but not most recent, bump the most
|
||||||
* recently used transport.]
|
* 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) {
|
first != asoc->peer.primary_path) {
|
||||||
second = first;
|
second = first;
|
||||||
first = asoc->peer.primary_path;
|
first = asoc->peer.primary_path;
|
||||||
|
@ -1054,7 +1060,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
|
||||||
transports);
|
transports);
|
||||||
if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr))
|
if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr))
|
||||||
sctp_assoc_add_peer(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;
|
asoc->ctsn_ack_point = asoc->next_tsn - 1;
|
||||||
|
@ -1094,7 +1100,8 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
|
||||||
|
|
||||||
/* Try to find an active transport. */
|
/* Try to find an active transport. */
|
||||||
|
|
||||||
if (t->state != SCTP_INACTIVE) {
|
if ((t->state == SCTP_ACTIVE) ||
|
||||||
|
(t->state == SCTP_UNKNOWN)) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
/* Keep track of the next transport in case
|
/* Keep track of the next transport in case
|
||||||
|
|
|
@ -691,7 +691,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
|
||||||
|
|
||||||
if (!new_transport) {
|
if (!new_transport) {
|
||||||
new_transport = asoc->peer.active_path;
|
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,
|
/* If the chunk is Heartbeat or Heartbeat Ack,
|
||||||
* send it to chunk->transport, even if it's
|
* send it to chunk->transport, even if it's
|
||||||
* inactive.
|
* inactive.
|
||||||
|
@ -848,7 +849,8 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
|
||||||
*/
|
*/
|
||||||
new_transport = chunk->transport;
|
new_transport = chunk->transport;
|
||||||
if (!new_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;
|
new_transport = asoc->peer.active_path;
|
||||||
|
|
||||||
/* Change packets if necessary. */
|
/* Change packets if necessary. */
|
||||||
|
@ -1464,7 +1466,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
|
||||||
/* Mark the destination transport address as
|
/* Mark the destination transport address as
|
||||||
* active if it is not so marked.
|
* 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(
|
sctp_assoc_control_transport(
|
||||||
transport->asoc,
|
transport->asoc,
|
||||||
transport,
|
transport,
|
||||||
|
|
|
@ -2017,7 +2017,7 @@ static int sctp_process_param(struct sctp_association *asoc,
|
||||||
af->from_addr_param(&addr, param.addr, asoc->peer.port, 0);
|
af->from_addr_param(&addr, param.addr, asoc->peer.port, 0);
|
||||||
scope = sctp_scope(peer_addr);
|
scope = sctp_scope(peer_addr);
|
||||||
if (sctp_in_scope(&addr, scope))
|
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;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2418,7 +2418,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc,
|
||||||
* Due to Resource Shortage'.
|
* 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)
|
if (!peer)
|
||||||
return SCTP_ERROR_RSRC_LOW;
|
return SCTP_ERROR_RSRC_LOW;
|
||||||
|
|
||||||
|
|
|
@ -430,6 +430,10 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
|
||||||
/* The check for association's overall error counter exceeding the
|
/* The check for association's overall error counter exceeding the
|
||||||
* threshold is done in the state function.
|
* threshold is done in the state function.
|
||||||
*/
|
*/
|
||||||
|
/* When probing UNCONFIRMED addresses, the association overall
|
||||||
|
* error count is NOT incremented
|
||||||
|
*/
|
||||||
|
if (transport->state != SCTP_UNCONFIRMED)
|
||||||
asoc->overall_error_count++;
|
asoc->overall_error_count++;
|
||||||
|
|
||||||
if (transport->state != SCTP_INACTIVE &&
|
if (transport->state != SCTP_INACTIVE &&
|
||||||
|
@ -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
|
/* Mark the destination transport address as active if it is not so
|
||||||
* marked.
|
* 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_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
|
||||||
SCTP_HEARTBEAT_SUCCESS);
|
SCTP_HEARTBEAT_SUCCESS);
|
||||||
|
|
||||||
|
@ -620,6 +624,10 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
|
||||||
*/
|
*/
|
||||||
hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
|
hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
|
||||||
sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at));
|
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
|
/* Helper function to do a transport reset at the expiry of the hearbeat
|
||||||
|
|
|
@ -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.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
|
||||||
hbinfo.daddr = transport->ipaddr;
|
hbinfo.daddr = transport->ipaddr;
|
||||||
hbinfo.sent_at = jiffies;
|
hbinfo.sent_at = jiffies;
|
||||||
|
hbinfo.hb_nonce = transport->hb_nonce;
|
||||||
|
|
||||||
/* Send a heartbeat to our peer. */
|
/* Send a heartbeat to our peer. */
|
||||||
paylen = sizeof(sctp_sender_hb_info_t);
|
paylen = sizeof(sctp_sender_hb_info_t);
|
||||||
|
@ -1048,6 +1049,10 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
|
||||||
return SCTP_DISPOSITION_DISCARD;
|
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;
|
max_interval = link->hbinterval + link->rto;
|
||||||
|
|
||||||
/* Check if the timestamp looks valid. */
|
/* Check if the timestamp looks valid. */
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/random.h>
|
||||||
#include <net/sctp/sctp.h>
|
#include <net/sctp/sctp.h>
|
||||||
#include <net/sctp/sm.h>
|
#include <net/sctp/sm.h>
|
||||||
|
|
||||||
|
@ -85,7 +86,6 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
|
||||||
|
|
||||||
peer->init_sent_count = 0;
|
peer->init_sent_count = 0;
|
||||||
|
|
||||||
peer->state = SCTP_ACTIVE;
|
|
||||||
peer->param_flags = SPP_HB_DISABLE |
|
peer->param_flags = SPP_HB_DISABLE |
|
||||||
SPP_PMTUD_ENABLE |
|
SPP_PMTUD_ENABLE |
|
||||||
SPP_SACKDELAY_ENABLE;
|
SPP_SACKDELAY_ENABLE;
|
||||||
|
@ -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.function = sctp_generate_heartbeat_event;
|
||||||
peer->hb_timer.data = (unsigned long)peer;
|
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);
|
atomic_set(&peer->refcnt, 1);
|
||||||
peer->dead = 0;
|
peer->dead = 0;
|
||||||
|
|
||||||
|
@ -517,7 +520,9 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport,
|
||||||
unsigned long sctp_transport_timeout(struct sctp_transport *t)
|
unsigned long sctp_transport_timeout(struct sctp_transport *t)
|
||||||
{
|
{
|
||||||
unsigned long timeout;
|
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;
|
timeout += jiffies;
|
||||||
return timeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue