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