[PATCH] dhclient active interface detection patch

Martin Blapp mb at imp.ch
Fri Apr 18 04:33:58 PDT 2003


Hi all,

Already promised a few times, now I have done it :)

Many of you may have a laptop and already had it a few times
happen that the new IP didn't got set automatically. So you
had to kill dhclient and restart it again.

I've added code to dhclient to exactly fix this issue. Now dhclient
behaves now like the win2000 dhcp-client.

It polls the interface status of the selected interfaces and
sends only requests if the link is there. It cancels all outstanding
requests if the link goes away. The polling is currently hardcoded
and done all 5 seconds. Of course I can change that later and make a
option.

The changes should still compile on different platforms, but of
course, the polling mode doesn't work there till they add a customized
version of interface_active().

Of course I'm happy about bugs and things which can be done better.
And additional patches. I really like to see this code comitted and
be part of isc-dhcpd.

The code is also available here:

http://people.freebsd.org/~mbr/patches/dhclient-interfacepolling.diff

diff -ruN contrib/isc-dhcp.old/client/dhclient.c contrib/isc-dhcp/client/dhclient.c
--- contrib/isc-dhcp.old/client/dhclient.c	Sun Mar 23 23:29:36 2003
+++ contrib/isc-dhcp/client/dhclient.c	Fri Apr 18 11:12:04 2003
@@ -48,6 +48,11 @@
 #include "dhcpd.h"
 #include "version.h"

+#ifdef __FreeBSD__
+#include <sys/ioctl.h>
+#include <net/if_media.h>
+#endif
+
 TIME cur_time;
 TIME default_lease_time = 43200; /* 12 hours... */
 TIME max_lease_time = 86400; /* 24 hours... */
@@ -85,6 +90,7 @@
 int onetry=0;
 int quiet=1;
 int nowait=0;
+int linkcheck=0;

 static void usage PROTO ((void));

@@ -233,6 +239,7 @@
 			    log_fatal ("%s: interface name too long (max %ld)",
 				       argv [i], (long)strlen (argv [i]));
  		    strlcpy (tmp -> name, argv [i], IFNAMSIZ);
+		    tmp->status = interface_active(tmp->name);
 		    if (interfaces) {
 			    interface_reference (&tmp -> next,
 						 interfaces, MDL);
@@ -379,19 +386,20 @@
 	} else if (!release_mode) {
 		/* Call the script with the list of interfaces. */
 		for (ip = interfaces; ip; ip = ip -> next) {
-			/* If interfaces were specified, don't configure
-			   interfaces that weren't specified! */
-			if (interfaces_requested &&
-			    ((ip -> flags & (INTERFACE_REQUESTED |
-					     INTERFACE_AUTOMATIC)) !=
-			     INTERFACE_REQUESTED))
-				continue;
-			script_init (ip -> client,
-				     "PREINIT", (struct string_list *)0);
-			if (ip -> client -> alias)
-				script_write_params (ip -> client, "alias_",
-						     ip -> client -> alias);
-			script_go (ip -> client);
+			if (interface_active(ip->name)) {
+				/* If interfaces were specified, don't configure
+				   interfaces that weren't specified! */
+				if (interfaces_requested &&
+				    ((ip -> flags & (INTERFACE_REQUESTED |
+						     INTERFACE_AUTOMATIC)) !=
+				     INTERFACE_REQUESTED))
+					continue;
+
+				if (ip -> client -> alias)
+					script_write_params (ip -> client, "alias_",
+							     ip -> client -> alias);
+				script_go (ip -> client);
+			}
 		}
 	}

@@ -428,8 +436,13 @@
 				client -> state = S_INIT;
 				/* Set up a timeout to start the initialization
 				   process. */
+#ifdef ENABLE_POLLING_MODE
 				add_timeout (cur_time + random () % 5,
-					     state_reboot, client, 0, 0);
+					(void *)state_link, client, 0, 0);
+#else
+				add_timeout (cur_time + random () % 5,
+					state_reboot, client, 0, 0);
+#endif
 			}
 		}
 	}
@@ -2771,7 +2784,9 @@
 				break;
 			}
 			client -> state = S_INIT;
-			state_reboot (client);
+			if (interface_active(ip->name)) {
+				state_reboot (client);
+			}
 		}
 	}
 }
@@ -2932,8 +2947,10 @@
 			client -> state = S_INIT;
 			/* Set up a timeout to start the initialization
 			   process. */
-			add_timeout (cur_time + random () % 5,
-				     state_reboot, client, 0, 0);
+			if (interface_active(ip->name)) {
+				add_timeout (cur_time + random () % 5,
+					     state_reboot, client, 0, 0);
+			}
 		}
 	}
 	return ISC_R_SUCCESS;
@@ -2997,7 +3014,9 @@
 		    break;

 		  case server_awaken:
-		    state_reboot (client);
+		    if (interface_active(ip->name)) {
+			    state_reboot (client);
+		    }
 		    break;
 		}
 	    }
@@ -3134,3 +3153,94 @@
 	data_string_forget (&ddns_dhcid, MDL);
 	return rcode;
 }
