Microsoft Windows DHCP Team Blog

The world's most deployed DHCP Server! Deploy and discuss about your fav. server, here!

DHCP Server Callout API usage

DHCP Server Callout API usage

  • Comments 14
  • Likes

Microsoft DHCP Server Callout API provides a way for developers to access the critical phases of DHCP protocol processing in Windows Server 2003 family and later. This enables developers to:

·         Create customized extensions to the Microsoft DHCP Server

·         Monitor statistics

·         Create parallel lease databases

·         Provide other customized solutions

Refer this link for a description of the callout API interface.

There are few things one should keep in mind when implementing the callout dll

1.       If a callout DLL needs to modify the new packet (packet received by the DHCP server from a DHCP client or a relay agent) or send packet (response packet being sent by DHCP server to a DHCP client or a relay agent), the packet buffer should be modified in place. The packet buffer passed in as parameter to the callback functions should not be freed. The size of the packet buffer passed in is 4096 bytes.

2.       If a callout DLL needs store additional information, related to the packet, which should persist across various callbacks, it can provide a packet context in PktContext parameter. One may use malloc/free or HeapAlloc/HeapFree APIs for allocating and de-allocating buffer for the pointer.

3.       The buffer allocated for packet context must be freed to avoid any memory leaks. One may choose to free it in

·         DhcpPktSendHook  and DhcpPktDropHook (when control code is not DHCP_DROP_PROCESSED), or

·         DhcpPktDropHook (when control code is DHCP_DROP_PROCESSED)

4.       A callout DLL should not be making calls to DHCP server management APIs as this may lead to deadlock situation while accessing shared resources like the database, registry and internal data structures. The DHCP Server management APIs are documented here.

Below is a sample callout code which enables option 82 support with Microsoft DHCP Server. DHCP option 82 is “DHCP Relay Agent Information Option” and allows a DHCP Relay Agent to insert circuit specific information into a request that is being forwarded to a DHCP Server.

Microsoft DHCP Server currently drops option 82 in the response packets. This callout DLL stores option 82 inside packet context in DhcpNewPktHook and then adds the option back in the DhcpPktSendHook using the information stored in packet context.

We thank Mr Aki Tuomi, the author of this sample callout code (“Sample Code”), for contributing this sample and for letting us publish it here. This Sample Code is published here “as is” and is for informational purposes only.  MICROSOFT MAKES NO CLAIMS OF OWNERSHIP OF THIS SAMPLE CODE AND MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE ACCURACY , SUITABILITY OR CORRECTNESS OF THIS SAMPLE CODE. For any questions regarding this Sample Code, please contact Mt. Aki Tuomi at 'cmouse AT desteem DOT org'.

Note: This sample code does not enable IP address or any other network configuration assignment to a DHCP client based on the relay agent information contained in option 82.

#include "stdafx.h"

#include <stdio.h>

#include "dhcpssdk.h"

 

DWORD CALLBACK DhcpControlHook(DWORD dwControlCode,LPVOID lpReserved)

{

      switch (dwControlCode)

      {

            case DHCP_CONTROL_START:

            {

                  CalloutFile = fopen("callout.txt", "w");

                  if (CalloutFile != NULL) fprintf(CalloutFile,"The DHCP server has successfully started.\r\n");

                        break;

            }

            case DHCP_CONTROL_STOP:

            {

                  if (CalloutFile != NULL) fprintf(CalloutFile,"The DHCP server has successfully stopped.\r\n");

                  if (CalloutFile != NULL) fclose(CalloutFile);

                  break;

            }

            case DHCP_CONTROL_PAUSE:

            {

                  if (CalloutFile != NULL) fprintf(CalloutFile,"The DHCP server has been paused.\r\n");

                  break;

            }

            case DHCP_CONTROL_CONTINUE:

            {

                  if (CalloutFile != NULL) fprintf(CalloutFile,"The DHCP server has been continued.\n");

                  break;

            }  

      }

 

      return ERROR_SUCCESS;

}

 

DWORD CALLBACK DhcpNewPktHook(LPBYTE* Packet,DWORD* PacketSize,DWORD IpAddress,LPVOID Reserved,LPVOID* PktContext,LPBOOL ProcessIt)

