The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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 */
26RCSID("$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
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 */
125
126char 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
164uint8_t eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
165
168
169static 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
196int8_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 */
240bool 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 */
308bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
309{
310 fr_pair_t const *vp = item;
311
313 return (vp->da->dict == dict_dhcpv4) && (!vp->da->flags.internal);
314}
315
316/** DHCPV4-specific iterator
317 *
318 */
319void *fr_dhcpv4_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
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
336ssize_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
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 */
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 {
481 }
482
483 /* 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) {
591 return 0;
592 }
593
595
596 if (fr_dict_autoload(dhcpv4_dict) < 0) {
597 fail:
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
638static 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
655static 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 */
672void 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
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:576
#define RCSID(id)
Definition build.h:483
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
static const char * tabs
Definition command.c:1581
#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_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
Definition dbuff.h:1004
#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
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
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
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
#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
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:149
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition dict.h:150
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
#define fr_dict_autofree(_to_free)
Definition dict.h:853
fr_dict_attr_t * fr_dict_attr_unconst(fr_dict_attr_t const *da)
Coerce to non-const.
Definition dict_util.c:4597
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
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
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:171
#define fr_dict_autoload(_to_load)
Definition dict.h:850
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:430
#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:408
static int8_t fr_dict_attr_cmp(fr_dict_attr_t const *a, fr_dict_attr_t const *b)
Definition dict.h:606
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
Protocol specific custom flag definitnion.
Definition dict.h:398
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:429
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
static uint32_t instance_count
Definition base.c:44
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:122
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
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
static fr_dict_t const * dict_dhcpv4
Definition base.c:34
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
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
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
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_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_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
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 bool attr_valid(fr_dict_attr_t *da)
Definition base.c:443
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
static rc_request_t * current
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.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_box_uint8(_val)
Definition value.h:310
static fr_slen_t data
Definition value.h:1265