ソースを参照

[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>
Sridhar Samudrala 19 年 前
コミット
ad8fec1720

+ 4 - 0
include/net/sctp/structs.h

@@ -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 *,

+ 9 - 0
include/net/sctp/user.h

@@ -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 */
 };
 };
 
 

+ 17 - 10
net/sctp/associola.c

@@ -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 &&
-		    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;
 		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

+ 6 - 3
net/sctp/outqueue.c

@@ -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,

+ 2 - 2
net/sctp/sm_make_chunk.c

@@ -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;
 
 

+ 10 - 2
net/sctp/sm_sideeffect.c

@@ -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
 	/* The check for association's overall error counter exceeding the
 	 * threshold is done in the state function.
 	 * 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 &&
 	if (transport->state != SCTP_INACTIVE &&
 	    (transport->error_count++ >= transport->pathmaxrxt)) {
 	    (transport->error_count++ >= transport->pathmaxrxt)) {
@@ -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

+ 5 - 0
net/sctp/sm_statefuns.c

@@ -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.  */

+ 7 - 2
net/sctp/transport.c

@@ -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;
 }
 }