{

      int guard;

      DWORD pos;

      LPBYTE pkt = (*Packet);

 

      (*PktContext) = NULL;

 

      if (CalloutFile != NULL) fprintf(CalloutFile, "Received packet size %d\r\n", *PacketSize);

 

      // packet size must be > 237

      if (*PacketSize < 241) return ERROR_SUCCESS;

 

      // capture possible option 82 in packet and store in PktContext

      // find where option 82 is, and then capture it

 

      for(pos = 240, guard = 0; pos < *PacketSize && guard < 20; guard++)

      {

            fprintf(CalloutFile, "Found option %d, len %d\r\n", pkt[pos], pkt[pos+1]);

            // scan option code

            if (static_cast<char>(pkt[pos]) == DHCP_OPTION_82)

            {

                  DWORD len = static_cast<DWORD>(pkt[pos+1]);

                  if (pos + len < *PacketSize)

                  {

                        if (CalloutFile != NULL) fprintf(CalloutFile, "Found Option82 on position %d, len %d\r\n", pos, len);

                        // Allocate buffer for packet context, where option 82 information will be stored.

                        LPBYTE ctx = static_cast<LPBYTE>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len+3));

                        ctx[0] = static_cast<BYTE>(len+2);

                        memcpy_s(ctx+1, len+2, &pkt[pos], len+2);

                        (*PktContext) = ctx;

                  }

            }

            pos += static_cast<DWORD>(pkt[pos+1])+2;

      }

 

 return ERROR_SUCCESS;

}

 

DWORD CALLBACK DhcpPktDropHook(LPBYTE* Packet,DWORD* PacketSize,DWORD ControlCode, DWORD IpAddress,LPVOID Reserved,LPVOID PktContext)

{

      if(NULL != PktContext && ControlCode != DHCP_DROP_PROCESSED)

      {

            HeapFree(GetProcessHeap(), 0, PktContext);

            PktContext = NULL;

      }

   

      return ERROR_SUCCESS;

}

 

DWORD CALLBACK DhcpPktSendHook(LPBYTE* Packet,DWORD* PacketSize,DWORD ControlCode,DWORD IpAddress,LPVOID Reserved,LPVOID PktContext)

{

      LPBYTE ctx = static_cast<LPBYTE>(PktContext);

 

      // we are supposed to append whatever is in PktContext here...

      if (ctx != NULL)

      {

            DWORD len82 = static_cast<DWORD>(ctx[0]);

            unsigned int iSize = *PacketSize + len82;

            DWORD newPacketSize = static_cast<DWORD>(iSize);

            if (CalloutFile != NULL) fprintf(CalloutFile, "Appending Option82 information to packet, size %d\r\n", len82);

            memcpy_s(*Packet + *PacketSize-1, len82+1, &ctx[1], len82);

            (*Packet)[iSize-1] = 0xff;

            HeapFree(GetProcessHeap(), 0, PktContext);

            PktContext = NULL;

            (*PacketSize) = newPacketSize;

      }

 

      fflush(CalloutFile);

      return ERROR_SUCCESS;

}

 

DWORD CALLBACK DhcpServerCalloutEntry(LPWSTR ChainDlls,DWORD CalloutVersion,LPDHCP_CALLOUT_TABLE CalloutTbl)

{

       CalloutTbl->DhcpControlHook=DhcpControlHook;

       CalloutTbl->DhcpPktDropHook=DhcpPktDropHook;

       CalloutTbl->DhcpNewPktHook=DhcpNewPktHook;

       CalloutTbl->DhcpPktSendHook=DhcpPktSendHook;

       return ERROR_SUCCESS;

}

 

 

Raunak Pandya

DHCP Server Team

