Remove unused variable
[freeradius.git] / src / lib / dhcp.c
index a7f8417..63db6e8 100644 (file)
@@ -34,6 +34,19 @@ RCSID("$Id$")
 #undef WITH_UDPFROMTO
 
 #ifdef WITH_DHCP
+
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <net/if_arp.h>
+
+
 #define DHCP_CHADDR_LEN        (16)
 #define DHCP_SNAME_LEN (64)
 #define DHCP_FILE_LEN  (128)
@@ -201,6 +214,7 @@ static uint8_t *dhcp_get_option(dhcp_packet_t *packet, size_t packet_size,
  */
 RADIUS_PACKET *fr_dhcp_recv(int sockfd)
 {
+       ssize_t                 len;
        uint32_t                magic;
        struct sockaddr_storage src;
        struct sockaddr_storage dst;
@@ -210,12 +224,12 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        int port;
        uint8_t                 *code;
 
-       packet = rad_alloc(0);
+       packet = rad_alloc(NULL, 0);
        if (!packet) {
                fr_strerror_printf("Failed allocating packet");
                return NULL;
        }
-       memset(packet, 0, sizeof(packet));
+       memset(packet, 0, sizeof(*packet));
 
        packet->data = malloc(MAX_PACKET_SIZE);
        if (!packet->data) {
@@ -228,23 +242,23 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        sizeof_src = sizeof(src);
 #ifdef WITH_UDPFROMTO
        sizeof_dst = sizeof(dst);
-       packet->data_len = recvfromto(sockfd, packet->data, MAX_PACKET_SIZE, 0,
-                                     (struct sockaddr *)&src, &sizeof_src,
-                                     (struct sockaddr *)&dst, &sizeof_dst);
+       len = recvfromto(sockfd, packet->data, MAX_PACKET_SIZE, 0,
+                        (struct sockaddr *)&src, &sizeof_src,
+                        (struct sockaddr *)&dst, &sizeof_dst);
 #else
-       packet->data_len = recvfrom(sockfd, packet->data, MAX_PACKET_SIZE, 0,
-                                   (struct sockaddr *)&src, &sizeof_src);
+       len = recvfrom(sockfd, packet->data, MAX_PACKET_SIZE, 0,
+                      (struct sockaddr *)&src, &sizeof_src);
 #endif
 
-       if (packet->data_len <= 0) {
+       if (len <= 0) {
                fr_strerror_printf("Failed reading DHCP socket: %s", strerror(errno));
                rad_free(&packet);
                return NULL;
        }
 
-       if (packet->data_len < MIN_PACKET_SIZE) {
+       if (len < MIN_PACKET_SIZE) {
                fr_strerror_printf("DHCP packet is too small (%d < %d)",
-                     packet->data_len, MIN_PACKET_SIZE);
+                                  (int) len, MIN_PACKET_SIZE);
                rad_free(&packet);
                return NULL;
        }
@@ -263,6 +277,8 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
                return NULL;
        }
 
+       packet->data_len = len;
+
        memcpy(&magic, packet->data + 236, 4);
        magic = ntohl(magic);
        if (magic != DHCP_OPTION_MAGIC_NUMBER) {
@@ -327,7 +343,11 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        /*
         *      This should never fail...
         */
-       getsockname(sockfd, (struct sockaddr *) &dst, &sizeof_dst);
+       if (getsockname(sockfd, (struct sockaddr *) &dst, &sizeof_dst) < 0) {
+               fr_strerror_printf("getsockname failed: %s", strerror(errno));
+               rad_free(&packet);
+               return NULL;    
+       }
 #endif
        
        fr_sockaddr2ipaddr(&dst, sizeof_dst, &packet->dst_ipaddr, &port);
@@ -377,8 +397,10 @@ int fr_dhcp_send(RADIUS_PACKET *packet)
        socklen_t               sizeof_src;
 #endif
 
-       fr_ipaddr2sockaddr(&packet->dst_ipaddr, packet->dst_port,
-                          &dst, &sizeof_dst);
+       if (!fr_ipaddr2sockaddr(&packet->dst_ipaddr, packet->dst_port,
+                               &dst, &sizeof_dst)) {
+               return -1;                      
+       }
 
        /*
         *      The client doesn't yet have an IP address, but is
@@ -398,8 +420,10 @@ int fr_dhcp_send(RADIUS_PACKET *packet)
        return sendto(packet->sockfd, packet->data, packet->data_len, 0,
                      (struct sockaddr *)&dst, sizeof_dst);
 #else
-       fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port,
-                          &src, &sizeof_src);
+       if (!fr_ipaddr2sockaddr(&packet->src_ipaddr, packet->src_port,
+                               &src, &sizeof_src)) {
+               return -1;                      
+       }
 
        return sendfromto(packet->sockfd,
                          packet->data, packet->data_len, 0,
@@ -434,7 +458,7 @@ static int decode_tlv(VALUE_PAIR *tlv, const uint8_t *data, size_t data_len)
 
        p = data;
        while (p < (data + data_len)) {
-               vp = paircreate(tlv->attribute | (p[0] << 8), DHCP_MAGIC_VENDOR, PW_TYPE_OCTETS);
+               vp = paircreate(tlv->da->attr | (p[0] << 8), DHCP_MAGIC_VENDOR);
                if (!vp) {
                        pairfree(&head);
                        goto make_tlv;
@@ -480,7 +504,7 @@ make_tlv:
  */
 static int fr_dhcp_attr2vp(VALUE_PAIR *vp, const uint8_t *p, size_t alen)
 {
-       switch (vp->type) {
+       switch (vp->da->type) {
        case PW_TYPE_BYTE:
                if (alen != 1) goto raw;
                vp->vp_integer = p[0];
@@ -508,10 +532,14 @@ static int fr_dhcp_attr2vp(VALUE_PAIR *vp, const uint8_t *p, size_t alen)
                memcpy(vp->vp_strvalue, p , alen);
                vp->vp_strvalue[alen] = '\0';
                break;
-               
+       
+       /*
+        *      Value doesn't match up with attribute type, overwrite the
+        *      vp's original DICT_ATTR with an unknown one.
+        */
        raw:
-               vp->type = PW_TYPE_OCTETS;
-
+               if (pair2unknown(vp) < 0) return -1;
+               
        case PW_TYPE_OCTETS:
                if (alen > 253) return -1;
                memcpy(vp->vp_octets, p, alen);
@@ -521,7 +549,7 @@ static int fr_dhcp_attr2vp(VALUE_PAIR *vp, const uint8_t *p, size_t alen)
                return decode_tlv(vp, p, alen);
                
        default:
-               fr_strerror_printf("Internal sanity check %d %d", vp->type, __LINE__);
+               fr_strerror_printf("Internal sanity check %d %d", vp->da->type, __LINE__);
                return -1;
        } /* switch over type */
 
@@ -529,10 +557,114 @@ static int fr_dhcp_attr2vp(VALUE_PAIR *vp, const uint8_t *p, size_t alen)
        return 0;
 }
 
-int fr_dhcp_decode(RADIUS_PACKET *packet)
+ssize_t fr_dhcp_decode_options(uint8_t *data, size_t len, VALUE_PAIR **head)
 {
        int i;
+       VALUE_PAIR *vp, **tail;
        uint8_t *p, *next;
+       next = data;
+
+       *head = NULL;
+       tail = head;
+       /*
+        *      FIXME: This should also check sname && file fields.
+        *      See the dhcp_get_option() function above.
+        */
+       while (next < (data + len)) {
+               int num_entries, alen;
+               const DICT_ATTR *da;
+               
+               p = next;
+
+               if (*p == 0) break;
+               if (*p == 255) break; /* end of options signifier */
+               if ((p + 2) > (data + len)) break;
+
+               next = p + 2 + p[1];
+
+               if (p[1] >= 253) {
+                       fr_strerror_printf("Attribute too long %u %u",
+                                          p[0], p[1]);
+                       continue;
+               }
+                               
+               da = dict_attrbyvalue(p[0], DHCP_MAGIC_VENDOR);
+               if (!da) {
+                       fr_strerror_printf("Attribute not in our dictionary: %u",
+                                          p[0]);
+                       continue;
+               }
+
+               vp = NULL;
+               num_entries = 1;
+               alen = p[1];
+               p += 2;
+
+               /*
+                *      Could be an array of bytes, integers, etc.
+                */
+               if (da->flags.array) {
+                       switch (da->type) {
+                       case PW_TYPE_BYTE:
+                               num_entries = alen;
+                               alen = 1;
+                               break;
+
+                       case PW_TYPE_SHORT: /* ignore any trailing data */
+                               num_entries = alen >> 1;
+                               alen = 2;
+                               break;
+
+                       case PW_TYPE_IPADDR:
+                       case PW_TYPE_INTEGER:
+                       case PW_TYPE_DATE: /* ignore any trailing data */
+                               num_entries = alen >> 2;
+                               alen = 4;
+                               break;
+
+                       default:
+
+                               break; /* really an internal sanity failure */
+                       }
+               }
+
+               /*
+                *      Loop over all of the entries, building VPs
+                */
+               for (i = 0; i < num_entries; i++) {
+                       vp = pairmake(da->name, NULL, T_OP_ADD);
+                       if (!vp) {
+                               fr_strerror_printf("Cannot build attribute %s",
+                                       fr_strerror());
+                               pairfree(head);
+                               return -1;
+                       }
+
+                       /*
+                        *      Hack for ease of use.
+                        */
+                       if (fr_dhcp_attr2vp(vp, p, alen) < 0) {
+                               pairfree(&vp);
+                               pairfree(head);
+                               return -1;
+                       }
+
+                       *tail = vp;
+                       while (*tail) {
+                               debug_pair(*tail);
+                               tail = &(*tail)->next;
+                       }
+                       p += alen;
+               } /* loop over array entries */
+       } /* loop over the entire packet */
+       
+       return next - data;
+}
+
+int fr_dhcp_decode(RADIUS_PACKET *packet)
+{
+       size_t i;
+       uint8_t *p;
        uint32_t giaddr;
        VALUE_PAIR *head, *vp, **tail;
        VALUE_PAIR *maxms, *mtu;
@@ -543,7 +675,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        
        if ((fr_debug_flag > 2) && fr_log_fp) {
                for (i = 0; i < packet->data_len; i++) {
-                       if ((i & 0x0f) == 0x00) fr_strerror_printf("%d: ", i);
+                       if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, "%d: ", (int) i);
                        fprintf(fr_log_fp, "%02x ", packet->data[i]);
                        if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
                }
@@ -562,18 +694,20 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        for (i = 0; i < 14; i++) {
                vp = pairmake(dhcp_header_names[i], NULL, T_OP_EQ);
                if (!vp) {
-                       fr_strerror_printf("Parse error %s", fr_strerror());
+                       char buffer[256];
+                       strlcpy(buffer, fr_strerror(), sizeof(buffer));
+                       fr_strerror_printf("Cannot decode packet due to internal error: %s", buffer);
                        pairfree(&head);
                        return -1;
                }
 
                if ((i == 11) && 
                    (packet->data[1] == 1) &&
-                   (packet->data[2] == 6)) {
-                       vp->type = PW_TYPE_ETHERNET;
+                   (packet->data[2] != 6)) {
+                       fr_strerror_printf("chaddr of incorrect length for ethernet");
                }
 
-               switch (vp->type) {
+               switch (vp->da->type) {
                case PW_TYPE_BYTE:
                        vp->vp_integer = p[0];
                        vp->length = 1;
@@ -615,7 +749,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                        break;
                        
                default:
-                       fr_strerror_printf("BAD TYPE %d", vp->type);
+                       fr_strerror_printf("BAD TYPE %d", vp->da->type);
                        pairfree(&vp);
                        break;
                }
@@ -631,106 +765,15 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        /*
         *      Loop over the options.
         */
-       next = packet->data + 240;
-
+        
        /*
-        *      FIXME: This should also check sname && file fields.
-        *      See the dhcp_get_option() function above.
+        *  Nothing uses tail after this call, if it does in the future 
+        *  it'll need to find the new tail...
         */
-       while (next < (packet->data + packet->data_len)) {
-               int num_entries, alen;
-               DICT_ATTR *da;
-               
-               p = next;
-
-               if (*p == 0) break;
-               if (*p == 255) break; /* end of options signifier */
-               if ((p + 2) > (packet->data + packet->data_len)) break;
-
-               next = p + 2 + p[1];
-
-               if (p[1] >= 253) {
-                       fr_strerror_printf("Attribute too long %u %u",
-                             p[0], p[1]);
-                       continue;
-               }
-                               
-               da = dict_attrbyvalue(p[0], DHCP_MAGIC_VENDOR);
-               if (!da) {
-                       fr_strerror_printf("Attribute not in our dictionary: %u",
-                             p[0]);
-                       continue;
-               }
-
-               vp = NULL;
-               num_entries = 1;
-               alen = p[1];
-               p += 2;
-
-               /*
-                *      Could be an array of bytes, integers, etc.
-                */
-               if (da->flags.array) {
-                       switch (da->type) {
-                       case PW_TYPE_BYTE:
-                               num_entries = alen;
-                               alen = 1;
-                               break;
-
-                       case PW_TYPE_SHORT: /* ignore any trailing data */
-                               num_entries = alen >> 1;
-                               alen = 2;
-                               break;
-
-                       case PW_TYPE_IPADDR:
-                       case PW_TYPE_INTEGER:
-                       case PW_TYPE_DATE: /* ignore any trailing data */
-                               num_entries = alen >> 2;
-                               alen = 4;
-                               break;
-
-                       default:
-
-                               break; /* really an internal sanity failure */
-                       }
-               }
-
-               /*
-                *      Loop over all of the entries, building VPs
-                */
-               for (i = 0; i < num_entries; i++) {
-                       vp = pairmake(da->name, NULL, T_OP_EQ);
-                       if (!vp) {
-                               fr_strerror_printf("Cannot build attribute %s",
-                                       fr_strerror());
-                               pairfree(&head);
-                               return -1;
-                       }
-
-                       /*
-                        *      Hack for ease of use.
-                        */
-                       if ((da->attr == 0x3d) &&
-                           !da->flags.array &&
-                           (alen == 7) && (*p == 1) && (num_entries == 1)) {
-                               vp->type = PW_TYPE_ETHERNET;
-                               memcpy(vp->vp_octets, p + 1, 6);
-                               vp->length = alen;
-
-                       } else if (fr_dhcp_attr2vp(vp, p, alen) < 0) {
-                                       pairfree(&vp);
-                                       pairfree(&head);
-                                       return -1;
-                       }
-
-                       *tail = vp;
-                       while (*tail) {
-                               debug_pair(*tail);
-                               tail = &(*tail)->next;
-                       }
-                       p += alen;
-               } /* loop over array entries */
-       } /* loop over the entire packet */
+       if (fr_dhcp_decode_options(packet->data + 240, packet->data_len - 240,
+                                  tail) < 0) { 
+               return -1;
+       }
 
        /*
         *      If DHCP request, set ciaddr to zero.
@@ -745,19 +788,19 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                /*
                 *      DHCP Opcode is request
                 */
-               vp = pairfind(head, 256, DHCP_MAGIC_VENDOR);
-               if (vp && vp->lvalue == 3) {
+               vp = pairfind(head, 256, DHCP_MAGIC_VENDOR, TAG_ANY);
+               if (vp && vp->vp_integer == 3) {
                        /*
                         *      Vendor is "MSFT 98"
                         */
-                       vp = pairfind(head, 63, DHCP_MAGIC_VENDOR);
+                       vp = pairfind(head, 63, DHCP_MAGIC_VENDOR, TAG_ANY);
                        if (vp && (strcmp(vp->vp_strvalue, "MSFT 98") == 0)) {
-                               vp = pairfind(head, 262, DHCP_MAGIC_VENDOR);
+                               vp = pairfind(head, 262, DHCP_MAGIC_VENDOR, TAG_ANY);
 
                                /*
                                 *      Reply should be broadcast.
                                 */
-                               if (vp) vp->lvalue |= 0x8000;
+                               if (vp) vp->vp_integer |= 0x8000;
                                packet->data[10] |= 0x80;                       
                        }
                }
@@ -773,8 +816,8 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
         *      Client can request a LARGER size, but not a smaller
         *      one.  They also cannot request a size larger than MTU.
         */
-       maxms = pairfind(packet->vps, 57, DHCP_MAGIC_VENDOR);
-       mtu = pairfind(packet->vps, 26, DHCP_MAGIC_VENDOR);
+       maxms = pairfind(packet->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY);
+       mtu = pairfind(packet->vps, 26, DHCP_MAGIC_VENDOR, TAG_ANY);
 
        if (mtu && (mtu->vp_integer < DEFAULT_PACKET_SIZE)) {
                fr_strerror_printf("DHCP Fatal: Client says MTU is smaller than minimum permitted by the specification.");
@@ -811,16 +854,16 @@ static int attr_cmp(const void *one, const void *two)
        /*
         *      DHCP-Message-Type is first, for simplicity.
         */
-       if (((*a)->attribute == 53) &&
-           (*b)->attribute != 53) return -1;
+       if (((*a)->da->attr == 53) &&
+           (*b)->da->attr != 53) return -1;
 
        /*
         *      Relay-Agent is last
         */
-       if (((*a)->attribute == 82) &&
-           (*b)->attribute != 82) return +1;
+       if (((*a)->da->attr == 82) &&
+           (*b)->da->attr != 82) return +1;
 
-       return ((*a)->attribute - (*b)->attribute);
+       return ((*a)->da->attr - (*b)->da->attr);
 }
 
 
@@ -839,7 +882,7 @@ static size_t fr_dhcp_vp2attr(VALUE_PAIR *vp, uint8_t *p, size_t room)
         *      type, and pack them into the same
         *      attribute.
         */
-       switch (vp->type) {
+       switch (vp->da->type) {
        case PW_TYPE_BYTE:
                length = 1;
                *p = vp->vp_integer & 0xff;
@@ -883,7 +926,7 @@ static size_t fr_dhcp_vp2attr(VALUE_PAIR *vp, uint8_t *p, size_t room)
                break;
                
        default:
-               fr_strerror_printf("BAD TYPE2 %d", vp->type);
+               fr_strerror_printf("BAD TYPE2 %d", vp->da->type);
                length = 0;
                break;
        }
@@ -898,9 +941,9 @@ static VALUE_PAIR *fr_dhcp_vp2suboption(VALUE_PAIR *vps)
        uint8_t *ptr;
        VALUE_PAIR *vp, *tlv;
 
-       attribute = vps->attribute & 0xffff00ff;
+       attribute = vps->da->attr & 0xffff00ff;
 
-       tlv = paircreate(attribute, DHCP_MAGIC_VENDOR, PW_TYPE_TLV);
+       tlv = paircreate(attribute, DHCP_MAGIC_VENDOR);
        if (!tlv) return NULL;
 
        tlv->length = 0;
@@ -909,9 +952,9 @@ static VALUE_PAIR *fr_dhcp_vp2suboption(VALUE_PAIR *vps)
                 *      Group the attributes ONLY until we see a
                 *      non-TLV attribute.
                 */
-               if (!vp->flags.is_tlv ||
-                   vp->flags.extended ||
-                   ((vp->attribute & 0xffff00ff) != attribute)) {
+               if (!vp->da->flags.is_tlv ||
+                   vp->da->flags.extended ||
+                   ((vp->da->attr & 0xffff00ff) != attribute)) {
                        break;
                }
 
@@ -931,24 +974,26 @@ static VALUE_PAIR *fr_dhcp_vp2suboption(VALUE_PAIR *vps)
 
        ptr = tlv->vp_tlv;
        for (vp = vps; vp != NULL; vp = vp->next) {
-               if (!vp->flags.is_tlv ||
-                   vp->flags.extended ||
-                   ((vp->attribute & 0xffff00ff) != attribute)) {
+               if (!vp->da->flags.is_tlv ||
+                   vp->da->flags.extended ||
+                   ((vp->da->attr & 0xffff00ff) != attribute)) {
                        break;
                }
 
                length = fr_dhcp_vp2attr(vp, ptr + 2,
                                         tlv->vp_tlv + tlv->length - ptr);
-               if (length > 255) return NULL;
+               if (length > 255) {
+                       pairfree(&tlv);
+                       return NULL;
+               }
 
                /*
                 *      Pack the attribute.
                 */
-               ptr[0] = (vp->attribute & 0xff00) >> 8;
+               ptr[0] = (vp->da->attr & 0xff00) >> 8;
                ptr[1] = length;
 
                ptr += length + 2;
-               vp->flags.extended = 1;
        }
 
        return tlv;
@@ -957,13 +1002,12 @@ static VALUE_PAIR *fr_dhcp_vp2suboption(VALUE_PAIR *vps)
 
 int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
 {
-       int i, num_vps;
+       unsigned int i, num_vps;
        uint8_t *p;
        VALUE_PAIR *vp;
        uint32_t lvalue, mms;
        size_t dhcp_size, length;
        dhcp_packet_t *dhcp;
-       char buffer[1024];
 
        if (packet->data) return 0;
 
@@ -1077,8 +1121,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
 
                if (fr_debug_flag) {
                        for (i = 256; i < 269; i++) {
-                               vp = pairfind(packet->vps, i,
-                                             DHCP_MAGIC_VENDOR);
+                               vp = pairfind(packet->vps, i, DHCP_MAGIC_VENDOR, TAG_ANY);
                                if (!vp) continue;
 
                                debug_pair(vp);
@@ -1096,7 +1139,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                 *      smaller one.  They also cannot request a size
                 *      larger than MTU.
                 */
-               vp = pairfind(original->vps, 57, DHCP_MAGIC_VENDOR);
+               vp = pairfind(original->vps, 57, DHCP_MAGIC_VENDOR, TAG_ANY);
                if (vp && (vp->vp_integer > mms)) {
                        mms = vp->vp_integer;
                        
@@ -1107,7 +1150,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        /*
         *      RFC 3118: Authentication option.
         */
-       vp = pairfind(packet->vps, 90, DHCP_MAGIC_VENDOR);
+       vp = pairfind(packet->vps, 90, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                if (vp->length < 2) {
                        memset(vp->vp_octets + vp->length, 0,
@@ -1131,7 +1174,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                        VALUE_PAIR *pass;
                        vp->vp_octets[1] = 0;
 
-                       pass = pairfind(packet->vps, PW_CLEARTEXT_PASSWORD, DHCP_MAGIC_VENDOR);
+                       pass = pairfind(packet->vps, PW_CLEARTEXT_PASSWORD, DHCP_MAGIC_VENDOR, TAG_ANY);
                        if (pass) {
                                length = pass->length;
                                if ((length + 11) > sizeof(vp->vp_octets)) {
@@ -1151,7 +1194,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                }
        }
 
-       vp = pairfind(packet->vps, 256, DHCP_MAGIC_VENDOR);
+       vp = pairfind(packet->vps, 256, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                *p++ = vp->vp_integer & 0xff;
        } else {
@@ -1164,7 +1207,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        *p++ = 1;               /* hardware type = ethernet */
        *p++ = 6;               /* 6 bytes of ethernet */
 
-       vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR);
+       vp = pairfind(packet->vps, 259, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                *p++ = vp->vp_integer & 0xff;
        } else {
@@ -1189,7 +1232,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        /*
         *      Allow the admin to set the broadcast flag.
         */
-       vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR);
+       vp = pairfind(packet->vps, 262, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                p[0] |= (vp->vp_integer & 0xff00) >> 8;
                p[1] |= (vp->vp_integer & 0xff);
@@ -1200,7 +1243,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        /*
         *      Set client IP address.
         */
-       vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR); /* Your IP address */
+       vp = pairfind(packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* Your IP address */
        if (vp) {
                lvalue = vp->vp_ipaddr;
        } else {
@@ -1209,8 +1252,8 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        memcpy(p, &lvalue, 4);  /* your IP address */
        p += 4;
 
-       vp = pairfind(packet->vps, 265, DHCP_MAGIC_VENDOR); /* server IP address */
-       if (!vp) vp = pairfind(packet->vps, 54, DHCP_MAGIC_VENDOR); /* identifier */
+       vp = pairfind(packet->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY); /* server IP address */
+       if (!vp) vp = pairfind(packet->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY); /* identifier */
        if (vp) {
                lvalue = vp->vp_ipaddr;
        } else {
@@ -1222,7 +1265,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        if (original) {
                memcpy(p, original->data + 24, 4); /* copy gateway IP address */
        } else {
-               vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR);
+               vp = pairfind(packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY);
                if (vp) {
                        lvalue = vp->vp_ipaddr;
                } else {
@@ -1235,7 +1278,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
        if (original) {
                memcpy(p, original->data + 28, DHCP_CHADDR_LEN);
        } else {
-               vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR);
+               vp = pairfind(packet->vps, 267, DHCP_MAGIC_VENDOR, TAG_ANY);
                if (vp) {
                        if (vp->length > DHCP_CHADDR_LEN) {
                                memcpy(p, vp->vp_octets, DHCP_CHADDR_LEN);
@@ -1261,7 +1304,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
         *      When that happens, the boot filename is passed as an option,
         *      instead of being placed verbatim in the filename field.
         */
-       vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR);
+       vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
                if (vp->length > DHCP_FILE_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
@@ -1286,11 +1329,13 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                for (i = 0; i < 14; i++) {
                        vp = pairmake(dhcp_header_names[i], NULL, T_OP_EQ);
                        if (!vp) {
-                               fr_strerror_printf("Parse error %s", fr_strerror());
+                               char buffer[256];
+                               strlcpy(buffer, fr_strerror(), sizeof(buffer));
+                               fr_strerror_printf("Cannot decode packet due to internal error: %s", buffer);
                                return -1;
                        }
                        
-                       switch (vp->type) {
+                       switch (vp->da->type) {
                        case PW_TYPE_BYTE:
                                vp->vp_integer = p[0];
                                vp->length = 1;
@@ -1329,15 +1374,14 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                                break;
                                
                        default:
-                               fr_strerror_printf("Internal sanity check failed %d %d", vp->type, __LINE__);
+                               fr_strerror_printf("Internal sanity check failed %d %d", vp->da->type, __LINE__);
                                pairfree(&vp);
                                break;
                        }
                        
                        p += dhcp_header_sizes[i];
-                       
-                       vp_prints(buffer, sizeof(buffer), vp);
-                       fr_strerror_printf("\t%s", buffer);
+
+                       debug_pair(vp);
                        pairfree(&vp);
                }
 
@@ -1391,48 +1435,46 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
         */
        vp = packet->vps;
        while (vp) {
-               int num_entries = 1;
+               unsigned int num_entries = 1;
                VALUE_PAIR *same;
-               uint8_t *plength, *pattr;
+               uint8_t *plength;
 
-               if (vp->vendor != DHCP_MAGIC_VENDOR) goto next;
-               if (vp->attribute == 53) goto next; /* already done */
-               if ((vp->attribute > 255) &&
-                   (DHCP_BASE_ATTR(vp->attribute) != PW_DHCP_OPTION_82)) goto next;
+               if (vp->da->vendor != DHCP_MAGIC_VENDOR) goto next;
+               if (vp->da->attr == 53) goto next; /* already done */
+               if ((vp->da->attr > 255) &&
+                   (DHCP_BASE_ATTR(vp->da->attr) != PW_DHCP_OPTION_82)) goto next;
 
                debug_pair(vp);
-               if (vp->flags.extended) goto next;
+               if (vp->da->flags.extended) goto next;
 
                length = vp->length;
 
                for (same = vp->next; same != NULL; same = same->next) {
-                       if (same->attribute != vp->attribute) break;
+                       if (same->da->attr != vp->da->attr) break;
                        num_entries++;
                }
 
                /*
                 *      For client-identifier
+                * @fixme What's this meant to be doing?!
                 */
-               if ((vp->type == PW_TYPE_ETHERNET) &&
+#if 0
+               if ((vp->da->type == PW_TYPE_ETHERNET) &&
                    (vp->length == 6) &&
                    (num_entries == 1)) {
-                       vp->type = PW_TYPE_OCTETS;
+                       vp->da->type = PW_TYPE_OCTETS;
                        memmove(vp->vp_octets + 1, vp->vp_octets, 6);
                        vp->vp_octets[0] = 1;
                }
-
-               pattr = p;
-               *(p++) = vp->attribute & 0xff;
+#endif
+               *(p++) = vp->da->attr & 0xff;
                plength = p;
                *(p++) = 0;     /* header isn't included in attr length */
 
                for (i = 0; i < num_entries; i++) {
-                       if (fr_debug_flag > 1) {
-                               vp_prints(buffer, sizeof(buffer), vp);
-                               fr_strerror_printf("\t%s", buffer);
-                       }
+                       debug_pair(vp);
 
-                       if (vp->flags.is_tlv) {
+                       if (vp->da->flags.is_tlv) {
                                VALUE_PAIR *tlv;
 
                                /*
@@ -1458,7 +1500,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                         *      limitations: sizeof(vp->vp_octets) < 255
                         */
                        if (length > 255) {
-                               fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->name);
+                               fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->da->name);
                                break;
                        }
 
@@ -1469,7 +1511,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                         *      go bananas!
                         */
                        if ((*plength + length) > 255) {
-                               fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->name);
+                               fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->da->name);
                                break;
                        }
                        
@@ -1477,7 +1519,7 @@ int fr_dhcp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
                        p += length;
 
                        if (vp->next &&
-                           (vp->next->attribute == vp->attribute))
+                           (vp->next->da->attr == vp->da->attr))
                                vp = vp->next;
                } /* loop over num_entries */
 
@@ -1538,12 +1580,13 @@ int fr_dhcp_add_arp_entry(int fd, const char *interface,
                          VALUE_PAIR *macaddr, VALUE_PAIR *ip)
 {
 #ifdef SIOCSARP
-       struct sockaddr_in *sin
+       struct sockaddr_in *sin;
        struct arpreq req;
 
        if (macaddr->length > sizeof (req.arp_ha.sa_data)) {
-               fr_strerror_printf("ERROR: DHCP only supports up to %d octets for "
-                                  "Client Hardware Address (got %d octets)\n",
+               fr_strerror_printf("ERROR: DHCP only supports up to %zu octets "
+                                  "for Client Hardware Address "
+                                  "(got %zu octets)\n",
                                   sizeof(req.arp_ha.sa_data),
                                   macaddr->length);
                return -1;
@@ -1565,6 +1608,11 @@ int fr_dhcp_add_arp_entry(int fd, const char *interface,
 
        return 0;
 #else
+       fd = fd;                /* -Wunused */
+       interface = interface;  /* -Wunused */
+       macaddr = macaddr;      /* -Wunused */
+       ip = ip;                /* -Wunused */
+
        fr_strerror_printf("Adding ARP entry is unsupported on this system");
        return -1;
 #endif