net: coap: Randomize initial ACK timeout

Add Kconfig option to randomize the initial ACK timeout, as specified in
RFC 7252. The option is enabled by default.

Additionally, finetune the default value of COAP_INIT_ACK_TIMEOUT_MS
option, to match the default ACK_TIMEOUT value specified by the RFC
7252. The RFC does not specify the minimum/maximum value of the
ACK_TIMEOUT parameter, but only suggests it should be no lower than 1
second, so adjust the option range to reflect this.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
diff --git a/include/net/coap.h b/include/net/coap.h
index 91e3b31..24afa8f 100644
--- a/include/net/coap.h
+++ b/include/net/coap.h
@@ -234,6 +234,7 @@
 			    const struct sockaddr *from);
 
 #define COAP_DEFAULT_MAX_RETRANSMIT 4
+#define COAP_DEFAULT_ACK_RANDOM_FACTOR 1.5
 
 /**
  * @brief Represents a request awaiting for an acknowledgment (ACK).
diff --git a/subsys/net/lib/coap/Kconfig b/subsys/net/lib/coap/Kconfig
index 913f373..9c7298d 100644
--- a/subsys/net/lib/coap/Kconfig
+++ b/subsys/net/lib/coap/Kconfig
@@ -54,11 +54,21 @@
 
 config COAP_INIT_ACK_TIMEOUT_MS
 	int "base length of the random generated initial ACK timeout in ms"
-	default 2345
-	range 2345 100000
+	default 2000
+	range 1000 100000
 	help
 	  This value is used as a base value to retry pending CoAP packets.
 
+config COAP_RANDOMIZE_ACK_TIMEOUT
+	bool "Randomize initial ACK timeout, as specified in RFC 7252"
+	default y
+	help
+	  If enabled, the initial ACK timeout will be randomized, as specified
+	  in RFC 7252, i.e. will be a random number between ACK_TIMEOUT and
+	  ACK_TIMEOUT * ACK_RANDOM_FACTOR (where ACK_TIMEOUT is specified by
+	  COAP_INIT_ACK_TIMEOUT_MS option). Otherwise, the initial ACK timeout
+	  will be fixed to the value of COAP_INIT_ACK_TIMEOUT_MS option.
+
 config COAP_URI_WILDCARD
 	bool "Enable wildcards in CoAP resource path"
 	default y
diff --git a/subsys/net/lib/coap/coap.c b/subsys/net/lib/coap/coap.c
index ff0a1f7..192e81a 100644
--- a/subsys/net/lib/coap/coap.c
+++ b/subsys/net/lib/coap/coap.c
@@ -1196,18 +1196,28 @@
 	return found;
 }
 
-/* TODO: random generated initial ACK timeout
- * ACK_TIMEOUT < INIT_ACK_TIMEOUT < ACK_TIMEOUT * ACK_RANDOM_FACTOR
- * where ACK_TIMEOUT = 2 and ACK_RANDOM_FACTOR = 1.5 by default
- * Ref: https://tools.ietf.org/html/rfc7252#section-4.8
- */
-#define INIT_ACK_TIMEOUT	CONFIG_COAP_INIT_ACK_TIMEOUT_MS
+static uint32_t init_ack_timeout(void)
+{
+#if defined(CONFIG_COAP_RANDOMIZE_ACK_TIMEOUT)
+	const uint32_t max_ack = CONFIG_COAP_INIT_ACK_TIMEOUT_MS *
+				 COAP_DEFAULT_ACK_RANDOM_FACTOR;
+	const uint32_t min_ack = CONFIG_COAP_INIT_ACK_TIMEOUT_MS;
+
+	/* Randomly generated initial ACK timeout
+	 * ACK_TIMEOUT < INIT_ACK_TIMEOUT < ACK_TIMEOUT * ACK_RANDOM_FACTOR
+	 * Ref: https://tools.ietf.org/html/rfc7252#section-4.8
+	 */
+	return min_ack + (sys_rand32_get() % (max_ack - min_ack));
+#else
+	return CONFIG_COAP_INIT_ACK_TIMEOUT_MS;
+#endif /* defined(CONFIG_COAP_RANDOMIZE_ACK_TIMEOUT) */
+}
 
 bool coap_pending_cycle(struct coap_pending *pending)
 {
 	if (pending->timeout == 0) {
 		/* Initial transmission. */
-		pending->timeout = INIT_ACK_TIMEOUT;
+		pending->timeout = init_ack_timeout();
 
 		return true;
 	}