Comments
  • Few hints for using this:

    1. It's easiest if you install Visual Studio C++ Express 2008.

    2. Make a project under 'Win32', 'Win32 Project'. Application type = DLL, [x] Export Symbols.

    3. Put this stuff into <project name>.cpp

    - you can move the includes into "stdafx.h"

    4. Compile

    If you get error:

    error C2065: 'CalloutFile' : undeclared identifier

    You need to add

    FILE * CalloutFile = NULL;

    at the top of the code, after include lines.

    If you get error:

    error C2065: 'DHCP_OPTION_82' : undeclared identifier

    You need to add

    #define DHCP_OPTION_82 0x52

    at the top of the code, after include lines.

    If you get warning:

    warning C4996: 'fopen': This function or variable may be unsafe.

    You can either remove/comment out line

    CalloutFile = fopen("callout.txt", "w");

    Or you can change it to

    fopen_s(&CalloutFile, "callout.txt", "w");

    4. To use it in your DHCP server, you will have to use the 'release' version (in folder release). You'll also have to install C++ Runtime libs. It will not work otherwise.

    5. To enable, you'll have to edit your registry

    http://msdn.microsoft.com/en-us/library/aa363389%28VS.85%29.aspx

    6. Stop, and start DHCP service. Monitor event viewer to see if it failed.

  • In case you get problems while loading the DLL about not finding the entry point, try adding this to your headers. Please change the OPT82_API into whatever you've used...

    #ifdef OPT82_EXPORTS

    #define OPT82_API __declspec(dllexport)

    #else

    #define OPT82_API __declspec(dllimport)

    #endif

    extern "C" {

    OPT82_API DWORD CALLBACK Opt82DhcpAddressDelHook(LPBYTE Packet,DWORD PacketSize,DWORD ControlCode,DWORD IpAddress,DWORD AltAddress,LPVOID Reserved,LPVOID PktContext);

    OPT82_API DWORD CALLBACK Opt82DhcpAddressOfferHook(LPBYTE Packet,DWORD PacketSize,DWORD ControlCode, DWORD IpAddress,DWORD AltAddress,DWORD AddrType,DWORD LeaseTime,LPVOID Reserved,LPVOID PktContext);

       OPT82_API DWORD CALLBACK Opt82DhcpControlHook(DWORD dwControlCode,LPVOID lpReserved);

    OPT82_API DWORD CALLBACK Opt82DhcpDeleteClientHook(DWORD IpAddress, LPBYTE HwAddress,ULONG HwAddressLength, DWORD Reserved,DWORD ClientType);

    OPT82_API DWORD CALLBACK Opt82DhcpNewPktHook(LPBYTE* Packet,DWORD* PacketSize,DWORD IpAddress,LPVOID Reserved,LPVOID* PktContext,LPBOOL ProcessIt);

    OPT82_API DWORD CALLBACK Opt82DhcpPktDropHook(LPBYTE* Packet,DWORD* PacketSize,DWORD ControlCode, DWORD IpAddress,LPVOID Reserved,LPVOID PktContext);

    OPT82_API DWORD CALLBACK Opt82DhcpPktSendHook(LPBYTE* Packet,DWORD* PacketSize,DWORD ControlCode,DWORD IpAddress,LPVOID Reserved,LPVOID PktContext);

    OPT82_API DWORD DhcpServerCalloutEntry(LPWSTR ChainDlls,DWORD CalloutVersion,LPDHCP_CALLOUT_TABLE CalloutTbl);

    };

  • Thank you Aki Tuomi (and AJ Anto).  With the 2 examples you guys provided I was able to write a dll to accept only PXE requests (crazy as it sounds, we do have a need for this).  I am now interested in maintaining a single binding database with SQL across multiple servers.  Without your sample code I would be completely lost as I only dabble in VB.Net, so these examples were invaluable to me.

  • I believe I just discovered something with the DHCP API I wasn't aware of.  It is single threaded.  While the DHCP server itself is multi-threaded.  It appears as though only a single thread is allowed to execute each entry point at any given time.  As if there is a critical section around the execution of the callout entry point.

    So, if a DhcpNewPktHook callout takes a long time, it will, hold up ALL incoming requests.  As it doesn't appear to allow multiple threads executing the same callout entry at the same time.

    Any truth to these observations?

  • MSDN does talk about this:

    msdn.microsoft.com/.../aa363292(VS.85).aspx

    Raunak Pandya

  • Does it officially support by Microsoft?

    Thanks,

    Natthaphol.

  • No, not supported officially.

    Thanks, Arun (Team DHCP)

  • Hello,

    I'm trying to add the option 82 on server 2008 R2 (64 bits)

    But the .dll i've created (correctly) with your c++ code can't be loaded by the DHCP server ...

    When I restart the DHCP Server this advertissement appear :

    Evenement ID : 1034

    The DHCP service has failed to load one or more callout DLLs. The following error occurred:

    %1 Not a valid Win32 application

    Not a valid Win32 application

    Not a valid Win32 application

    Not a valid Win32 application

    I have assigned administrative rights to my dll for all users, dhcp client, system ... etc but this error continue to appear ...

    PS : Sorry for my english, i'm French.

  • Hi

    I need just to log all the option 82 information(just need the switch id, and port ID), anyone has any information about how to do this, or how difficult it would be to implement?

    As far as I know/searched the www, windows dhcp is not capable of logging this information per default (to the dhcp log).

  • DHCP Server in Windows Server 2012 logs the relay agent information option (option 82) in the DHCP server audit log file. You should be able to parse the switch id and port id from the same.

  • Hi, teamdhcp!

    I'm interested in auditing 'Option 82' relay information on Windows Server 2008 R2. We have no plans to upgrade to Server 2012 in nearest future.

    Is it possible to force or configure audit of Option 82 on Windows Server 2008 R2? I've read your articles with tag Option 82, but didn't find answer for this question.

    Thanks for your articles about DHCP! Perfect content!

  • Hi Dmitry, there is no inbox functionality to audit option 82 relay information on Windows Server 2008 R2. You could write your own callout dll and log this information from the DHCP request packet.

  • Hi teamdhcp,

    does the Win2k12 DHCP log the relay agent informations without any config changes on the dhcp Server? if no, which settings are nessecary to log the informations?

  • johndoe, Win2k12 DHCP server logs relay agent information (option 82) always - provided of course the relay agent is configured to add the option 82 in each client request. There is no configuration or settings required on the DHCP server.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment