The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
base.c
Go to the documentation of this file.
1 /*
2  * This library is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU Lesser General Public
4  * License as published by the Free Software Foundation; either
5  * version 2.1 of the License, or (at your option) any later version.
6  *
7  * This library is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10  * Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public
13  * License along with this library; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: a20686cb62483681bc3055353d4544b2d073b541 $
19  *
20  * @file protocols/dhcpv4/base.c
21  * @brief Functions to send/receive dhcp packets.
22  *
23  * @copyright 2008 The FreeRADIUS server project
24  * @copyright 2008 Alan DeKok (aland@deployingradius.com)
25  */
26 RCSID("$Id: a20686cb62483681bc3055353d4544b2d073b541 $")
27 
28 #include <freeradius-devel/dhcpv4/dhcpv4.h>
29 #include <freeradius-devel/util/net.h>
30 #include <freeradius-devel/util/proto.h>
31 #include "attrs.h"
32 
34 
35 typedef struct {
39 
41 
44  { .out = &dict_dhcpv4, .proto = "dhcpv4" },
45  { NULL }
46 };
47 
72 
75  { .out = &attr_dhcp_boot_filename, .name = "Boot-Filename", .type = FR_TYPE_STRING, .dict = &dict_dhcpv4 },
76  { .out = &attr_dhcp_client_hardware_address, .name = "Client-Hardware-Address", .type = FR_TYPE_ETHERNET, .dict = &dict_dhcpv4 },
77  { .out = &attr_dhcp_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
78  { .out = &attr_dhcp_flags, .name = "Flags", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
79  { .out = &attr_dhcp_gateway_ip_address, .name = "Gateway-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
80  { .out = &attr_dhcp_hardware_address_length, .name = "Hardware-Address-Length", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
81  { .out = &attr_dhcp_hardware_type, .name = "Hardware-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
82  { .out = &attr_dhcp_hop_count, .name = "Hop-Count", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
83  { .out = &attr_dhcp_number_of_seconds, .name = "Number-of-Seconds", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
84  { .out = &attr_dhcp_opcode, .name = "Opcode", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
85  { .out = &attr_dhcp_server_host_name, .name = "Server-Host-Name", .type = FR_TYPE_STRING, .dict = &dict_dhcpv4 },
86  { .out = &attr_dhcp_server_ip_address, .name = "Server-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
87  { .out = &attr_dhcp_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
88  { .out = &attr_dhcp_your_ip_address, .name = "Your-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
89  { .out = &attr_dhcp_dhcp_maximum_msg_size, .name = "Maximum-Msg-Size", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
90  { .out = &attr_dhcp_interface_mtu_size, .name = "Interface-MTU-Size", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
91  { .out = &attr_dhcp_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
92  { .out = &attr_dhcp_parameter_request_list, .name = "Parameter-Request-List", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
93  { .out = &attr_dhcp_overload, .name = "Overload", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
94  { .out = &attr_dhcp_vendor_class_identifier, .name = "Vendor-Class-Identifier", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv4 },
95  { .out = &attr_dhcp_relay_link_selection, .name = "Relay-Agent-Information.Relay-Link-Selection", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
96  { .out = &attr_dhcp_subnet_selection_option, .name = "Subnet-Selection-Option", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
97  { .out = &attr_dhcp_network_subnet, .name = "Network-Subnet", .type = FR_TYPE_IPV4_PREFIX, .dict = &dict_dhcpv4 },
98  { .out = &attr_dhcp_option_82, .name = "Relay-Agent-Information", .type = FR_TYPE_TLV, .dict = &dict_dhcpv4 },
99  { NULL }
100 };
101 
102 /*
103  * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 DISCOVER
104  * INADDR_BROADCAST : 68 <- SERVER_IP : 67 OFFER
105  * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 REQUEST
106  * INADDR_BROADCAST : 68 <- SERVER_IP : 67 ACK
107  */
123 };
125 
126 char const *dhcp_message_types[] = {
127  "invalid",
128  "Discover",
129  "Offer",
130  "Request",
131  "Decline",
132  "Ack",
133  "NAK",
134  "Release",
135  "Inform",
136  "Force-Renew",
137  "Lease-Query",
138  "Lease-Unassigned",
139  "Lease-Unknown",
140  "Lease-Active",
141  "Bulk-Lease-Query",
142  "Lease-Query-Done"
143 };
144 
145 #define DHCP_MAX_MESSAGE_TYPE (NUM_ELEMENTS(dhcp_message_types))
146 
148  1, /* op */
149  1, /* htype */
150  1, /* hlen */
151  1, /* hops */
152  4, /* xid */
153  2, /* secs */
154  2, /* flags */
155  4, /* ciaddr */
156  4, /* yiaddr */
157  4, /* siaddr */
158  4, /* giaddr */
159  DHCP_CHADDR_LEN, /* chaddr */
160  DHCP_SNAME_LEN, /* sname */
161  DHCP_FILE_LEN /* file */
162 };
163 
164 uint8_t eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
165 
168 
169 static int dict_flag_prefix(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
170 {
171  static fr_table_num_sorted_t const table[] = {
172  { L("bits"), DHCPV4_FLAG_PREFIX_BITS },
173  { L("split"), DHCPV4_FLAG_PREFIX_SPLIT }
174  };
175  static size_t table_len = NUM_ELEMENTS(table);
176 
179 
181  if (flag == DHCPV4_FLAG_PREFIX_INVALID) {
182  fr_strerror_printf("Unknown prefix type '%s'", value);
183  return -1;
184  }
185  flags->prefix = flag;
186 
187  return 0;
188 }
189 
191  { L("dns_label"), { .func = dict_flag_dns_label } },
192  { L("exists"), { .func = dict_flag_exists } },
193  { L("prefix"), { .func = dict_flag_prefix } }
194 };
195 
196 int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
197 {
198  fr_pair_t const *my_a = a, *my_b = b;
199 
200  PAIR_VERIFY(my_a);
201  PAIR_VERIFY(my_b);
202 
203  /*
204  * Message-Type is first, for simplicity.
205  */
206  if ((my_a->da == attr_dhcp_message_type) && (my_b->da != attr_dhcp_message_type)) return -1;
207  if ((my_a->da != attr_dhcp_message_type) && (my_b->da == attr_dhcp_message_type)) return +1;
208 
209  /*
210  * Relay-Agent is last.
211  *
212  * RFC 3046:
213  * Servers SHOULD copy the Relay Agent Information
214  * option as the last DHCP option in the response.
215  *
216  * Some crazy DHCP relay agents idea of how to strip option 82 in
217  * a reply packet is to simply overwrite the 82 with 255 - the
218  * "Eod of Options" option - causing the client to then ignore
219  * any subsequent options.
220  *
221  * Check if either of the options are option 82
222  */
223  if ((my_a->da == attr_dhcp_option_82) && (my_b->da != attr_dhcp_option_82)) return +1;
224  if ((my_a->da != attr_dhcp_option_82) && (my_b->da == attr_dhcp_option_82)) return -1;
225 
226  return fr_dict_attr_cmp(my_a->da, my_b->da);
227 }
228 
229 /** Check received DHCP request is valid and build fr_packet_t structure if it is
230  *
231  * @param data pointer to received packet.
232  * @param data_len length of received data, and then length of the actual DHCP data.
233  * @param[out] message_type where the message type will be stored (if used)
234  * @param[out] xid where the xid will be stored (if used)
235  *
236  * @return
237  * - true if the packet is well-formed
238  * - false if it's a bad packet
239  */
240 bool fr_dhcpv4_ok(uint8_t const *data, ssize_t data_len, uint8_t *message_type, uint32_t *xid)
241 {
242  uint32_t magic;
243  uint8_t const *code;
244  size_t hlen;
245 
246  if (data_len < MIN_PACKET_SIZE) {
247  fr_strerror_printf("DHCP packet is too small (%zu < %d)", data_len, MIN_PACKET_SIZE);
248  return false;
249  }
250 
251  if (data_len > MAX_PACKET_SIZE) {
252  fr_strerror_printf("DHCP packet is too large (%zx > %d)", data_len, MAX_PACKET_SIZE);
253  return false;
254  }
255 
256  if (data[1] > 1) {
257  fr_strerror_printf("DHCP can only process ethernet requests, not type %02x", data[1]);
258  return false;
259  }
260 
261  hlen = data[2];
262  if ((hlen != 0) && (hlen != 6)) {
263  fr_strerror_printf("Ethernet HW length incorrect. Expected 6 got %zu", hlen);
264  return false;
265  }
266 
267  memcpy(&magic, data + 236, 4);
268  magic = ntohl(magic);
269  if (magic != DHCP_OPTION_MAGIC_NUMBER) {
270  fr_strerror_const("BOOTP not supported");
271  return false;
272  }
273 
275  if (!code || (code[1] == 0)) {
276  fr_strerror_const("No message-type option was found in the packet");
277  return false;
278  }
279 
280  if ((code[2] == 0) || (code[2] >= DHCP_MAX_MESSAGE_TYPE)) {
281  fr_strerror_printf("Unknown value %d for message-type option", code[2]);
282  return false;
283  }
284 
285  /*
286  * @todo - data_len MAY be larger than the data in the
287  * packet. In which case, we should update data_len with
288  * the true size of the packet.
289  */
290 
291  if (message_type) *message_type = code[2];
292 
293  if (xid) {
294  memcpy(&magic, data + 4, 4);
295  *xid = ntohl(magic);
296  }
297 
298  return true;
299 }
300 
301 /** Evaluation function for DCHPV4-encodability
302  *
303  * @param item pointer to a fr_pair_t
304  * @param uctx context
305  *
306  * @return true if the underlying fr_pair_t is DHCPv4 encodable, false otherwise
307  */
308 bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
309 {
310  fr_pair_t const *vp = item;
311 
312  PAIR_VERIFY(vp);
313  return (vp->da->dict == dict_dhcpv4) && (!vp->da->flags.internal);
314 }
315 
316 /** DHCPV4-specific iterator
317  *
318  */
320 {
321  fr_pair_t *c = current;
322  fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
323 
324  while ((c = fr_dlist_next(list, c))) {
325  PAIR_VERIFY(c);
326  if (c->da->dict != dict || c->da->flags.internal) continue;
327 
328  if (c->vp_type == FR_TYPE_BOOL && fr_dhcpv4_flag_exists(c->da) && !c->vp_bool) continue;
329 
330  break;
331  }
332 
333  return c;
334 }
335 
336 ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
337 {
338  return fr_dhcpv4_encode_dbuff(&FR_DBUFF_TMP(buffer, buflen), original, code, xid, vps);
339 }
340 
342 {
343  fr_dcursor_t cursor;
344  fr_pair_t *vp;
345  ssize_t len;
346  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
347 
348  /*
349  * @todo: Make this work again.
350  */
351 #if 0
352  mms = DEFAULT_PACKET_SIZE; /* maximum message size */
353 
354  /*
355  * Clients can request a LARGER size, but not a
356  * smaller one. They also cannot request a size
357  * larger than MTU.
358  */
359 
360  /* Maximum-Msg-Size */
362  if (vp && (vp->vp_uint32 > mms)) {
363  mms = vp->vp_uint32;
364 
365  if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE;
366  }
367 #endif
368 
369  vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_opcode);
370  if (vp) {
371  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
372  } else {
373  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x01); /* client message */
374  }
375 
376  /* Hardware-Type */
378  if (vp) {
379  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
380 
381  } else if (original) {
382  FR_DBUFF_IN_RETURN(&work_dbuff, original->htype);
383 
384  } else { /* we are ALWAYS ethernet */
385  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x01);
386  }
387 
388  /* Hardware-Address-len */
390  if (vp) {
391  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
392 
393  } else if (original) {
394  FR_DBUFF_IN_RETURN(&work_dbuff, original->hlen);
395 
396  } else { /* we are ALWAYS ethernet */
397  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x06);
398  }
399 
400  /* Hop-Count */
402  if (vp) {
403  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
404 
405  } else if (original) {
406  FR_DBUFF_IN_RETURN(&work_dbuff, original->hops);
407 
408  } else {
409  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x00);
410  }
411 
412  /* Transaction-Id */
413  FR_DBUFF_IN_RETURN(&work_dbuff, xid);
414 
415  /* Number-of-Seconds */
417  if (vp) {
418  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint16);
419  } else {
420  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_uint16));
421  }
422 
423  /* Flags */
424  vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_flags);
425  if (vp) {
426  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint16);
427  } else if (original) { /* Original flags, still in network order */
428  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t *)&original->flags, sizeof(original->flags));
429  } else {
430  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_uint16));
431  }
432 
433  /* Client-IP-Address */
435  if (vp) {
436  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
437  } else {
438  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_ipv4addr));
439  }
440 
441  /* Your-IP-address */
443  if (vp) {
444  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
445  } else {
446  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
447  }
448 
449  /* Server-IP-Address */
451  if (vp) {
452  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
453  } else {
454  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
455  }
456 
457  /*
458  * Gateway-IP-Address
459  */
461  if (vp) {
462  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
463 
464  } else if (original) { /* copy whatever value was in the original */
465  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&original->giaddr, sizeof(original->giaddr));
466 
467  } else {
468  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
469  }
470 
471  /* Client-Hardware-Address */
473  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)vp->vp_ether, sizeof(vp->vp_ether));
474  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_CHADDR_LEN - sizeof(vp->vp_ether));
475 
476  } else if (original) { /* copy whatever value was in the original */
477  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, &original->chaddr[0], sizeof(original->chaddr));
478 
479  } else {
480  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_CHADDR_LEN);
481  }
482 
483  /* Server-Host-Name */
484  if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_server_host_name))) {
485  if (vp->vp_length > DHCP_SNAME_LEN) {
486  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, DHCP_SNAME_LEN);
487  } else {
488  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
489  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_SNAME_LEN - vp->vp_length);
490  }
491  } else {
492  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_SNAME_LEN);
493  }
494 
495  /*
496  * Copy over Boot-Filename.
497  *
498  * FIXME: This copy should be delayed until AFTER the options
499  * have been processed. If there are too many options for
500  * the packet, then they go into the sname && filename fields.
501  * When that happens, the boot filename is passed as an option,
502  * instead of being placed verbatim in the filename field.
503  */
504 
505  /* Boot-Filename */
506  if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_boot_filename))) {
507  if (vp->vp_length > DHCP_FILE_LEN) {
508  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, DHCP_FILE_LEN);
509  } else {
510  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
511  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_FILE_LEN - vp->vp_length);
512  }
513  } else {
514  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_FILE_LEN);
515  }
516 
517  /* DHCP magic number */
519 
520  if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_message_type))) {
521  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_TYPE, 0x01, vp->vp_uint8);
522  } else {
523  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_TYPE, 0x01, (uint8_t)code);
524  }
525 
526  /*
527  * Pre-sort attributes into contiguous blocks so that fr_dhcpv4_encode_option
528  * operates correctly. This changes the order of the list, but never mind...
529  */
532 
533  /*
534  * Each call to fr_dhcpv4_encode_option will encode one complete DHCP option,
535  * and sub options.
536  */
537  while ((vp = fr_dcursor_current(&cursor))) {
538  /*
539  * The encoder skips message type, and returns
540  * "len==0" for it. We want to allow that, BUT
541  * stop when the encoder returns "len==0" for
542  * other attributes. So we need to skip it
543  * manually, too.
544  */
545  if (vp->da == attr_dhcp_message_type) {
546  (void) fr_dcursor_next(&cursor);
547  continue;
548  }
549 
550  len = fr_dhcpv4_encode_option(&work_dbuff,
551  &cursor, &(fr_dhcpv4_ctx_t){ .root = fr_dict_root(dict_dhcpv4) });
552  if (len <= 0) break;
553  }
554 
555  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)FR_END_OF_OPTIONS);
556 
557  /*
558  * FIXME: if (fr_dbuff_used(&work_dbuff) > mms),
559  * then we put the extra options into the "sname" and "file"
560  * fields, AND set the "end option option" in the "options"
561  * field. We also set the "overload option",
562  * and put options into the "file" field, followed by
563  * the "sname" field. Where each option is completely
564  * enclosed in the "file" and/or "sname" field, AND
565  * followed by the "end of option", and MUST be followed
566  * by padding option.
567  *
568  * Yuck. That sucks...
569  */
570  if (fr_dbuff_used(&work_dbuff) < DEFAULT_PACKET_SIZE) {
571  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DEFAULT_PACKET_SIZE - fr_dbuff_used(&work_dbuff));
572  }
573 
574  return fr_dbuff_set(dbuff, fr_dbuff_used(&work_dbuff));
575 }
576 
577 
578 /** Resolve/cache attributes in the DHCP dictionary
579  *
580  * @return
581  * - 0 on success.
582  * - -1 on failure.
583  */
585 {
587  uint8_t i;
588 
589  if (instance_count > 0) {
590  instance_count++;
591  return 0;
592  }
593 
594  instance_count++;
595 
596  if (fr_dict_autoload(dhcpv4_dict) < 0) {
597  fail:
598  instance_count--;
599  return -1;
600  }
601 
604  goto fail;
605  }
606 
607  /*
608  * Fixup dictionary entry for Paramter-Request-List adding all the options
609  */
610  for (i = 1; i < 255; i++) {
611  fr_dict_attr_t const *attr;
612 
614  if (!attr) {
615  continue;
616  }
617  value.vb_uint8 = i;
618 
620  attr->name, &value, true, false) < 0) {
621  goto fail;
622  }
623  }
624 
625  return 0;
626 }
627 
629 {
631 
632  if (--instance_count > 0) return;
633 
635 }
636 
637 
638 static char const *short_header_names[] = {
639  "opcode",
640  "hwtype",
641  "hwaddrlen",
642  "hop_count",
643  "xid",
644  "seconds",
645  "flags",
646  "ciaddr",
647  "yiaddr",
648  "siaddr",
649  "giaddr",
650  "chaddr",
651  "server_hostname",
652  "boot_filename",
653 };
654 
655 static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
656 {
657  int i;
658  static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
659 
660  for (i = 0; i < attrlen; i++) {
661  if ((i > 0) && ((i & 0x0f) == 0x00))
662  fprintf(fp, "%.*s", depth, tabs);
663  fprintf(fp, "%02x ", ptr[i]);
664  if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
665  }
666  if ((i & 0x0f) != 0) fprintf(fp, "\n");
667 }
668 
669 /** Print a raw DHCP packet as hex.
670  *
671  */
672 void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
673 {
674  int i;
675  uint8_t const *attr, *end;
676 
677  end = packet + packet_len;
678  attr = packet;
679 
680  for (i = 0; i < 14; i++) {
681  fprintf(fp, "\t%s: ", short_header_names[i]);
682  print_hex_data(fp, attr, dhcp_header_sizes[i], 2);
683  attr += dhcp_header_sizes[i];
684  }
685 
686  fprintf(fp, "\tmagic:\t%02x %02x %02x %02x\n", attr[0], attr[1], attr[2], attr[3]);
687  attr += 4;
688 
689  fprintf(fp, "\toptions\n");
690  while (attr < end) {
691  fprintf(fp, "\t\t");
692 
693  fprintf(fp, "%02x %02x ", attr[0], attr[1]);
694 
695  print_hex_data(fp, attr + 2, attr[1], 3);
696 
697  /*
698  * "End of option" option.
699  */
700  if (attr[0] == 255) break;
701 
702  attr += attr[1] + 2;
703  }
704 
705  fprintf(fp, "\n");
706 }
707 
708 static bool attr_valid(fr_dict_attr_t *da)
709 {
710  /*
711  * "arrays" of string/octets are encoded as a 8-bit
712  * length, followed by the actual data.
713  */
714  if (da->flags.array && ((da->type == FR_TYPE_STRING) || (da->type == FR_TYPE_OCTETS))) {
715  da->flags.is_known_width = true;
716 
717  if (da->flags.extra && (da->flags.subtype != FLAG_LENGTH_UINT8)) {
718  fr_strerror_const("string/octets arrays require the 'length=uint8' flag");
719  return false;
720  }
721  }
722 
723  if (da->flags.extra && (da->flags.subtype == FLAG_LENGTH_UINT16)) {
724  fr_strerror_const("The 'length=uint16' flag cannot be used for DHCPv4");
725  return false;
726  }
727 
728  /*
729  * "extra" signifies that subtype is being used by the
730  * dictionaries itself.
731  */
732  if (da->flags.extra || !da->flags.subtype) return true;
733 
734  if ((da->type != FR_TYPE_STRING) && (fr_dhcpv4_flag_dns_label(da))) {
735  fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
736  return false;
737  }
738 
739  if ((da->type != FR_TYPE_IPV4_PREFIX) &&
740  (fr_dhcpv4_flag_prefix(da))) {
741  fr_strerror_const("The 'prefix=...' flag can only be used with attributes of type 'ipv4prefix'");
742  return false;
743  }
744 
745  if ((da->type != FR_TYPE_BOOL) && fr_dhcpv4_flag_exists(da)) {
746  fr_strerror_const("The 'exists' flag can only be used with attributes of type 'bool'");
747  return false;
748  }
749 
750  return true;
751 }
752 
755  .name = "dhcpv4",
756  .default_type_size = 1,
757  .default_type_length = 1,
758  .attr = {
759  .flags = {
760  .table = dhcpv4_flags,
761  .table_len = NUM_ELEMENTS(dhcpv4_flags),
762  .len = sizeof(fr_dhcpv4_attr_flags_t)
763  },
764  .valid = attr_valid
765  },
766 
767  .init = fr_dhcpv4_global_init,
768  .free = fr_dhcpv4_global_free,
769 
770  .encode = fr_dhcpv4_encode_foreign,
771  .decode = fr_dhcpv4_decode_foreign,
772 };
static int const char char buffer[256]
Definition: acutest.h:574
static fr_dict_t * dict
Definition: fuzzer.c:46
#define RCSID(id)
Definition: build.h:481
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition: dbuff.h:767
#define FR_DBUFF_MEMSET_RETURN(_dbuff_or_marker, _c, _inlen)
Set _inlen bytes of a dbuff or marker to _c returning if there is insufficient space.
Definition: dbuff.h:1508
#define FR_DBUFF_IN_MEMCPY_RETURN(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1382
#define FR_DBUFF_IN_RETURN(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff returning if there is insufficient space.
Definition: dbuff.h:1585
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:222
#define FR_DBUFF_IN_BYTES_RETURN(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1472
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:514
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
fr_dcursor_iter_t void * current
Definition: dcursor.h:148
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:288
return item
Definition: dcursor.h:553
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:337
#define DEFAULT_PACKET_SIZE
Definition: dhcpv4.h:89
#define DHCP_CHADDR_LEN
Definition: dhcpv4.h:37
#define MIN_PACKET_SIZE
Definition: dhcpv4.h:88
#define fr_dhcpv4_flag_dns_label(_da)
Definition: dhcpv4.h:157
ssize_t fr_dhcpv4_encode_foreign(fr_dbuff_t *dbuff, fr_pair_list_t const *list)
Definition: encode.c:774
#define DHCP_SNAME_LEN
Definition: dhcpv4.h:38
#define DHCP_FILE_LEN
Definition: dhcpv4.h:39
fr_dhcpv4_attr_flags_prefix_t prefix
Definition: dhcpv4.h:149
fr_dhcpv4_attr_flags_prefix_t
Definition: dhcpv4.h:139
@ DHCPV4_FLAG_PREFIX_INVALID
Definition: dhcpv4.h:140
@ DHCPV4_FLAG_PREFIX_SPLIT
Definition: dhcpv4.h:143
@ DHCPV4_FLAG_PREFIX_BITS
Definition: dhcpv4.h:142
uint32_t giaddr
Definition: dhcpv4.h:77
#define fr_dhcpv4_flag_prefix(_da)
Definition: dhcpv4.h:160
#define ETH_ADDR_LEN
Definition: dhcpv4.h:103
#define MAX_PACKET_SIZE
Definition: dhcpv4.h:90
#define fr_dhcpv4_flag_exists(_da)
Definition: dhcpv4.h:158
uint8_t htype
Definition: dhcpv4.h:68
#define DHCP_OPTION_MAGIC_NUMBER
Definition: dhcpv4.h:41
uint8_t hlen
Definition: dhcpv4.h:69
uint8_t hops
Definition: dhcpv4.h:70
uint8_t const * fr_dhcpv4_packet_get_option(dhcp_packet_t const *packet, size_t packet_size, fr_dict_attr_t const *da)
Retrieve a DHCP option from a raw packet buffer.
Definition: packet.c:37
uint16_t flags
Definition: dhcpv4.h:73
ssize_t fr_dhcpv4_encode_option(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx)
Encode a DHCP option and any sub-options.
Definition: encode.c:726
uint8_t chaddr[DHCP_CHADDR_LEN]
Definition: dhcpv4.h:78
Used as the decoder ctx.
Definition: dhcpv4.h:134
@ FLAG_LENGTH_UINT8
string / octets type is prefixed by uint8 of length
Definition: dict.h:148
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition: dict.h:149
int fr_dict_enum_add_name(fr_dict_attr_t *da, char const *name, fr_value_box_t const *value, bool coerce, bool replace)
Add a value name.
Definition: dict_util.c:1941
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition: dict_util.c:4597
#define fr_dict_autofree(_to_free)
Definition: dict.h:850
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:2400
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition: dict_util.c:4090
@ FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC
Protocol specific extensions.
Definition: dict.h:170
#define fr_dict_autoload(_to_load)
Definition: dict.h:847
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition: dict_util.c:3328
char const * name
name of this protocol
Definition: dict.h:428
#define FR_DICT_ATTR_FLAG_FUNC(_struct, _name)
Define a flag setting function, which sets one bit in a fr_dict_attr_flags_t.
Definition: dict.h:406
static int8_t fr_dict_attr_cmp(fr_dict_attr_t const *a, fr_dict_attr_t const *b)
Definition: dict.h:603
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
Protocol specific custom flag definitnion.
Definition: dict.h:396
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition: dict.h:427
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition: dict_ext.h:140
Test enumeration values.
Definition: dict_test.h:92
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
Head of a doubly linked list.
Definition: dlist.h:51
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
Definition: merged_model.c:93
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
@ FR_TYPE_UINT16
16 Bit unsigned integer.
Definition: merged_model.c:98
@ FR_TYPE_UINT8
8 Bit unsigned integer.
Definition: merged_model.c:97
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
Definition: merged_model.c:87
@ FR_TYPE_BOOL
A truth value.
Definition: merged_model.c:95
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
unsigned int uint32_t
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
fr_dict_attr_t const * attr_dhcp_opcode
Definition: base.c:57
fr_dict_autoload_t dhcpv4_dict[]
Definition: base.c:43
#define DHCP_MAX_MESSAGE_TYPE
Definition: base.c:145
fr_dict_attr_t const * attr_dhcp_gateway_ip_address
Definition: base.c:52
fr_dict_protocol_t libfreeradius_dhcpv4_dict_protocol
Definition: base.c:754
char const * dhcp_message_types[]
Definition: base.c:126
fr_dict_attr_t const * attr_dhcp_server_ip_address
Definition: base.c:59
uint8_t eth_bcast[ETH_ADDR_LEN]
Definition: base.c:164
fr_dict_attr_t const * attr_dhcp_dhcp_maximum_msg_size
Definition: base.c:62
fr_dict_attr_t const * attr_dhcp_relay_link_selection
Definition: base.c:68
fr_dict_attr_t const * attr_dhcp_vendor_class_identifier
Definition: base.c:67
bool fr_dhcpv4_ok(uint8_t const *data, ssize_t data_len, uint8_t *message_type, uint32_t *xid)
Check received DHCP request is valid and build fr_packet_t structure if it is.
Definition: base.c:240
size_t dhcp_header_attrs_len
Definition: base.c:124
fr_dict_attr_t const * attr_dhcp_hop_count
Definition: base.c:55
fr_dict_attr_t const * attr_dhcp_parameter_request_list
Definition: base.c:65
int fr_dhcpv4_global_init(void)
Resolve/cache attributes in the DHCP dictionary.
Definition: base.c:584
fr_dict_attr_autoload_t dhcpv4_dict_attr[]
Definition: base.c:74
static int dict_flag_prefix(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
Definition: base.c:169
static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
Definition: base.c:655
fr_dict_attr_t const * attr_dhcp_server_host_name
Definition: base.c:58
fr_dict_attr_t const * attr_dhcp_hardware_address_length
Definition: base.c:53
uint8_t length
Definition: base.c:37
fr_dict_attr_t const * attr_dhcp_transaction_id
Definition: base.c:60
static uint32_t instance_count
Definition: base.c:33
fr_dict_attr_t const * attr_dhcp_message_type
Definition: base.c:64
uint8_t code
Definition: base.c:36
int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
Definition: base.c:196
static bool attr_valid(fr_dict_attr_t *da)
Definition: base.c:708
fr_dict_attr_t const * attr_dhcp_boot_filename
Definition: base.c:48
fr_dict_attr_t const * attr_dhcp_overload
Definition: base.c:66
static fr_dict_flag_parser_t const dhcpv4_flags[]
Definition: base.c:190
fr_dict_attr_t const * attr_dhcp_subnet_selection_option
Definition: base.c:69
ssize_t fr_dhcpv4_encode_dbuff(fr_dbuff_t *dbuff, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
Definition: base.c:341
fr_dict_t const * dict_dhcpv4
Definition: base.c:40
fr_dict_attr_t const * attr_dhcp_network_subnet
Definition: base.c:70
fr_dict_attr_t const * attr_dhcp_option_82
Definition: base.c:71
fr_dict_attr_t const * attr_dhcp_flags
Definition: base.c:51
void fr_dhcpv4_global_free(void)
Definition: base.c:628
fr_dict_attr_t const * attr_dhcp_hardware_type
Definition: base.c:54
fr_dict_attr_t const * attr_dhcp_your_ip_address
Definition: base.c:61
void * fr_dhcpv4_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
DHCPV4-specific iterator.
Definition: base.c:319
fr_dict_attr_t const * attr_dhcp_client_ip_address
Definition: base.c:50
fr_dict_attr_t const * attr_dhcp_interface_mtu_size
Definition: base.c:63
fr_dict_attr_t const * attr_dhcp_client_hardware_address
Definition: base.c:49
int dhcp_header_sizes[]
Definition: base.c:147
fr_dict_attr_t const * attr_dhcp_number_of_seconds
Definition: base.c:56
ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
Definition: base.c:336
bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
Evaluation function for DCHPV4-encodability.
Definition: base.c:308
fr_dict_attr_t const ** dhcp_header_attrs[]
Definition: base.c:108
static char const * short_header_names[]
Definition: base.c:638
void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
Print a raw DHCP packet as hex.
Definition: base.c:672
ssize_t fr_dhcpv4_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len)
Definition: decode.c:648
static char const tabs[]
Definition: base.c:800
VQP attributes.
fr_assert(0)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition: table.h:653
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:49
#define fr_pair_dcursor_iter_init(_cursor, _list, _iter, _uctx)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:569
#define PAIR_VERIFY(_x)
Definition: pair.h:191
void fr_pair_list_sort(fr_pair_list_t *list, fr_cmp_t cmp)
Sort a doubly linked list of fr_pair_ts using merge sort.
Definition: pair_inline.c:140
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_const(_msg)
Definition: strerror.h:223
return fr_dbuff_set(dbuff, &our_dbuff)
#define fr_box_uint8(_val)
Definition: value.h:310
static fr_slen_t data
Definition: value.h:1265