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: 76c832f04ee6fffbc085bbf3850ea4adb4aafb11 $
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: 76c832f04ee6fffbc085bbf3850ea4adb4aafb11 $")
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 
71 
74  { .out = &attr_dhcp_boot_filename, .name = "Boot-Filename", .type = FR_TYPE_STRING, .dict = &dict_dhcpv4 },
75  { .out = &attr_dhcp_client_hardware_address, .name = "Client-Hardware-Address", .type = FR_TYPE_ETHERNET, .dict = &dict_dhcpv4 },
76  { .out = &attr_dhcp_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
77  { .out = &attr_dhcp_flags, .name = "Flags", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
78  { .out = &attr_dhcp_gateway_ip_address, .name = "Gateway-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
79  { .out = &attr_dhcp_hardware_address_length, .name = "Hardware-Address-Length", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
80  { .out = &attr_dhcp_hardware_type, .name = "Hardware-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
81  { .out = &attr_dhcp_hop_count, .name = "Hop-Count", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
82  { .out = &attr_dhcp_number_of_seconds, .name = "Number-of-Seconds", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
83  { .out = &attr_dhcp_opcode, .name = "Opcode", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
84  { .out = &attr_dhcp_server_host_name, .name = "Server-Host-Name", .type = FR_TYPE_STRING, .dict = &dict_dhcpv4 },
85  { .out = &attr_dhcp_server_ip_address, .name = "Server-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
86  { .out = &attr_dhcp_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
87  { .out = &attr_dhcp_your_ip_address, .name = "Your-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
88  { .out = &attr_dhcp_dhcp_maximum_msg_size, .name = "Maximum-Msg-Size", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
89  { .out = &attr_dhcp_interface_mtu_size, .name = "Interface-MTU-Size", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
90  { .out = &attr_dhcp_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
91  { .out = &attr_dhcp_parameter_request_list, .name = "Parameter-Request-List", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
92  { .out = &attr_dhcp_overload, .name = "Overload", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
93  { .out = &attr_dhcp_vendor_class_identifier, .name = "Vendor-Class-Identifier", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv4 },
94  { .out = &attr_dhcp_relay_link_selection, .name = "Relay-Agent-Information.Relay-Link-Selection", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
95  { .out = &attr_dhcp_subnet_selection_option, .name = "Subnet-Selection-Option", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
96  { .out = &attr_dhcp_network_subnet, .name = "Network-Subnet", .type = FR_TYPE_IPV4_PREFIX, .dict = &dict_dhcpv4 },
97  { NULL }
98 };
99 
100 /*
101  * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 DISCOVER
102  * INADDR_BROADCAST : 68 <- SERVER_IP : 67 OFFER
103  * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 REQUEST
104  * INADDR_BROADCAST : 68 <- SERVER_IP : 67 ACK
105  */
121 };
123 
124 char const *dhcp_message_types[] = {
125  "invalid",
126  "Discover",
127  "Offer",
128  "Request",
129  "Decline",
130  "Ack",
131  "NAK",
132  "Release",
133  "Inform",
134  "Force-Renew",
135  "Lease-Query",
136  "Lease-Unassigned",
137  "Lease-Unknown",
138  "Lease-Active",
139  "Bulk-Lease-Query",
140  "Lease-Query-Done"
141 };
142 
143 #define DHCP_MAX_MESSAGE_TYPE (NUM_ELEMENTS(dhcp_message_types))
144 
146  1, /* op */
147  1, /* htype */
148  1, /* hlen */
149  1, /* hops */
150  4, /* xid */
151  2, /* secs */
152  2, /* flags */
153  4, /* ciaddr */
154  4, /* yiaddr */
155  4, /* siaddr */
156  4, /* giaddr */
157  DHCP_CHADDR_LEN, /* chaddr */
158  DHCP_SNAME_LEN, /* sname */
159  DHCP_FILE_LEN /* file */
160 };
161 
162 uint8_t eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
163 
165 
166 int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
167 {
168  fr_pair_t const *my_a = a, *my_b = b;
169  fr_dict_attr_t const *a_82, *b_82;
170 
171  PAIR_VERIFY(my_a);
172  PAIR_VERIFY(my_b);
173 
174  /*
175  * We can only use attribute numbers if we know they're
176  * not nested attributes.
177  *
178  * @fixme We should be able to use my_a->da->parent->flags.is_root,
179  * but the DHCP attributes are hacked into the server under a vendor
180  * dictionary, so we can't.
181  */
182 
183  /*
184  * Message-Type is first, for simplicity.
185  */
186  if (((my_a->da->parent->type != FR_TYPE_TLV) && (my_a->da == attr_dhcp_message_type)) &&
187  ((my_b->da->parent->type == FR_TYPE_TLV) || (my_b->da != attr_dhcp_message_type))) return -1;
188  if (((my_a->da->parent->type == FR_TYPE_TLV) || (my_a->da != attr_dhcp_message_type)) &&
189  ((my_b->da->parent->type != FR_TYPE_TLV) && (my_b->da == attr_dhcp_message_type))) return +1;
190 
191  /*
192  * Relay-Agent is last.
193  *
194  * Check if either of the options are descended from option 82.
195  */
196  a_82 = fr_dict_attr_common_parent(dhcp_option_82, my_a->da, true);
197  b_82 = fr_dict_attr_common_parent(dhcp_option_82, my_b->da, true);
198  if (a_82 && !b_82) return +1;
199  if (!a_82 && !b_82) return -1;
200 
201  return fr_pair_cmp_by_parent_num(my_a, my_b);
202 }
203 
204 /** Check received DHCP request is valid and build fr_packet_t structure if it is
205  *
206  * @param data pointer to received packet.
207  * @param data_len length of received data, and then length of the actual DHCP data.
208  * @param[out] message_type where the message type will be stored (if used)
209  * @param[out] xid where the xid will be stored (if used)
210  *
211  * @return
212  * - true if the packet is well-formed
213  * - false if it's a bad packet
214  */
215 bool fr_dhcpv4_ok(uint8_t const *data, ssize_t data_len, uint8_t *message_type, uint32_t *xid)
216 {
217  uint32_t magic;
218  uint8_t const *code;
219  size_t hlen;
220 
221  if (data_len < MIN_PACKET_SIZE) {
222  fr_strerror_printf("DHCP packet is too small (%zu < %d)", data_len, MIN_PACKET_SIZE);
223  return false;
224  }
225 
226  if (data_len > MAX_PACKET_SIZE) {
227  fr_strerror_printf("DHCP packet is too large (%zx > %d)", data_len, MAX_PACKET_SIZE);
228  return false;
229  }
230 
231  if (data[1] > 1) {
232  fr_strerror_printf("DHCP can only process ethernet requests, not type %02x", data[1]);
233  return false;
234  }
235 
236  hlen = data[2];
237  if ((hlen != 0) && (hlen != 6)) {
238  fr_strerror_printf("Ethernet HW length incorrect. Expected 6 got %zu", hlen);
239  return false;
240  }
241 
242  memcpy(&magic, data + 236, 4);
243  magic = ntohl(magic);
244  if (magic != DHCP_OPTION_MAGIC_NUMBER) {
245  fr_strerror_const("BOOTP not supported");
246  return false;
247  }
248 
250  if (!code || (code[1] == 0)) {
251  fr_strerror_const("No message-type option was found in the packet");
252  return false;
253  }
254 
255  if ((code[2] == 0) || (code[2] >= DHCP_MAX_MESSAGE_TYPE)) {
256  fr_strerror_printf("Unknown value %d for message-type option", code[2]);
257  return false;
258  }
259 
260  /*
261  * @todo - data_len MAY be larger than the data in the
262  * packet. In which case, we should update data_len with
263  * the true size of the packet.
264  */
265 
266  if (message_type) *message_type = code[2];
267 
268  if (xid) {
269  memcpy(&magic, data + 4, 4);
270  *xid = ntohl(magic);
271  }
272 
273  return true;
274 }
275 
276 /** Evaluation function for DCHPV4-encodability
277  *
278  * @param item pointer to a fr_pair_t
279  * @param uctx context
280  *
281  * @return true if the underlying fr_pair_t is DHCPv4 encodable, false otherwise
282  */
283 bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
284 {
285  fr_pair_t const *vp = item;
286 
287  PAIR_VERIFY(vp);
288  return (vp->da->dict == dict_dhcpv4) && (!vp->da->flags.internal);
289 }
290 
291 /** DHCPV4-specific iterator
292  *
293  */
294 void *fr_dhcpv4_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
295 {
296  fr_pair_t *c = current;
297  fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
298 
299  while ((c = fr_dlist_next(list, c))) {
300  PAIR_VERIFY(c);
301  if (c->da->dict != dict || c->da->flags.internal) continue;
302 
303  if (c->vp_type == FR_TYPE_BOOL && da_is_bool_exists(c->da) && !c->vp_bool) continue;
304 
305  break;
306  }
307 
308  return c;
309 }
310 
311 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)
312 {
313  return fr_dhcpv4_encode_dbuff(&FR_DBUFF_TMP(buffer, buflen), original, code, xid, vps);
314 }
315 
317 {
318  fr_dcursor_t cursor;
319  fr_pair_t *vp;
320  ssize_t len;
321  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
322 
323  /*
324  * @todo: Make this work again.
325  */
326 #if 0
327  mms = DEFAULT_PACKET_SIZE; /* maximum message size */
328 
329  /*
330  * Clients can request a LARGER size, but not a
331  * smaller one. They also cannot request a size
332  * larger than MTU.
333  */
334 
335  /* Maximum-Msg-Size */
337  if (vp && (vp->vp_uint32 > mms)) {
338  mms = vp->vp_uint32;
339 
340  if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE;
341  }
342 #endif
343 
344  vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_opcode);
345  if (vp) {
346  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
347  } else {
348  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x01); /* client message */
349  }
350 
351  /* Hardware-Type */
353  if (vp) {
354  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
355 
356  } else if (original) {
357  FR_DBUFF_IN_RETURN(&work_dbuff, original->htype);
358 
359  } else { /* we are ALWAYS ethernet */
360  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x01);
361  }
362 
363  /* Hardware-Address-len */
365  if (vp) {
366  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
367 
368  } else if (original) {
369  FR_DBUFF_IN_RETURN(&work_dbuff, original->hlen);
370 
371  } else { /* we are ALWAYS ethernet */
372  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x06);
373  }
374 
375  /* Hop-Count */
377  if (vp) {
378  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
379 
380  } else if (original) {
381  FR_DBUFF_IN_RETURN(&work_dbuff, original->hops);
382 
383  } else {
384  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x00);
385  }
386 
387  /* Transaction-Id */
388  FR_DBUFF_IN_RETURN(&work_dbuff, xid);
389 
390  /* Number-of-Seconds */
392  if (vp) {
393  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint16);
394  } else {
395  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_uint16));
396  }
397 
398  /* Flags */
399  vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_flags);
400  if (vp) {
401  FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint16);
402  } else {
403  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_uint16));
404  }
405 
406  /* Client-IP-Address */
408  if (vp) {
409  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
410  } else {
411  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_ipv4addr));
412  }
413 
414  /* Your-IP-address */
416  if (vp) {
417  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
418  } else {
419  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
420  }
421 
422  /* Server-IP-Address */
424  if (vp) {
425  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
426  } else {
427  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
428  }
429 
430  /*
431  * Gateway-IP-Address
432  */
434  if (vp) {
435  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
436 
437  } else if (original) { /* copy whatever value was in the original */
438  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&original->giaddr, sizeof(original->giaddr));
439 
440  } else {
441  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
442  }
443 
444  /* Client-Hardware-Address */
446  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)vp->vp_ether, sizeof(vp->vp_ether));
447  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_CHADDR_LEN - sizeof(vp->vp_ether));
448 
449  } else if (original) { /* copy whatever value was in the original */
450  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, &original->chaddr[0], sizeof(original->chaddr));
451 
452  } else {
453  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_CHADDR_LEN);
454  }
455 
456  /* Server-Host-Name */
457  if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_server_host_name))) {
458  if (vp->vp_length > DHCP_SNAME_LEN) {
459  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, DHCP_SNAME_LEN);
460  } else {
461  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
462  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_SNAME_LEN - vp->vp_length);
463  }
464  } else {
465  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_SNAME_LEN);
466  }
467 
468  /*
469  * Copy over Boot-Filename.
470  *
471  * FIXME: This copy should be delayed until AFTER the options
472  * have been processed. If there are too many options for
473  * the packet, then they go into the sname && filename fields.
474  * When that happens, the boot filename is passed as an option,
475  * instead of being placed verbatim in the filename field.
476  */
477 
478  /* Boot-Filename */
479  if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_boot_filename))) {
480  if (vp->vp_length > DHCP_FILE_LEN) {
481  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, DHCP_FILE_LEN);
482  } else {
483  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
484  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_FILE_LEN - vp->vp_length);
485  }
486  } else {
487  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_FILE_LEN);
488  }
489 
490  /* DHCP magic number */
492 
493  if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_message_type))) {
494  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_TYPE, 0x01, vp->vp_uint8);
495  } else {
496  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_TYPE, 0x01, (uint8_t)code);
497  }
498 
499  /*
500  * Pre-sort attributes into contiguous blocks so that fr_dhcpv4_encode_option
501  * operates correctly. This changes the order of the list, but never mind...
502  */
505 
506  /*
507  * Each call to fr_dhcpv4_encode_option will encode one complete DHCP option,
508  * and sub options.
509  */
510  while ((vp = fr_dcursor_current(&cursor))) {
511  /*
512  * The encoder skips message type, and returns
513  * "len==0" for it. We want to allow that, BUT
514  * stop when the encoder returns "len==0" for
515  * other attributes. So we need to skip it
516  * manually, too.
517  */
518  if (vp->da == attr_dhcp_message_type) {
519  (void) fr_dcursor_next(&cursor);
520  continue;
521  }
522 
523  len = fr_dhcpv4_encode_option(&work_dbuff,
524  &cursor, &(fr_dhcpv4_ctx_t){ .root = fr_dict_root(dict_dhcpv4) });
525  if (len <= 0) break;
526  }
527 
528  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_END_OF_OPTIONS, 0x00);
529 
530  /*
531  * FIXME: if (fr_dbuff_used(&work_dbuff) > mms),
532  * then we put the extra options into the "sname" and "file"
533  * fields, AND set the "end option option" in the "options"
534  * field. We also set the "overload option",
535  * and put options into the "file" field, followed by
536  * the "sname" field. Where each option is completely
537  * enclosed in the "file" and/or "sname" field, AND
538  * followed by the "end of option", and MUST be followed
539  * by padding option.
540  *
541  * Yuck. That sucks...
542  */
543  if (fr_dbuff_used(&work_dbuff) < DEFAULT_PACKET_SIZE) {
544  FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DEFAULT_PACKET_SIZE - fr_dbuff_used(&work_dbuff));
545  }
546 
547  return fr_dbuff_set(dbuff, fr_dbuff_used(&work_dbuff));
548 }
549 
550 
551 /** Resolve/cache attributes in the DHCP dictionary
552  *
553  * @return
554  * - 0 on success.
555  * - -1 on failure.
556  */
558 {
560  uint8_t i;
561 
562  if (instance_count > 0) {
563  instance_count++;
564  return 0;
565  }
566 
567  instance_count++;
568 
569  if (fr_dict_autoload(dhcpv4_dict) < 0) {
570  fail:
571  instance_count--;
572  return -1;
573  }
574 
577  goto fail;
578  }
579 
580  /*
581  * Fixup dictionary entry for Paramter-Request-List adding all the options
582  */
583  for (i = 1; i < 255; i++) {
584  fr_dict_attr_t const *attr;
585 
587  if (!attr) {
588  continue;
589  }
590  value.vb_uint8 = i;
591 
593  attr->name, &value, true, false) < 0) {
594  goto fail;
595  }
596  }
597 
598  return 0;
599 }
600 
602 {
604 
605  if (--instance_count > 0) return;
606 
608 }
609 
610 
611 static char const *short_header_names[] = {
612  "opcode",
613  "hwtype",
614  "hwaddrlen",
615  "hop_count",
616  "xid",
617  "seconds",
618  "flags",
619  "ciaddr",
620  "yiaddr",
621  "siaddr",
622  "giaddr",
623  "chaddr",
624  "server_hostname",
625  "boot_filename",
626 };
627 
628 static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
629 {
630  int i;
631  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";
632 
633  for (i = 0; i < attrlen; i++) {
634  if ((i > 0) && ((i & 0x0f) == 0x00))
635  fprintf(fp, "%.*s", depth, tabs);
636  fprintf(fp, "%02x ", ptr[i]);
637  if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
638  }
639  if ((i & 0x0f) != 0) fprintf(fp, "\n");
640 }
641 
642 /** Print a raw DHCP packet as hex.
643  *
644  */
645 void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
646 {
647  int i;
648  uint8_t const *attr, *end;
649 
650  end = packet + packet_len;
651  attr = packet;
652 
653  for (i = 0; i < 14; i++) {
654  fprintf(fp, "\t%s: ", short_header_names[i]);
655  print_hex_data(fp, attr, dhcp_header_sizes[i], 2);
656  attr += dhcp_header_sizes[i];
657  }
658 
659  fprintf(fp, "\tmagic:\t%02x %02x %02x %02x\n", attr[0], attr[1], attr[2], attr[3]);
660  attr += 4;
661 
662  fprintf(fp, "\toptions\n");
663  while (attr < end) {
664  fprintf(fp, "\t\t");
665 
666  fprintf(fp, "%02x %02x ", attr[0], attr[1]);
667 
668  print_hex_data(fp, attr + 2, attr[1], 3);
669 
670  /*
671  * "End of option" option.
672  */
673  if (attr[0] == 255) break;
674 
675  attr += attr[1] + 2;
676  }
677 
678  fprintf(fp, "\n");
679 }
680 
682  { L("dns_label"), FLAG_ENCODE_DNS_LABEL },
683  { L("encode=dns_label"), FLAG_ENCODE_DNS_LABEL },
684  { L("prefix=split"), FLAG_ENCODE_SPLIT_PREFIX },
685  { L("prefix=bits"), FLAG_ENCODE_BITS_PREFIX },
686  { L("encode=exists"), FLAG_ENCODE_BOOL_EXISTS },
687 };
688 
690  UNUSED char const *name, UNUSED int attr, fr_type_t type, fr_dict_attr_flags_t *flags)
691 {
692  /*
693  * "arrays" of string/octets are encoded as a 8-bit
694  * length, followed by the actual data.
695  */
696  if (flags->array && ((type == FR_TYPE_STRING) || (type == FR_TYPE_OCTETS))) {
697  flags->is_known_width = true;
698 
699  if (flags->extra && (flags->subtype != FLAG_LENGTH_UINT8)) {
700  fr_strerror_const("string/octets arrays require the 'length=uint8' flag");
701  return false;
702  }
703  }
704 
705  if (flags->extra && (flags->subtype == FLAG_LENGTH_UINT16)) {
706  fr_strerror_const("The 'length=uint16' flag cannot be used for DHCPv4");
707  return false;
708  }
709 
710  /*
711  * "extra" signifies that subtype is being used by the
712  * dictionaries itself.
713  */
714  if (flags->extra || !flags->subtype) return true;
715 
716  if ((type != FR_TYPE_STRING) && (flags->subtype == FLAG_ENCODE_DNS_LABEL)) {
717  fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
718  return false;
719  }
720 
721  if ((type != FR_TYPE_IPV4_PREFIX) &&
722  ((flags->subtype == FLAG_ENCODE_SPLIT_PREFIX) || (flags->subtype == FLAG_ENCODE_BITS_PREFIX))) {
723  fr_strerror_const("The 'prefix=...' flag can only be used with attributes of type 'ipv4prefix'");
724  return false;
725  }
726 
727  if ((type != FR_TYPE_BOOL) && (flags->subtype == FLAG_ENCODE_BOOL_EXISTS)) {
728  fr_strerror_const("The 'encode=exists' flag can only be used with attributes of type 'bool'");
729  return false;
730  }
731 
732  return true;
733 }
734 
737  .name = "dhcpv4",
738  .default_type_size = 1,
739  .default_type_length = 1,
740  .subtype_table = subtype_table,
741  .subtype_table_len = NUM_ELEMENTS(subtype_table),
742  .attr_valid = attr_valid,
743 
744  .init = fr_dhcpv4_global_init,
745  .free = fr_dhcpv4_global_free,
746 
747  .encode = fr_dhcpv4_encode_foreign,
748  .decode = fr_dhcpv4_decode_foreign,
749 };
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:444
#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:762
#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:1503
#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:1377
#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:1580
#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:1467
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
#define DEFAULT_PACKET_SIZE
Definition: dhcpv4.h:105
@ FLAG_ENCODE_SPLIT_PREFIX
encode IPv4 prefixes as Policy-Filter, split into IP/mask
Definition: dhcpv4.h:72
@ FLAG_ENCODE_BOOL_EXISTS
bool as existence checks
Definition: dhcpv4.h:74
@ FLAG_ENCODE_DNS_LABEL
encode as DNS label
Definition: dhcpv4.h:71
@ FLAG_ENCODE_BITS_PREFIX
encode IPv4 prefixes as prefix bits, followed by IP.
Definition: dhcpv4.h:73
#define da_is_bool_exists(_da)
Definition: dhcpv4.h:80
#define DHCP_CHADDR_LEN
Definition: dhcpv4.h:37
#define MIN_PACKET_SIZE
Definition: dhcpv4.h:104
ssize_t fr_dhcpv4_encode_foreign(fr_dbuff_t *dbuff, fr_pair_list_t const *list)
Definition: encode.c:759
#define DHCP_SNAME_LEN
Definition: dhcpv4.h:38
#define DHCP_FILE_LEN
Definition: dhcpv4.h:39
uint32_t giaddr
Definition: dhcpv4.h:93
#define ETH_ADDR_LEN
Definition: dhcpv4.h:120
#define MAX_PACKET_SIZE
Definition: dhcpv4.h:106
uint8_t htype
Definition: dhcpv4.h:84
#define DHCP_OPTION_MAGIC_NUMBER
Definition: dhcpv4.h:41
uint8_t hlen
Definition: dhcpv4.h:85
uint8_t hops
Definition: dhcpv4.h:86
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
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:711
uint8_t chaddr[DHCP_CHADDR_LEN]
Definition: dhcpv4.h:94
Used as the decoder ctx.
Definition: dhcpv4.h:152
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:1535
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition: dict_util.c:4191
#define fr_dict_autofree(_to_free)
Definition: dict.h:674
@ FLAG_LENGTH_UINT8
string / octets type is prefixed by uint8 of length
Definition: dict.h:145
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition: dict.h:146
fr_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition: dict_util.c:1631
unsigned int array
Pack multiples into 1 attr.
Definition: dict.h:88
unsigned int extra
really "subtype is used by dict, not by protocol"
Definition: dict.h:109
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
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:3647
#define fr_dict_autoload(_to_load)
Definition: dict.h:671
unsigned int is_known_width
is treated as if it has a known width for structs
Definition: dict.h:90
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:2925
char const * name
name of this protocol
Definition: dict.h:342
uint8_t subtype
protocol-specific values, OR key fields
Definition: dict.h:118
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Values of the encryption flags.
Definition: merged_model.c:139
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition: dict.h:341
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
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition: lst.c:122
fr_type_t
Definition: merged_model.c:80
@ 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:688
int8_t fr_pair_cmp_by_parent_num(void const *a, void const *b)
Order attributes by their parent(s), attribute number, and tag.
Definition: pair.c:1918
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:143
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:736
char const * dhcp_message_types[]
Definition: base.c:124
fr_dict_attr_t const * attr_dhcp_server_ip_address
Definition: base.c:59
uint8_t eth_bcast[ETH_ADDR_LEN]
Definition: base.c:162
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:215
size_t dhcp_header_attrs_len
Definition: base.c:122
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:557
fr_dict_attr_autoload_t dhcpv4_dict_attr[]
Definition: base.c:73
static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
Definition: base.c:628
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
fr_dict_attr_t const * dhcp_option_82
Definition: base.c:164
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:166
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
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:316
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_flags
Definition: base.c:51
void fr_dhcpv4_global_free(void)
Definition: base.c:601
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
static bool attr_valid(UNUSED fr_dict_t *dict, UNUSED fr_dict_attr_t const *parent, UNUSED char const *name, UNUSED int attr, fr_type_t type, fr_dict_attr_flags_t *flags)
Definition: base.c:689
void * fr_dhcpv4_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
DHCPV4-specific iterator.
Definition: base.c:294
fr_dict_attr_t const * attr_dhcp_client_ip_address
Definition: base.c:50
static fr_table_num_ordered_t const subtype_table[]
Definition: base.c:681
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:145
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:311
bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
Evaluation function for DCHPV4-encodability.
Definition: base.c:283
fr_dict_attr_t const ** dhcp_header_attrs[]
Definition: base.c:106
static char const * short_header_names[]
Definition: base.c:611
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:645
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:640
static char const tabs[]
Definition: base.c:797
VQP attributes.
static rc_request_t * current
Definition: radclient-ng.c:97
static char const * name
fr_assert(0)
fr_aka_sim_id_type_t type
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
An element in an arbitrarily ordered array of name to num mappings.
Definition: table.h:53
#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:568
#define PAIR_VERIFY(_x)
Definition: pair.h:190
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
static fr_slen_t parent
Definition: pair.h:844
#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:303
static fr_slen_t data
Definition: value.h:1259