allow/deny from - pool scoped permit

This patch (against DHCPD 3.1.0) enhances the pool scoped allow/deny permit lists with a timer conditional.

Example: Before 23/08/2007 15:30:00 CET pool A is active. Clients renewing after the deadline receive a NAK for this lease. As time approaches the deadline, the offered lease time will be shortend so that at the deadline all active clients have a min-lease-time lenght lease. This ensures that all clients stop using the lease within min-lease-time after the deadline.

DISCOVERs and REQUESTs for leases in pool B will be honored only after the stated date.

In effect: all clients move from pool A to pool B within min-lease-time after the given date.

subnet 192.168.240.0 netmask 255.255.255.0 {
        option routers 192.168.240.1;
        min-lease-time 30;
        pool {  # pool A
                deny after 4 23/08/2007 15:30:00 -7200;
                range 192.168.240.10 192.168.240.90;
        }
        pool {  # pool B
                allow after 4 23/08/2007 15:30:00 -7200;
                range 192.168.240.110 192.168.240.190;
        }

}
Index: DHCP/common/conflex.c
diff -c DHCP/common/conflex.c:1.105.8.2 DHCP/common/conflex.c:1.105.8.2.2.1
*** DHCP/common/conflex.c:1.105.8.2	Wed Aug 15 13:04:15 2007
--- DHCP/common/conflex.c	Fri Aug 24 19:01:06 2007
***************
*** 579,584 ****
--- 579,586 ----
  			return TOKEN_ACTIVE;
  		if (!strcasecmp (atom + 1, "tsfp"))
  			return ATSFP;
+                 if (!strcasecmp (atom + 1, "fter"))
+                         return AFTER;
  		break;
  	      case 'b':
  		if (!strcasecmp (atom + 1, "ackup"))
Index: DHCP/common/parse.c
diff -c DHCP/common/parse.c:1.117.8.5 DHCP/common/parse.c:1.117.8.5.2.1
*** DHCP/common/parse.c:1.117.8.5	Tue May 29 17:49:44 2007
--- DHCP/common/parse.c	Fri Aug 24 19:01:06 2007
***************
*** 739,745 ****
   * optional.
   */
 
