BEGIN-VENDOR DHCP
ATTRIBUTE DHCP-Opcode 256 byte
+
+VALUE DHCP-Opcode Client-Message 1
+VALUE DHCP-Opcode Server-Message 2
+
ATTRIBUTE DHCP-Hardware-Type 257 byte
ATTRIBUTE DHCP-Hardware-Address-Length 258 byte
ATTRIBUTE DHCP-Hop-Count 259 byte
ATTRIBUTE DHCP-Source-Route-Enable 20 byte
# Routing Policy Filters
ATTRIBUTE DHCP-Policy-Filter 21 octets
-ATTRIBUTE DHCP-Max-Datagram-Reassembly-Sz 22 short
+ATTRIBUTE DHCP-Max-Datagram-Reassembly-Size 22 short
ATTRIBUTE DHCP-Default-IP-TTL 23 octets
ATTRIBUTE DHCP-Path-MTU-Aging-Timeout 24 integer
ATTRIBUTE DHCP-Path-MTU-Plateau-Table 25 short array
# Overload "sname" or "file"
ATTRIBUTE DHCP-Overload 52 byte
ATTRIBUTE DHCP-Message-Type 53 byte
+
+VALUE DHCP-Message-Type DHCP-Do-Not-Respond 0
+VALUE DHCP-Message-Type DHCP-Discover 1
+VALUE DHCP-Message-Type DHCP-Offer 2
+VALUE DHCP-Message-Type DHCP-Request 3
+VALUE DHCP-Message-Type DHCP-Decline 4
+VALUE DHCP-Message-Type DHCP-Ack 5
+VALUE DHCP-Message-Type DHCP-NAK 6
+VALUE DHCP-Message-Type DHCP-Release 7
+VALUE DHCP-Message-Type DHCP-Inform 8
+VALUE DHCP-Message-Type DHCP-Force-Renew 9
+VALUE DHCP-Message-Type DHCP-Lease-Query 10
+VALUE DHCP-Message-Type DHCP-Lease-Unassigned 11
+VALUE DHCP-Message-Type DHCP-Lease-Unknown 12
+VALUE DHCP-Message-Type DHCP-Lease-Active 13
+VALUE DHCP-Message-Type DHCP-Bulk-Lease-Query 14
+VALUE DHCP-Message-Type DHCP-Lease-Query-Done 15
+
ATTRIBUTE DHCP-DHCP-Server-Identifier 54 ipaddr
# Array of 1-byte numbers indicating which options the client
# Client Identifier
# First octet MAY be DHCP-Hardware-Type, rest are type-specific data,
-# e.g. MAC address. It's up to the administrator to make sense of
+# e.g. MAC address. It's up to the administrator to make sense of
# the value. We can't do anything more in the parser.
ATTRIBUTE DHCP-Client-Identifier 61 octets
ATTRIBUTE DHCP-Netware-Domain-Name 62 octets
# Horribly complicated
ATTRIBUTE DHCP-Authentication-Information 82.8 octets
-ATTRIBUTE DHCP-Vendor-Specific-Information 82.9 octets
+ATTRIBUTE DHCP-Vendor-Specific-Information 82.9 vsa
ATTRIBUTE DHCP-Relay-Agent-Flags 82.10 byte
ATTRIBUTE DHCP-Server-Identifier-Override 82.11 ipaddr
ATTRIBUTE DHCP-NDS-Tree-Name 86 octets
# Novell Directory Services
ATTRIBUTE DHCP-NDS-Context 87 octets
+
+# RFC 4280 - Broadcast and Multicast Control Servers
+ATTRIBUTE DHCP-BCMS-Server-IPv4-FQDN 88 string array
+ATTRIBUTE DHCP-BCMS-Server-IPv4-Address 89 ipaddr array
+
# Authentication
ATTRIBUTE DHCP-Authentication 90 octets
ATTRIBUTE DHCP-UUID/GUID 97 octets
# Open Group's User Authentication
ATTRIBUTE DHCP-User-Auth 98 octets
+
+# RFC 4776 - Option for Civic Addresses Configuration Information
+ATTRIBUTE DHCP-GeoConf-Civic 99 octets
+
+# RFC 4833 - Timezone Options for DHCP
+ATTRIBUTE DHCP-Timezone-Posix 100 string
+ATTRIBUTE DHCP-Timezone-Database 101 string
+
# NetInfo Parent-Server Address
ATTRIBUTE DHCP-Netinfo-Address 112 octets
# NetInfo Parent-Server Tag
ATTRIBUTE DHCP-V-I-Vendor-Class 124 octets
# Vendor-Specific
ATTRIBUTE DHCP-V-I-Vendor-Specific 125 octets # tlv
+
ATTRIBUTE DHCP-Etherboot 128 ether
# (for IP Phone software load)
-ATTRIBUTE DHCP-TFTP-Server-IP-Address 128 octets
+# RFC 4578 - Options for the Intel Preboot eXecution Environment
+ATTRIBUTE DHCP-TFTP-Server-IP-Address 128 octets
ATTRIBUTE DHCP-Call-Server-IP-address 129 octets
-
ATTRIBUTE DHCP-Ethernet-Interface 130 octets
-
ATTRIBUTE DHCP-Vendor-Discrimination-Str 130 octets
-
ATTRIBUTE DHCP-Remote-Stats-Svr-IP-Address 131 octets
-
-ATTRIBUTE DHCP-IEEE-802.1Q-L2-Priority 132 octets
-
-ATTRIBUTE DHCP-IEEE-802.1P-VLAN-ID 133 octets
-
+ATTRIBUTE DHCP-IEEE-802.1P-VLAN-ID 132 octets
+ATTRIBUTE DHCP-IEEE-802.1Q-L2-Priority 133 octets
ATTRIBUTE DHCP-Diffserv-Code-Point 134 octets
-
ATTRIBUTE DHCP-HTTP-Proxy 135 octets
-ATTRIBUTE DHCP-Cisco-TFTP-Server-IP-Addresses 150 ipaddr array
+# RFC 5192 - PANA Authentication Agent
+ATTRIBUTE DHCP-PANA-Agent 136 ipaddr array
+
+# RFC 5223 - Discovering Location-to-Service Translation (LoST)
+ATTRIBUTE DHCP-LoST-Server 137 octets
+
+# RFC 5417 - CAPWAP Access Controller DHCP Option
+ATTRIBUTE DHCP-CAPWAP-AC-IPv4-Address 138 ipaddr array
+
+# RFC 5678 - Options for IEEE 802.21 Mobility Services (MoS)
+ATTRIBUTE DHCP-MoS-IPv4-Address 139 tlv
+ATTRIBUTE DHCP-MoS-IPv4-Address-IS 139.1 ipaddr array
+ATTRIBUTE DHCP-MoS-IPv4-Address-CS 139.2 ipaddr array
+ATTRIBUTE DHCP-MoS-IPv4-Address-ES 139.3 ipaddr array
+
+ATTRIBUTE DHCP-MoS-IPv4-FQDN 140 tlv
+ATTRIBUTE DHCP-MoS-IPv4-FQDN-IS 140.1 string array
+ATTRIBUTE DHCP-MoS-IPv4-FQDN-CS 140.2 string array
+ATTRIBUTE DHCP-MoS-IPv4-FQDN-ES 140.3 string array
+
+# RFC 6011 - SIP UA Configuration Service Domains
+ATTRIBUTE DHCP-SIP-UA-Configuration-Service-Domains 141 string
+
+# RFC 6153 - Access Network Discovery and Selection Function (ANDSF)
+ATTRIBUTE DHCP-ANDSF-IPv4-Address 142 ipaddr array
+ATTRIBUTE DHCP-ANDSF-IPv6-Address 143 ipv6addr array
+
+# 144 - 149 unused
+
+ATTRIBUTE DHCP-TFTP-Server-IPv4-Address 150 ipaddr array
+
+# RFC 6926 - Bulk Lease Query
+ATTRIBUTE DHCP-Query-Status-Code 151 octets
+ATTRIBUTE DHCP-Query-Server-Base-Time 152 date
+ATTRIBUTE DHCP-Query-Start-Time-Of-State 153 integer
+ATTRIBUTE DHCP-Query-Start-Time 154 date
+ATTRIBUTE DHCP-Query-End-Time 155 date
+ATTRIBUTE DHCP-State 156 byte
+
+VALUE DHCP-State Available 1
+VALUE DHCP-State Active 2
+VALUE DHCP-State Expired 3
+VALUE DHCP-State Released 4
+VALUE DHCP-State Abandoned 5
+VALUE DHCP-State Reset 6
+VALUE DHCP-State Remote 7
+VALUE DHCP-State Transitioning 8
+
+ATTRIBUTE DHCP-Data-Source 157 byte
+
+# RFC draft-ietf-pcp-dhcp-13
+ATTRIBUTE DHCP-PCP-IPv4-Server-Address 158 octets # Complex format (not just ipaddr array)
+
+# RFC 3942 - 159-174 - Unassigned
+# RFC 3942 - 178-207 - Unassigned
+
+# RFC 5071 - PXELINUX
+ATTRIBUTE DHCP-PXELINUX-Magic 208 octets
+ATTRIBUTE DHCP-Packet-Format 209 string
+ATTRIBUTE DHCP-Path-Prefix 210 string
+ATTRIBUTE DHCP-Reboot-Time 211 date
+
+# RFC 5969 - IPv6 Rapid Deployment on IPv4 Infrastructures (6rd)
+ATTRIBUTE DHCP-6RD 212 octets
+
+# RFC 5986 - Discovering the Local Location Information Server (LIS)
+ATTRIBUTE DHCP-Access-Network-Domain-Name 213 string array
+
+# RFC 3942 - 214-219 - Unassigned
+
+# RFC 6656 - Subnet Allocation Option
+ATTRIBUTE DHCP-Virtual-Subnet-Allocation 220 octets # Complex format not just tlv
+ATTRIBUTE DHCP-Virtual-Subnet-Selection 221 octets # Complex format not just tlv
+
+# RFC 3942 - 224-253 - Site Specific
+ATTRIBUTE DHCP-Site-specific-0 224 octets
+ATTRIBUTE DHCP-Site-specific-1 225 octets
+ATTRIBUTE DHCP-Site-specific-2 226 octets
+ATTRIBUTE DHCP-Site-specific-3 227 octets
+ATTRIBUTE DHCP-Site-specific-4 228 octets
+ATTRIBUTE DHCP-Site-specific-5 229 octets
+ATTRIBUTE DHCP-Site-specific-6 230 octets
+ATTRIBUTE DHCP-Site-specific-7 231 octets
+ATTRIBUTE DHCP-Site-specific-8 232 octets
+ATTRIBUTE DHCP-Site-specific-9 233 octets
+ATTRIBUTE DHCP-Site-specific-10 234 octets
+ATTRIBUTE DHCP-Site-specific-11 235 octets
+ATTRIBUTE DHCP-Site-specific-12 236 octets
+ATTRIBUTE DHCP-Site-specific-13 237 octets
+ATTRIBUTE DHCP-Site-specific-14 238 octets
+ATTRIBUTE DHCP-Site-specific-15 239 octets
+ATTRIBUTE DHCP-Site-specific-16 240 octets
+ATTRIBUTE DHCP-Site-specific-17 241 octets
+ATTRIBUTE DHCP-Site-specific-18 242 octets
+ATTRIBUTE DHCP-Site-specific-19 243 octets
+ATTRIBUTE DHCP-Site-specific-20 244 octets
+ATTRIBUTE DHCP-Site-specific-21 245 octets
+ATTRIBUTE DHCP-Site-specific-22 246 octets
+ATTRIBUTE DHCP-Site-specific-23 247 octets
+ATTRIBUTE DHCP-Site-specific-24 248 octets
+ATTRIBUTE DHCP-Site-specific-25 249 octets
+ATTRIBUTE DHCP-Site-specific-26 250 octets
+ATTRIBUTE DHCP-Site-specific-27 251 octets
+ATTRIBUTE DHCP-Site-specific-28 252 octets
+ATTRIBUTE DHCP-Site-specific-29 253 octets
+ATTRIBUTE DHCP-Site-specific-30 253 octets
ATTRIBUTE DHCP-End-Of-Options 255 byte
-VALUE DHCP-Opcode Client-Message 1
-VALUE DHCP-Opcode Server-Message 2
-
-VALUE DHCP-Message-Type DHCP-Do-Not-Respond 0
-VALUE DHCP-Message-Type DHCP-Discover 1
-VALUE DHCP-Message-Type DHCP-Offer 2
-VALUE DHCP-Message-Type DHCP-Request 3
-VALUE DHCP-Message-Type DHCP-Decline 4
-VALUE DHCP-Message-Type DHCP-Ack 5
-VALUE DHCP-Message-Type DHCP-NAK 6
-VALUE DHCP-Message-Type DHCP-Release 7
-VALUE DHCP-Message-Type DHCP-Inform 8
-VALUE DHCP-Message-Type DHCP-Force-Renew 9
-
VALUE DHCP-Parameter-Request-List DHCP-Subnet-Mask 1
VALUE DHCP-Parameter-Request-List DHCP-Time-Offset 2
VALUE DHCP-Parameter-Request-List DHCP-Router-Address 3
#endif
}
-static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const *p, size_t alen);
+static int fr_dhcp_attr2vp(VALUE_PAIR **vp_p, TALLOC_CTX *ctx, uint8_t const *p, size_t alen);
-static int fr_dhcp_decode_suboption(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *data, size_t data_len)
+/** Returns the number of array members for arrays with fixed element sizes
+ *
+ */
+static int fr_dhcp_array_members(size_t *len, DICT_ATTR const *da)
+{
+ int num_entries = 1;
+
+ /*
+ * Could be an array of bytes, integers, etc.
+ */
+ if (da->flags.array) switch (da->type) {
+ case PW_TYPE_BYTE:
+ num_entries = *len;
+ *len = 1;
+ break;
+
+ case PW_TYPE_SHORT: /* ignore any trailing data */
+ num_entries = *len >> 1;
+ *len = 2;
+ break;
+
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_DATE: /* ignore any trailing data */
+ num_entries = *len >> 2;
+ *len = 4;
+ break;
+
+ case PW_TYPE_IPV6_ADDR:
+ num_entries = *len >> 4;
+ *len = 16;
+ break;
+
+ default:
+ break;
+ }
+
+ return num_entries;
+}
+
+/** RFC 4243 Vendor Specific Suboptions
+ *
+ * Vendor specific suboptions are in the format.
+ @verbatim
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Enterprise Number 0 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Len 0 | /
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ / Suboption Data 0 /
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Enterprise Number n |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Len n | /
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ / Suboption Data n /
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ @endverbatim
+ *
+ * So although the vendor is identified, the format of the data isn't specified
+ * so we can't actually resolve the suboption to an attribute.
+ *
+ * To get around that, we create an attribute with a vendor matching the
+ * enterprise number, and attr 0.
+ *
+ * How the suboption data is then processed, is dependent on what type
+ * \<iana\>.0 is defined as in the dictionary.
+ *
+ * @param[in,out] tlv to decode. *tlv will be set to the head of the list of suboptions and original will be freed.
+ * @param[in] ctx context to alloc new attributes in.
+ * @param[in] data to parse.
+ * @param[in] len length of data to parse.
+ */
+static int fr_dhcp_decode_vsa(VALUE_PAIR **tlv, TALLOC_CTX *ctx, uint8_t const *data, size_t len)
+{
+ uint8_t const *p, *q;
+ vp_cursor_t cursor;
+
+ VALUE_PAIR *head;
+
+ if (len < 4) goto malformed;
+
+ p = data;
+ q = p + len;
+ while (p < q) {
+ if (p + 5 >= q) goto malformed;
+ p += sizeof(uint32_t);
+ p += p[0];
+
+ /*
+ * Check if length > the length of the buffer we have left
+ */
+ if (p >= q) goto malformed;
+ p++;
+ }
+
+ head = NULL;
+ fr_cursor_init(&cursor, &head);
+
+ /*
+ * Now we know its sane, start decoding!
+ */
+ p = data;
+ while (p < q) {
+ uint32_t vendor;
+ DICT_ATTR const *da;
+ VALUE_PAIR *vp;
+
+ vendor = ntohl(*((uint32_t const *) p));
+ /*
+ * This is pretty much all we can do. RFC 4243 doesn't specify
+ * an attribute field, so it's up to vendors to figure out how
+ * they want to encode their attributes.
+ */
+ da = dict_attrbyvalue(0, vendor);
+ if (!da) {
+ da = dict_attrunknown(0, vendor, true);
+ if (!da) {
+ pairfree(&head);
+ goto malformed;
+ }
+ }
+ vp = pairalloc(ctx, da);
+ if (!vp) {
+ pairfree(&head);
+ return -1;
+ }
+ vp->op = T_OP_ADD;
+
+ if (fr_dhcp_attr2vp(&vp, ctx, p + 5, p[4]) < 0) {
+ pairfree(&head);
+ return -1;
+ }
+
+ fr_cursor_insert(&cursor, vp);
+
+ p += 4 + 1 + p[4]; /* vendor id (4) + len (1) + vsa len (n) */
+ }
+
+ /*
+ * The caller allocated TLV, if decoding it generated additional
+ * attributes, we now need to free it, and write the HEAD of our
+ * new list of attributes in its place.
+ */
+ if (head) {
+ vp_cursor_t tlv_cursor;
+
+ /*
+ * Free the old TLV attribute
+ */
+ TALLOC_FREE(*tlv);
+
+ /*
+ * Cursor not necessary but means we don't have to set
+ * ->next directly.
+ */
+ fr_cursor_init(&tlv_cursor, tlv);
+ fr_cursor_insert(&tlv_cursor, head);
+ }
+
+ return 0;
+
+malformed:
+ (*tlv)->vp_tlv = talloc_array(*tlv, uint8_t, len);
+ if (!(*tlv)->vp_tlv) {
+ fr_strerror_printf("No memory");
+ return -1;
+ }
+ memcpy((*tlv)->vp_tlv, data, len);
+ (*tlv)->length = len;
+
+ return 0;
+}
+
+/** Decode DHCP suboptions
+ *
+ * @param[in,out] tlv to decode. *tlv will be set to the head of the list of suboptions and original will be freed.
+ * @param[in] ctx context to alloc new attributes in.
+ * @param[in] data to parse.
+ * @param[in] len length of data to parse.
+ */
+static int fr_dhcp_decode_suboption(VALUE_PAIR **tlv, TALLOC_CTX *ctx, uint8_t const *data, size_t len)
{
uint8_t const *p, *q;
VALUE_PAIR *head, *vp;
vp_cursor_t cursor;
/*
+ * TLV must already point to a VALUE_PAIR.
+ */
+ VERIFY_VP(*tlv);
+
+ /*
* Take a pass at parsing it.
*/
p = data;
- q = data + data_len;
+ q = data + len;
while (p < q) {
/*
- * The RFC 3046 is very specific about not allowing termination
- * with a 255 sub-option. But vendors are stupid, so allow it
- * and the 0 padding sub-option.
- * This requirement really should be a SHOULD anyway...
+ * RFC 3046 is very specific about not allowing termination
+ * with a 255 sub-option. But it's required for decoding
+ * option 43, and vendors will probably screw it up
+ * anyway.
*/
if (*p == 0) {
p++;
p = data;
while (p < q) {
- vp = paircreate(packet, tlv->da->attr | (p[0] << 8), DHCP_MAGIC_VENDOR);
- if (!vp) {
- pairfree(&head);
- goto malformed;
- }
+ uint8_t const *a_p;
+ size_t a_len;
+ int num_entries, i;
- if (fr_dhcp_attr2vp(packet, vp, p + 2, p[1]) < 0) {
- pairfree(&head);
- goto malformed;
+ DICT_ATTR const *da;
+ uint32_t attr;
+
+ /*
+ * The initial OID string looks like:
+ * <iana>.0
+ *
+ * If <iana>.0 is type TLV then we attempt to decode its contents as more
+ * DHCP suboptions, which gives us:
+ * <iana>.<attr>
+ *
+ * If <iana>.0 is not defined in the dictionary or is type octets, we leave
+ * the attribute as is.
+ */
+ attr = (*tlv)->da->attr ? ((*tlv)->da->attr | (p[0] << 8)) : p[0];
+
+ /*
+ * Use the vendor of the parent TLV which is not necessarily
+ * DHCP_MAGIC_VENDOR.
+ *
+ * Note: This does not deal with dictionary numbering clashes. If
+ * the vendor uses different numbers for DHCP suboptions and RADIUS
+ * attributes then it's time to break out %{hex:} and regular
+ * expressions.
+ */
+ da = dict_attrbyvalue(attr, (*tlv)->da->vendor);
+ if (!da) {
+ da = dict_attrunknown(attr, (*tlv)->da->vendor, true);
+ if (!da) {
+ pairfree(&head);
+ return -1;
+ }
}
- fr_cursor_insert(&cursor, vp);
- p += 2 + p[1];
+ a_len = p[1];
+ a_p = p + 2;
+ num_entries = fr_dhcp_array_members(&a_len, da);
+ for (i = 0; i < num_entries; i++) {
+ vp = pairalloc(ctx, da);
+ if (!vp) {
+ pairfree(&head);
+ return -1;
+ }
+ vp->op = T_OP_ADD;
+
+ if (fr_dhcp_attr2vp(&vp, ctx, a_p, a_len) < 0) {
+ pairfree(&head);
+ goto malformed;
+ }
+ fr_cursor_insert(&cursor, vp);
+
+ a_p += a_len;
+ }
+ p += 2 + p[1]; /* code (1) + len (1) + suboption len (n)*/
}
/*
- * The caller allocated TLV, so we need to copy the FIRST
- * attribute over top of that.
- *
- * This is a pretty awful hack, but we should be able to
- * clean it up when we get nested VPs so lets leave it for
- * now.
+ * The caller allocated a TLV, if decoding it generated
+ * additional attributes, we now need to free it, and write
+ * the HEAD of our new list of attributes in its place.
*/
if (head) {
- /* Cleanup any old TLV data */
- talloc_free(tlv->vp_tlv);
-
- /* @fixme fragile */
- memcpy(tlv, head, sizeof(*tlv));
+ vp_cursor_t tlv_cursor;
- /* If the VP has a talloced value we need to reparent it to the original TLV attribute */
- switch (head->da->type) {
- case PW_TYPE_STRING:
- case PW_TYPE_OCTETS:
- case PW_TYPE_TLV:
- (void) talloc_steal(tlv, head->data.ptr);
+ /*
+ * Free the old TLV attribute
+ */
+ TALLOC_FREE(*tlv);
- default:
- break;
- }
- tlv->next = head->next;
- talloc_free(head);
+ /*
+ * Cursor not necessary but means we don't have to set
+ * ->next directly.
+ */
+ fr_cursor_init(&tlv_cursor, tlv);
+ fr_cursor_insert(&tlv_cursor, head);
}
return 0;
malformed:
- tlv->vp_tlv = talloc_array(tlv, uint8_t, data_len);
- if (!tlv->vp_tlv) {
+ (*tlv)->vp_tlv = talloc_array(*tlv, uint8_t, len);
+ if (!(*tlv)->vp_tlv) {
fr_strerror_printf("No memory");
return -1;
}
- memcpy(tlv->vp_tlv, data, data_len);
- tlv->length = data_len;
+ memcpy((*tlv)->vp_tlv, data, len);
+ (*tlv)->length = len;
return 0;
}
-
/*
* Decode ONE value into a VP
*/
-static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const *p, size_t alen)
+static int fr_dhcp_attr2vp(VALUE_PAIR **vp_p, TALLOC_CTX *ctx, uint8_t const *data, size_t len)
{
- char *q;
+ VALUE_PAIR *vp = *vp_p;
+ VERIFY_VP(vp);
switch (vp->da->type) {
case PW_TYPE_BYTE:
- if (alen != 1) goto raw;
- vp->vp_byte = p[0];
+ if (len != 1) goto raw;
+ vp->vp_byte = data[0];
break;
case PW_TYPE_SHORT:
- if (alen != 2) goto raw;
- memcpy(&vp->vp_short, p, 2);
+ if (len != 2) goto raw;
+ memcpy(&vp->vp_short, data, 2);
vp->vp_short = ntohs(vp->vp_short);
break;
case PW_TYPE_INTEGER:
- if (alen != 4) goto raw;
- memcpy(&vp->vp_integer, p, 4);
+ if (len != 4) goto raw;
+ memcpy(&vp->vp_integer, data, 4);
vp->vp_integer = ntohl(vp->vp_integer);
break;
case PW_TYPE_IPV4_ADDR:
- if (alen != 4) goto raw;
+ if (len != 4) goto raw;
/*
* Keep value in Network Order!
*/
- memcpy(&vp->vp_ipaddr, p , 4);
+ memcpy(&vp->vp_ipaddr, data, 4);
vp->length = 4;
break;
+ /*
+ * In DHCPv4, string options which can also be arrays,
+ * have their values '\0' delimited.
+ */
case PW_TYPE_STRING:
- vp->vp_strvalue = q = talloc_array(vp, char, alen + 1);
- vp->type = VT_DATA;
- memcpy(q, p , alen);
- q[alen] = '\0';
+ {
+ uint8_t const *p;
+ uint8_t const *q, *end;
+ vp_cursor_t cursor;
+
+ /*
+ * Initialise the cursor as we may be inserting
+ * multiple additional VPs
+ */
+ if (vp->da->flags.array) fr_cursor_init(&cursor, vp_p);
+
+ p = data;
+ q = end = data + len;
+ for (;;) {
+ if (vp->da->flags.array) {
+ q = memchr(p, '\0', q - p);
+ /* Malformed but recoverable */
+ if (!q) q = end;
+ }
+
+ pairstrncpy(vp, (char const *)p, q - p);
+ p = q + 1;
+
+ /* Need another VP for the next round */
+ if (p < end) {
+ vp = pairalloc(ctx, vp->da);
+ if (!vp) {
+ pairfree(vp_p);
+ return -1;
+ }
+ fr_cursor_insert(&cursor, vp);
+ continue;
+ }
+ break;
+ }
+ }
break;
case PW_TYPE_ETHERNET:
- memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
+ memcpy(vp->vp_ether, data, sizeof(vp->vp_ether));
vp->length = sizeof(vp->vp_ether);
break;
if (pair2unknown(vp) < 0) return -1;
case PW_TYPE_OCTETS:
- if (alen > 255) return -1;
- pairmemcpy(vp, p, alen);
+ if (len > 255) return -1;
+ pairmemcpy(vp, data, len);
break;
/*
* For option 82 et al...
*/
case PW_TYPE_TLV:
- return fr_dhcp_decode_suboption(packet, vp, p, alen);
+ return fr_dhcp_decode_suboption(vp_p, ctx, data, len);
+
+ /*
+ * For option 82.9
+ */
+ case PW_TYPE_VSA:
+ return fr_dhcp_decode_vsa(vp_p, ctx, data, len);
default:
fr_strerror_printf("Internal sanity check %d %d", vp->da->type, __LINE__);
return -1;
} /* switch over type */
- vp->length = alen;
+ vp->length = len;
return 0;
}
-ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
- uint8_t const *data, size_t len, VALUE_PAIR **head)
+/** Decode DHCP options
+ *
+ * @param[in,out] out Where to write the decoded options.
+ * @param[in] ctx context to alloc new attributes in.
+ * @param[in] data to parse.
+ * @param[in] len of data to parse.
+ */
+ssize_t fr_dhcp_decode_options(VALUE_PAIR **out, TALLOC_CTX *ctx, uint8_t const *data, size_t len)
{
- int i;
VALUE_PAIR *vp;
vp_cursor_t cursor;
- uint8_t const *p, *next;
- next = data;
+ uint8_t const *p, *q;
- *head = NULL;
- fr_cursor_init(&cursor, head);
+ *out = NULL;
+ fr_cursor_init(&cursor, out);
/*
* FIXME: This should also check sname && file fields.
* See the dhcp_get_option() function above.
*/
- while (next < (data + len)) {
- int num_entries, alen;
- DICT_ATTR const *da;
+ p = data;
+ q = data + len;
+ while (p < q) {
+ uint8_t const *a_p;
+ size_t a_len;
+ int num_entries, i;
- p = next;
+ DICT_ATTR const *da;
if (*p == 0) { /* 0x00 - Padding option */
- next++;
+ p++;
continue;
}
- if (*p == 255) break; /* 0xff - End of options signifier */
- if ((p + 2) > (data + len)) break;
+ if (*p == 255) { /* 0xff - End of options signifier */
+ break;
+ }
- next = p + 2 + p[1];
+ if ((p + 2) > q) break;
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_IPV4_ADDR:
- 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 */
+ da = dict_attrunknown(p[0], DHCP_MAGIC_VENDOR, true);
+ if (!da) {
+ pairfree(out);
+ return -1;
}
+ goto next;
}
- /*
- * Loop over all of the entries, building VPs
- */
+ a_len = p[1];
+ a_p = p + 2;
+ num_entries = fr_dhcp_array_members(&a_len, da);
for (i = 0; i < num_entries; i++) {
- vp = pairmake(packet, NULL, da->name, NULL, T_OP_ADD);
+ vp = pairalloc(ctx, da);
if (!vp) {
- fr_strerror_printf("Cannot build attribute %s", fr_strerror());
- pairfree(head);
+ pairfree(out);
return -1;
}
+ vp->op = T_OP_ADD;
- if (fr_dhcp_attr2vp(packet, vp, p, alen) < 0) {
+ if (fr_dhcp_attr2vp(&vp, ctx, a_p, a_len) < 0) {
pairfree(&vp);
- pairfree(head);
+ pairfree(out);
return -1;
}
-
fr_cursor_insert(&cursor, vp);
for (vp = fr_cursor_current(&cursor);
vp = fr_cursor_next(&cursor)) {
debug_pair(vp);
}
- p += alen;
+ a_p += a_len;
} /* loop over array entries */
+ next:
+ p += 2 + p[1]; /* code (1) + len (1) + option len (n)*/
} /* loop over the entire packet */
- return next - data;
+ return p - data;
}
int fr_dhcp_decode(RADIUS_PACKET *packet)
{
VALUE_PAIR *options = NULL;
- if (fr_dhcp_decode_options(packet,
- packet->data + 240, packet->data_len - 240,
- &options) < 0) {
+ if (fr_dhcp_decode_options(&options, packet, packet->data + 240, packet->data_len - 240) < 0) {
return -1;
}