+
+/* Check to see if there's a wire plugged in */
+int
+interface_active(const char *ifname) {
+#ifdef __FreeBSD__
+	struct ifmediareq ifmr;
+	int *media_list, i;
+	int sock;
+
+	if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+		log_fatal ("Can't create interface_active socket");
+
+	(void) memset(&ifmr, 0, sizeof(ifmr));
+	(void) strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+	if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+		/*
+		 * Interface doesn't support SIOCGIFMEDIA, presume okay
+		 */
+		return (1);
+	}
+
+	if (ifmr.ifm_count == 0) {
+		/*
+		 * this is unexpected (to me), but we'll just assume
+		 * that this means interface does not support SIOCGIFMEDIA
+		 */
+		return (1);
+	}
+
+	if (ifmr.ifm_status & IFM_AVALID) {
+		if (ifmr.ifm_status & IFM_ACTIVE)
+			return (1);
+	} else
+		return (0);
+
+	return (0);
+}
+#else /* ifdef __FreeBSD__ */
+
+	return (1);
+#endif /* Add here implementations for other operating systems !!! */
+
+#ifdef ENABLE_POLLING_MODE
+/* Check the state of the NICs if we have link */
+int
+state_link() {
+	struct interface_info *ip;
+	struct client_state *client;
+
+	for (ip = interfaces; ip; ip = ip -> next) {
+#ifdef DEBUG
+		printf("Connection-Status = %d, Name = %s\n", ip->status, ip->name);
+#endif
+		if (ip->status == 0 || linkcheck == 0) {
+			if (interface_active(ip->name)) {
+#ifdef DEBUG
+				printf("%s: Found Link on interface\n", ip->name);
+#endif
+				for (client = ip -> client;
+				     client; client = client -> next) {
+					add_timeout (cur_time + 2,
+					    state_reboot, client, 0, 0);
+				}
+				ip->status = 1;
+				linkcheck = 1;
+				return(1);
+			} else {
+#ifdef DEBUG
+				printf("%s: No Link on interface\n", ip->name);
+#endif
+				for (client = ip -> client;
+				     client; client = client -> next) {
+			 		cancel_timeout (send_discover, client);
+					cancel_timeout (send_request, client);
+			 	}
+				ip->status = 0;
+			}
+		} else {
+			if (interface_active(ip->name) == 0) {
+#ifdef DEBUG
+				printf("%s: Lost Link on interface\n", ip->name);
+#endif
+				ip->status = 0;
+			}
+		}
+	}
+	linkcheck = 1;
+	return(0);
+}
+#endif /* ifdef ENABLE_POLLING_MODE */
diff -ruN contrib/isc-dhcp.old/common/dispatch.c contrib/isc-dhcp/common/dispatch.c
--- contrib/isc-dhcp.old/common/dispatch.c	Thu Jan 16 07:04:49 2003
+++ contrib/isc-dhcp/common/dispatch.c	Fri Apr 18 10:23:21 2003
@@ -51,6 +51,8 @@
 struct timeout *timeouts;
 static struct timeout *free_timeouts;

+extern int linkcheck;
+
 void set_time (u_int32_t t)
 {
 	/* Do any outstanding timeouts. */
@@ -96,10 +98,19 @@
 {
 	struct timeval tv, *tvp;
 	isc_result_t status;
+	TIME cur_time;

+	tvp = NULL;
 	/* Wait for a packet or a timeout... XXX */
 	do {
 		tvp = process_outstanding_timeouts (&tv);
+#ifdef ENABLE_POLLING_MODE
+		if (! state_link()) {
+			GET_TIME (&cur_time);
+			add_timeout (cur_time + 5, (void *)state_link, 0, 0, 0);
+			tvp = process_outstanding_timeouts (&tv);
+		}
+#endif /* ENABLE_POLLING_MODE */
 		status = omapi_one_dispatch (0, tvp);
 	} while (status == ISC_R_TIMEDOUT || status == ISC_R_SUCCESS);
 	log_fatal ("omapi_one_dispatch failed: %s -- exiting.",
diff -ruN contrib/isc-dhcp.old/includes/dhcpd.h contrib/isc-dhcp/includes/dhcpd.h
--- contrib/isc-dhcp.old/includes/dhcpd.h	Wed Jan 15 10:31:19 2003
+++ contrib/isc-dhcp/includes/dhcpd.h	Fri Apr 18 10:48:09 2003
@@ -778,6 +778,7 @@
 	unsigned remote_id_len;		/* Length of Remote ID. */

 	char name [IFNAMSIZ];		/* Its name... */
+	int status;                     /* Link status */
 	int index;			/* Its index. */
 	int rfdesc;			/* Its read file descriptor. */
 	int wfdesc;			/* Its write file descriptor, if
@@ -1853,6 +1854,9 @@
 void send_decline PROTO ((void *));

 void state_reboot PROTO ((void *));
+#ifdef ENABLE_POLLING_MODE
+int  state_link PROTO (());
+#endif
 void state_init PROTO ((void *));
 void state_selecting PROTO ((void *));
 void state_requesting PROTO ((void *));

Martin

Martin Blapp, <mb at imp.ch> <mbr at FreeBSD.org>
------------------------------------------------------------------
ImproWare AG, UNIXSP & ISP, Zurlindenstrasse 29, 4133 Pratteln, CH
Phone: +41 61 826 93 00 Fax: +41 61 826 93 01
PGP: <finger -l mbr at freebsd.org>
PGP Fingerprint: B434 53FC C87C FE7B 0A18 B84C 8686 EF22 D300 551E
------------------------------------------------------------------


More information about the freebsd-hackers mailing list