How can I configure a DHCP server to assign addresses based on the OS that is running

Ray Phillips r.phillips at uq.edu.au
Fri May 28 09:07:42 UTC 2010


Marc:

>It may be that using the option dhcp-client-identifier along with the -
>
>deny duplicates;
>
>declaration you told me about will be sufficient, but it is damn 
>hard to discover the value of the dhcp-client-identifier for some of 
>our systems that are already configured to receive a static IP 
>address based on their MAC address. If these systems go dual boot, 
>then I will have to temporarily configure them to receive a dynamic 
>IP address and look in the dhcpd.leases file to discover what they 
>are sending as a client identifier.

It may not help you to know them, but Edwin Groothuis' dhcpdump [1] 
shows the client identifier in hex, for example:

---------------------------------------------------------------------------

   TIME: 2010-05-04 18:53:22.209
     IP: 123.456.78.9 (00:13:72:4c:4d:03) > 255.255.255.255 (ff:ff:ff:ff:ff:ff)
     OP: 1 (BOOTPREQUEST)
  HTYPE: 1 (Ethernet)
   HLEN: 6
   HOPS: 0
    XID: c597bc8c
   SECS: 0
  FLAGS: 0
CIADDR: 123.456.78.9
YIADDR: 0.0.0.0
SIADDR: 0.0.0.0
GIADDR: 0.0.0.0
CHADDR: 00:13:72:4c:4d:03:00:00:00:00:00:00:00:00:00:00
  SNAME: .
  FNAME: .
OPTION:  53 (  1) DHCP message type         8 (DHCPINFORM)
OPTION:  61 (  7) Client-identifier         01:00:13:72:4c:4d:03
OPTION:  12 (  3) Host name                 happy
OPTION:  60 (  8) Vendor class identifier   MSFT 5.0
OPTION:  55 ( 12) Parameter Request List      1 (Subnet mask)
                                              15 (Domainname)
                                               3 (Routers)
                                               6 (DNS server)
                                              44 (NetBIOS name server)
                                              46 (NetBIOS node type)
                                              47 (NetBIOS scope)
                                              31 (Perform router discovery)
                                              33 (Static route)
                                             249 (MSFT - Classless route)
                                              43 (Vendor specific info)
                                             252 (MSFT - WinSock Proxy 
Auto Detec
t)

OPTION:  82 ( 24) Relay Agent Information
                   Circuit-ID    00:04:00:08:01:08
                   Remote-ID     01:01:6a:6b:6d:72:63:2d:73:74:61:67:65:32
---------------------------------------------------------------------------

I've made some small modifications to version 1.8's dhcpdump.c file 
to get it to compile under NetBSD/i386 5.0.2.  I'll append them to 
this message in case they're of interest. [2]

>I tried to set up a log statement as you suggested -
>
>log (info, option vendor-class-identifier);
>log (info, option dhcp-client-identifier);
>
>and discovered, much to my chagrin, that Microsoft sends a null byte 
>as the second byte of the dhcp-client-identifier, so the log 
>function truncates the string and only puts out the first byte. 
>Linux puts out a leading null byte so nothing gets reported in the 
>log file.

Windows uses the client's MAC address preceded by the hardware type 
as the client identifier, so the null value you observed in the 
second byte will change if the first byte of the client's MAC is 
non-null.

>How this gets properly put out in the dhcpd.leases files is an 
>interesting question!

I noticed 4.1.1's dhcpd.leases man page says:

   The client identifier is recorded as a colon-separated hexadecimal list
   or  as  a  quoted string.   If it is recorded as a quoted string and it
   contains one or more non-printable  characters,  those  characters  are
   represented  as octal escapes - a backslash character followed by three
   octal digits.

I've never seen it in /var/db/dhcpd.leases in hex form, only as octal 
values and printable characters.  Does anyone know when it would be 
printed as a colon-separated hex string?  Is there a way to choose 
which representation is used?


Ray



[1]

http://www.mavetju.org/unix/general.php


[2]

I've inserted comments in diff's output before each change to explain them.

% diff -c dhcpdump.c.orig dhcpdump.c
*** dhcpdump.c.orig     Fri May 28 16:07:41 2010
--- dhcpdump.c  Sun May  2 23:49:56 2010
***************
--------------------------------------------------------------------------------
NetBSD doesn't have the file /usr/include/net/ethernet.h. 
/usr/include/net/if.h and /usr/include/net/if_ether.h seem to contain 
what dhcpdump needs from it.
--------------------------------------------------------------------------------
*** 11,17 ****
--- 11,22 ----
   #include <sys/types.h>
   #include <sys/socket.h>
   #include <sys/time.h>
+ #if defined(__NetBSD__)
+ #include <net/if.h>
+ #include <net/if_ether.h>
+ #else
   #include <net/ethernet.h>
