From d47aa2101029238c9526914e454452c97beb26ce Mon Sep 17 00:00:00 2001
From: Marius Tomaschewski <mt@suse.de>
Date: Thu, 18 Mar 2021 11:38:33 +0100
Subject: [PATCH] dhcp4: discover on reboot timeout after start-delay
 (bsc#1181812)

When enabled, randomize start delay in range 1..config, max 10sec.

diff --git a/src/dhcp4/device.c b/src/dhcp4/device.c
index 6b448aa7..e225946b 100644
--- a/src/dhcp4/device.c
+++ b/src/dhcp4/device.c
@@ -620,8 +620,7 @@ ni_dhcp4_device_start(ni_dhcp4_device_t *dev)
 {
 	ni_netconfig_t *nc;
 	ni_netdev_t *ifp;
-	ni_int_range_t jitter = { .max = 500, /* msec */ };
-	unsigned long msec = dev->config->start_delay * 1000;
+	unsigned long sec;
 
 	ni_dhcp4_device_drop_buffer(dev);
 	dev->failed = 0;
@@ -634,10 +633,13 @@ ni_dhcp4_device_start(ni_dhcp4_device_t *dev)
 	}
 
 	/* Reuse defer pointer for this one-shot timer */
-	msec = ni_timeout_randomize(msec, &jitter);
+	sec = ni_dhcp4_fsm_start_delay(dev->config->start_delay);
+	if (dev->config->defer_timeout > sec)
+		dev->config->defer_timeout -= sec;
+
 	if (dev->defer.timer)
 		ni_timer_cancel(dev->defer.timer);
-	dev->defer.timer = ni_timer_register(msec, ni_dhcp4_device_start_delayed, dev);
+	dev->defer.timer = ni_timer_register(sec * 1000, ni_dhcp4_device_start_delayed, dev);
 
 	return 1;
 }
diff --git a/src/dhcp4/dhcp4.h b/src/dhcp4/dhcp4.h
index ed7947e1..a35ed5ef 100644
--- a/src/dhcp4/dhcp4.h
+++ b/src/dhcp4/dhcp4.h
@@ -88,6 +88,8 @@ typedef struct ni_dhcp4_device {
 	} best_offer;
 } ni_dhcp4_device_t;
 
+#define NI_DHCP4_START_DELAY_MIN	1	/* seconds */
+#define NI_DHCP4_START_DELAY_MAX	10	/* seconds */
 #define NI_DHCP4_RESEND_TIMEOUT_INIT	4	/* seconds */
 #define NI_DHCP4_RESEND_TIMEOUT_MAX	64	/* seconds */
 #define NI_DHCP4_REQUEST_TIMEOUT		60	/* seconds */
@@ -222,6 +224,7 @@ extern int		ni_dhcp4_release(ni_dhcp4_device_t *, const ni_uuid_t *);
 extern void		ni_dhcp4_restart_leases(void);
 
 extern const char *	ni_dhcp4_fsm_state_name(enum fsm_state);
+extern unsigned int	ni_dhcp4_fsm_start_delay(unsigned int);
 extern void		ni_dhcp4_fsm_init_device(ni_dhcp4_device_t *);
 extern void		ni_dhcp4_fsm_release_init(ni_dhcp4_device_t *);
 extern int		ni_dhcp4_fsm_process_dhcp4_packet(ni_dhcp4_device_t *, ni_buffer_t *, ni_sockaddr_t *);
diff --git a/src/dhcp4/fsm.c b/src/dhcp4/fsm.c
index dcd19103..77e9c293 100644
--- a/src/dhcp4/fsm.c
+++ b/src/dhcp4/fsm.c
@@ -356,13 +356,11 @@ ni_dhcp4_fsm_restart(ni_dhcp4_device_t *dev)
 static void
 ni_dhcp4_fsm_set_timeout_msec(ni_dhcp4_device_t *dev, unsigned int msec)
 {
-	if (msec != 0) {
-		ni_debug_dhcp("%s: setting fsm timeout to %u msec", dev->ifname, msec);
-		if (dev->fsm.timer)
-			ni_timer_rearm(dev->fsm.timer, msec);
-		else
-			dev->fsm.timer = ni_timer_register(msec, __ni_dhcp4_fsm_timeout, dev);
-	}
+	ni_debug_dhcp("%s: setting fsm timeout to %u msec", dev->ifname, msec);
+	if (dev->fsm.timer)
+		ni_timer_rearm(dev->fsm.timer, msec);
+	else
+		dev->fsm.timer = ni_timer_register(msec, __ni_dhcp4_fsm_timeout, dev);
 }
 
 static void
@@ -371,6 +369,17 @@ ni_dhcp4_fsm_set_timeout(ni_dhcp4_device_t *dev, unsigned int seconds)
 	ni_dhcp4_fsm_set_timeout_msec(dev, 1000 * seconds);
 }
 
+unsigned int
+ni_dhcp4_fsm_start_delay(unsigned int start_delay)
+{
+	ni_int_range_t range = {
+		.min = min_t(unsigned int, start_delay, NI_DHCP4_START_DELAY_MIN),
+		.max = min_t(unsigned int, start_delay, NI_DHCP4_START_DELAY_MAX),
+	};
+	unsigned int sec = min_t(unsigned int, start_delay, NI_DHCP4_START_DELAY_MIN);
+	return ni_timeout_randomize(sec, &range);
+}
+
 static void
 __ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev, int scan_offers)
 {
@@ -671,22 +680,22 @@ ni_dhcp4_fsm_timeout(ni_dhcp4_device_t *dev)
 	case NI_DHCP4_STATE_RENEWING:
 		if (ni_dhcp4_fsm_renewal(dev, FALSE) == TRUE)
 			return;
-		ni_error("unable to renew lease within renewal period; trying to rebind");
+		ni_error("%s: unable to renew lease; trying to rebind", dev->ifname);
 		ni_dhcp4_fsm_rebind_init(dev);
 		break;
 
 	case NI_DHCP4_STATE_REBINDING:
 		if (ni_dhcp4_fsm_rebind(dev, FALSE) == TRUE)
 			return;
-		ni_error("unable to rebind lease");
+		ni_error("%s: unable to rebind lease", dev->ifname);
 		ni_dhcp4_fsm_restart(dev);
-		ni_dhcp4_fsm_set_timeout(dev, 10);
+		ni_dhcp4_fsm_set_timeout(dev, ni_dhcp4_fsm_start_delay(conf->start_delay));
 		break;
 
 	case NI_DHCP4_STATE_REBOOT:
-		ni_error("unable to confirm lease");
+		ni_error("%s: unable to confirm lease", dev->ifname);
 		ni_dhcp4_fsm_restart(dev);
-		ni_dhcp4_fsm_set_timeout(dev, 10);
+		ni_dhcp4_fsm_set_timeout(dev, ni_dhcp4_fsm_start_delay(conf->start_delay));
 		break;
 	case __NI_DHCP4_STATE_MAX:
 		break;
-- 
2.26.2

