The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
base.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; 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: 8f722390d06be5651496e9583731c6d23c6b3081 $
19 * @file src/process/dhcpv6/base.c
20 * @brief Base DHCPV6 processing.
21 *
22 * This code was originally written under contract for Network RADIUS
23 * but has been substantially modified from its original form outside
24 * of the project that required its creation.
25 *
26 * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
27 * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
28 */
29#define LOG_PREFIX "process_dhcpv6 - "
30
31#include <freeradius-devel/io/application.h>
32#include <freeradius-devel/server/pair.h>
33#include <freeradius-devel/unlang/interpret.h>
34#include <freeradius-devel/util/dict.h>
35#include <freeradius-devel/util/debug.h>
36#include <freeradius-devel/dhcpv6/dhcpv6.h>
37#include <freeradius-devel/protocol/dhcpv6/freeradius.internal.h>
38
39/*
40 * DHCPV6 state machine configuration
41 */
67
68typedef struct {
69 CONF_SECTION *server_cs; //!< Our virtual server.
70 process_dhcpv6_sections_t sections; //!< Pointers to various config sections
71 ///< we need to execute.
72 bool status_code_on_success; //!< Controls whether we add a status-code
73 ///< option to outbound packets if the status
74 ///< code would be 0.
75 ///< This is allowed by RFC 3315, but seems
76 ///< to cause issues with some clients.
77
78 bool send_failure_message; //!< If true, all instances of
79 ///< Module-Failure-Message in the request
80 ///< are concatenated and returned in the
81 ///< status-message field of the status-code
82 ///< option if the status-code is anything
83 ///< other than success.
84 ///< This may leak information about the
85 ///< internal state of the server, so is
86 ///< disabled by default.
87
88 bool move_failure_message_to_parent; //!< If true, and a parent exists, and the
89 ///< parent is a DHCPv6 request, all module
90 ///< failure messages will get copied to the
91 ///< parent and then freed.
92 ///< When combined with send_failure_message
93 ///< this ensures only the outer relay message
94 ///< contains failure data. The outer relay
95 ///< typically being controlled by the admin
96 ///< and not the end user.
98
99/** Records fields from the original request so we have a known good copy
100 */
108
109/** Records fields from the original relay-request so we have a known good copy
110 */
117
118static fr_dict_t const *dict_dhcpv6;
120
123 { .out = &dict_dhcpv6, .proto = "dhcpv6" },
124 { .out = &dict_freeradius, .proto = "freeradius" },
126};
127
138
140
143 { .out = &attr_client_id, .name = "Client-ID", .type = FR_TYPE_STRUCT, .dict = &dict_dhcpv6 },
144 { .out = &attr_hop_count, .name = "Hop-Count", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv6 },
145 { .out = &attr_interface_id, .name = "Interface-ID", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv6 },
146 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv6 },
147 { .out = &attr_relay_link_address, .name = "Relay-Link-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_dhcpv6 },
148 { .out = &attr_relay_peer_address, .name = "Relay-Peer-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_dhcpv6 },
149 { .out = &attr_server_id, .name = "Server-ID", .type = FR_TYPE_STRUCT, .dict = &dict_dhcpv6 },
150 { .out = &attr_status_code_value, .name = "Status-Code.Value", .type = FR_TYPE_UINT16, .dict = &dict_dhcpv6 },
151 { .out = &attr_status_code_message, .name = "Status-Code.Message", .type = FR_TYPE_STRING, .dict = &dict_dhcpv6 },
152 { .out = &attr_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv6 },
153
154 { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
156};
157
162
165 { .out = &enum_status_code_success, .name = "success", .attr = &attr_status_code_value },
166 { .out = &enum_status_code_unspec_fail, .name = "UnspecFail", .attr = &attr_status_code_value },
167 { .out = &enum_status_code_not_on_link, .name = "NotOnLink", .attr = &attr_status_code_value },
168 { .out = &enum_status_code_no_binding, .name = "NoBinding", .attr = &attr_status_code_value },
170};
171
172#define FR_DHCPV6_PROCESS_CODE_VALID(_x) (FR_DHCPV6_PACKET_CODE_VALID(_x) || (_x == FR_DHCPV6_DO_NOT_RESPOND))
173
174#define PROCESS_PACKET_TYPE fr_dhcpv6_packet_code_t
175#define PROCESS_CODE_MAX FR_DHCPV6_CODE_MAX
176#define PROCESS_CODE_DO_NOT_RESPOND FR_DHCPV6_DO_NOT_RESPOND
177#define PROCESS_PACKET_CODE_VALID FR_DHCPV6_PROCESS_CODE_VALID
178#define PROCESS_INST process_dhcpv6_t
179#define PROCESS_RCTX process_dhcpv6_rctx_t
180#define PROCESS_CODE_DYNAMIC_CLIENT FR_DHCPV6_REPLY
181
182/*
183 * DHCPv6 is nonstandard in that we reply
184 * to the majority of requests, but include a
185 * status code to indicate failures.
186 */
187#define PROCESS_STATE_EXTRA_FIELDS fr_value_box_t const **status_codes[RLM_MODULE_NUMCODES];
188#include <freeradius-devel/server/process.h>
189
191 { FR_CONF_OFFSET("status_code_on_success", process_dhcpv6_t, status_code_on_success), .dflt = "no" },
192 { FR_CONF_OFFSET("send_failure_message", process_dhcpv6_t, send_failure_message), .dflt = "no" },
193 { FR_CONF_OFFSET("move_failure_message_to_parent", process_dhcpv6_t, move_failure_message_to_parent), .dflt = "yes" },
195};
196
198 {
199 .section = SECTION_NAME("recv", "Solicit"),
200 .actions = &mod_actions_postauth,
201 .offset = PROCESS_CONF_OFFSET(recv_solicit)
202 },
203 {
204 .section = SECTION_NAME("recv", "Request"),
206 .offset = PROCESS_CONF_OFFSET(recv_request)
207 },
208 {
209 .section = SECTION_NAME("recv", "Confirm"),
211 .offset = PROCESS_CONF_OFFSET(recv_confirm)
212 },
213 {
214 .section = SECTION_NAME("recv", "Renew"),
216 .offset = PROCESS_CONF_OFFSET(recv_renew)
217 },
218 {
219 .section = SECTION_NAME("recv", "Rebind"),
221 .offset = PROCESS_CONF_OFFSET(recv_rebind)
222 },
223 {
224 .section = SECTION_NAME("recv", "Release"),
226 .offset = PROCESS_CONF_OFFSET(recv_release)
227 },
228 {
229 .section = SECTION_NAME("recv", "Decline"),
231 .offset = PROCESS_CONF_OFFSET(recv_decline)
232 },
233 {
234 .section = SECTION_NAME("recv", "Reconfigure"),
236 .offset = PROCESS_CONF_OFFSET(recv_reconfigure)
237 },
238 {
239 .section = SECTION_NAME("recv", "Information-Request"),
241 .offset = PROCESS_CONF_OFFSET(recv_information_request)
242 },
243 {
244 .section = SECTION_NAME("recv", "Relay-Forward"),
246 .offset = PROCESS_CONF_OFFSET(recv_relay_forward)
247 },
248
249 {
250 .section = SECTION_NAME("send", "Advertise"),
252 .offset = PROCESS_CONF_OFFSET(send_advertise)
253 },
254 {
255 .section = SECTION_NAME("send", "Reply"),
258 },
259 {
260 .section = SECTION_NAME("send", "Relay-Reply"),
262 .offset = PROCESS_CONF_OFFSET(send_relay_reply)
263 },
264 {
265 .section = SECTION_NAME("send", "Do-Not-Respond"),
267 .offset = PROCESS_CONF_OFFSET(do_not_respond)
268 },
269
270 DYNAMIC_CLIENT_SECTIONS,
271
273};
274
275/** Keep a copy of header fields to prevent them being tampered with
276 *
277 */
278static inline CC_HINT(always_inline)
279int dhcpv6_client_fields_store(request_t *request, process_dhcpv6_rctx_t *rctx, bool expect_server_id)
280{
281 fr_pair_t *transaction_id;
282
283 transaction_id = fr_pair_find_by_da(&request->request_pairs, NULL, attr_transaction_id);
284 if (!transaction_id) {
285 REDEBUG("Missing Transaction-ID");
286 return -1;
287 }
288
289 if (transaction_id->vp_length != DHCPV6_TRANSACTION_ID_LEN) {
290 REDEBUG("Invalid Transaction-ID, expected len %u, got len %zu",
291 DHCPV6_TRANSACTION_ID_LEN, transaction_id->vp_length);
292 return -1;
293 }
294
295 MEM(rctx->transaction_id = fr_pair_copy(rctx, transaction_id));
296
297 fr_pair_list_init(&rctx->client_id);
298 fr_pair_list_init(&rctx->server_id);
299
300 /*
301 * These should just become straight copies
302 * when the structure pairs are nested.
303 */
304 switch (fr_pair_list_copy_by_ancestor(rctx, &rctx->client_id,
305 &request->request_pairs, attr_client_id)) {
306 case -1:
307 REDEBUG("Error copying Client-ID");
308 return -1;
309
310 case 0:
311 REDEBUG("Missing Client-ID");
312 return -1;
313
314 default:
315 break;
316 }
317
318 switch (fr_pair_list_copy_by_ancestor(rctx, &rctx->server_id,
319 &request->request_pairs, attr_server_id)) {
320 case -1:
321 REDEBUG("Error copying Server-ID");
322 return -1;
323
324 case 0:
325 if (expect_server_id) {
326 REDEBUG("Missing Server-ID");
327 return -1;
328 }
329 break;
330
331 default:
332 if (!expect_server_id) {
333 REDEBUG("Server-ID should not be present");
334 return -1;
335 }
336 break;
337 }
338
339 return 0;
340}
341
342/** Validate a solicit/rebind/confirm message
343 *
344 * Servers MUST discard any solicit/rebind/confirm messages that
345 * do not include a Client Identifier option or that do include a
346 * Server Identifier option.
347 */
348RECV(for_any_server)
349{
350 CONF_SECTION *cs;
351 fr_process_state_t const *state;
352 process_dhcpv6_t const *inst = mctx->mi->data;
353 process_dhcpv6_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_dhcpv6_rctx_t);
354
356
357 if (dhcpv6_client_fields_store(request, rctx, false) < 0) RETURN_UNLANG_INVALID;
358
359 UPDATE_STATE_CS(packet);
360
361 return unlang_module_yield_to_section(RESULT_P, request,
362 cs, state->default_rcode, state->resume,
363 NULL, 0, rctx);
364}
365
366/** Validate a request/renew/decline/release
367 *
368 * Servers MUST discard any received Request message that meet any of
369 * the following conditions:
370 *
371 * - the message does not include a Server Identifier option.
372 *
373 * - the contents of the Server Identifier option do not match the
374 * server's DUID.
375 *
376 * - the message does not include a Client Identifier option.
377 *
378 * Servers MUST discard any received Confirm messages that do not
379 * include a Client Identifier option or that do include a Server
380 * Identifier option.
381 */
382RECV(for_this_server)
383{
384 CONF_SECTION *cs;
385 fr_process_state_t const *state;
386 process_dhcpv6_t const *inst = mctx->mi->data;
387 process_dhcpv6_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_dhcpv6_rctx_t);
388
390
391 if (dhcpv6_client_fields_store(request, rctx, true) < 0) RETURN_UNLANG_INVALID;
392
393 UPDATE_STATE_CS(packet);
394
395 return unlang_module_yield_to_section(RESULT_P, request,
396 cs, state->default_rcode, state->resume,
397 NULL, 0, rctx);
398}
399
400/** Copy a reply pair back into the response
401 *
402 */
403static inline CC_HINT(always_inline)
404int restore_field(request_t *request, fr_pair_t **to_restore)
405{
406 fr_pair_t *vp;
407 int ret = 0;
408
409 PAIR_VERIFY(*to_restore);
410
411 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, (*to_restore)->da);
412 if (vp) {
413 if (fr_pair_cmp(vp, *to_restore) != 0) {
414 RWDEBUG("reply.%pP does not match request.%pP", vp, *to_restore);
415 free:
416 talloc_free(*to_restore);
417 *to_restore = NULL;
418 return ret;
419 }
420 } else if (fr_pair_steal_append(request->reply_ctx, &request->reply_pairs, *to_restore) < 0) {
421 RPERROR("Failed adding %s", (*to_restore)->da->name);
422 ret = -1;
423 goto free;
424 }
425 *to_restore = NULL;
426
427 return 0;
428}
429
430static inline CC_HINT(always_inline)
432{
433 fr_pair_t *vp;
434
435 while ((vp = fr_pair_list_head(to_restore))) {
436 fr_pair_remove(to_restore, vp);
437 if (restore_field(request, &vp) < 0) return -1;
438 }
439
440 return 0;
441}
442
443/** Add a status code if one doesn't already exist
444 *
445 */
446static inline CC_HINT(always_inline)
448{
449 fr_pair_t *vp, *failure_message = NULL;
450 fr_value_box_t const *vb;
451 bool moved_failure_message = false;
452
453 if (!code || !*code) return;
454
455 vb = *code;
456
457 /*
458 * If it's a success save some bytes
459 * in the packet and don't bother
460 * adding the success code unless
461 * explicitly requested to.
462 */
463 if ((vb->vb_uint16 == 0) && !inst->status_code_on_success) return;
464
465 /*
466 * Don't override the user status
467 * code.
468 */
470 if (unlikely(fr_value_box_copy(vp, &vp->data, vb) < 0)) {
471 RPERROR("Failed copying status code value");
473 return;
474 }
475 }
476
477 /*
478 * Move the module failure messages upwards
479 * if requested to by the user.
480 */
481 if (inst->move_failure_message_to_parent && request->parent && (request->parent->proto_dict == request->proto_dict)) {
482 fr_pair_t const *prev = NULL;
483
484 while ((failure_message = fr_pair_find_by_da(&request->request_pairs,
486 MEM(vp = fr_pair_copy(request->parent->request_ctx, failure_message));
487 fr_pair_append(&request->parent->request_pairs, vp);
488
489 prev = fr_pair_remove(&request->request_pairs, failure_message);
490 talloc_free(failure_message);
491 }
492
493 moved_failure_message = true;
494 }
495
496 /*
497 * Concat all the module failure messages
498 * and place them in the status code
499 * message.
500 */
501 if (inst->send_failure_message && !moved_failure_message &&
502 (failure_message = fr_pair_find_by_da(&request->request_pairs, NULL, attr_module_failure_message)) &&
505 fr_sbuff_t sbuff;
506
507 /*
508 * Create an aggregation buffer up to
509 * the maximum length of a status
510 * message.
511 */
512 if (!fr_sbuff_init_talloc(vp, &sbuff, &tctx, 1024, UINT16_MAX - 2)) return;
513
514 do {
515 /*
516 * Best effort... it's probably OK
517 * if we truncate really long messages.
518 */
519 if (unlikely(fr_sbuff_in_bstrncpy(&sbuff, failure_message->vp_strvalue,
520 failure_message->vp_length) < 0)) break;
521 } while ((failure_message = fr_pair_find_by_da(&request->request_pairs, failure_message,
523 (fr_sbuff_in_strcpy_literal(&sbuff, ". ") == 2));
524
525 fr_sbuff_trim_talloc(&sbuff, SIZE_MAX); /* Fix size */
527 }
528}
529
530/** Restore our copy of the header fields into the reply list
531 *
532 */
533RESUME(send_to_client)
534{
535 process_dhcpv6_t *inst = talloc_get_type_abort(mctx->mi->data, process_dhcpv6_t);
536 process_dhcpv6_rctx_t *fields = talloc_get_type_abort(mctx->rctx, process_dhcpv6_rctx_t);
537 fr_process_state_t const *state;
538
539
540 UPDATE_STATE(reply);
541
542 /*
543 * Don't bother adding VPs if we're not going
544 * be responding to the client.
545 */
546 if (state->packet_type[RESULT_RCODE] == FR_DHCPV6_DO_NOT_RESPOND) return CALL_RESUME(send_generic);
547
548 /*
549 * Add a status code if we have one
550 */
551 status_code_add(inst, request, state->status_codes[RESULT_RCODE]);
552
553 /*
554 * If we have a status code entry then we'll
555 * be returning something to the client and
556 * need to fill in all these fields
557 */
558 if (unlikely(restore_field(request, &fields->transaction_id) < 0)) {
559 fail:
560 p_result->rcode = RLM_MODULE_FAIL;
561 return CALL_RESUME(send_generic);
562 }
563 if (unlikely(restore_field_list(request, &fields->client_id) < 0)) goto fail;
564 if (unlikely(restore_field_list(request, &fields->server_id) < 0)) goto fail;
565
566 return CALL_RESUME(send_generic);
567}
568
569/** Record the original hop-count, link-address, peer-address etc...
570 *
571 */
572static inline CC_HINT(always_inline)
574{
575 fr_pair_t *hop_count, *link_address, *peer_address, *interface_id;
577
578 hop_count = fr_pair_find_by_da(&request->request_pairs, NULL, attr_hop_count);
579 if (!hop_count) {
580 REDEBUG("Missing Hop-Count");
581 return NULL;
582 }
583
584 link_address = fr_pair_find_by_da(&request->request_pairs, NULL, attr_relay_link_address);
585 if (!link_address) {
586 REDEBUG("Missing Link-Address");
587 return NULL;
588 }
589
590 peer_address = fr_pair_find_by_da(&request->request_pairs, NULL, attr_relay_peer_address);
591 if (!peer_address) {
592 REDEBUG("Missing Peer-Address");
593 return NULL;
594 }
595
596 interface_id = fr_pair_find_by_da(&request->request_pairs, NULL, attr_interface_id);
597
598 /*
599 * Remember the relay fields
600 */
601 MEM(rctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), process_dhcpv6_relay_fields_t)); /* Safer to zero the whole thing */
602 MEM(rctx->hop_count = fr_pair_copy(rctx, hop_count));
603 MEM(rctx->link_address = fr_pair_copy(rctx, link_address));
604 MEM(rctx->peer_address = fr_pair_copy(rctx, peer_address));
605 if (interface_id) MEM(rctx->interface_id = fr_pair_copy(rctx, interface_id)); /* Optional */
606
607 return rctx;
608}
609
610/** Ensure we have the necessary pairs from the relay
611 *
612 */
613RECV(from_relay)
614{
615 CONF_SECTION *cs;
616 fr_process_state_t const *state;
617 process_dhcpv6_t const *inst = mctx->mi->data;
618 process_dhcpv6_relay_fields_t *relay_fields = NULL;
619 process_dhcpv6_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_dhcpv6_rctx_t);
620
621 relay_fields = dhcpv6_relay_fields_store(request);
622 if (!relay_fields) RETURN_UNLANG_INVALID;
623 rctx->uctx = relay_fields;
624
625 UPDATE_STATE_CS(packet);
626
627 return unlang_module_yield_to_section(RESULT_P, request,
628 cs, state->default_rcode, state->resume,
629 NULL, 0, rctx);
630}
631
632/** Restore our copy of the header fields into the reply list
633 *
634 */
635RESUME(send_to_relay)
636{
637 process_dhcpv6_t *inst = talloc_get_type_abort(mctx->mi->data, process_dhcpv6_t);
638 process_dhcpv6_rctx_t *rctx = talloc_get_type_abort(mctx->rctx, process_dhcpv6_rctx_t);
639 process_dhcpv6_relay_fields_t *fields = talloc_get_type_abort(rctx->uctx, process_dhcpv6_relay_fields_t);
640 fr_process_state_t const *state;
641
642 UPDATE_STATE(reply);
643
644 /*
645 * Add a status code if we have one
646 */
647 status_code_add(inst, request, state->status_codes[RESULT_RCODE]);
648
649 /*
650 * Restore relay fields
651 */
652 if (unlikely(restore_field(request, &fields->hop_count) < 0)) {
653 fail:
654 p_result->rcode = RLM_MODULE_FAIL;
655 return CALL_RESUME(send_generic);
656 }
657 if (unlikely(restore_field(request, &fields->link_address) < 0)) goto fail;
658 if (unlikely(restore_field(request, &fields->peer_address) < 0)) goto fail;
659 if (fields->interface_id && unlikely(restore_field(request, &fields->interface_id) < 0)) goto fail;
660
661 return CALL_RESUME(send_generic);
662}
663
664/** Main dispatch function
665 *
666 */
667static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
668{
669 fr_process_state_t const *state;
670
672
674 fr_assert(PROCESS_PACKET_CODE_VALID(request->packet->code));
675
676 request->component = "dhcpv6";
677 request->module = NULL;
678 fr_assert(request->proto_dict == dict_dhcpv6);
679
680 UPDATE_STATE(packet);
681
682 if (!state->recv) {
683 REDEBUG("Invalid packet type (%u)", request->packet->code);
685 }
686
687 if (unlikely(request_is_dynamic_client(request))) {
688 return new_client(p_result, mctx, request);
689 }
690
691 return state->recv(p_result, mctx, request);
692}
693
694static int mod_instantiate(module_inst_ctx_t const *mctx)
695{
696 process_dhcpv6_t *inst = talloc_get_type_abort(mctx->mi->data, process_dhcpv6_t);
697
698 inst->server_cs = cf_item_to_section(cf_parent(mctx->mi->conf));
699
700 return 0;
701}
702
703static fr_process_state_t const process_state[] = {
704 /*
705 * A client sends a Solicit message to locate
706 * servers.
707 */
708 [ FR_DHCPV6_SOLICIT ] = {
709 .recv = recv_for_any_server,
710 .resume = resume_recv_generic,
711 .packet_type = {
715 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
716
723 },
724 .status_codes = {
725 /* RLM_MODULE_NOOP - No response */
728 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
729
730 /* RLM_MODULE_FAIL - No response */
731 /* RLM_MODULE_INVALID - No response */
732 /* RLM_MODULE_REJECT - No response */
733 /* RLM_MODULE_DISALLOW - No response */
734 /* RLM_MODULE_NOTFOUND - No response */
735 },
736 .default_rcode = RLM_MODULE_NOOP,
737 .section_offset = offsetof(process_dhcpv6_sections_t, recv_solicit),
738 },
739
740 /*
741 * A client sends a Request message to request
742 * configuration parameters, including IP
743 * addresses, from a specific server.
744 */
745 [ FR_DHCPV6_REQUEST ] = {
746 .recv = recv_for_this_server,
747 .resume = resume_recv_generic,
748 .packet_type = {
752 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
753
760 },
761 .status_codes = {
765 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
766
772 },
773 .default_rcode = RLM_MODULE_NOOP,
774 .section_offset = offsetof(process_dhcpv6_sections_t, recv_request),
775 },
776
777 /*
778 * A client sends a Confirm message to any
779 * available server to determine whether the
780 * addresses it was assigned are still appropriate
781 * to the link to which the client is connected.
782 */
783 [ FR_DHCPV6_CONFIRM ] = {
784 .recv = recv_for_any_server,
785 .resume = resume_recv_generic,
786 .packet_type = {
790 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
791
798 },
799
800 /*
801 * When the server receives a Confirm message, the server determines
802 * whether the addresses in the Confirm message are appropriate for the
803 * link to which the client is attached. If all of the addresses in the
804 * Confirm message pass this test, the server returns a status of
805 * Success. If any of the addresses do not pass this test, the server
806 * returns a status of NotOnLink. If the server is unable to perform
807 * this test (for example, the server does not have information about
808 * prefixes on the link to which the client is connected), or there were
809 * no addresses in any of the IAs sent by the client, the server MUST
810 * NOT send a reply to the client.
811 */
812 .status_codes = {
813 /* RLM_MODULE_NOOP - No response */
816 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
817
818 /* RLM_MODULE_FAIL - No response */
819 /* RLM_MODULE_INVALID - No response */
821 /* RLM_MODULE_DISALLOW - No response */
822 /* RLM_MODULE_NOTFOUND - No response */
823 },
824 .default_rcode = RLM_MODULE_NOOP,
825 .section_offset = offsetof(process_dhcpv6_sections_t, recv_confirm),
826 },
827
828 /*
829 * A client sends a Renew message to the server
830 * that originally provided the client's addresses
831 * and configuration parameters to extend the
832 * lifetimes on the addresses assigned to the
833 * client and to update other configuration
834 * parameters.
835 */
836 [ FR_DHCPV6_RENEW ] = {
837 .recv = recv_for_this_server,
838 .resume = resume_recv_generic,
839 .packet_type = {
843 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
844
851 },
852
853 /*
854 * If the server cannot find a client entry for the IA the server
855 * returns the IA containing no addresses with a Status Code option set
856 * to NoBinding in the Reply message.
857 */
858 .status_codes = {
862 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
863
865 /* RLM_MODULE_INVALID - No response */
869 },
870 .default_rcode = RLM_MODULE_NOOP,
871 .section_offset = offsetof(process_dhcpv6_sections_t, recv_renew),
872 },
873
874 /*
875 * A client sends a Rebind message to any
876 * available server to extend the lifetimes on the
877 * addresses assigned to the client and to update
878 * other configuration parameters; this message is
879 * sent after a client receives no response to a
880 * Renew message.
881 */
882 [ FR_DHCPV6_REBIND ] = {
883 .recv = recv_for_any_server,
884 .resume = resume_recv_generic,
885 .packet_type = {
889 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
890
897 },
898 .status_codes = {
902 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
903
905 /* RLM_MODULE_INVALID - No response */
909 },
910 .default_rcode = RLM_MODULE_NOOP,
911 .section_offset = offsetof(process_dhcpv6_sections_t, recv_rebind),
912 },
913 /*
914 * A client sends an Information-request
915 * message to a server to request configuration
916 * parameters without the assignment of any IP
917 * addresses to the client.
918 */
920 .recv = recv_for_any_server,
921 .resume = resume_recv_generic,
922 .packet_type = {
926 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
927
934 },
935 .status_codes = {
939 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
940
942 /* RLM_MODULE_INVALID - No response */
946 },
947 .default_rcode = RLM_MODULE_NOOP,
948 .section_offset = offsetof(process_dhcpv6_sections_t, recv_information_request),
949 },
950 /*
951 * A client sends a Release message to the server
952 * that assigned addresses to the client to
953 * indicate that the client will no longer use one
954 * or more of the assigned addresses.
955 */
956 [ FR_DHCPV6_RELEASE ] = {
957 .recv = recv_for_this_server,
958 .resume = resume_recv_generic,
959 .packet_type = {
963 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
964
971 },
972 .status_codes = {
976 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
977
979 /* RLM_MODULE_INVALID - No response */
983 },
984 .default_rcode = RLM_MODULE_NOOP,
985
986 .section_offset = offsetof(process_dhcpv6_sections_t, recv_release),
987 },
988 /*
989 *
990 * A client sends a Decline message to a server to
991 * indicate that the client has determined that
992 * one or more addresses assigned by the server
993 * are already in use on the link to which the
994 * client is connected.
995 */
996 [ FR_DHCPV6_DECLINE ] = {
997 .recv = recv_for_this_server, /* Need to check for attributes */
998 .resume = resume_recv_generic,
999 .packet_type = {
1003 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
1004
1011 },
1012 .status_codes = {
1016 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
1017
1019 /* RLM_MODULE_INVALID - No response */
1023 },
1024 .default_rcode = RLM_MODULE_NOOP,
1025 .result_rcode = RLM_MODULE_REJECT,
1026 .section_offset = offsetof(process_dhcpv6_sections_t, recv_decline),
1027 },
1028 /*
1029 * A relay agent sends a Relay-forward message
1030 * to relay messages to servers, either directly
1031 * or through another relay agent. The received
1032 * message, either a client message or a
1033 * Relay-forward message from another relay
1034 * agent, is encapsulated in an option in the
1035 * Relay-forward message.
1036 */
1038 .recv = recv_from_relay,
1039 .resume = resume_recv_generic,
1040 .packet_type = {
1044 /* RLM_MODULE_HANDLED - Requires the user to set packet-type */
1045
1052 },
1053 .status_codes = {
1057 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
1058
1060 /* RLM_MODULE_INVALID - No response */
1064 },
1065 .default_rcode = RLM_MODULE_NOOP,
1066 .section_offset = offsetof(process_dhcpv6_sections_t, recv_relay_forward),
1067 },
1068 /*
1069 * A server sends an Advertise message to indicate
1070 * that it is available for DHCP service, in
1071 * response to a Solicit message received from a
1072 * client.
1073 */
1074 [ FR_DHCPV6_ADVERTISE ] = {
1075 .send = send_generic,
1076 .resume = resume_send_to_client,
1077 .packet_type = {
1084 },
1085 .status_codes = {
1089 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
1090
1091 /* RLM_MODULE_FAIL - No response */
1092 /* RLM_MODULE_INVALID - No response */
1093 /* RLM_MODULE_REJECT - No response */
1094 /* RLM_MODULE_DISALLOW - No response */
1095 /* RLM_MODULE_NOTFOUND - No response */
1096 },
1097 .default_rcode = RLM_MODULE_NOOP,
1098 .result_rcode = RLM_MODULE_OK,
1099 .section_offset = offsetof(process_dhcpv6_sections_t, send_advertise),
1100 },
1101 /*
1102 * A server sends a Reply message containing
1103 * assigned addresses and configuration parameters
1104 * in response to a Solicit, Request, Renew,
1105 * Rebind message received from a client. A
1106 * server sends a Reply message containing
1107 * configuration parameters in response to an
1108 * Information-request message. A server sends a
1109 * Reply message in response to a Confirm message
1110 * confirming or denying that the addresses
1111 * assigned to the client are appropriate to the
1112 * link to which the client is connected. A
1113 * server sends a Reply message to acknowledge
1114 * receipt of a Release or Decline message.
1115 */
1116 [ FR_DHCPV6_REPLY ] = {
1117 .send = send_generic,
1118 .resume = resume_send_to_client,
1119 .packet_type = {
1120
1127 },
1128 .status_codes = {
1132 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
1133
1135 /* RLM_MODULE_INVALID - No response */
1139 },
1140 .default_rcode = RLM_MODULE_NOOP,
1141 .result_rcode = RLM_MODULE_OK,
1142 .section_offset = offsetof(process_dhcpv6_sections_t, send_reply),
1143 },
1144 /*
1145 * A server sends a Relay-reply message to a relay
1146 * agent containing a message that the relay
1147 * agent delivers to a client. The Relay-reply
1148 * message may be relayed by other relay agents
1149 * for delivery to the destination relay agent.
1150 * The server encapsulates the client message as
1151 * an option in the Relay-reply message, which the
1152 * relay agent extracts and relays to the client.
1153 */
1154 [ FR_DHCPV6_RELAY_REPLY ] = {
1155 .send = send_generic,
1156 .resume = resume_send_to_relay,
1157 .packet_type = {
1164 },
1165 .status_codes = {
1169 /* RLM_MODULE_HANDLED - Requires the user to set status-code */
1170
1172 /* RLM_MODULE_INVALID - No response */
1176 },
1177 .default_rcode = RLM_MODULE_NOOP,
1178 .result_rcode = RLM_MODULE_OK,
1179 .section_offset = offsetof(process_dhcpv6_sections_t, send_relay_reply),
1180 },
1181
1183 .send = send_generic,
1184 .resume = resume_send_generic,
1185 .packet_type = {
1190
1197 },
1198 .default_rcode = RLM_MODULE_NOOP,
1199 .result_rcode = RLM_MODULE_HANDLED,
1200 .section_offset = offsetof(process_dhcpv6_sections_t, do_not_respond),
1201 }
1202};
1203
1206 .common = {
1207 .magic = MODULE_MAGIC_INIT,
1208 .name = "dhcpv6",
1212
1213 .instantiate = mod_instantiate
1214 },
1215 .process = mod_process,
1216 .compile_list = compile_list,
1217 .dict = &dict_dhcpv6,
1218 .packet_type = &attr_packet_type
1219};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define unlikely(_x)
Definition build.h:455
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:695
#define cf_parent(_cf)
Definition cf_util.h:118
#define MEM(x)
Definition debug.h:36
@ FR_DHCPV6_RELEASE
Definition dhcpv6.h:75
@ FR_DHCPV6_DECLINE
Definition dhcpv6.h:76
@ FR_DHCPV6_ADVERTISE
Definition dhcpv6.h:69
@ FR_DHCPV6_DO_NOT_RESPOND
Definition dhcpv6.h:104
@ FR_DHCPV6_REBIND
Definition dhcpv6.h:73
@ FR_DHCPV6_REPLY
Definition dhcpv6.h:74
@ FR_DHCPV6_CONFIRM
Definition dhcpv6.h:71
@ FR_DHCPV6_SOLICIT
Definition dhcpv6.h:68
@ FR_DHCPV6_RENEW
Definition dhcpv6.h:72
@ FR_DHCPV6_INFORMATION_REQUEST
Definition dhcpv6.h:78
@ FR_DHCPV6_REQUEST
Definition dhcpv6.h:70
@ FR_DHCPV6_RELAY_FORWARD
Definition dhcpv6.h:79
@ FR_DHCPV6_RELAY_REPLY
Definition dhcpv6.h:80
#define DHCPV6_TRANSACTION_ID_LEN
Definition dhcpv6.h:37
fr_value_box_t const ** out
Enumeration value.
Definition dict.h:281
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
Specifies a value which must be present for the module to function.
Definition dict.h:280
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
free(array)
talloc_free(hp)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2052
static fr_dict_t const * dict_freeradius
Definition base.c:37
fr_dict_attr_t const * attr_packet_type
Definition base.c:91
static fr_dict_attr_t const * attr_module_failure_message
Definition log.c:206
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RPERROR(fmt,...)
Definition log.h:319
@ 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_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_OCTETS
Raw octets.
unlang_mod_actions_t const mod_actions_postauth
Definition mod_action.c:92
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:69
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
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:707
int fr_pair_cmp(fr_pair_t const *a, fr_pair_t const *b)
Compare two pairs, using the operator from "a".
Definition pair.c:1976
int fr_pair_value_bstrndup_shallow(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Assign a string to a "string" type value pair.
Definition pair.c:2863
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1352
int fr_pair_list_copy_by_ancestor(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from, fr_dict_attr_t const *parent_da)
Duplicate pairs in a list where the da is a descendant of parent_da.
Definition pair.c:2465
int fr_pair_steal_append(TALLOC_CTX *list_ctx, fr_pair_list_t *list, fr_pair_t *vp)
Change a vp's talloc ctx and insert it into a new list.
Definition pair.c:562
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:503
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition base.c:168
static const virtual_server_compile_t compile_list[]
Definition base.c:192
static fr_process_state_t const process_state[]
Definition base.c:68
#define PROCESS_PACKET_CODE_VALID
Definition base.c:64
CONF_SECTION * send_advertise
Definition base.c:57
CONF_SECTION * recv_information_request
Definition base.c:54
static fr_dict_attr_t const * attr_hop_count
Definition base.c:130
CONF_SECTION * send_reply
Definition base.c:58
static fr_dict_attr_t const * attr_status_code_message
Definition base.c:137
CONF_SECTION * recv_solicit
Definition base.c:45
static fr_dict_attr_t const * attr_status_code_value
Definition base.c:136
static fr_dict_attr_t const * attr_relay_link_address
Definition base.c:133
bool move_failure_message_to_parent
If true, and a parent exists, and the parent is a DHCPv6 request, all module failure messages will ge...
Definition base.c:88
CONF_SECTION * recv_request
Definition base.c:46
static int restore_field_list(request_t *request, fr_pair_list_t *to_restore)
Definition base.c:431
fr_dict_autoload_t process_dhcpv6_dict[]
Definition base.c:122
fr_dict_enum_autoload_t process_dhcpv6_dict_enum[]
Definition base.c:164
static fr_value_box_t const * enum_status_code_success
Definition base.c:158
fr_pair_list_t server_id
Definition base.c:104
static void status_code_add(process_dhcpv6_t const *inst, request_t *request, fr_value_box_t const **code)
Add a status code if one doesn't already exist.
Definition base.c:447
CONF_SECTION * server_cs
Our virtual server.
Definition base.c:69
static int restore_field(request_t *request, fr_pair_t **to_restore)
Copy a reply pair back into the response.
Definition base.c:404
CONF_SECTION * do_not_respond
Definition base.c:61
static fr_value_box_t const * enum_status_code_no_binding
Definition base.c:161
fr_pair_list_t client_id
Definition base.c:103
bool status_code_on_success
Controls whether we add a status-code option to outbound packets if the status code would be 0.
Definition base.c:72
CONF_SECTION * recv_decline
Definition base.c:51
static fr_dict_attr_t const * attr_interface_id
Definition base.c:131
static fr_dict_attr_t const * attr_server_id
Definition base.c:129
CONF_SECTION * recv_relay_forward
Definition base.c:55
CONF_SECTION * deny_client
Definition base.c:65
static fr_value_box_t const * enum_status_code_unspec_fail
Definition base.c:159
CONF_SECTION * recv_rebind
Definition base.c:49
static conf_parser_t dhcpv6_process_config[]
Definition base.c:190
CONF_SECTION * recv_release
Definition base.c:50
static int dhcpv6_client_fields_store(request_t *request, process_dhcpv6_rctx_t *rctx, bool expect_server_id)
Keep a copy of header fields to prevent them being tampered with.
Definition base.c:279
fr_pair_t * transaction_id
Definition base.c:102
CONF_SECTION * recv_renew
Definition base.c:48
CONF_SECTION * new_client
Definition base.c:63
fr_dict_attr_autoload_t process_dhcpv6_dict_attr[]
Definition base.c:142
unlang_result_t result
Definition base.c:105
CONF_SECTION * add_client
Definition base.c:64
bool send_failure_message
If true, all instances of Module-Failure-Message in the request are concatenated and returned in the ...
Definition base.c:78
CONF_SECTION * recv_reconfigure
Definition base.c:52
static fr_dict_t const * dict_dhcpv6
Definition base.c:118
static process_dhcpv6_relay_fields_t * dhcpv6_relay_fields_store(request_t *request)
Record the original hop-count, link-address, peer-address etc...
Definition base.c:573
static fr_dict_attr_t const * attr_transaction_id
Definition base.c:135
static fr_dict_attr_t const * attr_client_id
Definition base.c:128
static fr_value_box_t const * enum_status_code_not_on_link
Definition base.c:160
CONF_SECTION * recv_confirm
Definition base.c:47
process_dhcpv6_sections_t sections
Pointers to various config sections we need to execute.
Definition base.c:70
CONF_SECTION * send_relay_reply
Definition base.c:59
static fr_dict_attr_t const * attr_relay_peer_address
Definition base.c:134
RECV(for_any_server)
Validate a solicit/rebind/confirm message.
Definition base.c:348
fr_process_module_t process_dhcpv6
Definition base.c:1205
Records fields from the original request so we have a known good copy.
Definition base.c:101
Records fields from the original relay-request so we have a known good copy.
Definition base.c:111
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition base.c:213
#define PROCESS_TRACE
Trace each state function as it's entered.
Definition process.h:55
#define PROCESS_CONF_OFFSET(_x)
Definition process.h:79
module_t common
Common fields for all loadable modules.
Common public symbol definition for all process modules.
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
static void send_reply(int sockfd, fr_channel_data_t *reply)
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:56
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
#define request_is_dynamic_client(_x)
Definition request.h:189
int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len)
Trim a talloced sbuff to the minimum length required to represent the contained string.
Definition sbuff.c:433
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1495
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_used(_sbuff_or_marker)
#define fr_sbuff_in_strcpy_literal(_sbuff, _str)
Talloc sbuff extension structure.
Definition sbuff.h:137
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
void * data
Module's instance data.
Definition module.h:293
#define MODULE_RCTX(_ctype)
Definition module.h:259
#define MODULE_INST(_ctype)
Definition module.h:257
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition pair.h:129
#define pair_delete_reply(_pair_or_da)
Delete a fr_pair_t in the reply list.
Definition pair.h:181
unlang_action_t unlang_module_yield_to_section(unlang_result_t *p_result, request_t *request, CONF_SECTION *subcs, rlm_rcode_t default_rcode, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Definition module.c:236
eap_aka_sim_process_conf_t * inst
#define RESUME(_x)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:117
#define PAIR_VERIFY(_x)
Definition pair.h:204
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:4416
section_name_t const * section
Identifier for the section.
#define COMPILE_TERMINATOR
Processing sections which are allowed in this virtual server.