! TIME parse_date (cfile)
  	struct parse *cfile;
  {
  	struct tm tm;
--- 739,746 ----
   * optional.
   */
 
! /* just parse the date */
! TIME parse_date_core (cfile)
  	struct parse *cfile;
  {
  	struct tm tm;
***************
*** 899,908 ****
  	} else
  		tzoff = 0;
 
- 	/* Make sure the date ends in a semicolon... */
- 	if (!parse_semi (cfile))
- 		return 0;
- 
  	/* Guess the time value... */
  	guess = ((((((365 * (year - 70) +	/* Days in years since '70 */
  		      (year - 69) / 4 +		/* Leap days since '70 */
--- 900,905 ----
***************
*** 927,932 ****
--- 924,945 ----
  	return guess;
  }
 
+ /* Wrapper to consume the semicolon after the date */
+ TIME parse_date (cfile)
+        struct parse *cfile;
+ {
+        int guess;
+        guess = parse_date_core(cfile);
+ 
+        /* Make sure the date ends in a semicolon... */
+        if (!parse_semi (cfile))
+                return 0;
+ 
+        return guess;
+ }
+ 
+ 
+ 
  /*
   * option-name :== IDENTIFIER |
   		   IDENTIFIER . IDENTIFIER
Index: DHCP/includes/dhcpd.h
diff -c DHCP/includes/dhcpd.h:1.226.2.8 DHCP/includes/dhcpd.h:1.226.2.8.2.1
*** DHCP/includes/dhcpd.h:1.226.2.8	Tue May 29 17:49:44 2007
--- DHCP/includes/dhcpd.h	Fri Aug 24 19:01:06 2007
***************
*** 680,688 ****
  		permit_unauthenticated_clients,
  		permit_all_clients,
  		permit_dynamic_bootp_clients,
! 		permit_class
  	} type;
  	struct class *class;
  };
 
  struct pool {
--- 680,690 ----
  		permit_unauthenticated_clients,
  		permit_all_clients,
  		permit_dynamic_bootp_clients,
! 		permit_class,
! 		permit_after
  	} type;
  	struct class *class;
+ 	TIME after;	/* date after which this clause applies */
  };
 
  struct pool {
***************
*** 703,708 ****
--- 705,713 ----
  	int free_leases;
  	int backup_leases;
  	int index;
+ 	TIME valid_from;        /* deny pool use before this date */
+ 	TIME valid_until;       /* deny pool use after this date */
+ 
  #if defined (FAILOVER_PROTOCOL)
  	dhcp_failover_state_t *failover_peer;
  #endif
Index: DHCP/includes/dhctoken.h
diff -c DHCP/includes/dhctoken.h:1.93 DHCP/includes/dhctoken.h:1.93.98.1
*** DHCP/includes/dhctoken.h:1.93	Mon Jul 31 22:19:51 2006
--- DHCP/includes/dhctoken.h	Fri Aug 24 19:01:06 2007
***************
*** 325,331 ****
  	MIN_BALANCE = 629,
  	DOMAIN_LIST = 630,
  	LEASEQUERY = 631,
! 	EXECUTE = 632
  };
 
  #define is_identifier(x)	((x) >= FIRST_TOKEN &&	\
--- 325,332 ----
  	MIN_BALANCE = 629,
  	DOMAIN_LIST = 630,
  	LEASEQUERY = 631,
! 	EXECUTE = 632,
! 	AFTER = 633
  };
 
  #define is_identifier(x)	((x) >= FIRST_TOKEN &&	\
Index: DHCP/server/confpars.c
diff -c DHCP/server/confpars.c:1.159.16.7 DHCP/server/confpars.c:1.159.16.7.2.1
*** DHCP/server/confpars.c:1.159.16.7	Wed Aug 15 15:09:01 2007
--- DHCP/server/confpars.c	Fri Aug 24 19:01:06 2007
***************
*** 1305,1310 ****
--- 1305,1312 ----
  	int declaration = 0;
  	isc_result_t status;
  	struct lease *lpchain = (struct lease *)0, *lp;
+ 	TIME t;
+ 	int is_allow = 0;
 
  	pool = (struct pool *)0;
  	status = pool_allocate (&pool, MDL);
***************
*** 1401,1406 ****
--- 1403,1410 ----
  			break;
  		      case ALLOW:
  			permit_head = &pool -> permit_list;
+ 			/* remember the clause which leads to get_permit */
+ 			is_allow = 1;
  		      get_permit:
  			permit = new_permit (MDL);
  			if (!permit)
***************
*** 1483,1488 ****
--- 1487,1503 ----
  						    "no such class: %s", val);
  				break;
 
+ 			      case AFTER:
+ 				t = parse_date_core (cfile);
+ 				permit->type = permit_after;
+ 				permit->after = t;
+ 				if (is_allow) {
+ 					pool->valid_from = t;
+ 				} else {
+ 					pool->valid_until = t;
+ 				}
+ 				break;
+ 
  			      default:
  				parse_warn (cfile, "expecting permit type.");
  				skip_to_semi (cfile);
***************
*** 1496,1501 ****
--- 1511,1518 ----
 
  		      case DENY:
  			permit_head = &pool -> prohibit_list;
+ 			/* remember the clause which leads to get_permit */
+ 			is_allow = 0; 
  			goto get_permit;
 
  		      case RBRACE:
Index: DHCP/server/dhcp.c
diff -c DHCP/server/dhcp.c:1.211.2.6 DHCP/server/dhcp.c:1.211.2.6.6.1
*** DHCP/server/dhcp.c:1.211.2.6	Mon May 21 22:13:50 2007
--- DHCP/server/dhcp.c	Fri Aug 24 19:01:06 2007
***************
*** 1460,1465 ****
--- 1460,1467 ----
  	TIME ping_timeout;
  	TIME lease_cltt;
  	struct in_addr from;
+ 	TIME remaining_time;
+ 	struct iaddr cip;
 
  	unsigned i, j;
  	int s1, s2;
***************
*** 2082,2087 ****
--- 2084,2138 ----
  			data_string_forget(&d1, MDL);
  		}
 
+ 		/* a client requests an address which is not yet active*/
+ 		if (lease->pool && lease->pool->valid_from) {
+ 			if (cur_time < lease->pool->valid_from) {
+ 				/* NAK leases before pool activation date */
+ 				cip.len = 4;
+ 				memcpy (cip.iabuf, &lt->ip_addr.iabuf, 4);
+ 				nak_lease(packet, &cip);
+ 				free_lease_state (state, MDL);
+ 				lease_dereference (&lt, MDL);
+ 				if (host)
+ 					host_dereference (&host, MDL);
+ 				return;
+ 			}
+ 		}
+ 
+ 		/* CC:
+ 		a) NAK current lease if past the expiration date
+ 		b) extend lease only up to the expiration date, but not
+ 		below min-lease-time
+ 		Setting min-lease-time is essential for this to work!
+ 		The value of min-lease-time determines the lenght
+ 		of the transition window:
+ 		A client renewing a second before the deadline will
+ 		get a min-lease-time lease. Since the current ip might not
+ 		be routable after the deadline, the client will
+ 		be offline until it DISCOVERS again. Otherwise it will
+ 		receive a NAK at T/2.
+ 		A min-lease-time of 6 seconds effectively switches over
+ 		all clients in this pool very quickly.
+ 			*/
+  
+ 		if (lease->pool && lease->pool->valid_until) {
+ 			if (cur_time >= lease->pool->valid_until) {
+ 				/* NAK leases after pool expiration date */
+ 				cip.len = 4;
+ 				memcpy (cip.iabuf, &lt->ip_addr.iabuf, 4);
+ 				nak_lease(packet, &cip);
+ 				free_lease_state (state, MDL);
+ 				lease_dereference (&lt, MDL);
+ 				if (host)
+ 					host_dereference (&host, MDL);
+ 				return;
+ 			}
+ 			remaining_time = lease->pool->valid_until - cur_time;
+ 			if (lease_time > remaining_time)
+ 				lease_time = remaining_time;
+ 		}
+  
+ 
  		if (lease_time < min_lease_time) {
  			if (min_lease_time)
  				lease_time = min_lease_time;
***************
*** 2089,2094 ****
--- 2140,2146 ----
  				lease_time = default_lease_time;
  		}
 
+ 
  #if defined (FAILOVER_PROTOCOL)
  		/* Okay, we know the lease duration.   Now check the
  		   failover state, if any. */
***************
*** 3849,3854 ****
--- 3901,3911 ----
  					return 1;
  			}
  			break;
+ 
+ 		      case permit_after:
+ 			if (cur_time > p->after)
+ 				return 1;
+ 			break;
  		}
  	}
  	return 0;
