these attributes are byte, not integer. Closes #1954
[freeradius.git] / src / modules / proto_dhcp / dhcpd.c
index 8408ee5..218198c 100644 (file)
 #include <freeradius-devel/dhcp.h>
 #include <freeradius-devel/rad_assert.h>
 
-
-extern int check_config;       /* @todo globals are bad, m'kay? */
+#ifndef __MINGW32__
+#include <sys/ioctl.h>
+#endif
 
 /*
  *     Same contents as listen_socket_t.
  */
 typedef struct dhcp_socket_t {
-       /*
-        *      For normal sockets.
-        */
-       fr_ipaddr_t     ipaddr;
-       int             port;
-       char const              *interface;
-       RADCLIENT_LIST  *clients;
+       listen_socket_t lsock;
 
        /*
         *      DHCP-specific additions.
         */
-       int             suppress_responses;
+       bool            suppress_responses;
        RADCLIENT       dhcp_client;
        char const      *src_interface;
        fr_ipaddr_t     src_ipaddr;
@@ -80,22 +75,22 @@ typedef struct dhcp_socket_t {
 static int dhcprelay_process_client_request(REQUEST *request)
 {
        uint8_t maxhops = 16;
-       VALUE_PAIR *vp, *giaddrvp;
+       VALUE_PAIR *vp, *giaddr;
        dhcp_socket_t *sock;
 
        rad_assert(request->packet->data[0] == 1);
 
        /*
-        * Do the forward by ourselves, do not rely on dhcp_socket_send()
+        *      Do the forward by ourselves, do not rely on dhcp_socket_send()
         */
        request->reply->code = 0;
 
        /*
         * It's invalid to have giaddr=0 AND a relay option
         */
-       giaddrvp = vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
-       if (vp && (vp->vp_ipaddr == htonl(INADDR_ANY)) &&
-           pairfind(request->packet->vps, 82, DHCP_MAGIC_VENDOR, TAG_ANY)) { /* DHCP-Relay-Agent-Information */
+       giaddr = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY)) &&
+           fr_pair_find_by_num(request->packet->vps, 82, DHCP_MAGIC_VENDOR, TAG_ANY)) { /* DHCP-Relay-Agent-Information */
                DEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet\n");
                return 1;
        }
@@ -105,17 +100,17 @@ static int dhcprelay_process_client_request(REQUEST *request)
         *
         * Drop requests if hop-count > 16 or admin specified another value
         */
-       if ((vp = pairfind(request->config_items, 271, DHCP_MAGIC_VENDOR, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
+       if ((vp = fr_pair_find_by_num(request->config, 271, DHCP_MAGIC_VENDOR, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
            maxhops = vp->vp_integer;
        }
-       vp = pairfind(request->packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Hop-Count */
+       vp = fr_pair_find_by_num(request->packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Hop-Count */
        rad_assert(vp != NULL);
-       if (vp->vp_integer > maxhops) {
+       if (vp->vp_byte > maxhops) {
                DEBUG("DHCP: Number of hops is greater than %d: not relaying\n", maxhops);
                return 1;
        } else {
-           /* Increment hop count */
-           vp->vp_integer++;
+               /* Increment hop count */
+               vp->vp_byte++;
        }
 
        sock = request->listener->data;
@@ -126,15 +121,16 @@ static int dhcprelay_process_client_request(REQUEST *request)
         */
        /* set SRC ipaddr/port to the listener ipaddr/port */
        request->packet->src_ipaddr.af = AF_INET;
-       request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = giaddrvp->vp_ipaddr;
-       request->packet->src_port = sock->port;
+       request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = sock->lsock.my_ipaddr.ipaddr.ip4addr.s_addr;
+       request->packet->src_port = sock->lsock.my_port;
 
-       vp = pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-To-IP-Address */
+       vp = fr_pair_find_by_num(request->config, 270, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-To-IP-Address */
        rad_assert(vp != NULL);
 
        /* set DEST ipaddr/port to the next server ipaddr/port */
        request->packet->dst_ipaddr.af = AF_INET;
        request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+       request->packet->dst_port = sock->lsock.my_port;
 
        if (fr_dhcp_encode(request->packet) < 0) {
                DEBUG("dhcprelay_process_client_request: ERROR in fr_dhcp_encode\n");
@@ -144,9 +140,14 @@ static int dhcprelay_process_client_request(REQUEST *request)
        return fr_dhcp_send(request->packet);
 }
 
+
+/*
+ *     We've seen a reply from a server.
+ *     i.e. we're a relay.
+ */
 static int dhcprelay_process_server_reply(REQUEST *request)
 {
-       VALUE_PAIR *vp, *giaddrvp;
+       VALUE_PAIR *vp, *giaddr;
        dhcp_socket_t *sock;
 
        rad_assert(request->packet->data[0] == 2);
@@ -161,11 +162,10 @@ static int dhcprelay_process_server_reply(REQUEST *request)
        /*
         * Check that packet is for us.
         */
-       giaddrvp = vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
-       rad_assert(vp != NULL);
+       giaddr = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
 
        /* --with-udpfromto is needed just for the following test */
-       if (!vp || vp->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) {
+       if (!giaddr || giaddr->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) {
                DEBUG("DHCP: Packet received from server was not for us (was for 0x%x). Discarding packet",
                    ntohl(request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr));
                return 1;
@@ -173,17 +173,22 @@ static int dhcprelay_process_server_reply(REQUEST *request)
 
        /* set SRC ipaddr/port to the listener ipaddr/port */
        request->packet->src_ipaddr.af = AF_INET;
-       request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = giaddrvp->vp_ipaddr;
-       request->packet->src_port = sock->port;
+       request->packet->src_port = sock->lsock.my_port;
 
        /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */
        request->packet->dst_ipaddr.af = AF_INET;
-       request->packet->dst_port = request->packet->dst_port + 1; /* Port 68 */
+
+       /*
+        *      We're a relay, and send the reply to giaddr.
+        */
+       request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = giaddr->vp_ipaddr;
+       request->reply->dst_port = request->packet->dst_port;           /* server port */
 
        if ((request->packet->code == PW_DHCP_NAK) ||
-           ((vp = pairfind(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ &&
+           !sock->src_interface ||
+           ((vp = fr_pair_find_by_num(request->packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Flags */ &&
             (vp->vp_integer & 0x8000) &&
-            ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
+            ((vp = fr_pair_find_by_num(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
              (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
                /*
                 * RFC 2131, page 23
@@ -203,13 +208,13 @@ static int dhcprelay_process_server_reply(REQUEST *request)
                 * - ciaddr if present
                 * otherwise to yiaddr
                 */
-               if ((vp = pairfind(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
+               if ((vp = fr_pair_find_by_num(request->packet->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
                    (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                        request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                } else {
-                       vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
+                       vp = fr_pair_find_by_num(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
                        if (!vp) {
-                               DEBUG("DHCP: Failed to find IP Address for request.");
+                               DEBUG("DHCP: Failed to find IP Address for request");
                                return -1;
                        }
 
@@ -223,7 +228,7 @@ static int dhcprelay_process_server_reply(REQUEST *request)
                         * the client was requesting an IP address.
                         */
                        if (request->packet->code == PW_DHCP_OFFER) {
-                               VALUE_PAIR *hwvp = pairfind(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
+                               VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
                                if (hwvp == NULL) {
                                        DEBUG("DHCP: DHCP_OFFER packet received with "
                                            "no Client Hardware Address. Discarding packet");
@@ -247,13 +252,13 @@ static int dhcprelay_process_server_reply(REQUEST *request)
 #else  /* WITH_UDPFROMTO */
 static int dhcprelay_process_server_reply(UNUSED REQUEST *request)
 {
-       WDEBUG("DHCP Relaying requires the server to be configured with UDPFROMTO");
+       WARN("DHCP Relaying requires the server to be configured with UDPFROMTO");
        return -1;
 }
 
 static int dhcprelay_process_client_request(UNUSED REQUEST *request)
 {
-       WDEBUG("DHCP Relaying requires the server to be configured with UDPFROMTO");
+       WARN("DHCP Relaying requires the server to be configured with UDPFROMTO");
        return -1;
 }
 
@@ -284,30 +289,30 @@ static int dhcp_process(REQUEST *request)
         *      in the response.  That way the later code knows where
         *      to send the reply.
         */
-       vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       vp = fr_pair_find_by_num(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
        if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                VALUE_PAIR *relay;
 
                /* DHCP-Relay-IP-Address */
-               relay = radius_paircreate(request, &request->reply->vps,
+               relay = radius_pair_create(request->reply, &request->reply->vps,
                                          272, DHCP_MAGIC_VENDOR);
                if (relay) relay->vp_ipaddr = vp->vp_ipaddr;
        }
 
-       vp = pairfind(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
+       vp = fr_pair_find_by_num(request->packet->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
        if (vp) {
-               DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_integer);
+               DICT_VALUE *dv = dict_valbyattr(53, DHCP_MAGIC_VENDOR, vp->vp_byte);
                DEBUG("Trying sub-section dhcp %s {...}",
-                     dv->name ? dv->name : "<unknown>");
-               rcode = process_post_auth(vp->vp_integer, request);
+                     dv ? dv->name : "<unknown>");
+               rcode = process_post_auth(vp->vp_byte, request);
        } else {
                DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
                rcode = RLM_MODULE_FAIL;
        }
 
-       vp = pairfind(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
+       vp = fr_pair_find_by_num(request->reply->vps, 53, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Message-Type */
        if (vp) {
-               request->reply->code = vp->vp_integer;
+               request->reply->code = vp->vp_byte;
                if ((request->reply->code != 0) &&
                    (request->reply->code < PW_DHCP_OFFSET)) {
                        request->reply->code += PW_DHCP_OFFSET;
@@ -354,24 +359,24 @@ static int dhcp_process(REQUEST *request)
        /*
         *      Handle requests when acting as a DHCP relay
         */
-       vp = pairfind(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
+       vp = fr_pair_find_by_num(request->packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
        if (!vp) {
                RDEBUG("FAILURE: Someone deleted the DHCP-Opcode!");
                return 1;
        }
 
        /* BOOTREPLY received on port 67 (i.e. from a server) */
-       if (vp->vp_integer == 2) {
+       if (vp->vp_byte == 2) {
                return dhcprelay_process_server_reply(request);
        }
 
        /* Packet from client, and we have DHCP-Relay-To-IP-Address */
-       if (pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) {
+       if (fr_pair_find_by_num(request->config, 270, DHCP_MAGIC_VENDOR, TAG_ANY)) {
                return dhcprelay_process_client_request(request);
        }
 
        /* else it's a packet from a client, without relaying */
-       rad_assert(vp->vp_integer == 1); /* BOOTREQUEST */
+       rad_assert(vp->vp_byte == 1); /* BOOTREQUEST */
 
        sock = request->listener->data;
 
@@ -399,25 +404,81 @@ static int dhcp_process(REQUEST *request)
        for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
                uint32_t attr = attrnums[i];
 
-               if (pairfind(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue;
-               
-               vp = pairfind(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY);
+               if (fr_pair_find_by_num(request->reply->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY)) continue;
+
+               vp = fr_pair_find_by_num(request->packet->vps, attr, DHCP_MAGIC_VENDOR, TAG_ANY);
                if (vp) {
-                       pairadd(&request->reply->vps, paircopyvp(request->reply, vp));
+                       fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
                }
        }
 
-       vp = pairfind(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
+       vp = fr_pair_find_by_num(request->reply->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Opcode */
        rad_assert(vp != NULL);
-       vp->vp_integer = 2; /* BOOTREPLY */
+       vp->vp_byte = 2; /* BOOTREPLY */
+
+       /*
+        *      Allow NAKs to be delayed for a short period of time.
+        */
+       if (request->reply->code == PW_DHCP_NAK) {
+               vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY);
+               if (vp) {
+                       if (vp->vp_integer <= 10) {
+                               request->response_delay.tv_sec = vp->vp_integer;
+                               request->response_delay.tv_usec = 0;
+                       } else {
+                               request->response_delay.tv_sec = 10;
+                               request->response_delay.tv_usec = 0;
+                       }
+               } else {
+#define USEC 1000000
+                       vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY_USEC, 0, TAG_ANY);
+                       if (vp) {
+                               if (vp->vp_integer <= 10 * USEC) {
+                                       request->response_delay.tv_sec = vp->vp_integer / USEC;
+                                       request->response_delay.tv_usec = vp->vp_integer % USEC;
+                               } else {
+                                       request->response_delay.tv_sec = 10;
+                                       request->response_delay.tv_usec = 0;
+                               }
+                       }
+               }
+       }
 
        /*
-        * Prepare the reply packet for sending through dhcp_socket_send()
+        *      Prepare the reply packet for sending through dhcp_socket_send()
         */
        request->reply->dst_ipaddr.af = AF_INET;
        request->reply->src_ipaddr.af = AF_INET;
-       request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr;
+       request->reply->src_ipaddr.prefix = 32;
 
+       /*
+        *      Packet-Src-IP-Address has highest precedence
+        */
+       vp = fr_pair_find_by_num(request->reply->vps, PW_PACKET_SRC_IP_ADDRESS, 0, TAG_ANY);
+       if (vp) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+       /*
+        *      The request was unicast (via a relay)
+        */
+       } else if (request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_BROADCAST) &&
+                  request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_ANY)) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+       /*
+        *      The listener was bound to an IP address, or we determined
+        *      the address automatically, as it was the only address bound
+        *      to the interface, and we bound to the interface.
+        */
+       } else if (sock->src_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_ANY)) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr;
+       /*
+        *      There's a Server-Identification attribute
+        */
+       } else if ((vp = fr_pair_find_by_num(request->reply->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+               request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+       } else {
+               REDEBUG("Unable to determine correct src_ipaddr for response");
+               return -1;
+       }
        request->reply->dst_port = request->packet->src_port;
        request->reply->src_port = request->packet->dst_port;
 
@@ -428,10 +489,15 @@ static int dhcp_process(REQUEST *request)
         *      packet to the client.  i.e. the relay may have a
         *      public IP, but the gateway a private one.
         */
-       vp = pairfind(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
-       if (vp) {
+       vp = fr_pair_find_by_num(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
+       if (vp && (vp->vp_ipaddr != ntohl(INADDR_ANY))) {
                RDEBUG("DHCP: Reply will be unicast to giaddr from original packet");
                request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+               request->reply->dst_port = request->packet->dst_port;
+
+               vp = fr_pair_find_by_num(request->reply->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
+               if (vp) request->reply->dst_port = vp->vp_integer;
+
                return 1;
        }
 
@@ -439,11 +505,16 @@ static int dhcp_process(REQUEST *request)
         *      Answer to client's nearest DHCP gateway.  In this
         *      case, the client can reach the gateway, as can the
         *      server.
+        *
+        *      We also use *our* source port as the destination port.
+        *      Gateways are servers, and listen on the server port,
+        *      not the client port.
         */
-       vp = pairfind(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       vp = fr_pair_find_by_num(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
        if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                RDEBUG("DHCP: Reply will be unicast to giaddr");
                request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+               request->reply->dst_port = request->packet->dst_port;
                return 1;
        }
 
@@ -452,9 +523,9 @@ static int dhcp_process(REQUEST *request)
         *      there's no client-ip-address, send a broadcast.
         */
        if ((request->reply->code == PW_DHCP_NAK) ||
-           ((vp = pairfind(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */
+           ((vp = fr_pair_find_by_num(request->reply->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Flags */
             (vp->vp_integer & 0x8000) &&
-            ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
+            ((vp = fr_pair_find_by_num(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
              (vp->vp_ipaddr == htonl(INADDR_ANY))))) {
                /*
                 * RFC 2131, page 23
@@ -474,22 +545,45 @@ static int dhcp_process(REQUEST *request)
         *
         *      Unicast to ciaddr if present, otherwise to yiaddr.
         */
-       if ((vp = pairfind(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
+       if ((vp = fr_pair_find_by_num(request->reply->vps, 263, DHCP_MAGIC_VENDOR, TAG_ANY)) && /* DHCP-Client-IP-Address */
            (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                RDEBUG("DHCP: Reply will be sent unicast to client-ip-address");
                request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                return 1;
        }
 
-       vp = pairfind(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
+       vp = fr_pair_find_by_num(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
        if (!vp) {
-               DEBUG("DHCP: Failed to find DHCP-Your-IP-Address for request.");
+               RDEBUG("DHCP: Failed to find DHCP-Client-IP-Address or DHCP-Your-IP-Address for request; "
+                      "not responding");
+               /*
+                *      There is nowhere to send the response to, so don't bother.
+                */
+               request->reply->code = 0;
                return -1;
        }
-       
-       RDEBUG("DHCP: Reply will be sent unicast to your-ip-address");
+
+#ifdef SIOCSARP
+       /*
+        *      The system is configured to listen for broadcast
+        *      packets, which means we'll need to send unicast
+        *      replies, to IPs which haven't yet been assigned.
+        *      Therefore, we need to update the ARP table.
+        *
+        *      However, they haven't specified a interface.  So we
+        *      can't update the ARP table.  And we must send a
+        *      broadcast response.
+        */
+       if (sock->lsock.broadcast && !sock->src_interface) {
+               WARN("You MUST set \"interface\" if you have \"broadcast = yes\"");
+               RDEBUG("DHCP: Reply will be broadcast as no interface was defined");
+               request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
+               return 1;
+       }
+
+       RDEBUG("DHCP: Reply will be unicast to your-ip-address");
        request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
-       
+
        /*
         *      When sending a DHCP_OFFER, make sure our ARP table
         *      contains an entry for the client IP address.
@@ -500,14 +594,24 @@ static int dhcp_process(REQUEST *request)
         *      socket to send DHCP packets.
         */
        if (request->reply->code == PW_DHCP_OFFER) {
-               VALUE_PAIR *hwvp = pairfind(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
-               
+               VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Client-Hardware-Address */
+
                if (!hwvp) return -1;
-               
+
                if (fr_dhcp_add_arp_entry(request->reply->sockfd, sock->src_interface, hwvp, vp) < 0) {
+                       RDEBUG("Failed adding arp entry: %s", fr_strerror());
                        return -1;
                }
        }
+#else
+       if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != ntohl(INADDR_NONE)) {
+               RDEBUG("DHCP: Request will be unicast to the unicast source IP address");
+               request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+       } else {
+               RDEBUG("DHCP: Reply will be broadcast as this system does not support ARP updates");
+               request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
+       }
+#endif
 
        return 1;
 }
@@ -520,6 +624,12 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        RADCLIENT *client;
        CONF_PAIR *cp;
 
+       /*
+        *      Set if before parsing, so the user can forcibly turn
+        *      it off later.
+        */
+       this->nodup = true;
+
        rcode = common_socket_parse(cs, this);
        if (rcode != 0) return rcode;
 
@@ -527,8 +637,8 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 
        sock = this->data;
 
-       if (!sock->interface) {
-               WDEBUG("No \"interface\" setting is defined.  Only unicast DHCP will work.");
+       if (!sock->lsock.interface) {
+               WARN("No \"interface\" setting is defined.  Only unicast DHCP will work");
        }
 
        /*
@@ -545,14 +655,14 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        if (broadcast) {
                if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
                        ERROR("Can't set broadcast option: %s\n",
-                              strerror(errno));
+                              fr_syserror(errno));
                        return -1;
                }
        }
 
        if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
                ERROR("Can't set re-use addres option: %s\n",
-                      strerror(errno));
+                      fr_syserror(errno));
                return -1;
        }
 
@@ -563,33 +673,32 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        sock->suppress_responses = false;
        cp = cf_pair_find(cs, "suppress_responses");
        if (cp) {
-               cf_item_parse(cs, "suppress_responses", PW_TYPE_BOOLEAN,
-                             &sock->suppress_responses, NULL);
+               rcode = cf_item_parse(cs, "suppress_responses", FR_ITEM_POINTER(PW_TYPE_BOOLEAN, &sock->suppress_responses), NULL);
+               if (rcode < 0) return -1;
        }
-       
+
        cp = cf_pair_find(cs, "src_interface");
        if (cp) {
-               cf_item_parse(cs, "src_interface", PW_TYPE_STRING_PTR,
-                             &sock->src_interface, NULL);
+               rcode = cf_item_parse(cs, "src_interface", FR_ITEM_POINTER(PW_TYPE_STRING, &sock->src_interface), NULL);
+               if (rcode < 0) return -1;
        } else {
-               sock->src_interface = sock->interface;
+               sock->src_interface = sock->lsock.interface;
        }
 
-       if (!sock->src_interface && sock->interface) {
-               sock->src_interface = talloc_strdup(sock, sock->interface);
+       if (!sock->src_interface && sock->lsock.interface) {
+               sock->src_interface = talloc_typed_strdup(sock, sock->lsock.interface);
        }
 
        cp = cf_pair_find(cs, "src_ipaddr");
        if (cp) {
                memset(&sock->src_ipaddr, 0, sizeof(sock->src_ipaddr));
                sock->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-               rcode = cf_item_parse(cs, "src_ipaddr", PW_TYPE_IPADDR,
-                                     &sock->src_ipaddr.ipaddr.ip4addr, NULL);
+               rcode = cf_item_parse(cs, "src_ipaddr", FR_ITEM_POINTER(PW_TYPE_IPV4_ADDR, &sock->src_ipaddr), NULL);
                if (rcode < 0) return -1;
 
                sock->src_ipaddr.af = AF_INET;
        } else {
-               memcpy(&sock->src_ipaddr, &sock->ipaddr, sizeof(sock->src_ipaddr));
+               memcpy(&sock->src_ipaddr, &sock->lsock.my_ipaddr, sizeof(sock->src_ipaddr));
        }
 
        /*
@@ -598,11 +707,11 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        client = &sock->dhcp_client;
        memset(client, 0, sizeof(*client));
        client->ipaddr.af = AF_INET;
-       client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
-       client->prefix = 0;
+       client->ipaddr.ipaddr.ip4addr.s_addr = ntohl(INADDR_NONE);
+       client->ipaddr.prefix = 0;
        client->longname = client->shortname = "dhcp";
        client->secret = client->shortname;
-       client->nastype = talloc_strdup(sock, "none");
+       client->nas_type = talloc_typed_strdup(sock, "none");
 
        return 0;
 }
@@ -626,7 +735,7 @@ static int dhcp_socket_recv(rad_listen_t *listener)
        }
 
        sock = listener->data;
-       if (!request_receive(listener, packet, &sock->dhcp_client, dhcp_process)) {
+       if (!request_receive(NULL, listener, packet, &sock->dhcp_client, dhcp_process)) {
                rad_free(&packet);
                return 0;
        }
@@ -647,10 +756,11 @@ static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
 
        if (request->reply->code == 0) return 0; /* don't reply */
 
-       if (fr_dhcp_encode(request->packet) < 0) {
+       if (fr_dhcp_encode(request->reply) < 0) {
                DEBUG("dhcp_socket_send: ERROR\n");
                return -1;
        }
+
        sock = listener->data;
        if (sock->suppress_responses) return 0;
 
@@ -670,12 +780,15 @@ static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
        return fr_dhcp_decode(request->packet);
 }
 
+extern fr_protocol_t proto_dhcp;
 fr_protocol_t proto_dhcp = {
-       RLM_MODULE_INIT,
-       "dhcp",
-       sizeof(dhcp_socket_t),
-       NULL,
-       dhcp_socket_parse, NULL,
-       dhcp_socket_recv, dhcp_socket_send,
-       common_socket_print, dhcp_socket_encode, dhcp_socket_decode
+       .magic          = RLM_MODULE_INIT,
+       .name           = "dhcp",
+       .inst_size      = sizeof(dhcp_socket_t),
+       .parse          = dhcp_socket_parse,
+       .recv           = dhcp_socket_recv,
+       .send           = dhcp_socket_send,
+       .print          = common_socket_print,
+       .encode         = dhcp_socket_encode,
+       .decode         = dhcp_socket_decode
 };