OpenBSD dhclient verbosity patches

Purpose:
I have created a number of working WiFi access points and needed to inspect just what information they were actually providing to clients, not what I thought they were providing. I've also run into some wacky APs run by other people where such things as the DNS server address and netmask often didn't make sense. This can be used to either point out flaws in these implementations or let you silently correct for them and get your client machine running then sneak away when you're done.

History:
I wrote this originally under OpenBSD 5.0 then a year later frantically searched for my modifications and applied them to a 5.2 laptop, then still later to my 4.7 laptop. There are enough differences between versions that a single set of patches wouldn't work on all, I had to do them manually. I go into an unusual amount of explanation of each patch hunk below for that reason. This can work as a normal dhclient, but providing the -t or -v options on the command line invokes the test and/or verbose options.

If you've got the source tree extracted, dhclient lives in /usr/src/sbin/dhclient. You can either modify that or make a copy of the entire directory, make changes inside it, then do a make & make install. To fall back to the original, just make & make install in the original directory.

A sample run:

freebie# ./dhclient -tv ath0
DHCPREQUEST on ath0 to 255.255.255.255 port 67
DHCPACK from 192.168.0.54 (00:0b:6b:35:77:93)
lease {
  interface "ath0";
  fixed-address 192.168.0.250;
  option subnet-mask 255.255.255.0;
  option routers 192.168.0.7;
  option domain-name-servers 64.80.224.125,63.246.206.29,192.168.0.3;
  option domain-name "my.domain";
  option dhcp-lease-time 43200;
  option dhcp-message-type 5;
  option dhcp-server-identifier 192.168.0.54;
  option dhcp-renewal-time 21600;
  option dhcp-rebinding-time 37800;
  renew 3 2012/4/4 20:37:32;
  rebind 4 2012/4/5 01:07:32;
  expire 4 2012/4/5 02:37:32;
}
dhclient dry run complete.
freebie#

hunk #1: Define global variables
int dryrun = 0;
int verbose = 0;	
#2: provide triggering options for getopt
   case 't':
        dryrun = 1;
        break;
   case 'v':
        verbose = 1;
        break;                                                                                                                        break;
#3: update the usage string, adding t and v
fprintf(stderr, "usage: %s [-dqtuv] [-c file] [-l file] interface\n",__progname);
#4: be verbose about declining a lease
if (verbose > 0)
  fprintf(stderr,"%s %s %s\n","Lease", piaddr(lp->address) ,"declined.\n");
#5: don't write out (adopt) the lease if we're in dry run mode
if (dryrun == 0)
  write_client_lease(client->new, 0);
#6: define a blab_client_lease function (write lease to screen)
void
blab_client_lease(struct client_lease *lease, int rewrite)
{
	struct tm *t;
	int i;

	fprintf(stdout, "lease {\n");
	if (lease->is_bootp)
		fprintf(stdout, "  bootp;\n");
	fprintf(stdout, "  interface \"%s\";\n", ifi->name);
	fprintf(stdout, "  fixed-address %s;\n", piaddr(lease->address));
	if (lease->filename)
		fprintf(stdout, "  filename \"%s\";\n", lease->filename);
	if (lease->server_name)
		fprintf(stdout, "  server-name \"%s\";\n",
		    lease->server_name);
	for (i = 0; i < 256; i++)
		if (lease->options[i].len)
			fprintf(stdout, "  option %s %s;\n",
			    dhcp_options[i].name,
			    pretty_print_option(i, lease->options[i].data,
			    lease->options[i].len, 1, 1));

	t = gmtime(&lease->renewal);
	fprintf(stdout, "  renew %d %d/%d/%d %02d:%02d:%02d;\n",
	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
	    t->tm_hour, t->tm_min, t->tm_sec);
	t = gmtime(&lease->rebind);
	fprintf(stdout, "  rebind %d %d/%d/%d %02d:%02d:%02d;\n",
	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
	    t->tm_hour, t->tm_min, t->tm_sec);
	t = gmtime(&lease->expiry);
	fprintf(stdout, "  expire %d %d/%d/%d %02d:%02d:%02d;\n",
	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
	    t->tm_hour, t->tm_min, t->tm_sec);
	fprintf(stdout, "}\n");
	fflush(stdout);
	if (dryrun > 0) {
        	printf("dhclient dry run complete.\n");
        	exit(0);
	}
}
#7: write the lease to the screen if in verbose mode
if (verbose > 0)
   blab_client_lease(client->new,0);
#8: block doing anything with the lease if in dryrun mode
if (dryrun == 0) {                        
   /* Run the client script with the new parameters. */
    script_init((client->state ==   [etc...]
}                                        
#9: announce if lease is via bootp (in verbose mode)
if ((verbose > 0) && (lease->is_bootp > 0))
   fprintf(stderr, "lease via bootp.\n");
#10: show timeout (in verbose mode)
if (verbose > 0)
   fprintf(stderr, "Trying for %i seconds.\n",config->timeout - interval);
#11: show retry time
if (verbose > 0)
  fprintf(stderr,"%s %i %s\n","retry in ",config->retry_interval/60,  "minutes.");
#12: fail, if not dryrun mode
if (dryrun == 0) {
  [original]
} else {
  fprintf(stderr,"Test mode giving up.\n");
  exit(1);                                 
}
                        

The actual patch from OpenBSD 5.2 is here as a text patch file and below in HTML form. The version below has HTML escapes added manually for &, >, and <, so don't copy from the source of this page. The diff was done with -c. What's above may be from a different version of OpenBSD but the concept is the same.

*** dhclient_original.c	Tue Jul 17 19:03:49 2012
--- dhclient.c	Sat May 18 23:42:55 2013
***************
*** 77,82 ****
--- 77,84 ----
  int no_daemon;
  int unknown_ok = 1;
  int routefd = -1;
+ int dryrun = 0;
+ int verbose = 0;
  
  struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
  struct in_addr inaddr_any;
***************
*** 280,286 ****
  	openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
  	setlogmask(LOG_UPTO(LOG_INFO));
  
! 	while ((ch = getopt(argc, argv, "c:dl:qu")) != -1)
  		switch (ch) {
  		case 'c':
  			path_dhclient_conf = optarg;
--- 282,288 ----
  	openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY);
  	setlogmask(LOG_UPTO(LOG_INFO));
  
! 	while ((ch = getopt(argc, argv, "c:dl:qtvu")) != -1)
  		switch (ch) {
  		case 'c':
  			path_dhclient_conf = optarg;
***************
*** 297,302 ****
--- 299,310 ----
  		case 'u':
  			unknown_ok = 0;
  			break;
+                 case 't':
+                         dryrun = 1;
+                         break;
+                 case 'v':
+                         verbose = 1;
+                         break;
  		default:
  			usage();
  		}
***************
*** 431,437 ****
  {
  	extern char	*__progname;
  
! 	fprintf(stderr, "usage: %s [-dqu] [-c file] [-l file] interface\n",
  	    __progname);
  	exit(1);
  }
--- 439,445 ----
  {
  	extern char	*__progname;
  
! 	fprintf(stderr, "usage: %s [-dqtuv] [-c file] [-l file] interface\n",
  	    __progname);
  	exit(1);
  }
***************
*** 540,545 ****
--- 548,556 ----
  		} else {
  			make_decline(lp);
  			send_decline();
+ 			if (verbose > 0)
+ 			  fprintf(stderr,"%s %s %s\n","Lease", \
+ 			    piaddr(lp->address) ,"declined.\n");
  			free_client_lease(lp);
  		}
  	}
