Patch1. For ISP. Provides high performance using CLASS spawn
http://www.miquels.cistron.nl/isc-dhcpd/patch-server::01-subclassdiff -ruN ../../dhcp-3.0.5.orig/includes/dhcpd.h ./includes/dhcpd.h
--- ../../dhcp-3.0.5.orig/includes/dhcpd.h Wed May 17 22:16:59 2006
+++ ./includes/dhcpd.h Tue Jan 22 21:28:24 2008
@@ -551,6 +551,7 @@
permit_class
} type;
struct class *class;
+ struct data_string hash_string;
};
struct pool {
diff -ruN ../../dhcp-3.0.5.orig/server/confpars.c ./server/confpars.c
--- ../../dhcp-3.0.5.orig/server/confpars.c Thu Jul 20 18:02:52 2006
+++ ./server/confpars.c Tue Jan 22 21:28:24 2008
@@ -1231,6 +1231,21 @@
}
#endif /* defined (FAILOVER_PROTOCOL) */
+
+/* Permit_same_class returns 1 if both permit structs contain
+ * the same class and the same hash_string aka subclass. */
+
+int permit_same_class(struct permit *plp, struct permit *prp)
+{
+ if (plp -> class != prp -> class)
+ return 0;
+ if (plp -> hash_string.len == 0 ||
+ plp -> hash_string.len != prp -> hash_string.len)
+ return 0;
+ return memcmp(plp -> hash_string.data,
+ prp -> hash_string.data, prp -> hash_string.len) == 0;
+}
+
/* Permit_list_match returns 1 if every element of the permit list in lhs
also appears in rhs. Note that this doesn't by itself mean that the
two lists are equal - to check for equality, permit_list_match has to
@@ -1250,7 +1265,7 @@
for (prp = rhs; prp; prp = prp -> next) {
if (prp -> type == plp -> type &&
(prp -> type != permit_class ||
- prp -> class == plp -> class)) {
+ permit_same_class(prp, plp))) {
matched = 1;
break;
}
@@ -1275,6 +1290,7 @@
int declaration = 0;
isc_result_t status;
struct lease *lpchain = (struct lease *)0, *lp;
+ struct data_string *dp;
pool = (struct pool *)0;
status = pool_allocate (&pool, MDL);
@@ -1440,10 +1456,39 @@
}
permit -> type = permit_class;
permit -> class = (struct class *)0;
+
find_class (&permit -> class, val, MDL);
- if (!permit -> class)
+ if (!permit -> class) {
parse_warn (cfile,
"no such class: %s", val);
+ break;
+ }
+
+ dp = &permit -> hash_string;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token(&val,
+ &dp -> len, cfile);
+ dp -> buffer = (struct buffer *)0;
+ if (!buffer_allocate (&dp -> buffer,
+ dp -> len + 1, MDL)) {
+ free_permit(permit, MDL);
+ continue;
+ }
+ dp -> terminated = 1;
+ dp -> data = &dp -> buffer -> data [0];
+ memcpy ((char *)dp -> buffer -> data,
+ val, dp -> len + 1);
+ } else if (token == NUMBER_OR_NAME ||
+ token == NUMBER) {
+ memset (dp, 0, sizeof *dp);
+ if (!parse_cshl (dp, cfile)) {
+ free_permit(permit, MDL);
+ continue;
+ }
+ }
+
break;
default:
diff -ruN ../../dhcp-3.0.5.orig/server/dhcp.c ./server/dhcp.c
--- ../../dhcp-3.0.5.orig/server/dhcp.c Tue Aug 22 19:15:56 2006
+++ ./server/dhcp.c Tue Jan 22 21:28:24 2008
@@ -3807,12 +3807,28 @@
case permit_class:
for (i = 0; i < packet -> class_count; i++) {
- if (p -> class == packet -> classes [i])
- return 1;
- if (packet -> classes [i] &&
- packet -> classes [i] -> superclass &&
- (packet -> classes [i] -> superclass ==
- p -> class))
+ int r = 0;
+ struct class *pc = packet -> classes[i];
+
+ /* Does packet class or superclass match ? */
+ if (!p -> class || !pc)
+ continue;
+ if (p -> class == pc)
+ r = 1;
+ if (p -> class == pc -> superclass)
+ r = 1;
+
+ /* Does the subclass match as well ? */
+ if (r && p -> hash_string.len) {
+
+ if (p -> hash_string.len !=
+ pc -> hash_string.len ||
+ memcmp(p -> hash_string.data,
+ pc -> hash_string.data,
+ pc -> hash_string.len))
+ r = 0;
+ }
+ if (r)
return 1;
}
break;
diff -ruN ../../dhcp-3.0.5.orig/server/salloc.c ./server/salloc.c
--- ../../dhcp-3.0.5.orig/server/salloc.c Wed Feb 22 23:43:27 2006
+++ ./server/salloc.c Tue Jan 22 21:28:24 2008
@@ -252,7 +252,10 @@
const char *file;
int line;
{
- if (permit -> type == permit_class)
+ if (permit -> type == permit_class) {
class_dereference (&permit -> class, MDL);
+ if (permit -> hash_string.buffer)
+ buffer_dereference(&permit -> hash_string.buffer, MDL);
+ }
dfree (permit, file, line);
}
After this DHCP dhcpd.conf will look like this:
class "OPT82" {spawn with concat(substring(option agent.remote-id, 2,6),substring (option agent.circuit-id, 5, 1));}
...
shared-network NET-192.168.33.0 {
subnet 192.168.33.0 netmask 255.255.255.0 { option routers 192.168.33.254; option subnet-mask 255.255.255.0; option broadcast-address 192.168.33.255 ; }
pool { range 192.168.33.9; allow members of "OPT82" 00:1a:30:ad:60:00:23; deny dynamic bootp clients; }
After applying this patch we getting awesome performance: CPU dual-core Xeon, 30000 pools, lease-time 15min, DHCP rate about 80-100 PPS, CPU load: 2-3%
Now client will be able to receive IP address based on only OPTION82. Everything look good, but:
As soon as you put into network new device you will receive "no free lease" log
DHCPDISCOVER
from 00:26:11:22:33:44 via
213.129.33.254: network NET-213.129.33.0: no free leases
There are few options:
Wait for the old lease expiresConnect old device and RELEASE IP addressClone MAC from old device (but wait where is the Option82 sharm?! )
DHCPDISCOVER from 00:26:11:22:33:44 via 213.129.33.254: network NET-213.129.33.0: no free leases
There only new MAC is avaliable, no old MAC, no IP address no Option82.
What we gonna do? Patch, patch and patch. There is it PATCH number 2:
Patch2. Prints Option82 agent params in DHCPDISCOVER packet, if any exists
diff -ruN dhcp-4.2.3-P2+01/server/dhcp.c dhcp-4.2.3-P2/server/dhcp.c--- dhcp-4.2.3-P2+01/server/dhcp.c 2012-04-02 22:47:00.838775000 +0200
+++ dhcp-4.2.3-P2/server/dhcp.c 2012-04-02 22:55:40.159751000 +0200
@@ -41,6 +41,12 @@
static void maybe_return_agent_options(struct packet *packet,
struct option_state *options);
+/*
+ * This setting makes the server log the agent.circuit-id
+ * and agent.remote-id options on DHCPDISCOVER.
+ */
+#define LOG_AGENT_OPTIONS 1
+
int outstanding_pings;
struct leasequeue *ackqueue_head, *ackqueue_tail;
@@ -262,6 +268,60 @@
lease_dereference (&lease, MDL);
}
+#if LOG_AGENT_OPTIONS
+static int get_agent_option(struct packet *packet, int opt,
+ struct data_string *data)
+{
+ struct data_string new;
+ struct option_cache *oc;
+ char *b;
+ int i;
+
+ oc = lookup_option (&agent_universe, packet -> options, opt);
+ if (!oc)
+ return 0;
+ memset (data, 0, sizeof(*data));
+ if (!evaluate_option_cache (data, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL))
+ return 0;
+
+ if (data -> len == 0) {
+ data_string_forget (data, MDL);
+ return 0;
+ }
+
+ /* Printable ? */
+ for (i = 0; i < data -> len; i++)
+ if (data -> data[i] < 32 || data -> data[i] > 127)
+ break;
+ if (i == data -> len)
+ return 1;
+
+ /* Non-printable, return hex string */
+ memset (&new, 0, sizeof(new));
+ if (!buffer_allocate (&new.buffer, 3 * data -> len, MDL)) {
+ log_error ("get_agent_option: No memory");
+ data_string_forget (data, MDL);
+ return 0;
+ }
+ new.data = new.buffer -> data;
+ b = (char *)new.data;
+ for (i = 0; i < data -> len; i++) {
+ /* This is Safe. */
+ b += sprintf (b, "%x:", data -> data[i]);
+ }
+ b--;
+ new.len = b - (char *)new.data;
+ data_string_forget (data, MDL);
+ data_string_copy (data, &new, MDL);
+ data_string_forget (&new, MDL);
+
+ return 1;
+}
+#endif
+
void dhcpdiscover (packet, ms_nulltp)
struct packet *packet;
int ms_nulltp;
@@ -271,9 +331,14 @@
TIME when;
const char *s;
int peer_has_leases = 0;
+ char agentbuf[1024];
#if defined (FAILOVER_PROTOCOL)
dhcp_failover_state_t *peer;
#endif
+#if LOG_AGENT_OPTIONS
+ struct data_string data;
+ int l;
+#endif
find_lease (&lease, packet, packet -> shared_network,
0, &peer_has_leases, (struct lease *)0, MDL);
@@ -287,10 +352,25 @@
} else
s = (char *)0;
+ agentbuf[0] = 0;
+#if LOG_AGENT_OPTIONS
+ l = 0;
+ if (get_agent_option(packet, RAI_CIRCUIT_ID, &data)) {
+ l += snprintf (agentbuf + l, sizeof(agentbuf) - l,
+ "circuit-id %.*s ", data.len, data.data);
+ data_string_forget (&data, MDL);
+ }
+ if (get_agent_option(packet, RAI_REMOTE_ID, &data)) {
+ l += snprintf (agentbuf + l, sizeof(agentbuf) - l,
+ "remote-id %.*s ", data.len, data.data);
+ data_string_forget (&data, MDL);
+ }
+#endif
+
/* %Audit% This is log output. %2004.06.17,Safe%
* If we truncate we hope the user can get a hint from the log.
*/
- snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%svia %s",
+ snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%s%svia %s",
(packet -> raw -> htype
? print_hw_addr (packet -> raw -> htype,
packet -> raw -> hlen,
@@ -299,6 +379,7 @@
? print_hex_1(lease->uid_len, lease->uid, 60)
: "
s ? "(" : "", s ? s : "", s ? ") " : "",
+ agentbuf,
packet -> raw -> giaddr.s_addr
? inet_ntoa (packet -> raw -> giaddr)
: packet -> interface -> name);
after applying this patch DHCPDISCOVER will looks like this
DHCPDISCOVER from 00:26:11:22:33:44 c-id 0:4:6:13:1:23 r-id 0:6:0:1a:30:ad:60:0 via 213.129.33.254: network NET-213.129.33.0: no free leases
you can see additional info: c-id 0:4:6:13:1:23 r-id 0:6:0:1a:30:ad:60:0, where c-id is circuit-id, r-id is remote-id, cute!
Log, log, log
DHCP should put logs locally or on to the sepatare server, it is up to youNext step - log analisys. We need to make some changes to syslog-ng.conf
source remote { udp( port(514) ); };
filter free_lease { program("^dhcpd$") and match("no free leases" value("MESSAGE")) };
destination perl_dhcp { program("/usr/local/scripts/parse_log2.pl"); };
log { source(remote); filter( free_lease ); destination(perl_dhcp); };
So syslog-ng will catch dhcpd logs which contains "no free leases" string and put them intp /usr/local/scripts/parse_log2.pl analyzer
Parsing the log
Almost there. Few steps left.There is some sample PERL script which do following:- analyse, parse DHCP log, getting circuit-id, remote-id
- Connects to MySQL DB and verify what is the current lease obtained for common Option82
- Using OMAPI connect to DHCP server, and free current lease
s/^<\d{1,2}>//;
my $mac = $1 if /from ((?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2})/;
my $remote_id = $1 if /r-id (([^;]+))via/;
my $circuit_id = $1 if /c-id (([^;]+))r-id/;
Some script for getting IP address fro MySQL
my $query = "SELECT ip FROM `dhcp` WHERE `switch`='$switch' and port=$port and sw_ven <> 'HP' order by ts desc";
my $query_handle = $dbstore->prepare($query);
$query_handle->execute() or die "Unable to EXECUTE: $DBI::errstr\n ===\n$query \n ===\n";
while (my @row = $query_handle->fetchrow_array()) {
($ip ) = @row;
}
Use OMAPI for free current lease
open (OMSHELL, "|omshell") || die ("Unable to open omshell\n");
print OMSHELL "server 192.168.95.105\n";
print OMSHELL "port 7911\n";
print OMSHELL "key dhcp_key \"KYS4h5dWtgv25rEWJhRTqw2U7kJvJZ/eHkuWSSF7rw8Z5adysqbChM/4z0bZxxIn0l5U30QXyfF+0mnMi0Y6FQ==\"\n";
print OMSHELL "connect\n";
print OMSHELL "new lease\n";
#print OMSHELL "set hardware-address = $mac\n";
print OMSHELL "set ip-address = ".$ip."\n";
print OMSHELL "open\n";
print OMSHELL "set ends = 0\n";
print OMSHELL "update \n";
close (OMSHELL) || die "Unable to close omshell.\n";
THE RESULT
============== getting IP address with MAC 00:26:9e:d9:34:ff ==============
May 14 09:15:42 dhcpserver dhcpd: DHCPREQUEST for 192.168.33.9 from 00:26:9e:d9:34:ff via 192.168.33.254
May 14 09:15:42 dhcpserver dhcpd: DHCPACK on 192.168.33.9 to 00:26:9e:d9:34:ff (PC23) via 192.168.33.254
============== Connect other device with new MAC 00:26:99:88:77:66 ==============
May 14 09:20:17 dhcpserver dhcpd: DHCPREQUEST for 192.168.33.9 from 00:26:99:88:77:66 via 192.168.33.254: lease 192.168.33.9 unavailable.
May 14 09:20:17 dhcpserver dhcpd: DHCPNAK on 192.168.33.9 to 00:26:99:88:77:66 via 192.168.33.254
May 14 09:20:17 dhcpserver dhcpd: DHCPDISCOVER from 00:26:99:88:77:66 c-id 0:4:6:13:1:23 r-id 0:6:0:1a:30:ad:60:0 via 192.168.33.254: networkET-192.168.33.0: no free leases
============== LEASE FREE's automatically ==============
May 14 09:20:17 dhcpserver dhcpd: lease 192.168.33.9 end changed from 1400049042 to 0
============== Device with MAC 00:26:99:88:77:66 get IP ==============
May 14 09:20:21 dhcpserver dhcpd: Lease for ==0:6:0:1a:30:ad:60:0==192.168.33.9 , is connected to interface 1/35 , VLAN 1555 , on switch 0:1a0:ad:60:0 , client MAC 0:26:99:88:77:66 , Vendor MSFT 5.0 Len: 0601a30ad600
May 14 09:20:21 dhcpserver dhcpd: DHCPDISCOVER from 00:26:99:88:77:66 c-id 0:4:6:13:1:23 r-id 0:6:0:1a:30:ad:60:0 via 192.168.33.254
May 14 09:20:21 dhcpserver dhcpd: DHCPOFFER on 192.168.33.9 to 00:26:99:88:77:66 (PC23) via 192.168.33.254
May 14 09:20:21 dhcpserver dhcpd: Lease for ==0:6:0:1a:30:ad:60:0==192.168.33.9 , is connected to interface 1/35 , VLAN 1555 , on switch 0:1a0:ad:60:0 , client MAC 0:26:99:88:77:66 , Vendor MSFT 5.0 Len: 0601a30ad600
May 14 09:20:21 dhcpserver dhcpd: DHCPREQUEST for 192.168.33.9 (192.168.95.171) from 00:26:99:88:77:66 (PC23) via 192.168.33.254
May 14 09:20:21 dhcpserver dhcpd: DHCPACK on 192.168.33.9 to 00:26:99:88:77:66 (PC23) via 192.168.33.254
MAGIC HAPPENS!