Index: DHCP/server/dhcpd.conf.5
diff -c DHCP/server/dhcpd.conf.5:1.80.4.6 DHCP/server/dhcpd.conf.5:1.80.4.6.2.1
*** DHCP/server/dhcpd.conf.5:1.80.4.6	Fri Jul 20 19:18:44 2007
--- DHCP/server/dhcpd.conf.5	Fri Aug 24 19:01:06 2007
***************
*** 1785,1790 ****
--- 1785,1802 ----
  want to renumber your network quickly, and thus want the server to
  force all clients that have been allocated addresses from this pool to
  obtain new addresses immediately when they next renew.
+ .PP
+  \fBafter \fItime\fR\fB;\fR
+ .PP
+ If specified, this statement either allows or prevents allocation from
+ this pool after a given date. This can be used when you want to move
+ clients from one pool to another. The server adjusts the regular lease
+ time so that the latest expiry time is at the given time+min-lease-time.
+ A short min-lease-time enforces a step change, whereas a longer
+ min-lease-time allows for a gradual change.
+ \fItime\fR is either second since epoch, or a GMT time string e.g.
+ 4 2007/08/24 09:14:32 or a string with time zone offset in seconds
+ e.g. 4 2007/08/24 11:14:32 -7200
  .SH REFERENCE: PARAMETERS
  The
  .I adaptive-lease-time-threshold
Copyright © 2024 Christof Chen