***************
*** 656,690 ****
  }
  
  void
  bind_lease(void)
  {
! 	/* Run the client script with the new parameters. */
! 	script_init((client->state == S_REQUESTING ? "BOUND" :
! 	    (client->state == S_RENEWING ? "RENEW" :
! 	    (client->state == S_REBOOTING ? "REBOOT" : "REBIND"))));
! 	if (client->active && client->state != S_REBOOTING)
! 		script_write_params("old_", client->active);
! 	script_write_params("new_", client->new);
! 	script_go();
  
! 	/* Replace the old active lease with the new one. */
! 	if (client->active)
! 		free_client_lease(client->active);
! 	client->active = client->new;
! 	client->new = NULL;
  
! 	/* Write out new leases file. */
! 	rewrite_client_leases();
  
! 	/* Set timeout to start the renewal process. */
! 	set_timeout(client->active->renewal, state_bound);
  
! 	note("bound to %s -- renewal in %d seconds.",
! 	    piaddr(client->active->address),
! 	    client->active->renewal - cur_time);
! 	client->state = S_BOUND;
! 	reinitialize_interface();
! 	go_daemon();
  }
  
  /*
--- 667,757 ----
  }
  
  void
+ blab_client_lease(struct client_lease *lease)
+ {
+ 	struct tm *t;
+ 	int i;
+ 
+ 	fprintf(stdout, "lease {\n");
+ 	if (lease->is_bootp)
+ 		fprintf(stdout, "  bootp;\n");
+ 	fprintf(stdout, "  interface \"%s\";\n", ifi->name);
+ 	fprintf(stdout, "  fixed-address %s;\n", piaddr(lease->address));
+ 	if (lease->filename)
+ 		fprintf(stdout, "  filename \"%s\";\n", lease->filename);
+ 	if (lease->server_name)
+ 		fprintf(stdout, "  server-name \"%s\";\n",
+ 		    lease->server_name);
+ 	for (i = 0; i < 256; i++)
+ 		if (lease->options[i].len)
+ 			fprintf(stdout, "  option %s %s;\n",
+ 			    dhcp_options[i].name,
+ 			    pretty_print_option(i, lease->options[i].data,
+ 			    lease->options[i].len, 1, 1));
+ 
+ 	t = gmtime(&lease->renewal);
+ 	fprintf(stdout, "  renew %d %d/%d/%d %02d:%02d:%02d;\n",
+ 	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ 	    t->tm_hour, t->tm_min, t->tm_sec);
+ 	t = gmtime(&lease->rebind);
+ 	fprintf(stdout, "  rebind %d %d/%d/%d %02d:%02d:%02d;\n",
+ 	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ 	    t->tm_hour, t->tm_min, t->tm_sec);
+ 	t = gmtime(&lease->expiry);
+ 	fprintf(stdout, "  expire %d %d/%d/%d %02d:%02d:%02d;\n",
+ 	    t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ 	    t->tm_hour, t->tm_min, t->tm_sec);
+ 	fprintf(stdout, "}\n");
+ 	fflush(stdout);
+ 	if (dryrun > 0) {
+         	printf("dhclient dry run complete.\n");
+         	exit(0);
+ 	}
+ }
+ 
+ 
+ void
  bind_lease(void)
  {
!         /* Display the new lease */
!         if (dryrun == 0)
!                 write_client_lease(client->new);
!         if (verbose > 0)
!                 blab_client_lease(client->new);
!         if (dryrun == 0) {
!         	/* Run the client script with the new parameters. */
!         	script_init((client->state == S_REQUESTING ? "BOUND" :
!         	    (client->state == S_RENEWING ? "RENEW" :
!         	    (client->state == S_REBOOTING ? "REBOOT" : "REBIND"))));
!         	if (client->active && client->state != S_REBOOTING)
!         		script_write_params("old_", client->active);
!         	script_write_params("new_", client->new);
!         	script_go();
  
!         	/* Replace the old active lease with the new one. */
!         	if (client->active)
!         		free_client_lease(client->active);
!         	client->active = client->new;
!         	client->new = NULL;
  
!         	/* Write out new leases file. */
!         	rewrite_client_leases();
  
!         	/* Set timeout to start the renewal process. */
!         	set_timeout(client->active->renewal, state_bound);
  
!         	note("bound to %s -- renewal in %d seconds.",
! 	            piaddr(client->active->address),
!         	    client->active->renewal - cur_time);
!         	client->state = S_BOUND;
!         	reinitialize_interface();
!         	go_daemon();
!         } else {  /* dryrun != 0 */
!                 note("Would bind to %s -- renewal in %d seconds.",
!                   piaddr(client->active->address),
!                   client->active->renewal - cur_time);
!                   exit(0);
!         }
  }
  
  /*
***************
*** 760,765 ****
--- 827,834 ----
  	   fact. */
  	if (!options[DHO_DHCP_MESSAGE_TYPE].len)
  		lease->is_bootp = 1;
+         if ((verbose > 0) && (lease->is_bootp > 0))
+                 fprintf(stderr,"lease via bootp.\n");
  
  	/* Figure out when we're supposed to stop selecting. */
  	stop_selecting = client->first_sending + config->select_interval;
***************
*** 902,907 ****
--- 971,979 ----
  
  	/* Figure out how long it's been since we started transmitting. */
  	interval = cur_time - client->first_sending;
+ 	
+ 	if (verbose > 0)
+ 	        fprintf(stderr,"Trying for %i seconds.\n", config->timeout - interval);
  
  	/* If we're past the panic timeout, call the script and tell it
  	   we haven't found anything for this interface yet. */
***************
*** 1040,1050 ****
  	   tell the shell script that we failed to allocate an address,
  	   and try again later. */
  	note("No working leases in persistent database - sleeping.");
! 	script_init("FAIL");
! 	script_go();
! 	client->state = S_INIT;
! 	set_timeout(cur_time + config->retry_interval, state_init);
! 	go_daemon();
  }
  
  void
--- 1112,1130 ----
  	   tell the shell script that we failed to allocate an address,
  	   and try again later. */
  	note("No working leases in persistent database - sleeping.");
! 	if (verbose > 0)
!                 fprintf(stderr,"%s %i %s\n","retry in ",\
!                 config->retry_interval/60, "minutes.");
!         if (dryrun == 0) {
!         	script_init("FAIL");
!         	script_go();
!         	client->state = S_INIT;
! 	        set_timeout(cur_time + config->retry_interval, state_init);
!         	go_daemon();
!         } else {
!                 fprintf(stderr,"Test mode giving up.\n");
!                 exit(1);
!         }
  }
  
  void
***************
*** 2108,2110 ****
--- 2188,2191 ----
  	} else if (strlcpy(ifi->name, arg, IFNAMSIZ) >= IFNAMSIZ)
  		error("Interface name too long");
  }
+ 
AB1JX / calcs