From: Alan T. DeKok Date: Wed, 17 Mar 2021 17:46:11 +0000 (-0400) Subject: allow detail files to be in any virtual server. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=99a2d5e6d8c3f4468ac0cdc51bd11d9699fa9561;p=thirdparty%2Ffreeradius-server.git allow detail files to be in any virtual server. Currently not allowed in RADIUS, but that has to be fixed :( --- diff --git a/raddb/sites-available/detail b/raddb/sites-available/detail index 2a14d6b490e..ca24d2e590a 100644 --- a/raddb/sites-available/detail +++ b/raddb/sites-available/detail @@ -12,7 +12,9 @@ server detail { # In v4, all "server" sections MUST start with a "namespace" # parameter. This tells the server which protocol is being used. # - namespace = detail + # RADIUS, or DHCPv4, or any other protocol supported by the server. + # + namespace = radius # # This virtual server will read detail files from the @@ -21,28 +23,16 @@ server detail { directory = ${radacctdir}/detail listen { - # - # This has to be listed first, sorry. - # - # The value given here must match the `namespace` of - # the virtual server which originally created the - # detail files. - # - dictionary = radius - # # Types of packets we are reading. # type = Accounting-Request # - # What transports are being used to read detail - # files. For now, only "file" is supported. - # - # Future releases will allow reading "detail" - # style information from sockets or streams. + # There is no need to specify a transport. + # The default is `file`, which is the only + # one supported. # - transport = file # # Unlike v3, there is no "load_factor" configuration. @@ -231,10 +221,9 @@ server detail { } # -# All packets read by the detail listener are processed through the -# 'recv' section. Note that there is no second name to the section. +# The detail file reader runs the normal RADIUS / DHCP / etc. processing sections. # -recv { +recv Accounting-Request { update request { &Acct-Delay-Time := "%{expr:%{%{Acct-Delay-Time}:-0} + %c - %{%{integer:Event-Timestamp}:-%{integer:Packet-Original-Timestamp}}}" } @@ -253,35 +242,26 @@ recv { # } -# -# All successful packets sent by the detail listener are processed through the -# 'send ok' section. Note that the name is the same for all packet types. # # If the listener is configured with 'track = yes', then the entry in # the detail file is marked up as being "done". Subsequent re-reads # of the same detail file (e.g. on server restart) will skip the # "done" entries. # -send ok { +send Accounting-Response { ok } # -# All failed packets sent by the detail listener are processed through the -# 'send fail' section. Note that the name is the same for all packet types. +# All failed packets sent by the detail listener should be processed +# through the 'send Do-Not-Respond' section. # # If the listener is configured with 'track = yes', then the packet # will be retransmitted by the detail file reader, until the packet # returns "success". See the "limit" subsection above for retransmission # configuration. # -send fail { +send Do-Not-Respond { ok } - -# -# Note that unlike other listeners, there is no "send Do-Not-Respond" -# section. You should just return "fail" instead. -# - } # virtual server "detail" diff --git a/src/listen/detail/all.mk b/src/listen/detail/all.mk index 17905d0fd8e..403e1e0ecc6 100644 --- a/src/listen/detail/all.mk +++ b/src/listen/detail/all.mk @@ -1 +1 @@ -SUBMAKEFILES := proto_detail.mk proto_detail_file.mk proto_detail_work.mk proto_detail_process.mk +SUBMAKEFILES := proto_detail.mk proto_detail_file.mk proto_detail_work.mk diff --git a/src/listen/detail/proto_detail.c b/src/listen/detail/proto_detail.c index 2ae1c49276f..50a548ddc21 100644 --- a/src/listen/detail/proto_detail.c +++ b/src/listen/detail/proto_detail.c @@ -33,7 +33,6 @@ #include "proto_detail.h" extern fr_app_t proto_detail; -static int dictionary_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule); static int type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule); static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule); @@ -51,12 +50,10 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF * */ static CONF_PARSER const proto_detail_config[] = { - { FR_CONF_OFFSET("dictionary", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY | FR_TYPE_REQUIRED, proto_detail_t, - dict), .dflt = "radius", .func = dictionary_parse }, { FR_CONF_OFFSET("type", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY | FR_TYPE_REQUIRED, proto_detail_t, - type_submodule), .func = type_parse }, + type), .func = type_parse }, { FR_CONF_OFFSET("transport", FR_TYPE_VOID, proto_detail_t, io_submodule), - .func = transport_parse }, + .func = transport_parse, .dflt = "file" }, /* * Add this as a synonym so normal humans can understand it. @@ -106,33 +103,6 @@ fr_dict_attr_autoload_t proto_detail_dict_attr[] = { { NULL } }; -/** Wrapper around fr_dict_t* which translates the dictionary name into a dictionary - * - * @param[in] ctx to allocate data in (instance of proto_detail). - * @param[out] out Where to write a fr_dict_t * - * @param[in] parent Base structure address. - * @param[in] ci #CONF_PAIR specifying the name of the type module. - * @param[in] rule unused. - * @return - * - 0 on success. - * - -1 on failure. - */ -static int dictionary_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) -{ - char const *dict_str = cf_pair_value(cf_item_to_pair(ci)); - fr_dict_t const *dict; - - dict = fr_dict_by_protocol_name(dict_str); - if (!dict) { - cf_log_err(ci, "Unknown dictionary"); - return -1; - } - - *(fr_dict_t **) out = fr_dict_unconst(dict); - - return 0; -} - /** Wrapper around dl_instance which translates the packet-type into a submodule name * * @param[in] ctx to allocate data in (instance of proto_detail). @@ -144,41 +114,39 @@ static int dictionary_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *pare * - 0 on success. * - -1 on failure. */ -static int type_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) +static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { - dl_module_inst_t *process_dl; - proto_detail_process_t *process_inst; - fr_dict_attr_t const *attr_packet_type; proto_detail_t *inst = talloc_get_type_abort(parent, proto_detail_t); - int code; + fr_dict_enum_t const *type_enum; + CONF_PAIR *cp = cf_item_to_pair(ci); + char const *value = cf_pair_value(cp); + *((char const **) out) = value; + + inst->dict = virtual_server_namespace_by_ci(ci); if (!inst->dict) { - cf_log_err(ci, "Please define 'dictionary' BEFORE 'type'"); + cf_log_err(ci, "Please define 'namespace' in this virtual server"); return -1; } - attr_packet_type = fr_dict_attr_by_name(NULL, fr_dict_root(inst->dict), "Packet-Type"); - if (!attr_packet_type) { + inst->attr_packet_type = fr_dict_attr_by_name(NULL, fr_dict_root(inst->dict), "Packet-Type"); + if (!inst->attr_packet_type) { cf_log_err(ci, "Failed to find 'Packet-Type' attribute"); return -1; } - code = fr_app_process_type_parse(ctx, out, ci, attr_packet_type, "proto_detail", - NULL, 0, NULL, 0); - if (code < 0) return -1; - - inst->code = code; - - /* - * Find the process module, and tell it what dictionary - * and packet type to use. - */ - process_dl = *(dl_module_inst_t **) out; - process_inst = inst->process_instance = talloc_get_type_abort(process_dl->data, proto_detail_process_t); + if (!value) { + cf_log_err(ci, "No value given for 'type'"); + return -1; + } - process_inst->dict = inst->dict; - process_inst->attr_packet_type = attr_packet_type; + type_enum = fr_dict_enum_by_name(inst->attr_packet_type, value, -1); + if (!type_enum) { + cf_log_err(ci, "Invalid type \"%s\"", value); + return -1; + } + inst->code = type_enum->value->vb_uint32; return 0; } @@ -230,6 +198,7 @@ static int mod_decode(void const *instance, request_t *request, uint8_t *const d RHEXDUMP3(data, data_len, "proto_detail decode packet"); + request->dict = inst->dict; request->packet->code = inst->code; /* @@ -376,19 +345,11 @@ static ssize_t mod_encode(UNUSED void const *instance, request_t *request, uint8 return 1; } -static void mod_entry_point_set(void const *instance, request_t *request) +static void mod_entry_point_set(UNUSED void const *instance, request_t *request) { - proto_detail_t const *inst = talloc_get_type_abort_const(instance, proto_detail_t); - fr_app_worker_t const *app_process; - - /* - * Only one "process" function: proto_detail_process. - */ - app_process = (fr_app_worker_t const *)inst->type_submodule->module->common; + fr_assert(request->server_cs != NULL); - request->server_cs = inst->server_cs; - request->async->process = app_process->entry_point; - request->async->process_inst = inst->process_instance; + virtual_server_entry_point_set(request); } /** Open listen sockets/connect to external event source @@ -482,7 +443,6 @@ static int mod_open(void *instance, fr_schedule_t *sc, CONF_SECTION *conf) static int mod_instantiate(void *instance, CONF_SECTION *conf) { proto_detail_t *inst = talloc_get_type_abort(instance, proto_detail_t); - CONF_PAIR *cp = NULL; /* * Instantiate the I/O module. But DON'T instantiate the @@ -495,26 +455,6 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf) return -1; } - /* - * Instantiate the process module. - */ - while ((cp = cf_pair_find_next(conf, cp, "type")) != NULL) { - fr_app_worker_t const *app_process; - -#ifdef __clang_analyzer__ - DEBUG("Instantiating %s", cf_pair_value(cp)); -#endif - - app_process = (fr_app_worker_t const *)inst->type_submodule->module->common; - if (app_process->instantiate && (app_process->instantiate(inst->type_submodule->data, - inst->type_submodule->conf) < 0)) { - cf_log_err(conf, "Instantiation failed for \"%s\"", app_process->name); - return -1; - } - - break; - } - /* * These configuration items are not printed by default, * because normal people shouldn't be touching them. @@ -558,7 +498,6 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf) static int mod_bootstrap(void *instance, CONF_SECTION *conf) { proto_detail_t *inst = talloc_get_type_abort(instance, proto_detail_t); - CONF_PAIR *cp = NULL; /* * The listener is inside of a virtual server. @@ -569,21 +508,6 @@ static int mod_bootstrap(void *instance, CONF_SECTION *conf) virtual_server_dict_set(inst->server_cs, inst->dict, false); - /* - * Bootstrap the process module. - */ - while ((cp = cf_pair_find_next(conf, cp, "type"))) { - dl_module_t const *module = talloc_get_type_abort_const(inst->type_submodule->module, - dl_module_t); - fr_app_worker_t const *app_process = (fr_app_worker_t const *)(module->common); - - if (app_process->bootstrap && (app_process->bootstrap(inst->type_submodule->data, - inst->type_submodule->conf) < 0)) { - cf_log_err(conf, "Bootstrap failed for \"%s\"", app_process->name); - return -1; - } - } - /* * No IO module, it's an empty listener. That's not * allowed for the detail file reader. diff --git a/src/listen/detail/proto_detail.h b/src/listen/detail/proto_detail.h index ac005661dde..376e1e63ef4 100644 --- a/src/listen/detail/proto_detail.h +++ b/src/listen/detail/proto_detail.h @@ -33,15 +33,11 @@ RCSIDH(detail_h, "$Id$") extern "C" { #endif -typedef struct { - fr_dict_t *dict; //!< root dictionary - fr_dict_attr_t const *attr_packet_type; -} proto_detail_process_t; - typedef struct { CONF_SECTION *server_cs; //!< server CS for this listener CONF_SECTION *cs; //!< my configuration fr_app_t *self; //!< child / parent linking issues + char const *type; //!< packet type name dl_module_inst_t *io_submodule; //!< As provided by the transport_parse ///< callback. Broken out into the @@ -58,10 +54,8 @@ typedef struct { void *work_io_instance; //!< Easy access to the app_io instance. CONF_SECTION *work_io_conf; //!< Easy access to the app_io's config secti - void *process_instance; //!< app_process instance - - fr_dict_t *dict; //!< root dictionary - dl_module_inst_t *type_submodule; //!< Instance of the type + fr_dict_t const *dict; //!< root dictionary + fr_dict_attr_t const *attr_packet_type; uint32_t code; //!< packet code to use for incoming packets uint32_t max_packet_size; //!< for message ring buffer diff --git a/src/listen/detail/proto_detail_process.c b/src/listen/detail/proto_detail_process.c deleted file mode 100644 index a27a33b292d..00000000000 --- a/src/listen/detail/proto_detail_process.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * $Id$ - * @file proto_detail_process.c - * @brief Detail file processing - * - * @copyright 2017 The FreeRADIUS server project. - * @copyright 2017 Alan DeKok (aland@deployingradius.com) - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "proto_detail.h" - -static fr_dict_t const *dict_freeradius; - -extern fr_dict_autoload_t proto_detail_process_dict[]; -fr_dict_autoload_t proto_detail_process_dict[] = { - { .out = &dict_freeradius, .proto = "freeradius" }, - - { NULL } -}; - -extern fr_dict_attr_autoload_t proto_detail_process_dict_attr[]; -fr_dict_attr_autoload_t proto_detail_process_dict_attr[] = { - { NULL } -}; - -static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) -{ - proto_detail_process_t const *inst = talloc_get_type_abort_const(mctx->instance, proto_detail_process_t); - fr_pair_t *vp; - rlm_rcode_t rcode; - CONF_SECTION *unlang; - - REQUEST_VERIFY(request); - - switch (request->request_state) { - case REQUEST_INIT: - RDEBUG("Received %s ID %i", - fr_dict_enum_name_by_value(inst->attr_packet_type, fr_box_uint32(request->packet->code)), - request->packet->id); - log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->request_pairs, NULL); - - request->component = "radius"; - - unlang = cf_section_find(request->server_cs, "recv", NULL); - if (!unlang) { - REDEBUG("Failed to find 'recv' section"); - RETURN_MODULE_FAIL; - } - - RDEBUG("Running 'recv' from file %s", 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_MODULE_YIELD; - - switch (rcode) { - /* - * The module has a number of OK return codes. - */ - case RLM_MODULE_OK: - case RLM_MODULE_UPDATED: - switch (request->packet->code) { - case FR_CODE_ACCOUNTING_REQUEST: - request->reply->code = FR_CODE_ACCOUNTING_RESPONSE; - break; - - case FR_CODE_COA_REQUEST: - request->reply->code = FR_CODE_COA_ACK; - break; - - case FR_CODE_DISCONNECT_REQUEST: - request->reply->code = FR_CODE_DISCONNECT_ACK; - break; - - default: - request->reply->code = 0; - break; - } - FALL_THROUGH; - - case RLM_MODULE_HANDLED: - unlang = cf_section_find(request->server_cs, "send", "ok"); - break; - - /* - * The module failed, or said the request is - * invalid, therefore we stop here. - */ - case RLM_MODULE_NOOP: - case RLM_MODULE_FAIL: - case RLM_MODULE_INVALID: - case RLM_MODULE_NOTFOUND: - case RLM_MODULE_REJECT: - case RLM_MODULE_DISALLOW: - default: - request->reply->code = 0; - unlang = cf_section_find(request->server_cs, "send", "fail"); - break; - } - - /* - * Allow for over-ride of reply code. - */ - vp = fr_pair_find_by_da(&request->reply_pairs, inst->attr_packet_type); - if (vp) request->reply->code = vp->vp_uint32; - - if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { - RWARN("Ignoring 'do_not_respond' as it does not apply to detail files"); - } - - if (!unlang) goto send_reply; - - 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_MODULE_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: - request->reply->code = 0; - break; - } - - send_reply: - /* - * Failed, but we still reply with a magic code, - * so that the reader can retransmit. - */ - if (!request->reply->code) { - REDEBUG("Failed ID %i", request->reply->id); - } else { - RDEBUG("Sent %s ID %i", - fr_dict_enum_name_by_value(inst->attr_packet_type, fr_box_uint32(request->reply->code)), - request->reply->id); - } - - log_request_proto_pair_list(L_DBG_LVL_1, request, NULL, &request->reply_pairs, NULL); - break; - - default: - RETURN_MODULE_FAIL; - } - - RETURN_MODULE_OK; -} - - -static const virtual_server_compile_t compile_list[] = { - { .name = "recv", .name2 = NULL, .component = MOD_AUTHORIZE }, - { .name = "send", .name2 = "ok", .component = MOD_POST_AUTH }, - { .name = "send", .name2 = "fail", .component = MOD_POST_AUTH }, - - COMPILE_TERMINATOR -}; - - -static int mod_instantiate(void *instance, CONF_SECTION *conf) -{ - proto_detail_process_t *inst = talloc_get_type_abort(instance, proto_detail_process_t); - CONF_SECTION *server_cs; - tmpl_rules_t parse_rules; - - /* - * The detail file reader gets its dictionary from the - * configuration, and not from a static protocol. So we - * have to set it manually. - */ - memset(&parse_rules, 0, sizeof(parse_rules)); - parse_rules.dict_def = inst->dict; - - /* - * We are passed a CONF_SECTION for our configuration. - * The parent is the listener, and it's parent is the - * virtual server. - */ - server_cs = cf_item_to_section(cf_parent(cf_parent(conf))); - fr_assert(strcmp(cf_section_name1(server_cs), "server") == 0); - - return virtual_server_compile_sections(server_cs, compile_list, &parse_rules, inst); -} - - -extern fr_app_worker_t proto_detail_process; -fr_app_worker_t proto_detail_process = { - .magic = RLM_MODULE_INIT, - .name = "detail_process", - .inst_size = sizeof(proto_detail_process_t), - .instantiate = mod_instantiate, - .entry_point = mod_process, -}; diff --git a/src/listen/detail/proto_detail_process.mk b/src/listen/detail/proto_detail_process.mk deleted file mode 100644 index 1a1d89f2468..00000000000 --- a/src/listen/detail/proto_detail_process.mk +++ /dev/null @@ -1,9 +0,0 @@ -TARGETNAME := proto_detail_process - -ifneq "$(TARGETNAME)" "" -TARGET := $(TARGETNAME).a -endif - -SOURCES := proto_detail_process.c - -TGT_PREREQS := libfreeradius-util.a