#include <freeradius-devel/io/application.h>
#include <freeradius-devel/server/protocol.h>
-#include <freeradius-devel/server/process.h>
#include <freeradius-devel/unlang/base.h>
#include <freeradius-devel/util/dict.h>
#include <freeradius-devel/util/debug.h>
static fr_dict_attr_t const *attr_message_type;
static fr_dict_attr_t const *attr_yiaddr;
+static fr_dict_attr_t const *attr_packet_type;
extern fr_dict_attr_autoload_t process_dhcpv4_dict_attr[];
fr_dict_attr_autoload_t process_dhcpv4_dict_attr[] = {
{ .out = &attr_message_type, .name = "Message-Type", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv4},
{ .out = &attr_yiaddr, .name = "Your-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_dhcpv4},
+ { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv4},
{ NULL }
};
}
}
-static unlang_action_t mod_process(rlm_rcode_t *p_result, UNUSED module_ctx_t const *mctx, request_t *request)
+typedef struct {
+ uint64_t nothing; // so that the next field isn't at offset 0
+
+ CONF_SECTION *discover;
+ CONF_SECTION *offer;
+ CONF_SECTION *request;
+ CONF_SECTION *decline;
+ CONF_SECTION *ack;
+ CONF_SECTION *nak;
+ CONF_SECTION *release;
+ CONF_SECTION *inform;
+ CONF_SECTION *force_renew;
+#if 0
+ CONF_SECTION *lease_query;
+ CONF_SECTION *lease_unassigned;
+ CONF_SECTION *lease_unknown;
+ CONF_SECTION *lease_active;
+#endif
+ CONF_SECTION *do_not_respond;
+} process_dhcpv4_sections_t;
+
+typedef struct {
+ process_dhcpv4_sections_t sections;
+} process_dhcpv4_t;
+
+#define PROCESS_PACKET_TYPE fr_dhcpv4_packet_code_t
+#define PROCESS_CODE_MAX FR_DHCP_CODE_MAX
+#define PROCESS_CODE_DO_NOT_RESPOND FR_DHCP_DO_NOT_RESPOND
+#define PROCESS_PACKET_CODE_VALID FR_DHCP_PACKET_CODE_VALID
+#define PROCESS_INST process_dhcpv4_t
+#include <freeradius-devel/server/process.h>
+
+RESUME(check_yiaddr)
{
- rlm_rcode_t rcode;
- CONF_SECTION *unlang;
- fr_dict_enum_t const *dv;
fr_pair_t *vp;
- static int reply_ok[] = {
- [0] = FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND,
- [FR_DHCP_DISCOVER] = FR_DHCP_OFFER,
- [FR_DHCP_OFFER] = FR_DHCP_OFFER,
- [FR_DHCP_REQUEST] = FR_DHCP_ACK,
- [FR_DHCP_DECLINE] = 0,
- [FR_DHCP_ACK] = FR_DHCP_ACK,
- [FR_DHCP_NAK] = FR_DHCP_NAK,
- [FR_DHCP_RELEASE] = 0,
- [FR_DHCP_INFORM] = FR_DHCP_ACK,
- [FR_DHCP_LEASE_QUERY] = FR_DHCP_LEASE_ACTIVE, /* not really correct, but whatever */
- };
-
- static int reply_fail[] = {
- [0] = FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND,
- [FR_DHCP_DISCOVER] = 0,
- [FR_DHCP_OFFER] = FR_DHCP_NAK,
- [FR_DHCP_REQUEST] = FR_DHCP_NAK,
- [FR_DHCP_DECLINE] = 0,
- [FR_DHCP_ACK] = FR_DHCP_NAK,
- [FR_DHCP_NAK] = FR_DHCP_NAK,
- [FR_DHCP_RELEASE] = 0,
- [FR_DHCP_INFORM] = FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND,
- [FR_DHCP_LEASE_QUERY] = FR_DHCP_LEASE_UNKNOWN,
- };
-
-#define REPLY_OK(_code, _default) ((_code < NUM_ELEMENTS(reply_ok)) ? reply_ok[_code] : _default)
-#define REPLY_FAIL(_code, _default) ((_code < NUM_ELEMENTS(reply_fail)) ? reply_fail[_code] : _default)
-
- REQUEST_VERIFY(request);
- fr_assert(request->packet->code > 0);
- fr_assert(request->packet->code < NUM_ELEMENTS(reply_ok));
-
- switch (request->request_state) {
- case REQUEST_INIT:
- dhcpv4_packet_debug(request, request->packet, &request->request_pairs, true);
-
- request->component = "dhcpv4";
-
- dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->packet->code));
- if (!dv) {
- REDEBUG("Failed to find value for &request.DHCP-Message-Type");
- RETURN_MODULE_FAIL;
- }
-
- unlang = cf_section_find(unlang_call_current(request), "recv", dv->name);
- if (!unlang) {
- RWDEBUG("Failed to find 'recv %s' section", dv->name);
- request->reply->code = FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND;
- goto send_reply;
- }
-
- RDEBUG("Running 'recv %s' from file %s", cf_section_name2(unlang), cf_filename(unlang));
- if (unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME) < 0) {
- RETURN_MODULE_FAIL;
- }
-
- request->request_state = REQUEST_RECV;
- FALL_THROUGH;
-
- case REQUEST_RECV:
- rcode = unlang_interpret(request);
-
- if (request->master_state == REQUEST_STOP_PROCESSING) {
- *p_result = RLM_MODULE_HANDLED;
- return UNLANG_ACTION_STOP_PROCESSING;
- }
-
- if (rcode == RLM_MODULE_YIELD) return UNLANG_ACTION_YIELD;
-
- /*
- * Allow the admin to explicitly set the reply
- * type.
- */
- vp = fr_pair_find_by_da(&request->reply_pairs, attr_message_type);
- if (vp) {
- request->reply->code = vp->vp_uint8;
- } else switch (rcode) {
- case RLM_MODULE_NOOP:
- case RLM_MODULE_OK:
- case RLM_MODULE_UPDATED:
- request->reply->code = REPLY_OK(request->packet->code, FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND);
- break;
-
- default:
- case RLM_MODULE_REJECT:
- case RLM_MODULE_FAIL:
- request->reply->code = REPLY_FAIL(request->packet->code, FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND);
- break;
-
- case RLM_MODULE_HANDLED:
- if (!request->reply->code) request->reply->code = FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND;
- break;
- }
-
- /*
- * Release / Decline doesn't send a reply, and doesn't run "send Do-Not-Respond"
- */
- if (!request->reply->code) {
- RETURN_MODULE_HANDLED;
- }
-
- /*
- * Offer and ACK MUST have YIADDR.
- */
- if ((request->reply->code == FR_DHCP_OFFER) || (request->reply->code == FR_DHCP_ACK)) {
- vp = fr_pair_find_by_da(&request->reply_pairs, attr_yiaddr);
- if (!vp) {
- REDEBUG("%s packet does not have YIADDR. The client will not receive an IP address.",
- dhcp_message_types[request->reply->code]);
- }
- }
-
- dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->reply->code));
- unlang = NULL;
- if (dv) unlang = cf_section_find(unlang_call_current(request), "send", dv->name);
-
- if (!unlang) goto send_reply;
-
- rerun_nak:
- RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang));
- if (unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME) < 0) {
- RETURN_MODULE_FAIL;
- }
-
- request->request_state = REQUEST_SEND;
- FALL_THROUGH;
-
- case REQUEST_SEND:
- rcode = unlang_interpret(request);
-
- if (request->master_state == REQUEST_STOP_PROCESSING) {
- *p_result = RLM_MODULE_HANDLED;
- return UNLANG_ACTION_STOP_PROCESSING;
- }
-
- if (rcode == RLM_MODULE_YIELD) return UNLANG_ACTION_YIELD;
-
- switch (rcode) {
- case RLM_MODULE_NOOP:
- case RLM_MODULE_OK:
- case RLM_MODULE_UPDATED:
- case RLM_MODULE_HANDLED:
- /* reply is already set */
- break;
-
- default:
- /*
- * If we over-ride an ACK with a NAK, run
- * the NAK section.
- */
- if (request->reply->code != FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND) {
- dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->reply->code));
- RWDEBUG("Failed running 'send %s', trying 'send Do-Not-Respond'", dv->name);
-
- request->reply->code = FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND;
-
- dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->reply->code));
- unlang = NULL;
- if (!dv) goto send_reply;
-
- unlang = cf_section_find(unlang_call_current(request), "send", dv->name);
- if (unlang) goto rerun_nak;
-
- RWDEBUG("Not running 'send %s' section as it does not exist", dv->name);
- }
- break;
- }
-
- send_reply:
- /*
- * Check for "do not respond".
- */
- if (request->reply->code == FR_MESSAGE_TYPE_VALUE_DO_NOT_RESPOND) {
- RDEBUG("Not sending reply to client");
- RETURN_MODULE_HANDLED;
- }
-
- if (RDEBUG_ENABLED) dhcpv4_packet_debug(request, request->reply, &request->reply_pairs, false);
- break;
-
- default:
- RETURN_MODULE_FAIL;
+ vp = fr_pair_find_by_da(&request->reply_pairs, attr_yiaddr);
+ if (!vp) {
+ REDEBUG("%s packet does not have YIADDR. The client will not receive an IP address.",
+ dhcp_message_types[request->reply->code]);
}
- RETURN_MODULE_OK;
+ return CALL_RESUME(send_generic);
+}
+
+static fr_process_state_t const process_state[] = {
+ [FR_DHCP_DISCOVER] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_OFFER,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_OFFER,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_FAIL] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_INVALID] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_DO_NOT_RESPOND,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .recv = recv_generic,
+ .resume = resume_recv_generic,
+ .section_offset = PROCESS_CONF_OFFSET(discover),
+ },
+ [FR_DHCP_OFFER] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_OFFER,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_OFFER,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_FAIL] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_INVALID] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_DO_NOT_RESPOND,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .send = send_generic,
+ .resume = resume_check_yiaddr,
+ .section_offset = PROCESS_CONF_OFFSET(offer),
+ },
+
+ [FR_DHCP_REQUEST] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_ACK,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_ACK,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_NAK,
+ [RLM_MODULE_FAIL] = FR_DHCP_NAK,
+ [RLM_MODULE_INVALID] = FR_DHCP_NAK,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_NAK,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_NAK,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .recv = recv_generic,
+ .resume = resume_recv_generic,
+ .section_offset = PROCESS_CONF_OFFSET(request),
+ },
+ [FR_DHCP_ACK] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_ACK,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_ACK,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_NAK,
+ [RLM_MODULE_FAIL] = FR_DHCP_NAK,
+ [RLM_MODULE_INVALID] = FR_DHCP_NAK,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_NAK,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_NAK,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .send = send_generic,
+ .resume = resume_check_yiaddr,
+ .section_offset = PROCESS_CONF_OFFSET(ack),
+ },
+ [FR_DHCP_NAK] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_NAK,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_NAK,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_NAK,
+ [RLM_MODULE_FAIL] = FR_DHCP_NAK,
+ [RLM_MODULE_INVALID] = FR_DHCP_NAK,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_NAK,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_NAK,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .send = send_generic,
+ .resume = resume_send_generic,
+ .section_offset = PROCESS_CONF_OFFSET(ack),
+ },
+
+ [FR_DHCP_INFORM] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_ACK,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_ACK,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_FAIL] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_INVALID] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_DO_NOT_RESPOND,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .recv = recv_generic,
+ .resume = resume_recv_generic,
+ .section_offset = PROCESS_CONF_OFFSET(inform),
+ },
+
+ [FR_DHCP_RELEASE] = { /* releases are not responded to */
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_DO_NOT_RESPOND,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_FAIL] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_INVALID] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_DO_NOT_RESPOND,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .recv = recv_generic,
+ .resume = resume_recv_generic,
+ .section_offset = PROCESS_CONF_OFFSET(request),
+ },
+
+ [FR_DHCP_DO_NOT_RESPOND] = {
+ .packet_type = {
+ [RLM_MODULE_OK] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOOP] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_UPDATED] = FR_DHCP_DO_NOT_RESPOND,
+
+ [RLM_MODULE_REJECT] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_FAIL] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_INVALID] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_DISALLOW] = FR_DHCP_DO_NOT_RESPOND,
+ [RLM_MODULE_NOTFOUND] = FR_DHCP_DO_NOT_RESPOND,
+ },
+ .rcode = RLM_MODULE_NOOP,
+ .default_reply = FR_DHCP_DO_NOT_RESPOND,
+ .recv = recv_generic,
+ .resume = resume_recv_generic,
+ .section_offset = PROCESS_CONF_OFFSET(do_not_respond),
+ },
+};
+
+static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
+{
+ fr_process_state_t const *state;
+
+ PROCESS_TRACE;
+
+ (void)talloc_get_type_abort_const(mctx->instance, process_dhcpv4_t);
+ fr_assert(PROCESS_PACKET_CODE_VALID(request->packet->code));
+
+ request->component = "dhcpv4";
+ fr_assert(request->dict == dict_dhcpv4);
+
+ UPDATE_STATE(packet);
+
+ dhcpv4_packet_debug(request, request->packet, &request->request_pairs, true);
+
+ return state->recv(p_result, mctx, request);
}
static const virtual_server_compile_t compile_list[] = {
},
COMPILE_TERMINATOR
},
+ .offset = PROCESS_CONF_OFFSET(discover),
},
{
.name = "send",
.name2 = "Offer",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(offer),
},
{
.name = "recv",
},
COMPILE_TERMINATOR
},
+ .offset = PROCESS_CONF_OFFSET(request),
},
{
.name = "send",
.name2 = "Ack",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(ack),
},
{
.name = "send",
.name2 = "NAK",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(nak),
},
{
.name = "recv",
},
COMPILE_TERMINATOR
},
+ .offset = PROCESS_CONF_OFFSET(decline),
},
{
},
COMPILE_TERMINATOR
},
+ .offset = PROCESS_CONF_OFFSET(release),
},
{
.name = "recv",
.name2 = "Inform",
.component = MOD_POST_AUTH,
- },
- {
- .name = "send",
- .name2 = "Do-Not-Respond",
- .component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(inform),
},
+#if 0
+ /*
+ * These are for TCP transport.
+ */
{
.name = "recv",
.name2 = "Lease-Query",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(lease_query),
},
{
.name = "send",
.name2 = "Lease-Unassigned",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(lease_unassigned),
},
{
.name = "send",
.name2 = "Lease-Unknown",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(lease_unknown),
},
{
.name = "send",
.name2 = "Lease-Active",
.component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(lease_active),
+ },
+#endif
+
+ {
+ .name = "send",
+ .name2 = "Do-Not-Respond",
+ .component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(do_not_respond),
},
COMPILE_TERMINATOR