+ #endif /* __NetBSD__ */
   #include <netinet/in_systm.h>
   #include <netinet/in.h>
   #include <netinet/ip.h>
***************
--------------------------------------------------------------------------------
These arrays don't need to be unsigned chars.  Changing them to 
signed chars prevents some "pointer targets in passing argument 1 of 
'strlen' differ in signedness" compiler warnings.
--------------------------------------------------------------------------------
*** 38,48 ****
   #define LARGESTRING 1024

   // header variables
! u_char        timestamp[40];                  // timestamp on header
! u_char        mac_origin[40];                 // mac address of origin
! u_char        mac_destination[40];            // mac address of destination
! u_char        ip_origin[40];                  // ip address of origin
! u_char        ip_destination[40];             // ip address of destination
   int   max_data_len;                   // maximum size of a packet

   int   tcpdump_style = -1;
--- 43,53 ----
   #define LARGESTRING 1024

   // header variables
! char  timestamp[40];                  // timestamp on header
! char  mac_origin[40];                 // mac address of origin
! char  mac_destination[40];            // mac address of destination
! char  ip_origin[40];                  // ip address of origin
! char  ip_destination[40];             // ip address of destination
   int   max_data_len;                   // maximum size of a packet

   int   tcpdump_style = -1;
***************
--------------------------------------------------------------------------------
strcpy()'s second argument should be type char * so no need to cast 
it to u_char *
--------------------------------------------------------------------------------
*** 160,167 ****
         strcpy(mac_destination,
             ether_ntoa((struct ether_addr *)eh->ether_dhost));

!       strcpy(ip_origin, (u_char *)inet_ntoa(ip->ip_src));
!       strcpy(ip_destination, (u_char *)inet_ntoa(ip->ip_dst));

         if (hmask && check_ch((u_char *)(sp + offset), ntohs(udp->uh_ulen)))
                 return;
--- 165,172 ----
         strcpy(mac_destination,
             ether_ntoa((struct ether_addr *)eh->ether_dhost));

!       strcpy(ip_origin, inet_ntoa(ip->ip_src));
!       strcpy(ip_destination, inet_ntoa(ip->ip_dst));

         if (hmask && check_ch((u_char *)(sp + offset), ntohs(udp->uh_ulen)))
                 return;
***************
--------------------------------------------------------------------------------
The CAVEATS section of isprint()'s man page says:

      The argument to isprint() must be EOF or representable as an unsigned
      char; otherwise, the behavior is undefined.  See the CAVEATS section of
      ctype(3) for more details.

The last paragraph of ctype()'s man page's CAVEATS section says:

      Values of type char or signed char must first be cast to unsigned char,
      to ensure that the values are within the correct range.  The result
      should then be cast to int to avoid warnings from some compilers.  Cast-
      ing a negative-valued char or signed char directly to int will produce a
      negative-valued int, which will be outside the range of allowed values
      (unless it happens to be equal to EOF, but even that would not give the
      desired result).

So I cast isprint()'s argument first to u_char then to int
--------------------------------------------------------------------------------
*** 259,265 ****
                 for (j = 0; j < 8; j++) {
                         char c = data[i * 8 + j];
                         if (i * 8 + j >= len) break;
!                       printf("%c", isprint(c) ? c : '.');
                 }
                 if (i * 8 + j < len) printf("\n\t\t\t\t\t    ");
         }
--- 264,270 ----
                 for (j = 0; j < 8; j++) {
                         char c = data[i * 8 + j];
                         if (i * 8 + j >= len) break;
!                       printf("%c", isprint((int)(u_char)c) ? c : '.');
                 }
                 if (i * 8 + j < len) printf("\n\t\t\t\t\t    ");
         }
***************
--------------------------------------------------------------------------------
Spelling mistake
--------------------------------------------------------------------------------
*** 278,284 ****
         }
   }

! // print the data as a hex-list seperated by colons
   void printHexColon(u_char *data, int len) {
         int i;

--- 283,289 ----
         }
   }

