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: b0c2350abb4eb3066f71b767b06eb74048419325 $
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: b0c2350abb4eb3066f71b767b06eb74048419325 $")
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
34static bool instantiated = false;
35
40
42
45 { .out = &dict_dhcpv4, .proto = "dhcpv4" },
46
48};
49
74
77 { .out = &attr_dhcp_boot_filename, .name = "Boot-Filename", .type = FR_TYPE_STRING, .dict = &dict_dhcpv4 },
78 { .out = &attr_dhcp_client_hardware_address, .name = "Client-Hardware-Address", .type = FR_TYPE_ETHERNET, .dict = &dict_dhcpv4 },
79 { .out = &attr_dhcp_client_ip_address, .name = "Client-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
80 { .out = &attr_dhcp_flags, .name = "Flags", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
81 { .out = &attr_dhcp_gateway_ip_address, .name = "Gateway-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
82 { .out = &attr_dhcp_hardware_address_length, .name = "Hardware-Address-Length", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
83 { .out = &attr_dhcp_hardware_type, .name = "Hardware-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
84 { .out = &attr_dhcp_hop_count, .name = "Hop-Count", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
85 { .out = &attr_dhcp_number_of_seconds, .name = "Number-of-Seconds", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
86 { .out = &attr_dhcp_opcode, .name = "Opcode", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
87 { .out = &attr_dhcp_server_host_name, .name = "Server-Host-Name", .type = FR_TYPE_STRING, .dict = &dict_dhcpv4 },
88 { .out = &attr_dhcp_server_ip_address, .name = "Server-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
89 { .out = &attr_dhcp_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4 },
90 { .out = &attr_dhcp_your_ip_address, .name = "Your-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
91 { .out = &attr_dhcp_dhcp_maximum_msg_size, .name = "Maximum-Msg-Size", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
92 { .out = &attr_dhcp_interface_mtu_size, .name = "Interface-MTU-Size", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv4 },
93 { .out = &attr_dhcp_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
94 { .out = &attr_dhcp_parameter_request_list, .name = "Parameter-Request-List", .type = FR_TYPE_ATTR, .dict = &dict_dhcpv4 },
95 { .out = &attr_dhcp_overload, .name = "Overload", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4 },
96 { .out = &attr_dhcp_vendor_class_identifier, .name = "Vendor-Class-Identifier", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv4 },
97 { .out = &attr_dhcp_relay_link_selection, .name = "Relay-Agent-Information.Relay-Link-Selection", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
98 { .out = &attr_dhcp_subnet_selection_option, .name = "Subnet-Selection-Option", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4 },
99 { .out = &attr_dhcp_network_subnet, .name = "Network-Subnet", .type = FR_TYPE_IPV4_PREFIX, .dict = &dict_dhcpv4 },
100 { .out = &attr_dhcp_option_82, .name = "Relay-Agent-Information", .type = FR_TYPE_TLV, .dict = &dict_dhcpv4 },
101
103};
104
105/*
106 * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 DISCOVER
107 * INADDR_BROADCAST : 68 <- SERVER_IP : 67 OFFER
108 * INADDR_ANY : 68 -> INADDR_BROADCAST : 67 REQUEST
109 * INADDR_BROADCAST : 68 <- SERVER_IP : 67 ACK
110 */
128
129char const *dhcp_message_types[] = {
130 "invalid",
131 "Discover",
132 "Offer",
133 "Request",
134 "Decline",
135 "Ack",
136 "NAK",
137 "Release",
138 "Inform",
139 "Force-Renew",
140 "Lease-Query",
141 "Lease-Unassigned",
142 "Lease-Unknown",
143 "Lease-Active",
144 "Bulk-Lease-Query",
145 "Lease-Query-Done"
146};
147
148#define DHCP_MAX_MESSAGE_TYPE (NUM_ELEMENTS(dhcp_message_types))
149
151 1, /* op */
152 1, /* htype */
153 1, /* hlen */
154 1, /* hops */
155 4, /* xid */
156 2, /* secs */
157 2, /* flags */
158 4, /* ciaddr */
159 4, /* yiaddr */
160 4, /* siaddr */
161 4, /* giaddr */
162 DHCP_CHADDR_LEN, /* chaddr */
163 DHCP_SNAME_LEN, /* sname */
164 DHCP_FILE_LEN /* file */
165};
166
167uint8_t eth_bcast[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
168
171
172static int dict_flag_prefix(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
173{
174 static fr_table_num_sorted_t const table[] = {
175 { L("bits"), DHCPV4_FLAG_PREFIX_BITS },
176 { L("split"), DHCPV4_FLAG_PREFIX_SPLIT }
177 };
178 static size_t table_len = NUM_ELEMENTS(table);
179
182
184 if (flag == DHCPV4_FLAG_PREFIX_INVALID) {
185 fr_strerror_printf("Unknown prefix type '%s'", value);
186 return -1;
187 }
188 flags->prefix = flag;
189
190 return 0;
191}
192
194 { L("dns_label"), { .func = dict_flag_dns_label } },
195 { L("exists"), { .func = dict_flag_exists } },
196 { L("prefix"), { .func = dict_flag_prefix } }
197};
198
199/*
200 * @todo - arguably we don't want to mutate the input list.
201 * Instead, the encoder should just do 3 passes, where middle one
202 * ignores the message-type and option 82.
203 */
204int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
205{
206 fr_pair_t const *my_a = a, *my_b = b;
207
208 PAIR_VERIFY(my_a);
209 PAIR_VERIFY(my_b);
210
211 /*
212 * Message-Type is first, for simplicity.
213 */
214 if ((my_a->da == attr_dhcp_message_type) && (my_b->da != attr_dhcp_message_type)) return -1;
215 if ((my_a->da != attr_dhcp_message_type) && (my_b->da == attr_dhcp_message_type)) return +1;
216
217 /*
218 * Relay-Agent is last.
219 *
220 * RFC 3046:
221 * Servers SHOULD copy the Relay Agent Information
222 * option as the last DHCP option in the response.
223 *
224 * Some crazy DHCP relay agents idea of how to strip option 82 in
225 * a reply packet is to simply overwrite the 82 with 255 - the
226 * "Eod of Options" option - causing the client to then ignore
227 * any subsequent options.
228 *
229 * Check if either of the options are option 82
230 */
231 if ((my_a->da == attr_dhcp_option_82) && (my_b->da != attr_dhcp_option_82)) return +1;
232 if ((my_a->da != attr_dhcp_option_82) && (my_b->da == attr_dhcp_option_82)) return -1;
233
234 return fr_dict_attr_cmp(my_a->da, my_b->da);
235}
236
237/** Check received DHCP request is valid and build fr_packet_t structure if it is
238 *
239 * @param data pointer to received packet.
240 * @param data_len length of received data, and then length of the actual DHCP data.
241 * @param[out] message_type where the message type will be stored (if used)
242 * @param[out] xid where the xid will be stored (if used)
243 *
244 * @return
245 * - true if the packet is well-formed
246 * - false if it's a bad packet
247 */
248bool fr_dhcpv4_ok(uint8_t const *data, ssize_t data_len, uint8_t *message_type, uint32_t *xid)
249{
250 uint32_t magic;
251 uint8_t const *code;
252 size_t hlen;
253
254 if (data_len < MIN_PACKET_SIZE) {
255 fr_strerror_printf("DHCP packet is too small (%zu < %d)", data_len, MIN_PACKET_SIZE);
256 return false;
257 }
258
259 if (data_len > MAX_PACKET_SIZE) {
260 fr_strerror_printf("DHCP packet is too large (%zx > %d)", data_len, MAX_PACKET_SIZE);
261 return false;
262 }
263
264 if (data[1] > 1) {
265 fr_strerror_printf("DHCP can only process ethernet requests, not type %02x", data[1]);
266 return false;
267 }
268
269 hlen = data[2];
270 if ((hlen != 0) && (hlen != 6)) {
271 fr_strerror_printf("Ethernet HW length incorrect. Expected 6 got %zu", hlen);
272 return false;
273 }
274
275 memcpy(&magic, data + 236, 4);
276 magic = ntohl(magic);
277 if (magic != DHCP_OPTION_MAGIC_NUMBER) {
278 fr_strerror_const("BOOTP not supported");
279 return false;
280 }
281
283 if (!code || (code[1] == 0)) {
284 fr_strerror_const("No message-type option was found in the packet");
285 return false;
286 }
287
288 if ((code[2] == 0) || (code[2] >= DHCP_MAX_MESSAGE_TYPE)) {
289 fr_strerror_printf("Unknown value %d for message-type option", code[2]);
290 return false;
291 }
292
293 /*
294 * @todo - data_len MAY be larger than the data in the
295 * packet. In which case, we should update data_len with
296 * the true size of the packet.
297 */
298
299 if (message_type) *message_type = code[2];
300
301 if (xid) {
302 memcpy(&magic, data + 4, 4);
303 *xid = ntohl(magic);
304 }
305
306 return true;
307}
308
309/** Evaluation function for DCHPV4-encodability
310 *
311 * @param item pointer to a fr_pair_t
312 * @param uctx context
313 *
314 * @return true if the underlying fr_pair_t is DHCPv4 encodable, false otherwise
315 */
316bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
317{
318 fr_pair_t const *vp = item;
319
321 return (vp->da->dict == dict_dhcpv4) && (!vp->da->flags.internal);
322}
323
324/** DHCPV4-specific iterator
325 *
326 */
327void *fr_dhcpv4_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
328{
329 fr_pair_t *c = current;
330 fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
331
332 while ((c = fr_dlist_next(cursor->dlist, c))) {
333 PAIR_VERIFY(c);
334 if (c->da->dict != dict || c->da->flags.internal) continue;
335
336 if (c->vp_type == FR_TYPE_BOOL && fr_dhcpv4_flag_exists(c->da) && !c->vp_bool) continue;
337
338 break;
339 }
340
341 return c;
342}
343
344ssize_t fr_dhcpv4_encode(uint8_t *buffer, size_t buflen, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
345{
346 return fr_dhcpv4_encode_dbuff(&FR_DBUFF_TMP(buffer, buflen), original, code, xid, vps);
347}
348
350{
351 fr_dcursor_t cursor;
352 fr_pair_t *vp;
353 ssize_t len;
354 fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
355
356 /*
357 * @todo: Make this work again.
358 */
359#if 0
360 mms = DEFAULT_PACKET_SIZE; /* maximum message size */
361
362 /*
363 * Clients can request a LARGER size, but not a
364 * smaller one. They also cannot request a size
365 * larger than MTU.
366 */
367
368 /* Maximum-Msg-Size */
370 if (vp && (vp->vp_uint32 > mms)) {
371 mms = vp->vp_uint32;
372
373 if (mms > MAX_PACKET_SIZE) mms = MAX_PACKET_SIZE;
374 }
375#endif
376
378 if (vp) {
379 FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
380 } else {
381 FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x01); /* client message */
382 }
383
384 /* Hardware-Type */
386 if (vp) {
387 FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
388
389 } else if (original) {
390 FR_DBUFF_IN_RETURN(&work_dbuff, original->htype);
391
392 } else { /* we are ALWAYS ethernet */
393 FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x01);
394 }
395
396 /* Hardware-Address-len */
398 if (vp) {
399 FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
400
401 } else if (original) {
402 FR_DBUFF_IN_RETURN(&work_dbuff, original->hlen);
403
404 } else { /* we are ALWAYS ethernet */
405 FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x06);
406 }
407
408 /* Hop-Count */
410 if (vp) {
411 FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint8);
412
413 } else if (original) {
414 FR_DBUFF_IN_RETURN(&work_dbuff, original->hops);
415
416 } else {
417 FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)0x00);
418 }
419
420 /* Transaction-Id */
421 FR_DBUFF_IN_RETURN(&work_dbuff, xid);
422
423 /* Number-of-Seconds */
425 if (vp) {
426 FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint16);
427 } else {
428 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_uint16));
429 }
430
431 /* Flags */
433 if (vp) {
434 FR_DBUFF_IN_RETURN(&work_dbuff, vp->vp_uint16);
435 } else if (original) { /* Original flags, still in network order */
436 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t *)&original->flags, sizeof(original->flags));
437 } else {
438 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_uint16));
439 }
440
441 /* Client-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_MEMSET_RETURN(&work_dbuff, 0, sizeof(vp->vp_ipv4addr));
447 }
448
449 /* Your-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 /* Server-IP-Address */
459 if (vp) {
460 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
461 } else {
462 FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
463 }
464
465 /*
466 * Gateway-IP-Address
467 */
469 if (vp) {
470 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, sizeof(vp->vp_ipv4addr));
471
472 } else if (original) { /* copy whatever value was in the original */
473 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&original->giaddr, sizeof(original->giaddr));
474
475 } else {
476 FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) INADDR_ANY);
477 }
478
479 /* Client-Hardware-Address */
481 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)vp->vp_ether, sizeof(vp->vp_ether));
482 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_CHADDR_LEN - sizeof(vp->vp_ether));
483
484 } else if (original) { /* copy whatever value was in the original */
485 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, &original->chaddr[0], sizeof(original->chaddr));
486
487 } else {
489 }
490
491 /* Server-Host-Name */
493 if (vp->vp_length > DHCP_SNAME_LEN) {
494 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, DHCP_SNAME_LEN);
495 } else {
496 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
497 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_SNAME_LEN - vp->vp_length);
498 }
499 } else {
500 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_SNAME_LEN);
501 }
502
503 /*
504 * Copy over Boot-Filename.
505 *
506 * FIXME: This copy should be delayed until AFTER the options
507 * have been processed. If there are too many options for
508 * the packet, then they go into the sname && filename fields.
509 * When that happens, the boot filename is passed as an option,
510 * instead of being placed verbatim in the filename field.
511 */
512
513 /* Boot-Filename */
514 if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_boot_filename))) {
515 if (vp->vp_length > DHCP_FILE_LEN) {
516 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, DHCP_FILE_LEN);
517 } else {
518 FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_strvalue, vp->vp_length);
519 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_FILE_LEN - vp->vp_length);
520 }
521 } else {
522 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DHCP_FILE_LEN);
523 }
524
525 /* DHCP magic number */
527
528 if ((vp = fr_pair_find_by_da(vps, NULL, attr_dhcp_message_type))) {
529 FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_TYPE, 0x01, vp->vp_uint8);
530 } else {
531 FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, FR_MESSAGE_TYPE, 0x01, (uint8_t)code);
532 }
533
534 /*
535 * Pre-sort attributes into contiguous blocks so that fr_dhcpv4_encode_option
536 * operates correctly. This changes the order of the list, but never mind...
537 */
540
541 /*
542 * Each call to fr_dhcpv4_encode_option will encode one complete DHCP option,
543 * and sub options.
544 */
545 while ((vp = fr_dcursor_current(&cursor))) {
546 /*
547 * The encoder skips message type, and returns
548 * "len==0" for it. We want to allow that, BUT
549 * stop when the encoder returns "len==0" for
550 * other attributes. So we need to skip it
551 * manually, too.
552 */
553 if (vp->da == attr_dhcp_message_type) {
554 (void) fr_dcursor_next(&cursor);
555 continue;
556 }
557
558 len = fr_dhcpv4_encode_option(&work_dbuff,
559 &cursor, &(fr_dhcpv4_ctx_t){ .root = fr_dict_root(dict_dhcpv4) });
560 if (len <= 0) break;
561 }
562
563 FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t)FR_END_OF_OPTIONS);
564
565 /*
566 * FIXME: if (fr_dbuff_used(&work_dbuff) > mms),
567 * then we put the extra options into the "sname" and "file"
568 * fields, AND set the "end option option" in the "options"
569 * field. We also set the "overload option",
570 * and put options into the "file" field, followed by
571 * the "sname" field. Where each option is completely
572 * enclosed in the "file" and/or "sname" field, AND
573 * followed by the "end of option", and MUST be followed
574 * by padding option.
575 *
576 * Yuck. That sucks...
577 */
578 if (fr_dbuff_used(&work_dbuff) < DEFAULT_PACKET_SIZE) {
579 FR_DBUFF_MEMSET_RETURN(&work_dbuff, 0, DEFAULT_PACKET_SIZE - fr_dbuff_used(&work_dbuff));
580 }
581
582 return fr_dbuff_set(dbuff, fr_dbuff_used(&work_dbuff));
583}
584
585
586/** Resolve/cache attributes in the DHCP dictionary
587 *
588 * @return
589 * - 0 on success.
590 * - -1 on failure.
591 */
593{
594 if (instance_count > 0) {
596 return 0;
597 }
598
600
601 if (fr_dict_autoload(dhcpv4_dict) < 0) {
602 fail:
604 return -1;
605 }
606
609 goto fail;
610 }
611
612 instantiated = true;
613 return 0;
614}
615
617{
618 if (!instantiated) return;
619
621
622 if (--instance_count > 0) return;
623
625 instantiated = false;
626}
627
628
629static char const *short_header_names[] = {
630 "opcode",
631 "hwtype",
632 "hwaddrlen",
633 "hop_count",
634 "xid",
635 "seconds",
636 "flags",
637 "ciaddr",
638 "yiaddr",
639 "siaddr",
640 "giaddr",
641 "chaddr",
642 "server_hostname",
643 "boot_filename",
644};
645
646static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
647{
648 int i;
649 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";
650
651 for (i = 0; i < attrlen; i++) {
652 if ((i > 0) && ((i & 0x0f) == 0x00))
653 fprintf(fp, "%.*s", depth, tabs);
654 fprintf(fp, "%02x ", ptr[i]);
655 if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
656 }
657 if ((i & 0x0f) != 0) fprintf(fp, "\n");
658}
659
660/** Print a raw DHCP packet as hex.
661 *
662 */
663void fr_dhcpv4_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
664{
665 int i;
666 uint8_t const *attr, *end;
667
668 end = packet + packet_len;
669 attr = packet;
670
671 for (i = 0; i < 14; i++) {
672 fprintf(fp, "\t%s: ", short_header_names[i]);
673 print_hex_data(fp, attr, dhcp_header_sizes[i], 2);
674 attr += dhcp_header_sizes[i];
675 }
676
677 fprintf(fp, "\tmagic:\t%02x %02x %02x %02x\n", attr[0], attr[1], attr[2], attr[3]);
678 attr += 4;
679
680 fprintf(fp, "\toptions\n");
681 while (attr < end) {
682 fprintf(fp, "\t\t");
683
684 fprintf(fp, "%02x %02x ", attr[0], attr[1]);
685
686 print_hex_data(fp, attr + 2, attr[1], 3);
687
688 /*
689 * "End of option" option.
690 */
691 if (attr[0] == 255) break;
692
693 attr += attr[1] + 2;
694 }
695
696 fprintf(fp, "\n");
697}
698
700{
701 /*
702 * DNS labels are strings, but are known width.
703 */
704 if (fr_dhcpv4_flag_dns_label(da)) {
705 if (da->type != FR_TYPE_STRING) {
706 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
707 return false;
708 }
709
710 da->flags.is_known_width = true;
711 da->flags.length = 0;
712 }
713
714 if (da->type == FR_TYPE_ATTR) {
715 da->flags.is_known_width = true;
716 da->flags.length = 1;
717 }
718
719 if (da_is_length_field16(da)) {
720 fr_strerror_const("The 'length=uint16' flag cannot be used for DHCPv4");
721 return false;
722 }
723
724 /*
725 * "arrays" of string/octets are encoded as a 8-bit
726 * length, followed by the actual data.
727 */
728 if (da->flags.array) {
729 if ((da->type == FR_TYPE_STRING) || (da->type == FR_TYPE_OCTETS)) {
730 if (da->flags.extra && !da_is_length_field8(da)) {
731 fr_strerror_const("Invalid flags");
732 return false;
733 }
734
735 da->flags.is_known_width = true;
736 da->flags.extra = true;
737 da->flags.subtype = FLAG_LENGTH_UINT8;
738 }
739
740 if (!da->flags.is_known_width) {
741 fr_strerror_const("DHCPv4 arrays require data types which have known width");
742 return false;
743 }
744 }
745
746 /*
747 * "extra" signifies that subtype is being used by the
748 * dictionaries itself.
749 */
750 if (da->flags.extra || !da->flags.subtype) return true;
751
752 if ((da->type != FR_TYPE_IPV4_PREFIX) &&
753 (fr_dhcpv4_flag_prefix(da))) {
754 fr_strerror_const("The 'prefix=...' flag can only be used with attributes of type 'ipv4prefix'");
755 return false;
756 }
757
758 if ((da->type != FR_TYPE_BOOL) && fr_dhcpv4_flag_exists(da)) {
759 fr_strerror_const("The 'exists' flag can only be used with attributes of type 'bool'");
760 return false;
761 }
762
763 if ((da->type == FR_TYPE_ATTR) && !da->parent->flags.is_root) {
764 fr_strerror_const("The 'attribute' data type can only be used at the dictionary root");
765 return false;
766 }
767
768 return true;
769}
770
773 .name = "dhcpv4",
774 .default_type_size = 1,
775 .default_type_length = 1,
776 .attr = {
777 .flags = {
778 .table = dhcpv4_flags,
779 .table_len = NUM_ELEMENTS(dhcpv4_flags),
780 .len = sizeof(fr_dhcpv4_attr_flags_t)
781 },
782 .valid = attr_valid
783 },
784
785 .init = fr_dhcpv4_global_init,
786 .free = fr_dhcpv4_global_free,
787
788 .encode = fr_dhcpv4_encode_foreign,
789 .decode = fr_dhcpv4_decode_foreign,
790};
static int const char char buffer[256]
Definition acutest.h:578
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
static const char * tabs
Definition command.c:1589
#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:769
#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:1006
#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:1511
#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:1385
#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:1588
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:224
#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:1475
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:516
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:290
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
fr_dlist_head_t * dlist
Head of the doubly linked list being iterated over.
Definition dcursor.h:94
#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:779
#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:730
uint8_t chaddr[DHCP_CHADDR_LEN]
Definition dhcpv4.h:78
Used as the decoder ctx.
Definition dhcpv4.h:134
#define da_is_length_field16(_da)
Definition dict.h:173
@ FLAG_LENGTH_UINT8
string / octets type is prefixed by uint8 of length
Definition dict.h:165
#define fr_dict_autofree(_to_free)
Definition dict.h:895
#define da_is_length_field8(_da)
Definition dict.h:172
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2496
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:287
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:300
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:4227
@ FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC
Protocol specific extensions.
Definition dict.h:190
#define fr_dict_autoload(_to_load)
Definition dict.h:892
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:306
char const * name
name of this protocol
Definition dict.h:451
#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:430
static int8_t fr_dict_attr_cmp(fr_dict_attr_t const *a, fr_dict_attr_t const *b)
Definition dict.h:636
Specifies an attribute which must be present for the module to function.
Definition dict.h:286
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:299
Protocol specific custom flag definitnion.
Definition dict.h:420
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:450
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:141
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
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:708
static fr_dict_t const * dict_dhcpv4
Definition base.c:34
static fr_dict_attr_t const * attr_dhcp_option_82
Definition base.c:45
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:814
fr_dict_attr_t const * attr_dhcp_opcode
Definition base.c:59
fr_dict_autoload_t dhcpv4_dict[]
Definition base.c:44
#define DHCP_MAX_MESSAGE_TYPE
Definition base.c:148
fr_dict_attr_t const * attr_dhcp_gateway_ip_address
Definition base.c:54
fr_dict_protocol_t libfreeradius_dhcpv4_dict_protocol
Definition base.c:772
char const * dhcp_message_types[]
Definition base.c:129
fr_dict_attr_t const * attr_dhcp_server_ip_address
Definition base.c:61
uint8_t eth_bcast[ETH_ADDR_LEN]
Definition base.c:167
fr_dict_attr_t const * attr_dhcp_dhcp_maximum_msg_size
Definition base.c:64
fr_dict_attr_t const * attr_dhcp_relay_link_selection
Definition base.c:70
fr_dict_attr_t const * attr_dhcp_vendor_class_identifier
Definition base.c:69
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:248
size_t dhcp_header_attrs_len
Definition base.c:127
fr_dict_attr_t const * attr_dhcp_hop_count
Definition base.c:57
fr_dict_attr_t const * attr_dhcp_parameter_request_list
Definition base.c:67
int fr_dhcpv4_global_init(void)
Resolve/cache attributes in the DHCP dictionary.
Definition base.c:592
fr_dict_attr_autoload_t dhcpv4_dict_attr[]
Definition base.c:76
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:172
static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
Definition base.c:646
fr_dict_attr_t const * attr_dhcp_server_host_name
Definition base.c:60
fr_dict_attr_t const * attr_dhcp_hardware_address_length
Definition base.c:55
uint8_t length
Definition base.c:38
fr_dict_attr_t const * attr_dhcp_transaction_id
Definition base.c:62
fr_dict_attr_t const * attr_dhcp_message_type
Definition base.c:66
uint8_t code
Definition base.c:37
int8_t fr_dhcpv4_attr_cmp(void const *a, void const *b)
Definition base.c:204
fr_dict_attr_t const * attr_dhcp_boot_filename
Definition base.c:50
void * fr_dhcpv4_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
DHCPV4-specific iterator.
Definition base.c:327
fr_dict_attr_t const * attr_dhcp_overload
Definition base.c:68
static fr_dict_flag_parser_t const dhcpv4_flags[]
Definition base.c:193
fr_dict_attr_t const * attr_dhcp_subnet_selection_option
Definition base.c:71
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:349
static bool instantiated
Definition base.c:34
fr_dict_attr_t const * attr_dhcp_network_subnet
Definition base.c:72
fr_dict_attr_t const * attr_dhcp_flags
Definition base.c:53
void fr_dhcpv4_global_free(void)
Definition base.c:616
fr_dict_attr_t const * attr_dhcp_hardware_type
Definition base.c:56
fr_dict_attr_t const * attr_dhcp_your_ip_address
Definition base.c:63
fr_dict_attr_t const * attr_dhcp_client_ip_address
Definition base.c:52
fr_dict_attr_t const * attr_dhcp_interface_mtu_size
Definition base.c:65
fr_dict_attr_t const * attr_dhcp_client_hardware_address
Definition base.c:51
int dhcp_header_sizes[]
Definition base.c:150
fr_dict_attr_t const * attr_dhcp_number_of_seconds
Definition base.c:58
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:344
bool fr_dhcpv4_is_encodable(void const *item, UNUSED void const *uctx)
Evaluation function for DCHPV4-encodability.
Definition base.c:316
fr_dict_attr_t const ** dhcp_header_attrs[]
Definition base.c:111
static char const * short_header_names[]
Definition base.c:629
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:663
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:668
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:193
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
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:84
static fr_slen_t data
Definition value.h:1293