# 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
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.
}
#
-# 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}}}"
}
#
}
-#
-# 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"
-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
#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);
*
*/
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.
{ 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).
* - 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;
}
RHEXDUMP3(data, data_len, "proto_detail decode packet");
+ request->dict = inst->dict;
request->packet->code = inst->code;
/*
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
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
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.
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.
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.
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
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
+++ /dev/null
-/*
- * 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 <freeradius-devel/io/application.h>
-#include <freeradius-devel/server/protocol.h>
-#include <freeradius-devel/server/module.h>
-#include <freeradius-devel/unlang/base.h>
-#include <freeradius-devel/util/dict.h>
-#include <freeradius-devel/server/state.h>
-#include <freeradius-devel/util/debug.h>
-#include <freeradius-devel/radius/radius.h>
-
-#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,
-};
+++ /dev/null
-TARGETNAME := proto_detail_process
-
-ifneq "$(TARGETNAME)" ""
-TARGET := $(TARGETNAME).a
-endif
-
-SOURCES := proto_detail_process.c
-
-TGT_PREREQS := libfreeradius-util.a