! // print the data as a hex-list separated by colons
   void printHexColon(u_char *data, int len) {
         int i;

***************
--------------------------------------------------------------------------------
strncpy()'s first and second arguments should be char *
--------------------------------------------------------------------------------
*** 301,307 ****
   // print the header and the options.
   int printdata(u_char *data, int data_len) {
         int     j, i;
!       u_char  buf[LARGESTRING];

         if (data_len == 0)
                 return 0;
--- 306,312 ----
   // print the header and the options.
   int printdata(u_char *data, int data_len) {
         int     j, i;
!       char    buf[LARGESTRING];

         if (data_len == 0)
                 return 0;
***************
*** 364,370 ****
         case 60:        // Domain name
         case 86:        // NDS Tree name
         case 87:        // NDS context
!               strncpy(buf, &data[j + 2], data[j + 1]);
                 buf[data[j + 1]] = 0;
                 printf("%s", buf);
                 break;
--- 369,375 ----
         case 60:        // Domain name
         case 86:        // NDS Tree name
         case 87:        // NDS context
!               strncpy(buf, (char *)&data[j + 2], data[j + 1]);
                 buf[data[j + 1]] = 0;
                 printf("%s", buf);
                 break;
***************
--------------------------------------------------------------------------------
There should be a new line before each Option 82 sub option, not just 
the first.
--------------------------------------------------------------------------------
*** 510,523 ****
                 printf("-");
                 print8bits(data + j + 4);
                 printf(" ");
!               strncpy(buf, &data[j + 5], data[j + 1] - 3);
                 buf[data[j + 1] - 3]=0;
                 printf("%s", buf);
                 break;

         case 82:        // Relay Agent Information
-               printf("\n");
                 for (i = j + 2; i < j + data[j + 1]; ) {
                         printf("%-17s %-13s ", " ",
                             data[i] > sizeof(relayagent_suboptions) ?
                             "*wrong value*" :
--- 515,528 ----
                 printf("-");
                 print8bits(data + j + 4);
                 printf(" ");
!               strncpy(buf, (char *)(&data[j + 5]), data[j + 1] - 3);
                 buf[data[j + 1] - 3]=0;
                 printf("%s", buf);
                 break;

         case 82:        // Relay Agent Information
                 for (i = j + 2; i < j + data[j + 1]; ) {
+               printf("\n");
                         printf("%-17s %-13s ", " ",
                             data[i] > sizeof(relayagent_suboptions) ?
                             "*wrong value*" :
***************
--------------------------------------------------------------------------------
There was an off-by-two error when indexing in the Option 82 sub options.
Previously the Option 82 section of dhcpdump's output would be like this:

OPTION:  82 ( 24) Relay Agent Information
                   Circuit-ID    00:04:00:08:03:0b                  (null)
  02:0e:01:0c:6a:6b:6d:72:63:2d:73                  *wrong value* 
*MALFORMED -- T
OO LARGE*

instead of:

OPTION:  82 ( 24) Relay Agent Information
                   Circuit-ID    00:04:00:08:03:0b
                   Remote-ID     01:0c:6a:6b:6d:72:63:2d:73:74:61:67:65:32
--------------------------------------------------------------------------------
*** 527,533 ****
                                 break;
                         }
                         printHexColon(data + i + 2, data[i + 1]);
!                       i += data[i + 1];
                 }
                 break;

--- 532,538 ----
                                 break;
                         }
                         printHexColon(data + i + 2, data[i + 1]);
!                       i += data[i + 1] + 2;
                 }
                 break;

%

Once more for convenience, without the comments:

% diff dhcpdump.c.orig dhcpdump.c
13a14,17
>  #if defined(__NetBSD__)
>  #include <net/if.h>
>  #include <net/if_ether.h>
>  #else
14a19
>  #endif /* __NetBSD__ */
41,45c46,50
< u_char        timestamp[40];                  // timestamp on header
< u_char        mac_origin[40];                 // mac address of origin
< u_char        mac_destination[40];            // mac address of destination
< u_char        ip_origin[40];                  // ip address of origin
< u_char        ip_destination[40];             // ip address of destination
---
>  char  timestamp[40];                  // timestamp on header
>  char  mac_origin[40];                 // mac address of origin
>  char  mac_destination[40];            // mac address of destination
>  char  ip_origin[40];                  // ip address of origin
>  char  ip_destination[40];             // ip address of destination
163,164c168,169
<       strcpy(ip_origin, (u_char *)inet_ntoa(ip->ip_src));
<       strcpy(ip_destination, (u_char *)inet_ntoa(ip->ip_dst));
---
>        strcpy(ip_origin, inet_ntoa(ip->ip_src));
>        strcpy(ip_destination, inet_ntoa(ip->ip_dst));
262c267
<                       printf("%c", isprint(c) ? c : '.');
---
>                        printf("%c", isprint((int)(u_char)c) ? c : '.');
281c286
< // print the data as a hex-list seperated by colons
---
>  // print the data as a hex-list separated by colons
304c309
<       u_char  buf[LARGESTRING];
---
>        char    buf[LARGESTRING];
367c372
<               strncpy(buf, &data[j + 2], data[j + 1]);
---
>                strncpy(buf, (char *)&data[j + 2], data[j + 1]);
513c518
<               strncpy(buf, &data[j + 5], data[j + 1] - 3);
---
>                strncpy(buf, (char *)(&data[j + 5]), data[j + 1] - 3);
519d523
<               printf("\n");
520a525
>                printf("\n");
530c535
<                       i += data[i + 1];
---
>                        i += data[i + 1] + 2;
%



More information about the dhcp-users mailing list