It doesn't work yet, but the old tests pass and i'm tired.
typedef void _mismatch_size_m; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
typedef void _mismatch_timeval; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
typedef void _mismatch_timeval_m; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
+typedef void _mismatch_void; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
+typedef void _mismatch_void_m; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
typedef void _mismatch_default; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
typedef void conf_type_mismatch; //!< Dummy type used to indicate FR_TYPE_*/C type mismatch.
_p, (_mismatch_timeval) 0), \
_timeval_t ** : __builtin_choose_expr((FR_BASE_TYPE(_t) == FR_TYPE_TIMEVAL) && ((_t) & FR_TYPE_MULTI), \
_p, (_mismatch_timeval_m) 0), \
+ void * : __builtin_choose_expr((FR_BASE_TYPE(_t) == FR_TYPE_VOID) && !((_t) & FR_TYPE_MULTI), \
+ _p, (_mismatch_void) 0), \
+ void ** : __builtin_choose_expr((FR_BASE_TYPE(_t) == FR_TYPE_VOID) && ((_t) & FR_TYPE_MULTI), \
+ _p, (_mismatch_void_m) 0), \
default: (conf_type_mismatch)0))))))
# define FR_CONF_OFFSET(_n, _t, _s, _f) \
* user-supplied variables.
*/
#define FR_TYPE_SUBSECTION 102
+#define FR_TYPE_VOID 103
/** @name #CONF_PARSER type flags
*
dl_common_t const *common; //!< Symbol exported by the module, containing its public
//!< functions, name and behaviour control flags.
- CONF_SECTION *conf; //!< The module's configuration (as opposed to the instance,
+ CONF_SECTION *conf; //!< The module's global configuration (as opposed to the instance,
//!< configuration). May be NULL.
void *handle; //!< Handle returned by dlopen.
};
+/** A module/inst tuple
+ *
+ * Used to pass data back from dl_submodule_parse_func
+ */
+typedef struct {
+ dl_t const *module;
+ void *inst;
+ CONF_SECTION *conf; //!< Module's instance configuration.
+} dl_submodule_t;
+
+
int dl_symbol_init_cb_register(char const *symbol, dl_init_t func, void *ctx);
void dl_symbol_init_cb_unregister(char const *symbol, dl_init_t func);
void dl_symbol_free_cb_unregister(char const *symbol, dl_free_t func);
-int dl_instance_data_alloc(void **out, TALLOC_CTX *ctx, dl_t const *module, CONF_SECTION *cs);
+int dl_instance_data_alloc(TALLOC_CTX *ctx, void **out, dl_t const *module, CONF_SECTION *cs);
dl_t const *dl_by_symbol(void *sym);
dl_t const *dl_module(CONF_SECTION *conf, dl_t const *parent, char const *name, dl_type_t type);
+int dl_submodule(TALLOC_CTX *ctx, dl_submodule_t **out,
+ CONF_SECTION *conf, dl_t const *parent, char const *name);
+
#ifdef __cplusplus
}
#endif
#else
# define MODULE_NULL_COA_FUNCS
#endif
+extern const CONF_PARSER virtual_servers_config[];
+int virtual_servers_open(fr_schedule_t *sc);
+int virtual_servers_instantiate(CONF_SECTION *config);
int virtual_servers_bootstrap(CONF_SECTION *config);
-int virtual_servers_init(fr_schedule_t *sc, CONF_SECTION *config);
/*
* In unlang_interpret.c, but here for public consumption.
typedef int (*rad_listen_unlang_t)(CONF_SECTION *, CONF_SECTION *);
typedef void (*rad_listen_free_t)(rad_listen_t *);
+/*
+ * @todo: fix for later
+ */
+int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this);
+int common_socket_open(CONF_SECTION *cs, rad_listen_t *this);
+int common_socket_print(rad_listen_t const *this, char *buffer, size_t bufsize);
+void common_packet_debug(REQUEST *request, RADIUS_PACKET *packet, bool received);
+
/** Struct exported by a proto_* module
*
* Used to pass information common to proto_* modules to the server core,
#define TRANSPORT_UDP (1 << IPPROTO_UDP)
#define TRANSPORT_DUAL (TRANSPORT_UDP | TRANSPORT_TCP)
-/*
- * @todo: fix for later
- */
-int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this);
-int common_socket_open(CONF_SECTION *cs, rad_listen_t *this);
-int common_socket_print(rad_listen_t const *this, char *buffer, size_t bufsize);
-void common_packet_debug(REQUEST *request, RADIUS_PACKET *packet, bool received);
-
-typedef int (*fr_app_bootstrap_t)(CONF_SECTION *);
-
-/*
- * src/lib/io/io.h
- */
-typedef struct fr_io_op_t fr_io_op_t;
-
-/** Validate configurable elements of an fr_ctx_t
- *
- * @param[in] io_cs Configuration describing the I/O mechanism.
- * @param[in] instance data. Pre-populated by parsing io_cs.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-typedef int (*fr_app_io_instantiate_t)(CONF_SECTION *io_cs, void *instance);
-
-/** Public structure describing an I/O path for a protocol
- *
- * This structure is exported by I/O modules e.g. proto_radius_udp.
- */
-typedef struct fr_app_io_t {
- RAD_MODULE_COMMON; //!< Common fields to all loadable modules.
-
- fr_app_io_instantiate_t instantiate; //!< Perform any config validation, and per-instance work.
- fr_io_op_t op; //!< Open/close/read/write functions for sending/receiving
- //!< protocol data.
-} fr_app_io_t;
-
-/*
- * src/lib/io/schedule.h
- */
-typedef struct fr_schedule_t fr_schedule_t;
-typedef int (*fr_app_instantiate_t)(fr_schedule_t *sc, CONF_SECTION *cs, bool validate_config);
-
-/** Describes a new application (protocol)
- *
- */
-typedef struct fr_app_t {
- RAD_MODULE_COMMON; //!< Common fields to all loadable modules.
-
- fr_app_bootstrap_t bootstrap;
- fr_app_instantiate_t instantiate;
-} fr_app_t;
-
-typedef int (*fr_app_subtype_instantiate_t)(CONF_SECTION *cs);
-
-/** Public structure describing an application (protocol) specialisation
- *
- * Some protocols perform multiple distinct functions, and use
- * different state machines to perform those functions.
- */
-typedef struct fr_app_subtype_t {
- RAD_MODULE_COMMON; //!< Common fields to all loadable modules.
-
- fr_app_subtype_instantiate_t instantiate; //!< Perform any config validation, and per-instance work.
- fr_io_process_t process; //!< Entry point into the protocol subtype's state machine.
-} fr_app_subtype_t;
-
#ifdef __cplusplus
}
#endif
*/
typedef struct fr_io_op_t fr_io_op_t;
+typedef int (*fr_app_open_t)(void *instance, fr_schedule_t *sc, CONF_SECTION *cs);
+typedef int (*fr_app_instantiate_t)(void *instance, CONF_SECTION *cs);
+typedef int (*fr_app_bootstrap_t)( void *instance, CONF_SECTION *cs);
/** Set the next state executed by the request to be one of the application subtype's entry points
*
* @param[in] request To set the next state function for.
*/
-typedef void (*fr_app_op_set_process_t)(REQUEST *request);
+typedef void (*fr_app_set_process_t)(REQUEST *request, void const *uctx);
-/** Public functions exported by the application
+/** Allows submodules to receive uctx data (a structure provided by their parent)
*
+ * @param[in] instance of #fr_app_subtype_t or #fr_app_io_t.
+ * @param[in] uctx provided by caller.
*/
- typedef struct {
- fr_app_op_set_process_t set_process;
- } fr_app_op_t;
-
-typedef int (*fr_app_instantiate_t)(fr_schedule_t *sc, fr_conf_section_t *cs, bool validate_config);
-typedef int (*fr_app_bootstrap_t)(fr_conf_section_t *cs);
+typedef void (*fr_app_set_uctx_t)(void *instance, void *uctx);
/** Describes a new application (protocol)
*
fr_app_bootstrap_t bootstrap;
fr_app_instantiate_t instantiate;
-
- fr_app_op_t op; //!< Public functions for apps.
+ fr_app_open_t open; //!< Open listen sockets.
+ fr_app_set_process_t set_process;
} fr_app_t;
-typedef int (*fr_app_subtype_instantiate_t)(fr_conf_section_t *cs);
-
/** Public structure describing an application (protocol) specialisation
*
* Some protocols perform multiple distinct functions, and use
typedef struct fr_app_subtype_t {
RAD_MODULE_COMMON; //!< Common fields to all loadable modules.
- fr_app_subtype_instantiate_t instantiate; //!< Perform any config validation, and per-instance work.
+ fr_app_bootstrap_t bootstrap;
+ fr_app_instantiate_t instantiate;
+ fr_app_set_uctx_t set_uctx; //!< Allow the submodule to receive data from the main module.
fr_io_process_t process; //!< Entry point into the protocol subtype's state machine.
} fr_app_subtype_t;
-/** Validate configurable elements of an fr_ctx_t
- *
- * @param[in] io_cs Configuration describing the I/O mechanism.
- * @param[in] instance data. Pre-populated by parsing io_cs.
- * @return
- * - 0 on success.
- * - -1 on failure.
- */
-typedef int (*fr_app_io_instantiate_t)(fr_conf_section_t *io_cs, void *instance);
-
/** Public structure describing an I/O path for a protocol
*
* This structure is exported by I/O modules e.g. proto_radius_udp.
typedef struct fr_app_io_t {
RAD_MODULE_COMMON; //!< Common fields to all loadable modules.
- fr_app_io_instantiate_t instantiate; //!< Perform any config validation, and per-instance work.
+ fr_app_bootstrap_t bootstrap;
+ fr_app_instantiate_t instantiate;
+ fr_app_set_uctx_t set_uctx; //!< Allow the submodule to receive data from the main module.
fr_io_op_t op; //!< Open/close/read/write functions for sending/receiving
//!< protocol data.
} fr_app_io_t;
#include <freeradius-devel/event.h>
#include <freeradius-devel/io/time.h>
#include <freeradius-devel/io/channel.h>
-//#include <freeradius-devel/io/io.h>
#ifdef __cplusplus
extern "C" {
*
* No data will be read from or written to the fd, except by the io_data callbacks here.
*
- * @param[in] io_ctx the context for this function
+ * @param[in] instance the context for this function
* @return
* - 0 on success
* - <0 on error
*/
-typedef int (*fr_io_open_t)(void *io_ctx);
+typedef int (*fr_io_open_t)(void *instance);
/** Return a selectable file descriptor for this I/O path
*
* Return the file descriptor associated with this I/O path.
*
- * @param[in] io_ctx containing the file descriptor (amongst other things).
+ * @param[in] instance containing the file descriptor (amongst other things).
*/
-typedef int (*fr_io_get_fd_t)(void *io_ctx);
+typedef int (*fr_io_get_fd_t)(void const *instance);
/** Decode a raw packet and convert it into a request.
*
* know anything about how the data will be used (e.g. authorize,
* authenticate, etc. for Access-Request)
*
- * @param[in] io_ctx the context for this function.
+ *
* @param[in] data the raw packet data
* @param[in] data_len the length of the raw data
* @param[in,out] request where the decoded VPs should be placed.
+ * @param[in] instance the context for this function.
* @return
* - <0 on error
* - 0 on success
*/
-typedef int (*fr_io_decode_t)(void *io_ctx, uint8_t *const data, size_t data_len, REQUEST *request);
+typedef int (*fr_io_decode_t)(void const *instance, REQUEST *request, uint8_t *const data, size_t data_len);
/** Encode data from a REQUEST into a raw packet.
*
* know anything about how the data will be used (e.g. reject delay
* on Access-Reject)
*
- * @param[in] io_ctx the context for this function.
+
* @param[in,out] request where the VPs to be encoded are located
* @param[in] buffer the buffer where the raw packet will be written
* @param[in] buffer_len the length of the buffer
+ * @param[in] instance the context for this function.
* @return
* - <0 on error
* - >=0 length of the encoded data in the buffer, will be <=buffer_len
*/
-typedef ssize_t (*fr_io_encode_t)(void *io_ctx, REQUEST *request, uint8_t *buffer, size_t buffer_len);
+typedef ssize_t (*fr_io_encode_t)(void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len);
/** NAK a packet.
*
* take the appropriate action. e.g. for RADIUS, mark a request as
* "do not respond", even if duplicates come in.
*
- * @param[in] io_ctx the context for this function.
+ * @param[in] instance the context for this function.
* @param[in] packet the packet to NAK
* @param[in] packet_len length of the packet to NAK
* @param[in] reply the NAK reply
* @param[in] reply_len length of the buffer where the reply should be placed.
* @return length of the data in the reply buffer.
*/
-typedef size_t (*fr_io_nak_t)(void const *io_ctx, uint8_t *const packet, size_t packet_len,
+typedef size_t (*fr_io_nak_t)(void const *instance, uint8_t *const packet, size_t packet_len,
uint8_t *reply, size_t reply_len);
-/** Read/write from a socket.
+/** Read from a socket.
+ *
+ * If the socket is a datagram socket, then the function can read or
+ * write directly into the buffer. Stream sockets are a bit more complicated.
+ *
+ * A stream reader can read data into the buffer, and be guaranteed
+ * that the data will not change in between subsequent calls to the
+ * read routine.
+ *
+ * A stream writer MUST be prepared for the caller to delete the data
+ * immediately after calling the write routine. This means that if
+ * the socket is not ready, the writer MUST copy the data to an
+ * internal buffer, usually in instance. It MUST then have a
+ * write callback on the socket, which is called when the socket is
+ * ready for writing. That callback can then write the internal
+ * buffer to the socket.
+ *
+ * i.e. this write() function is a way for the network thread to
+ * write packets to the transport context. The data may or may not
+ * go out to the network right away.
+ *
+ * If the writer returns LESS THAN buffer_len, that's a special case
+ * saying "I took saved the data, but the socket wasn't ready, so you
+ * need to call me again at a later point".
+ *
+ * @param[in] instance the context for this function
+ * @param[out] packet_ctx Where to write a newly allocated packet_ctx struct containing request specific data.
+ * @param[in,out] buffer the buffer where the raw packet will be written to (or read from)
+ * @param[in] buffer_len the length of the buffer
+ * @return
+ * - <0 on error
+ * - >=0 length of the data read or written.
+ */
+typedef ssize_t (*fr_io_data_read_t)(void const *instance, void **packet_ctx, uint8_t *buffer, size_t buffer_len);
+
+/** Write a socket.
*
* If the socket is a datagram socket, then the function can read or
* write directly into the buffer. Stream sockets are a bit more complicated.
* A stream writer MUST be prepared for the caller to delete the data
* immediately after calling the write routine. This means that if
* the socket is not ready, the writer MUST copy the data to an
- * internal buffer, usually in io_ctx. It MUST then have a
+ * internal buffer, usually in instance. It MUST then have a
* write callback on the socket, which is called when the socket is
* ready for writing. That callback can then write the internal
* buffer to the socket.
* saying "I took saved the data, but the socket wasn't ready, so you
* need to call me again at a later point".
*
- * @param[in] io_ctx the context for this function
* @param[in,out] buffer the buffer where the raw packet will be written to (or read from)
* @param[in] buffer_len the length of the buffer
+ * @param[in] instance the context for this function
+ * @param[in] packet_ctx Request specific data.
* @return
* - <0 on error
* - >=0 length of the data read or written.
*/
-typedef ssize_t (*fr_io_data_t)(void *io_ctx, uint8_t *buffer, size_t buffer_len);
+typedef ssize_t (*fr_io_data_write_t)(void const *instance, void *packet_ctx, uint8_t *buffer, size_t buffer_len);
/** Handle a close or error on the socket.
*
* before "close". On normal finish, the "close" function will be
* called.
*
- * @param[in] io_ctx the context for this function
+ * @param[in] instance the context for this function
* @return
* - 0 on success
* - <0 on error
*/
-typedef int (*fr_io_signal_t)(void *io_ctx);
+typedef int (*fr_io_signal_t)(void const *instance);
/** Process a request through the transport async state machine.
*
char const *name; //!< Name of this transport
size_t default_message_size; // Usually minimum message size
- fr_io_decode_t decode; //!< Function to decode packet to request (worker)
- fr_io_encode_t encode; //!< Function to encode request to packet (worker)
-
fr_io_open_t open; //!< Open a new socket for listening, or accept/connect a new
//!< connection.
- fr_io_get_fd_t fd; //!< Return the file descriptor from the io_ctx.
- fr_io_data_t read; //!< Read from a socket to a data buffer
- fr_io_data_t write; //!< Write from a data buffer to a socket
+ fr_io_get_fd_t fd; //!< Return the file descriptor from the instance.
+ fr_io_data_read_t read; //!< Read from a socket to a data buffer
+ fr_io_data_write_t write; //!< Write from a data buffer to a socket
fr_io_signal_t flush; //!< Flush the data when the socket is ready for writing.
fr_io_signal_t error; //!< There was an error on the socket.
fr_io_signal_t close; //!< Close the transport.
fr_io_nak_t nak; //!< Function to send a NAK.
} fr_io_op_t;
-typedef struct fr_io fr_io_t;
-struct fr_io {
- void *ctx; //!< I/O path specific context.
- fr_io_op_t const *op; //!< I/O path functions.
- void *parent; //!< Parent usually the proto_* ctx.
-};
-
-#ifndef _FR_RADIUSD_H
-/**
- * Minimal data structure to use the new code.
- */
-struct rad_request {
- uint64_t number;
- int heap_id;
-
- fr_dlist_t time_order; //!< tracking requests by time order
- fr_heap_t *runnable; //!< heap of runnable requests
-
- fr_time_t recv_time;
- fr_time_t *original_recv_time;
- fr_event_list_t *el;
- fr_io_process_t process_async;
- fr_time_tracking_t tracking;
- fr_channel_t *channel;
-
- uint32_t priority;
- fr_io_t const *io; //!< How we received this request,
- //!< and how we'll send the reply.
-};
-#endif
-
#ifdef __cplusplus
}
#endif
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#ifndef _FR_LISTEN_H
-#define _FR_LISTEN_H
+#ifndef _FR_IO_LISTEN_H
+#define _FR_IO_LISTEN_H
#include <freeradius-devel/io/io.h>
#include <freeradius-devel/io/application.h>
typedef struct fr_io fr_io_t;
struct fr_io {
fr_io_op_t const *op; //!< I/O path functions.
- void const *ctx; //!< I/O path specific context.
-
- fr_app_op_t const *app_op; //!< Application functions.
- void const *app_ctx; //!< Application context.
+ void *ctx; //!< I/O path specific context.
fr_io_decode_t decode; //!< Function to decode packet to request (worker)
fr_io_encode_t encode; //!< Function to encode request to packet (worker)
+
+ fr_app_set_process_t set_process; //!< Set the state machine entry point for a request.
+ void const *app_ctx;
};
#ifndef _FR_RADIUSD_H
#include <freeradius-devel/io/control.h>
#include <freeradius-devel/io/worker.h>
#include <freeradius-devel/io/network.h>
+#include <freeradius-devel/io/listen.h>
typedef struct fr_network_worker_t {
int heap_id; //!< workers are in a heap
* network side knows that it needs to close the
* connection.
*/
- data_size = s->io->op->read(s->io->ctx, cd->m.data, cd->m.rb_size);
+ data_size = s->io->op->read(s->io->ctx, &cd->packet_ctx, cd->m.data, cd->m.rb_size);
if (data_size == 0) {
fr_log(nr->log, L_DBG_ERR, "got no data from transport read");
* the reply is a NAK, don't write it to the
* network.
*/
- rcode = io->op->write(io->ctx, cd->m.data, cd->m.data_size);
+ rcode = io->op->write(io->ctx, cd->packet_ctx, cd->m.data, cd->m.data_size);
if (rcode < 0) {
fr_network_socket_t my_socket, *s;
typedef struct fr_schedule_t fr_schedule_t;
typedef int (*fr_schedule_thread_instantiate_t)(void *ctx);
-fr_schedule_t *fr_schedule_create(TALLOC_CTX *ctx, fr_log_t *log, int max_inputs, int max_workers,
- fr_schedule_thread_instantiate_t worker_thread_instantiate,
- void *worker_thread_ctx);
+fr_schedule_t *fr_schedule_create(TALLOC_CTX *ctx, fr_log_t *log, int max_inputs, int max_workers,
+ fr_schedule_thread_instantiate_t worker_thread_instantiate,
+ void *worker_thread_ctx);
/* schedulers are async, so there's no fr_schedule_run() */
-int fr_schedule_destroy(fr_schedule_t *sc);
-int fr_schedule_get_worker_kq(fr_schedule_t *sc);
+int fr_schedule_destroy(fr_schedule_t *sc);
+int fr_schedule_get_worker_kq(fr_schedule_t *sc);
-fr_network_t *fr_schedule_socket_add(fr_schedule_t *sc, fr_io_t const *io) CC_HINT(nonnull);
+fr_network_t *fr_schedule_socket_add(fr_schedule_t *sc, fr_io_t const *io) CC_HINT(nonnull);
#ifdef __cplusplus
}
#include <freeradius-devel/io/worker.h>
#include <freeradius-devel/io/channel.h>
#include <freeradius-devel/io/message.h>
+#include <freeradius-devel/io/listen.h>
/**
* Track things by priority and time.
if (size) {
ssize_t encoded;
- encoded = request->io->op->encode(request->io->ctx, request, reply->m.data, reply->m.rb_size);
+ encoded = request->io->encode(request->io->ctx, request, reply->m.data, reply->m.rb_size);
if (encoded < 0) {
fr_log(worker->log, L_DBG, "\t%sfails encode", worker->name);
encoded = 0;
*
* Note that this also sets the "process_async" function.
*/
- rcode = io->op->decode(io->ctx, cd->m.data, cd->m.data_size, request);
+ rcode = io->decode(io->ctx, request, cd->m.data, cd->m.data_size);
if (rcode < 0) {
fr_log(worker->log, L_DBG, "\t%sFAILED decode of request %"PRIu64, worker->name, request->number);
talloc_free(ctx);
* @param[in] ctx to allocate any additional structures under.
* @param[out] out pointer to a struct/pointer to fill with data.
* @param[in] cs to parse.
- * @param[in] name of subsection to parse.
- * @param[in] type flags.
- * @param[in] rules to push for subsections.
- * @param[in] subcs_size size of subsection structures to allocate.
+ * @param[in] rule to parse the subcs with.
* @return
* - 0 on success.
* - -1 on general error.
* - -2 if a deprecated #CONF_ITEM was found.
*/
-static int cf_subsection_parse(TALLOC_CTX *ctx, void *out, CONF_SECTION *cs,
- char const *name, fr_type_t type,
- CONF_PARSER const *rules, size_t subcs_size)
+static int cf_subsection_parse(TALLOC_CTX *ctx, void *out, CONF_SECTION *cs, CONF_PARSER const *rule)
{
- CONF_SECTION *subcs = NULL;
- int count = 0, i, ret;
- uint8_t **array;
+ CONF_SECTION *subcs = NULL;
+ int count = 0, i, ret;
+
+ char const *name = rule->name;
+ fr_type_t type = rule->type;
+ size_t subcs_size = rule->subcs_size;
+ CONF_PARSER const *rules = rule->subcs;
+
+ uint8_t **array;
rad_assert(type & FR_TYPE_SUBSECTION);
if (!(type & FR_TYPE_MULTI)) {
uint8_t *buff;
+ /*
+ * Add any rules, so the func can just call cf_section_parse
+ * if it wants to continue after doing its stuff.
+ */
if (cf_section_rules_push(subcs, rules) < 0) return -1;
+ if (rule->func) return rule->func(ctx, out, cf_section_to_item(cs), rule);
/*
* FIXME: We shouldn't allow nested structures like this.
MEM(buff = talloc_zero_array(array, uint8_t, subcs_size));
array[i] = buff;
+ /*
+ * Add any rules, so the func can just call cf_section_parse
+ * if it wants to continue after doing its stuff.
+ */
if (cf_section_rules_push(subcs, rules) < 0) {
talloc_free(array);
return -1;
}
+ if (rule->func) {
+ ret = rule->func(ctx, buff, cf_section_to_item(cs), rule);
+ if (ret < 0) {
+ talloc_free(array);
+ return ret;
+ }
+ continue;
+ }
ret = cf_section_parse(buff, buff, subcs);
if (ret < 0) {
*/
if (cf_section_parse_init(cs, base, rule) < 0) return -1;
+ if (rule->data) {
+ data = rule->data; /* prefer this. */
+ } else if (base) {
+ data = ((uint8_t *)base) + rule->offset;
+ } else {
+ data = NULL;
+ }
+
/*
* Handle subsections specially
*/
if (FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) {
- if (cf_subsection_parse(ctx, (uint8_t *)base + rule->offset, cs,
- rule->name, rule->type,
- rule->subcs, rule->subcs_size) < 0) goto finish;
+ if (cf_subsection_parse(ctx, data, cs, rule) < 0) goto finish;
continue;
} /* else it's a CONF_PAIR */
- if (rule->data) {
- data = rule->data; /* prefer this. */
- } else if (base) {
- data = ((uint8_t *)base) + rule->offset;
- } else if (!rad_cond_assert(0)) {
- ret = -1;
- goto finish;
- }
/*
* Get pointer to where we need to write out
/** Allocate module instance data, and parse the module's configuration
*
- * @param[out] data Module's private data, the result of parsing the config.
* @param[in] ctx to allocate this instance data in.
+ * @param[out] data Module's private data, the result of parsing the config.
* @param[in] module to alloc instance data for.
* @param[in] cs module's config section.
* @return
* - 0 on success.
* - -1 on failure.
*/
-int dl_instance_data_alloc(void **data, TALLOC_CTX *ctx, dl_t const *module, CONF_SECTION *cs)
+int dl_instance_data_alloc(TALLOC_CTX *ctx, void **data, dl_t const *module, CONF_SECTION *cs)
{
*data = NULL;
return dl_module;
}
+/** Load a submodule and parse its #CONF_SECTION in one operation
+ *
+ * @note This is here as a convenience function for wrapping by #cf_parse_t callbacks.
+ *
+ * @param[in] ctx to allocate structures in.
+ * @param[out] out where to write our #dl_submodule_t containing the module
+ * handle and instance.
+ * @param[in] conf section to parse.
+ * @param[in] parent module.
+ * @param[in] name of the submodule to load .e.g. 'udp' for 'proto_radius_udp'
+ * if the parent were 'proto_radius'.
+
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int dl_submodule(TALLOC_CTX *ctx, dl_submodule_t **out,
+ CONF_SECTION *conf, dl_t const *parent, char const *name)
+{
+ dl_submodule_t *submodule = talloc_zero(ctx, dl_submodule_t);
+
+ /*
+ * Find a section with the same name as the submodule
+ */
+ submodule->module = dl_module(conf, parent, name, DL_TYPE_SUBMODULE);
+ if (!submodule->module) {
+ cf_log_err(conf, "Failed finding submodule library for '%s_%s'", parent->name, name);
+ return -1;
+ }
+
+ /*
+ * ctx here is the main module's instance data
+ */
+ if (dl_instance_data_alloc(submodule, &submodule->inst, submodule->module, conf) < 0) {
+ cf_log_perr(conf, "Failed allocating instance data for '%s_%s'", parent->name, name);
+ return -1;
+ }
+
+ *out = submodule;
+
+ return 0;
+}
+
/** Initialise structures needed by the dynamic linker
*
*/
* radiusd.conf, the other configuration files exist.
*/
if (cf_section_rules_push(cs, server_config) < 0) return -1;
+// if (cf_section_rules_push(cs, virtual_servers_config) < 0) return -1;
if (cf_section_parse(NULL, NULL, cs) < 0) return -1;
/*
/*
* Parse the modules configuration.
*/
- if (dl_instance_data_alloc(&instance->data, instance, instance->handle, cs) < 0) {
+ if (dl_instance_data_alloc(instance, &instance->data, instance->handle, cs) < 0) {
talloc_free(instance);
return NULL;
}
/*
* And then load the virtual servers.
*/
- if (virtual_servers_init(sc, main_config.config) < 0) exit(EXIT_FAILURE);
+ if (virtual_servers_instantiate(main_config.config) < 0) exit(EXIT_FAILURE);
+
+ /*
+ * If this isn't just a config check, open the sockets
+ */
+ if (!check_config && virtual_servers_open(sc) < 0) exit(EXIT_FAILURE);
/*
* Initialise the SNMP stats structures
/*
* And then load the virtual servers.
*/
- if (virtual_servers_init(NULL, main_config.config) < 0) goto exit_failure;
+ if (virtual_servers_instantiate(main_config.config) < 0) goto exit_failure;
state = fr_state_tree_init(NULL, main_config.max_requests * 2, 10);
#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/parser.h>
#include <freeradius-devel/protocol.h>
+#include <freeradius-devel/dl.h>
+#include <freeradius-devel/io/application.h>
static int default_component_results[MOD_COUNT] = {
RLM_MODULE_REJECT, /* AUTH */
#endif
};
+typedef struct {
+ dl_submodule_t *proto_module; //!< The proto_* module for a listen section.
+ fr_app_t const *app; //!< Easy access to the exported struct.
+} fr_virtual_listen_t;
+
+typedef struct {
+ char const *namespace; //!< Protocol namespace
+ fr_virtual_listen_t **listener; //!< Listeners in this virtual server.
+} fr_virtual_server_t;
+
+typedef struct {
+ fr_virtual_server_t **server; //!< All the servers in the server.
+} fr_virtual_t;
+
+/** Top level structure holding all virtual servers
+ *
+ * Blame LLDB for forcing the stupid name
+ */
+static fr_virtual_t *virtual_root;
+static int listen_parse(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, CONF_PARSER const *rule);
+
+static const CONF_PARSER server_config[] = {
+ { FR_CONF_OFFSET("namespace", FR_TYPE_STRING, fr_virtual_server_t, namespace) },
+ { FR_CONF_OFFSET("listen", FR_TYPE_SUBSECTION | FR_TYPE_MULTI, fr_virtual_server_t, listener), \
+ .subcs_size = sizeof(fr_virtual_listen_t), .func = listen_parse},
+
+ CONF_PARSER_TERMINATOR
+};
+
+const CONF_PARSER virtual_servers_config[] = {
+ { FR_CONF_POINTER("server", FR_TYPE_SUBSECTION | FR_TYPE_MULTI, &virtual_root), \
+ .subcs_size = sizeof(virtual_root), .subcs = (void const *) server_config},
+
+ CONF_PARSER_TERMINATOR
+};
+
+/** dl_open a proto_* module
+ *
+ * @param[in] ctx to allocate data in (instance of proto_radius).
+ * @param[out] out Where to our listen configuration. Is a #fr_virtual_listen_t structure.
+ * @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 listen_parse(UNUSED TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
+{
+ fr_virtual_listen_t *listen = out; /* Pre-allocated for us */
+ CONF_SECTION *listen_cs = cf_item_to_section(ci);
+ CONF_SECTION *server = cf_item_to_section(cf_parent(ci));
+ CONF_PAIR *namespace;
+ dl_t const *module;
+
+ talloc_set_type(listen, fr_virtual_listen_t);
+
+ namespace = cf_pair_find(server, "namespace");
+ if (!namespace) return 0; /* Old style server, skip */
+
+ module = dl_module(listen_cs, NULL, cf_pair_value(namespace), DL_TYPE_PROTO);
+ if (!module) {
+ cf_log_perr(listen_cs, "Failed loading proto module");
+ return -1;
+ }
+
+ MEM(listen->proto_module = talloc_zero(listen, dl_submodule_t));
+
+ if (dl_instance_data_alloc(listen, &listen->proto_module->inst, module, listen_cs) < 0) {
+ cf_log_perr(listen_cs, "Failed parsing config");
+ talloc_free(listen);
+ return -1;
+ }
+
+ listen->proto_module->module = module;
+ listen->proto_module->conf = listen_cs;
+
+ *(void **)out = listen;
+
+ return 0;
+}
+
/**
*/
static rlm_rcode_t module_method_call(rlm_components_t comp, int idx, REQUEST *request)
}
#endif
-static int define_type(CONF_SECTION *cs, fr_dict_attr_t const *da, char const *name)
-{
- fr_value_box_t value = { .type = FR_TYPE_UINT32 };
- fr_dict_enum_t *dval;
-
- /*
- * If the value already exists, don't
- * create it again.
- */
- dval = fr_dict_enum_by_alias(NULL, da, name);
- if (dval) {
- if (dval->value == 0) {
- ERROR("The dictionaries must not define VALUE %s %s 0", da->name, name);
- return -1;
- }
- return 0;
- }
-
- /*
- * Create a new unique value with a
- * meaningless number. You can't look at
- * it from outside of this code, so it
- * doesn't matter. The only requirement
- * is that it's unique.
- */
- do {
- value.vb_uint32 = (fr_rand() & 0x00ffffff) + 1;
- } while (fr_dict_enum_by_value(NULL, da, &value));
-
- cf_log_debug(cs, "Creating %s = %s", da->name, name);
- if (fr_dict_enum_add_alias(da, name, &value, true, false) < 0) {
- ERROR("%s", fr_strerror());
- return -1;
- }
-
- return 0;
-}
-
/*
* Load a sub-module list, as found inside an Auth-Type foo {}
* block
char const *name = cf_section_name2(cs);
CONF_PAIR *cp;
- cf_log_info(cs, "server %s { # from file %s",
- name, cf_filename(cs));
+ cf_log_info(cs, "server %s { # from file %s", name, cf_filename(cs));
cp = cf_pair_find(cs, "namespace");
if (cp) {
return 0;
}
+static int define_type(CONF_SECTION *cs, fr_dict_attr_t const *da, char const *name)
+{
+ fr_value_box_t value = { .type = FR_TYPE_UINT32 };
+ fr_dict_enum_t *dval;
+
+ /*
+ * If the value already exists, don't
+ * create it again.
+ */
+ dval = fr_dict_enum_by_alias(NULL, da, name);
+ if (dval) {
+ if (dval->value == 0) {
+ ERROR("The dictionaries must not define VALUE %s %s 0", da->name, name);
+ return -1;
+ }
+ return 0;
+ }
+
+ /*
+ * Create a new unique value with a
+ * meaningless number. You can't look at
+ * it from outside of this code, so it
+ * doesn't matter. The only requirement
+ * is that it's unique.
+ */
+ do {
+ value.vb_uint32 = (fr_rand() & 0x00ffffff) + 1;
+ } while (fr_dict_enum_by_value(NULL, da, &value));
+
+ cf_log_debug(cs, "Creating %s = %s", da->name, name);
+ if (fr_dict_enum_add_alias(da, name, &value, true, false) < 0) {
+ ERROR("%s", fr_strerror());
+ return -1;
+ }
+
+ return 0;
+}
+
static bool virtual_server_define_types(CONF_SECTION *cs, rlm_components_t comp)
{
fr_dict_attr_t const *da;
return true;
}
-
-/*
- * Bootstrap Auth-Type, etc.
+/** Open all the listen sockets
+ *
+ * @param[in] sc Scheduler to add I/O paths to.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
*/
-int virtual_servers_bootstrap(CONF_SECTION *config)
+int virtual_servers_open(fr_schedule_t *sc)
{
- CONF_SECTION *cs = NULL;
- char const *server_name;
-
- if (!cf_section_find(config, "server", CF_IDENT_ANY)) {
- ERROR("No virtual servers found");
- return -1;
- }
-
- /*
- * Bootstrap global listeners.
- */
- while ((cs = cf_section_find_next(config, cs, "listen", NULL))) {
- if (cf_pair_find(cs, "namespace") != NULL) {
- main_config.namespace = true;
- continue;
- }
+ size_t i, server_cnt = virtual_root ? talloc_array_length(virtual_root->server) : 0;
- if (listen_bootstrap(config, cs, NULL) < 0) return -1;
- }
+ DEBUG2("%s: #### Opening listener interfaces ####", main_config.name);
- cs = NULL;
- while ((cs = cf_section_find_next(config, cs, "server", CF_IDENT_ANY))) {
- CONF_ITEM *ci;
- CONF_SECTION *subcs;
- CONF_PAIR *cp;
+ for (i = 0; i < server_cnt; i++) {
+ fr_virtual_listen_t **listener;
+ size_t j, listen_cnt;
- server_name = cf_section_name2(cs);
- if (!server_name) {
- cf_log_err(cs, "server sections must have a name");
- return -1;
- }
+ if (!virtual_root->server[i]) continue; /* Skip old style */
- /*
- * Check for duplicates.
- */
- subcs = cf_section_find(config, "server", server_name);
- if (subcs && (subcs != cs)) {
- ERROR("Duplicate virtual server \"%s\", in file %s:%d and file %s:%d",
- server_name,
- cf_filename(cs),
- cf_lineno(cs),
- cf_filename(subcs),
- cf_lineno(subcs));
- return -1;
- }
+ listener = virtual_root->server[i]->listener;
+ listen_cnt = talloc_array_length(listener);
- /*
- * New-style virtual servers are special.
- */
- cp = cf_pair_find(cs, "namespace");
- if (cp) {
- char const *value;
- dl_t const *module;
- fr_app_t const *app;
+ for (j = 0; j < listen_cnt; j++) {
+ fr_virtual_listen_t *listen = listener[j];
- value = cf_pair_value(cp);
- if (!value) {
- cf_log_err(cs, "Cannot have empty namespace");
+ if (listen->app->open &&
+ listen->app->open(listen->proto_module->inst, sc, listen->proto_module->conf) < 0) {
+ cf_log_err(listen->proto_module->conf, "Opening I/O interface failed");
return -1;
}
+ }
- if (strcmp(value, "radius") != 0) {
- cf_log_err(cs, "Unknown namespace '%s'", value);
- return -1;
- }
+ }
- module = dl_module(cs, NULL, value, DL_TYPE_PROTO);
- if (!module) {
- cf_log_err(cs, "Failed to find library for 'namespace = %s'", value);
- return -1;
- }
+ return 0;
+}
- app = (fr_app_t const *) module->common;
+/** Instantiate all the virtual servers
+ *
+ * @param[in] config section containing all the virtual servers.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int virtual_servers_instantiate(CONF_SECTION *config)
+{
+ size_t i, server_cnt = virtual_root ? talloc_array_length(virtual_root->server) : 0;
- if (app->bootstrap && (app->bootstrap(cs) < 0)) {
- cf_log_err(cs, "Failed to bootstrap library for 'namespace = %s'", value);
- return -1;
- }
+ CONF_SECTION *cs = NULL;
- if (!app->instantiate) {
- cf_log_err(cs, "Failed to find initialization function for 'transport = %s'",
- value);
- return -1;
- }
+ DEBUG2("%s: #### Instantiating listeners ####", main_config.name);
- cf_data_add(cs, module, "app", false);
- continue;
- }
+ /*
+ * Load all of the virtual servers.
+ */
+ while ((cs = cf_section_find_next(config, cs, "server", CF_IDENT_ANY))) {
+ /*
+ * Skip old-style virtual servers.
+ */
+ if (cf_pair_find(cs, "namespace")) continue;
- for (ci = cf_item_next(cs, NULL);
- ci != NULL;
- ci = cf_item_next(cs, ci)) {
- rlm_components_t comp;
- char const *name1;
+ if (virtual_servers_compile(cs) < 0) return -1;
+ }
- if (cf_item_is_pair(ci)) {
- cf_log_err(ci, "Cannot set variables inside of a virtual server.");
- return -1;
- }
+ for (i = 0; i < server_cnt; i++) {
+ fr_virtual_listen_t **listener;
+ size_t j, listen_cnt;
- if (!cf_item_is_section(ci)) continue;
+ if (!virtual_root->server[i]) continue; /* Skip old style */
- subcs = cf_item_to_section(ci);
- name1 = cf_section_name1(subcs);
+ listener = virtual_root->server[i]->listener;
+ listen_cnt = talloc_array_length(listener);
- if (strcmp(name1, "listen") == 0) {
- if (listen_bootstrap(cs, subcs, server_name) < 0) return -1;
- continue;
- }
+ for (j = 0; j < listen_cnt; j++) {
+ fr_virtual_listen_t *listen = listener[j];
- /*
- * See if it's a RADIUS section.
- */
- for (comp = 0; comp < MOD_COUNT; ++comp) {
- if (strcmp(name1, section_type_value[comp].section) == 0) {
- if (!virtual_server_define_types(subcs, comp)) return -1;
- }
+ if (listen->app->instantiate &&
+ listen->app->instantiate(listen->proto_module->inst, listen->proto_module->conf) < 0) {
+ cf_log_err(listen->proto_module->conf, "Instantiate failed");
+ return -1;
}
- } /* loop over things inside of a virtual server */
- } /* loop over virtual servers */
+ }
+
+ }
return 0;
}
-/*
- * Load all of the virtual servers.
+/** Load protocol modules and call their bootstrap methods
+ *
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
*/
-int virtual_servers_init(fr_schedule_t *sc, CONF_SECTION *config)
+int virtual_servers_bootstrap(CONF_SECTION *config)
{
+ size_t i, server_cnt = virtual_root ? talloc_array_length(virtual_root->server) : 0;
+
CONF_SECTION *cs = NULL;
- DEBUG2("%s: #### Loading Virtual Servers ####", main_config.name);
+ DEBUG2("%s: #### Bootstrapping listeners ####", main_config.name);
/*
* Load all of the virtual servers.
*/
while ((cs = cf_section_find_next(config, cs, "server", CF_IDENT_ANY))) {
- char const *name2;
+ CONF_SECTION *subcs = NULL;
+ char const *server_name;
- name2 = cf_section_name2(cs);
+ server_name = cf_section_name2(cs);
+ if (!server_name) {
+ cf_log_err(cs, "server sections must have a name");
+ return -1;
+ }
/*
- * Skip new-style virtual servers.
+ * Skip old-style virtual servers.
*/
- if (cf_pair_find(cs, "namespace")) {
- dl_t const *module;
- fr_app_t const *app;
-
- if (!sc) continue;
+ if (cf_pair_find(cs, "namespace")) continue;
- module = cf_data_value(cf_data_find(cs, dl_t, "app"));
- if (!module) continue;
+ while ((subcs = cf_section_next(cs, subcs))) {
+ rlm_components_t comp;
- app = (fr_app_t const *) module->common;
+ if (strcmp(cf_section_name1(subcs), "listen") == 0) {
+ if (listen_bootstrap(cs, subcs, server_name) < 0) return -1;
+ continue;
+ }
/*
- * @todo - create a scheduler
+ * See if it's a RADIUS section.
*/
+ for (comp = 0; comp < MOD_COUNT; ++comp) {
+ if (strcmp(cf_section_name1(subcs), section_type_value[comp].section) == 0) {
+ if (!virtual_server_define_types(subcs, comp)) return -1;
+ }
+ }
+ }
+ }
- cf_log_info(cs, "server %s { # from file %s",
- name2, cf_filename(cs));
- cf_log_info(cs, " namespace = %s", app->name);
+ for (i = 0; i < server_cnt; i++) {
+ fr_virtual_listen_t **listener;
+ size_t j, listen_cnt;
- if (app->instantiate(sc, cs, check_config) < 0) {
- cf_log_err(cs, "Failed loading virtual server %s", name2);
- return -1;
- }
+ if (!virtual_root->server[i]) continue; /* Skip old style */
- DEBUG(" Loaded Protocol %s", module->name);
+ listener = virtual_root->server[i]->listener;
+ listen_cnt = talloc_array_length(listener);
- cf_log_info(cs, "} # server %s", name2);
- continue;
- }
+ for (j = 0; j < listen_cnt; j++) {
+ fr_virtual_listen_t *listen = listener[j];
- if (virtual_servers_compile(cs) < 0) {
- return -1;
+ listen->app = (fr_app_t const *)listen->proto_module->module->common; /* For laziness */
+
+ if (listen->app->bootstrap &&
+ listen->app->bootstrap(listen->proto_module->inst, listen->proto_module->conf) < 0) {
+ cf_log_err(listen->proto_module->conf, "Bootstrap failed");
+ return -1;
+ }
}
+
}
return 0;
/*
- * proto_radius.c RADIUS master protocol handler
- *
- * Version: $Id$
- *
* 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
* 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
- *
- * Copyright 2016 The FreeRADIUS server project
- * Copyright 2016 Alan DeKok <aland@deployingradius.com>
*/
+/**
+ * $Id$
+ * @file proto_radius.c
+ * @brief RAIDUS master protocol handler.
+ *
+ * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ * @copyright 2016 Alan DeKok (aland@freeradius.org)
+ */
#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/protocol.h>
#include <freeradius-devel/radius/radius.h>
+#include <freeradius-devel/io/listen.h>
#include <freeradius-devel/io/schedule.h>
+#include <freeradius-devel/io/application.h>
#include <freeradius-devel/rad_assert.h>
#include "proto_radius.h"
-/** Decode the packet, and set the request->process function
+/** An instance of a proto_radius listen section
*
*/
-static int mod_decode(UNUSED void *io_ctx, uint8_t *const data, UNUSED size_t data_len, REQUEST *request)
-{
-// proto_radius_ctx_t *ctx = io_ctx;
-
- if (fr_radius_verify(data, NULL, (uint8_t const *) "testing123", 10) < 0) {
- return -1;
- }
+typedef struct {
+ dl_submodule_t *io_submodule; //!< I/O module's instance.
+ dl_submodule_t **type_submodule; //!< Instance of the various types
+ //!< only one instance per type allowed.
- rad_assert(data[0] < FR_MAX_PACKET_CODE);
+ fr_io_t const *io;
- if (fr_radius_packet_decode(request->packet, NULL, "testing123") < 0) {
- return -1;
- }
-
-// request->async_process = ctx->process[data[0]];
+ fr_app_io_t const *app_io; //!< Easy access to the app_io handle.
+ fr_app_subtype_t const *app_by_code[FR_CODE_MAX]; //!< Lookup submodule by code.
+} proto_radius_ctx_t;
- return 0;
-}
+extern fr_app_t proto_radius;
+static int subtype_parse(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, CONF_PARSER const *rule);
+static int transport_parse(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, CONF_PARSER const *rule);
-static ssize_t mod_encode(UNUSED void *io_ctx, UNUSED REQUEST *request, UNUSED uint8_t *buffer, UNUSED size_t buffer_len)
-{
- return -1;
-}
+/** How to parse a RADIUS listen section
+ *
+ */
+static CONF_PARSER const proto_radius_config[] = {
+ { FR_CONF_OFFSET("type", FR_TYPE_VOID | FR_TYPE_MULTI | FR_TYPE_NOT_EMPTY, proto_radius_ctx_t, type_submodule),
+ .dflt = "Status-Server", .func = subtype_parse },
+ { FR_CONF_OFFSET("transport", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY, proto_radius_ctx_t, io_submodule),
+ .dflt = "udp", .func = transport_parse },
+ CONF_PARSER_TERMINATOR
+};
-/** Load the RADIUS protocol
+/** Wrapper around dl_submodule which translates the packet-type into a submodule name
*
- * Typically loads dictionaries, etc.
+ * @param[in] ctx to allocate data in (instance of proto_radius).
+ * @param[out] out Where to write a dl_submodule_t containing the module handle and instance.
+ * @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 mod_load(void)
+static int subtype_parse(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
{
+ static char const *type_lib_table[] = {
+ [FR_CODE_ACCESS_REQUEST] = "auth",
+ [FR_CODE_ACCOUNTING_REQUEST] = "acct",
+ [FR_CODE_COA_REQUEST] = "coa",
+ [FR_CODE_DISCONNECT_REQUEST] = "coa",
+ [FR_CODE_STATUS_SERVER] = "status",
+ [FR_CODE_MAX] = NULL
+ };
+
+ char const *type_str = cf_pair_value(cf_item_to_pair(ci));
+ CONF_SECTION *parent_cs = cf_item_to_section(cf_parent(ci));
+ char const *name;
+ fr_dict_attr_t const *da;
+ fr_dict_enum_t const *type_enum;
+ uint32_t code;
+
+ da = fr_dict_attr_by_name(NULL, "Packet-Type");
+ if (!da) {
+ ERROR("Missing definiton for Packet-Type");
+ return -1;
+ }
+
/*
- * @todo - load the RADIUS dictionaries
+ * Look the type up using the Packet-Type enumv
*/
+ type_enum = fr_dict_enum_by_alias(NULL, da, type_str);
+ if (!type_enum) {
+ invalid_type:
+ cf_log_err(ci, "Invalid type \"%s\"", type_str);
+ return -1;
+ }
- return 0;
+ code = type_enum->value->vb_uint32;
+ if (code >= FR_CODE_MAX) goto invalid_type;
+
+ name = type_lib_table[code];
+
+ return dl_submodule(ctx, out, cf_section_find(parent_cs, name, NULL), dl_by_symbol(&proto_radius), name);
}
-/** Bootstrap the RADIUS protocol in a particular virtual server.
+/** Wrapper around dl_submodule
*
+ * @param[in] ctx to allocate data in (instance of proto_radius).
+ * @param[out] out Where to write a dl_submodule_t containing the module handle and instance.
+ * @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 mod_bootstrap(UNUSED CONF_SECTION *cs)
+static int transport_parse(TALLOC_CTX *ctx, void *out, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
{
- return 0;
-}
-
-typedef struct type2lib_t {
- char const *type;
- char const *lib;
- char const *port_name;
-} type2lib_t;
-
-static const type2lib_t type2lib[] = {
- { "Access-Request", "radius_auth", "radius" },
- { "Accounting-Request", "radius_acct", "radius-acct" },
- { "CoA-Request", "coa", "radius-dynauth" },
- { "Disconnect-Request", "radius_coa", "radius-dynauth" },
- { "Status-Server", "radius_status", NULL },
- { NULL, NULL }
-};
-
-typedef struct pr_config_t {
- char const **types;
- char const *transport;
-} pr_config_t;
-
-
-static const CONF_PARSER mod_config[] = {
- { FR_CONF_OFFSET("type", FR_TYPE_STRING | FR_TYPE_MULTI, pr_config_t, types), .dflt = "Status-Server" },
- { FR_CONF_OFFSET("transport", FR_TYPE_STRING, pr_config_t, transport), .dflt = "udp" },
-
- CONF_PARSER_PARTIAL_TERMINATOR
-};
+ char const *name = cf_pair_value(cf_item_to_pair(ci));
+ CONF_SECTION *parent_cs = cf_item_to_section(cf_parent(ci));
+ return dl_submodule(ctx, out, cf_section_find(parent_cs, name, NULL), dl_by_symbol(&proto_radius), name);
+}
-static int compile_type(proto_radius_ctx_t *ctx, CONF_SECTION *server, CONF_SECTION *cs, char const *value)
+/** Decode the packet, and set the request->process function
+ *
+ */
+static int mod_decode(UNUSED void const *io_ctx, REQUEST *request,
+ UNUSED uint8_t *const data, UNUSED size_t data_len)
{
- int i, code;
- char const *lib, *port_name;
- dl_t const *module;
- fr_app_subtype_t const *app;
-
- if (!value || !*value) {
- cf_log_err(cs, "Must specify a value for 'type'");
- return -1;
- }
-
- code = 0;
- for (i = 1; i < FR_MAX_PACKET_CODE; i++) {
- if (strcmp(value, fr_packet_codes[i]) == 0) {
- code = i;
- break;
- }
- }
-
- if (!code) {
- cf_log_err(cs, "Unknown 'type = %s'", value);
- return -1;
- }
+// proto_radius_ctx_t *ctx = io_ctx;
- if (ctx->process[i]) {
- cf_log_err(cs, "Duplicate 'type = %s'", value);
+ if (fr_radius_verify(data, NULL, (uint8_t const *) "testing123", 10) < 0) {
return -1;
}
- /*
- * Already loaded the module in this virtual
- * server, don't do anything more.
- */
- if (cf_data_find(server, dl_t, value)) return 0;
-
- /*
- * Convert "Access-Request" -> "auth"
- */
- port_name = lib = NULL;
- for (i = 0; type2lib[i].type != NULL; i++) {
- if (strcmp(type2lib[i].type, value) == 0) {
- lib = type2lib[i].lib;
- port_name = type2lib[i].port_name;
- break;
- }
- }
+ rad_assert(data[0] < FR_MAX_PACKET_CODE);
- if (!lib) {
- cf_log_err(cs, "Unknown 'type = %s'", value);
+ if (fr_radius_packet_decode(request->packet, NULL, "testing123") < 0) {
return -1;
}
- /*
- * Add the default port name, if it exists.
- */
- if (port_name) {
- CONF_PAIR *cp;
-
- cp = cf_pair_find(cs, "port_name");
- if (!cp) {
- cp = cf_pair_alloc(cs, "port_name", port_name,
- T_OP_SET, T_BARE_WORD, T_BARE_WORD);
- if (!cp) {
- cf_log_err(cs, "Out of memory");
- return -1;
- }
-
- (void) cf_pair_add(cs, cp);
- }
- }
+// request->async_process = ctx->process[data[0]];
+ return 0;
+}
- /*
- * Load the module.
- */
- module = dl_module(server, NULL, lib, DL_TYPE_PROTO);
- if (!module) {
- cf_log_err(cs, "Failed finding submodule library for 'type = %s'", value);
- return -1;
- }
+static ssize_t mod_encode(UNUSED void const *io_ctx, UNUSED REQUEST *request,
+ UNUSED uint8_t *buffer, UNUSED size_t buffer_len)
+{
+ return -1;
+}
- app = (fr_app_subtype_t const *) module->common;
- ctx->process[i] = app->process;
+static void mod_set_process(UNUSED REQUEST *request, UNUSED void const *uctx)
+{
+// proto_radius_ctx_t const *inst = talloc_get_type_abort(uctx, proto_radius_ctx_t);
/*
- * Remember that we loaded the module in the server.
+ * - Figure out the request code
+ * - Look it up in the app_by_code array
+ * - Set the state machine entry point to the one provided by the subtype
*/
- cf_data_add(server, module, value, false);
-
- return 0;
}
-
-static int open_transport(proto_radius_ctx_t *ctx, fr_schedule_t *handle,
- CONF_SECTION *server, CONF_SECTION *cs, char const *value,
- bool verify_config)
+/** Open listen sockets/connect to external event source
+ *
+ * @param[in] instance Ctx data for this application.
+ * @param[in] sc to add our file descriptor to.
+ * @param[in] conf Listen section parsed to give us isntance.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int mod_open(void *instance, fr_schedule_t *sc, CONF_SECTION *conf)
{
- dl_t const *module;
- fr_app_io_t const *app_io;
- CONF_SECTION *io_cs;
- void *io_ctx;
- CONF_PAIR *cp;
- char buffer[256];
-
- if (!value || !*value) {
- cf_log_err(cs, "Must specify a value for 'transport'");
- return -1;
- }
-
- snprintf(buffer, sizeof(buffer), "radius_%s", value);
-
- module = dl_module(server, NULL, buffer, DL_TYPE_PROTO);
- if (!module) {
- cf_log_err(cs, "Failed finding submodule library for 'transport = %s'", value);
- return -1;
- }
+ int fd;
+ fr_io_t *io;
+ proto_radius_ctx_t *inst = talloc_get_type_abort(instance, proto_radius_ctx_t);
/*
- * Lookup io section.
+ * Open the listen socket
*/
- io_cs = cf_section_find(cs, value, NULL);
- if (!io_cs) {
- cf_log_err(cs, "Must contain a '%s' section", value);
- return -1;
- }
-
- if (dl_instance_data_alloc(&io_ctx, NULL, module, io_cs) < 0) {
- cf_log_perr(cs, "Failed io_ctx data");
- return -1;
- }
-
- cp = cf_pair_find(cs, "port_name");
- if (cp) {
- cp = cf_pair_alloc(io_cs, "port_name", cf_pair_value(cp),
- T_OP_SET, T_BARE_WORD, T_BARE_WORD);
- if (!cp) {
- cf_log_err(cs, "Out of memory");
- return -1;
- }
-
- (void) cf_pair_add(io_cs, cp);
- }
-
- app_io = (fr_app_io_t const *) module->common;
- if (app_io->instantiate(io_cs, io_ctx) < 0) {
- cf_log_err(cs, "Failed instantiating 'transport = %s'", value);
- talloc_free(io_ctx);
+ if (inst->app_io->op.open(inst->io_submodule->inst) < 0) {
+ cf_log_err(conf, "Failed opening I/O interface");
return -1;
}
- if (verify_config) return 0;
-
- if (app_io->op.open(io_ctx) < 0) {
- cf_log_err(cs, "Failed opening I/O interface '%s'", value);
- return -1;
- }
+ fd = inst->app_io->op.fd(inst->io_submodule->inst);
+ rad_assert(fd >= 0);
/*
- * Set to the function which will decode the packet and
- * set request->process to the correct entry.
- *
- * @note - could also do this in the recv_request function?
+ * Build the fr_io_t from the op array of the transport and its
+ * instance data.
*/
- ctx->transport = app_io->op;
- ctx->transport.decode = mod_decode;
- ctx->transport.encode = mod_encode;
+ io = talloc_zero(inst, fr_io_t);
+ io->ctx = inst->io_submodule->inst;
+ io->op = &inst->app_io->op;
+
+ io->set_process = mod_set_process;
+ io->app_ctx = instance;
+ io->encode = mod_encode;
+ io->decode = mod_decode;
/*
* Add it to the scheduler. Note that we add our context
* instead of the transport one, as we need to swap out
* the process function.
- *
- * @todo - more cleanup on error.
*/
- if (!fr_schedule_socket_add(handle, io_ctx)) {
- talloc_free(ctx);
+ if (!fr_schedule_socket_add(sc, io)) {
+ talloc_free(io);
return -1;
}
- /*
- * Remember that we loaded the transport library in the server.
- */
- cf_data_add(server, module, value, false);
+ inst->io = io; /* Probably won't need it, but doesn't hurt */
return 0;
}
-static int open_listen(fr_schedule_t *handle, CONF_SECTION *server, CONF_SECTION *cs, bool verify_config)
+/** Instantiate the application
+ *
+ * Instantiate I/O and type submodules.
+ *
+ * @param[in] instance Ctx data for this application.
+ * @param[in] conf Listen section parsed to give us isntance.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int mod_instantiate(void *instance, CONF_SECTION *conf)
{
- size_t i;
- pr_config_t config;
- proto_radius_ctx_t *ctx;
+ proto_radius_ctx_t *inst = talloc_get_type_abort(instance, proto_radius_ctx_t);
+ size_t i = 0;
- if (cf_section_rules_push(cs, mod_config) < 0) return -1;
+ fr_dict_attr_t const *da;
+ CONF_PAIR *cp = NULL;
- if ((cf_section_parse(cs, &config, cs) < 0) || (cf_section_parse_pass2(&config, cs) < 0)) {
- cf_log_err(cs, "Failed parsing listen { ...}");
- return -1;
- }
-
- if (!config.types) {
- cf_log_err(cs, "type MUST be specified");
- return -1;
- }
-
- if (!config.transport) {
- cf_log_err(cs, "transport MUST be specified");
+ /*
+ * Instantiate the IO module
+ */
+ if (inst->app_io->instantiate && (inst->app_io->instantiate(inst->io_submodule->conf,
+ inst->io_submodule->inst) < 0)) {
+ cf_log_err(conf, "I/O instantiation failed");
return -1;
}
- ctx = talloc_zero(NULL, proto_radius_ctx_t);
- if (!ctx) {
- cf_log_err(cs, "Failed allocating memory");
+ /*
+ * Needed to populate the code array
+ */
+ da = fr_dict_attr_by_name(NULL, "Packet-Type");
+ if (!da) {
+ ERROR("Missing definiton for Packet-Type");
return -1;
}
/*
- * Compile one or more types.
+ * Instantiate the subtypes
*/
- for (i = 0; i < talloc_array_length(config.types); i++) {
- if (compile_type(ctx, server, cs, config.types[i]) < 0) {
- cf_log_err(server, "Failed compiling unlang for 'type = %s'",
- config.types[i]);
+ while ((cp = cf_pair_find_next(conf, cp, "type"))) {
+ fr_app_subtype_t const *subtype = (fr_app_subtype_t const *)inst->type_submodule[i]->module->common;
+
+ if (subtype->instantiate && (subtype->instantiate(inst->type_submodule[i]->conf,
+ inst->type_submodule[i]->inst) < 0)) {
+ cf_log_err(conf, "Subtype instantiation failed");
return -1;
}
- }
- /*
- * Call transport-specific library to open the socket.
- */
- if (open_transport(ctx, handle, server, cs, config.transport, verify_config) < 0) {
- cf_log_err(server, "Failed opening connection for 'transport = %s'",
- config.transport);
- return -1;
+ /*
+ * We've already done bounds checking in the subtype_parse function
+ */
+ inst->app_by_code[fr_dict_enum_by_alias(NULL, da, cf_pair_value(cp))->value->vb_uint32] = subtype;
+
+ i++;
}
return 0;
}
-
-/** Open a RADIUS application in a virtual server,
+/** Bootstrap the application
+ *
+ * Bootstrap I/O and type submodules.
*
+ * @param[in] instance Ctx data for this application.
+ * @param[in] conf Listen section parsed to give us isntance.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
*/
-static int mod_parse(fr_schedule_t *handle, CONF_SECTION *cs, bool verify_config)
+static int mod_bootstrap(void *instance, CONF_SECTION *conf)
{
- CONF_SECTION *subcs = NULL;
+ proto_radius_ctx_t *inst = talloc_get_type_abort(instance, proto_radius_ctx_t);
+ size_t i = 0;
+ CONF_PAIR *cp = NULL;
/*
- * Load all of the listen sections. They do all of the
- * dirty work.
+ * Bootstrap the IO module
*/
- while ((subcs = cf_section_find_next(cs, subcs, "listen", NULL))) {
- if (open_listen(handle, cs, subcs, verify_config) < 0) {
- return -1;
- }
+ inst->app_io = (fr_app_io_t const *) inst->io_submodule->module->common;
+ if (inst->app_io->bootstrap && (inst->app_io->bootstrap(inst->io_submodule->conf,
+ inst->io_submodule->inst) < 0)) {
+ cf_log_err(inst->io_submodule->conf, "I/O bootstrap failed");
+ return -1;
}
- subcs = NULL;
-
/*
- * Compile the sub-sections AFTER parsing all of the
- * listen sections. This is mainly for nice debugging
- * output. It's inefficient as heck, but it's pretty.
+ * Bootstrap the subtypes
*/
- while ((subcs = cf_section_find_next(cs, subcs, "listen", NULL))) {
- CONF_PAIR *cp;
-
- for (cp = cf_pair_find(subcs, "type");
- cp != NULL;
- cp = cf_pair_find_next(subcs, cp, "type")) {
- char const *value;
- dl_t const *module;
- fr_app_subtype_t const *app;
-
- value = cf_pair_value(cp);
-
- module = cf_data_value(cf_data_find(cs, dl_t, value));
- if (!module) {
- cf_log_err(cs, "Section missing module data");
- return -1;
- }
- if (cf_data_find(cs, char const *, value)) continue;
-
- app = (fr_app_subtype_t const *) module->common;
- if (app->instantiate(cs) < 0) {
- cf_log_err(cs, "Failed compiling unlang for 'type = %s'", value);
- return -1;
- }
-
- cf_data_add(cs, value, value, false);
+ while ((cp = cf_pair_find_next(conf, cp, "type"))) {
+ fr_app_subtype_t const *subtype = (fr_app_subtype_t const *)inst->type_submodule[i]->module->common;
+
+ if (subtype->bootstrap && (subtype->bootstrap(inst->type_submodule[i]->conf,
+ inst->type_submodule[i]->inst) < 0)) {
+ cf_log_err(inst->type_submodule[i]->conf, "Subtype bootstrap failed");
+ return -1;
}
+ i++;
}
return 0;
}
-extern fr_app_t proto_radius;
fr_app_t proto_radius = {
.magic = RLM_MODULE_INIT,
.name = "radius",
- .load = mod_load,
+ .config = proto_radius_config,
+ .inst_size = sizeof(proto_radius_ctx_t),
+ .inst_type = "proto_radius_ctx_t",
+
.bootstrap = mod_bootstrap,
- .instantiate = mod_parse,
+ .instantiate = mod_instantiate,
+ .open = mod_open,
+ .set_process = mod_set_process
};
* @copyright 2017 Alan DeKok <aland@freeradius.org>
*/
-typedef struct proto_radius_ctx_t {
- int sockfd; //!< sanity checks
- void *ctx; //!< for the underlying IO layer
-
- char const *secret; //!< shared secret
- size_t secret_len; //!< length of the shared secret
-
- fr_io_op_t transport;
- fr_io_process_t process[FR_MAX_PACKET_CODE];
-} proto_radius_ctx_t;
-
#endif /* _PROTO_RADIUS_H */
#include <freeradius-devel/udp.h>
#include <freeradius-devel/radius/radius.h>
#include <freeradius-devel/io/io.h>
+#include <freeradius-devel/io/application.h>
#include <freeradius-devel/rad_assert.h>
static fr_io_final_t mod_process(REQUEST *request, UNUSED fr_io_action_t action)
/*
* Ensure that the "radius" section is compiled.
*/
-static int mod_compile(CONF_SECTION *server_cs)
+static int mod_compile(UNUSED void *instance, CONF_SECTION *server_cs)
{
int rcode;
#include <freeradius-devel/udp.h>
#include <freeradius-devel/radius/radius.h>
#include <freeradius-devel/io/io.h>
+#include <freeradius-devel/io/application.h>
#include <freeradius-devel/rad_assert.h>
#include "proto_radius.h"
+typedef struct {
+ uint8_t header[20];
+ uint8_t id;
+
+ fr_ipaddr_t src_ipaddr;
+ fr_ipaddr_t dst_ipaddr;
+ uint16_t src_port;
+ uint16_t dst_port;
+
+ RADCLIENT *client;
+} fr_proto_radius_packet_ctx_t;
+
typedef struct {
int sockfd;
uint32_t recv_buff; //!< How big the kernel's receive buffer should be.
bool recv_buff_is_set; //!< Whether we were provided with a receive
//!< buffer value.
- /*
- * SHIT
- */
- uint8_t const *secret;
- size_t secret_len;
-
- uint8_t original[20];
- uint8_t id;
- struct sockaddr_storage src;
- socklen_t salen;
+ RADCLIENT *dummy_client;
} fr_proto_radius_udp_ctx_t;
-static const CONF_PARSER udp_listen_conf[] = {
+static const CONF_PARSER udp_listen_config[] = {
{ FR_CONF_IS_SET_OFFSET("ipaddr", FR_TYPE_COMBO_IP_ADDR, fr_proto_radius_udp_ctx_t, ipaddr) },
{ FR_CONF_IS_SET_OFFSET("ipv4addr", FR_TYPE_IPV4_ADDR, fr_proto_radius_udp_ctx_t, ipaddr) },
{ FR_CONF_IS_SET_OFFSET("ipv6addr", FR_TYPE_IPV6_ADDR, fr_proto_radius_udp_ctx_t, ipaddr) },
CONF_PARSER_TERMINATOR
};
-static ssize_t mod_read(void *ctx, uint8_t *buffer, size_t buffer_len)
+static ssize_t mod_read(void const *instance, void **packet_ctx, uint8_t *buffer, size_t buffer_len)
{
+ fr_proto_radius_udp_ctx_t const *inst = talloc_get_type_abort(instance, fr_proto_radius_udp_ctx_t);
+ fr_proto_radius_packet_ctx_t *pctx;
+
ssize_t data_size;
size_t packet_len;
- fr_proto_radius_udp_ctx_t *pc = ctx;
decode_fail_t reason;
- pc->salen = sizeof(pc->src);
+ struct sockaddr_storage sockaddr;
+ socklen_t salen = sizeof(sockaddr);
+
- data_size = recvfrom(pc->sockfd, buffer, buffer_len, 0, (struct sockaddr *) &pc->src, &pc->salen);
+ memset(&sockaddr, 0, sizeof(sockaddr));
+
+ data_size = recvfrom(inst->sockfd, buffer, buffer_len, 0, (struct sockaddr *)&sockaddr, &salen);
if (data_size <= 0) return data_size;
packet_len = data_size;
/*
* If it's not a RADIUS packet, ignore it.
*/
- if (!fr_radius_ok(buffer, &packet_len, false, &reason)) {
- return 0;
- }
+ if (!fr_radius_ok(buffer, &packet_len, false, &reason)) return -1;
/*
* If the signature fails validation, ignore it.
*/
- if (fr_radius_verify(buffer, NULL, pc->secret, pc->secret_len) < 0) {
- return 0;
+ if (fr_radius_verify(buffer, NULL,
+ (uint8_t const *)inst->dummy_client->secret,
+ talloc_array_length(inst->dummy_client->secret)) < 0) return -1;
+
+ /*
+ * Populate the packet context
+ */
+ pctx = talloc_zero(NULL, fr_proto_radius_packet_ctx_t);
+ pctx->client = inst->dummy_client;
+ if (fr_ipaddr_from_sockaddr(&sockaddr, salen, &pctx->src_ipaddr, &pctx->src_port) < 0) {
+ PERROR("Read failed");
+ talloc_free(pctx);
+ return -1;
}
+ pctx->id = buffer[1];
+ memcpy(pctx->header, buffer, sizeof(pctx->header));
- pc->id = buffer[1];
- memcpy(pc->original, buffer, sizeof(pc->original));
+ *packet_ctx = pctx;
return packet_len;
}
-static ssize_t mod_write(void *ctx, uint8_t *buffer, size_t buffer_len)
+static ssize_t mod_write(void const *instance, void *packet_ctx, uint8_t *buffer, size_t buffer_len)
{
- ssize_t data_size;
- fr_proto_radius_udp_ctx_t *pc = ctx;
+ fr_proto_radius_udp_ctx_t const *inst = talloc_get_type_abort(instance, fr_proto_radius_udp_ctx_t);
+ fr_proto_radius_packet_ctx_t *pctx = talloc_get_type_abort(packet_ctx, fr_proto_radius_packet_ctx_t);
- pc->salen = sizeof(pc->src);
+ ssize_t data_size;
+ struct sockaddr_storage sockaddr;
+ socklen_t salen;
+
+ if (fr_ipaddr_to_sockaddr(&pctx->src_ipaddr, pctx->src_port, &sockaddr, &salen) < 0) {
+ PERROR("Write failed");
+ talloc_free(packet_ctx);
+ return -1;
+ }
/*
* @todo - do more stuff
*/
- data_size = sendto(pc->sockfd, buffer, buffer_len, 0, (struct sockaddr *) &pc->src, pc->salen);
+ data_size = sendto(inst->sockfd, buffer, buffer_len, 0, (struct sockaddr *) &sockaddr, salen);
if (data_size <= 0) return data_size;
+ talloc_free(packet_ctx); /* Probably a bad idea */
+
/*
* @todo - post-write cleanups
*/
-
return data_size;
}
-static int mod_instantiate(CONF_SECTION *cs, void *instance)
+/** Open a UDP listener for RADIUS
+ *
+ * @param[in] instance of the RADIUS UDP I/O path.
+ * @return
+ * - <0 on error
+ * - 0 on success
+ */
+static int mod_open(void *instance)
{
- fr_proto_radius_udp_ctx_t *inst = instance;
+ fr_proto_radius_udp_ctx_t *inst = instance;
+
+ int sockfd = 0;
+ uint16_t port = inst->port;
+
+ sockfd = fr_socket_server_udp(&inst->ipaddr, &port, inst->port_name, true);
+ if (sockfd < 0) {
+ ERROR("%s", fr_strerror());
+ error:
+ return -1;
+ }
+
+ if (fr_socket_bind(sockfd, &inst->ipaddr, &port, inst->interface) < 0) {
+ ERROR("Failed binding socket: %s", fr_strerror());
+ goto error;
+ }
+
+ inst->sockfd = sockfd;
+
+ return 0;
+}
+
+static int mod_instantiate(void *instance, CONF_SECTION *cs)
+{
+ fr_proto_radius_udp_ctx_t *inst = instance;
/*
* Default to all IPv6 interfaces (it's the future)
inst->port = ntohl(s->s_port);
}
- inst->secret = (uint8_t const *) "testing123";
- inst->secret_len = 10;
-
- return 0;
-}
-
-/** Open a UDP listener for RADIUS
- *
- * @param[in] instance of the RADIUS UDP I/O path.
- * @return
- * - <0 on error
- * - 0 on success
- */
-static int mod_open(void *instance)
-{
- fr_proto_radius_udp_ctx_t *inst = instance;
-
- int sockfd = 0;
-
- sockfd = fr_socket_server_udp(&inst->ipaddr, &inst->port, inst->port_name, true);
- if (sockfd < 0) {
- ERROR("%s", fr_strerror());
- error:
- return -1;
- }
-
- if (fr_socket_bind(sockfd, &inst->ipaddr, &inst->port, inst->interface) < 0) {
- ERROR("Failed binding socket: %s", fr_strerror());
- goto error;
- }
-
- inst->sockfd = sockfd;
+ inst->dummy_client = client_afrom_query(inst, "127.0.0.1", "testing123", "test", "test", NULL, false);
return 0;
}
* @param[in] instance of the RADIUS UDP I/O path.
* @return the file descriptor
*/
-static int mod_fd(void *instance)
+static int mod_fd(void const *instance)
{
- fr_proto_radius_udp_ctx_t *inst = instance;
+ fr_proto_radius_udp_ctx_t const *inst = instance;
return inst->sockfd;
}
fr_app_io_t proto_radius_udp = {
.magic = RLM_MODULE_INIT,
.name = "radius_udp",
- .config = udp_listen_conf,
+ .config = udp_listen_config,
.inst_size = sizeof(fr_proto_radius_udp_ctx_t),
.inst_type = "fr_proto_radius_udp_ctx_t",
.instantiate = mod_instantiate,
rad_assert(inst->driver->insert);
rad_assert(inst->driver->expire);
- if (dl_instance_data_alloc(&inst->driver_inst, inst, inst->driver_handle, driver_cs) < 0) return -1;
+ if (dl_instance_data_alloc(inst, &inst->driver_inst, inst->driver_handle, driver_cs) < 0) return -1;
if (inst->driver->instantiate &&
(inst->driver->instantiate(&inst->config, inst->driver_inst, driver_cs) < 0)) return -1;
* Allocate submodule instance data and parse the method's
* configuration.
*/
- if (dl_instance_data_alloc(&method->submodule_inst, method, method->submodule_handle, cs) < 0) {
+ if (dl_instance_data_alloc(method, &method->submodule_inst, method->submodule_handle, cs) < 0) {
talloc_free(method);
return -1;
}
}
if (inst->auth_type) {
- vp = radius_pair_create(request, &request->control,
- FR_AUTH_TYPE, 0);
+ vp = radius_pair_create(request, &request->control, FR_AUTH_TYPE, 0);
vp->vp_uint32 = inst->auth_type;
}
* Pre-allocate the driver's instance data,
* and parse the driver's configuration.
*/
- if (dl_instance_data_alloc(&inst->driver_inst, inst, inst->driver_handle, driver_cs) < 0) {
+ if (dl_instance_data_alloc(inst, &inst->driver_inst, inst->driver_handle, driver_cs) < 0) {
error:
talloc_decrease_ref_count(inst->driver_handle);
return -1;
#include <freeradius-devel/io/control.h>
#include <freeradius-devel/io/worker.h>
+#include <freeradius-devel/io/listen.h>
#include <freeradius-devel/inet.h>
#include <freeradius-devel/fr_log.h>
#include <freeradius-devel/radius.h>
}
-static int test_decode(void *packet_ctx, uint8_t *const data, size_t data_len, REQUEST *request)
+static int test_decode(void const *instance, REQUEST *request, uint8_t *const data, size_t data_len)
{
- fr_radius_packet_ctx_t const *pc = talloc_get_type_abort(packet_ctx, fr_radius_packet_ctx_t);
+ fr_radius_packet_ctx_t const *pc = talloc_get_type_abort(instance, fr_radius_packet_ctx_t);
request->number = pc->id;
request->process_async = test_process;
return 0;
}
-static ssize_t test_encode(void *packet_ctx, REQUEST *request, uint8_t *buffer, size_t buffer_len)
+static ssize_t test_encode(void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len)
{
FR_MD5_CTX context;
- fr_radius_packet_ctx_t const *pc = talloc_get_type_abort(packet_ctx, fr_radius_packet_ctx_t);
+ fr_radius_packet_ctx_t const *pc = talloc_get_type_abort(instance, fr_radius_packet_ctx_t);
MPRINT1("\t\tENCODE >>> request %"PRIu64" - data %p %p room %zd\n",
request->number, pc, buffer, buffer_len);
return 20;
}
-static size_t test_nak(void const *packet_ctx, uint8_t *const packet, size_t packet_len, UNUSED uint8_t *reply, UNUSED size_t reply_len)
+static size_t test_nak(void const *instance, uint8_t *const packet, size_t packet_len, UNUSED uint8_t *reply, UNUSED size_t reply_len)
{
- MPRINT1("\t\tNAK !!! request %d - data %p %p size %zd\n", packet[1], packet_ctx, packet, packet_len);
+ MPRINT1("\t\tNAK !!! request %d - data %p %p size %zd\n", packet[1], instance, packet, packet_len);
return 10;
}
static fr_io_op_t op = {
.name = "worker-test",
.default_message_size = 4096,
- .decode = test_decode,
- .encode = test_encode,
.nak = test_nak,
};
int kq_master;
fr_atomic_queue_t *aq_master;
fr_control_t *control_master;
- fr_io_t io = { .ctx = NULL, .op = &op };
+ fr_io_t io = { .ctx = NULL, .op = &op, .encode = test_encode, .decode = test_decode };
int sockfd;
MPRINT1("Master started.\n");
RCSID("$Id$")
#include <freeradius-devel/io/schedule.h>
+#include <freeradius-devel/io/listen.h>
#include <freeradius-devel/inet.h>
#include <freeradius-devel/radius.h>
#include <freeradius-devel/md5.h>
return FR_IO_REPLY;
}
-static int test_decode(void *ctx, uint8_t *const data, size_t data_len, REQUEST *request)
+static int test_decode(void const *instance, REQUEST *request, uint8_t *const data, size_t data_len)
{
- fr_io_test_ctx_t const *pc = ctx;
+ fr_io_test_ctx_t const *pc = instance;
request->process_async = test_process;
return 0;
}
-static ssize_t test_encode(void *ctx, REQUEST *request, uint8_t *buffer, size_t buffer_len)
+static ssize_t test_encode(void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len)
{
FR_MD5_CTX context;
- fr_io_test_ctx_t const *pc = ctx;
+ fr_io_test_ctx_t const *pc = instance;
MPRINT1("\t\tENCODE >>> request %"PRIu64"- data %p %p room %zd\n", request->number, pc, buffer, buffer_len);
return 0;
}
-static ssize_t test_read(void *ctx, uint8_t *buffer, size_t buffer_len)
+static ssize_t test_read(void const *ctx, UNUSED void **packet_ctx, uint8_t *buffer, size_t buffer_len)
{
ssize_t data_size;
fr_io_test_ctx_t *io_ctx = talloc_get_type_abort(ctx, fr_io_test_ctx_t);
}
-static ssize_t test_write(void *ctx, uint8_t *buffer, size_t buffer_len)
+static ssize_t test_write(void const *ctx, UNUSED void *packet_ctx, uint8_t *buffer, size_t buffer_len)
{
ssize_t data_size;
fr_io_test_ctx_t *io_ctx = talloc_get_type_abort(ctx, fr_io_test_ctx_t);
return data_size;
}
-static int test_fd(void *ctx)
+static int test_fd(void const *ctx)
{
fr_io_test_ctx_t *io_ctx = talloc_get_type_abort(ctx, fr_io_test_ctx_t);
.open = test_open,
.read = test_read,
.write = test_write,
- .decode = test_decode,
- .encode = test_encode,
.fd = test_fd,
.nak = test_nak
};
uint16_t port16 = 0;
TALLOC_CTX *autofree = talloc_init("main");
fr_schedule_t *sched;
- fr_io_t io = { .op = &op };
+ fr_io_t io = { .op = &op, .decode = test_decode, .encode = test_encode };
fr_io_test_ctx_t *io_ctx;
io.ctx = io_ctx = talloc_zero(autofree, fr_io_test_ctx_t);
#include <freeradius-devel/io/control.h>
#include <freeradius-devel/io/worker.h>
+#include <freeradius-devel/io/listen.h>
#include <freeradius-devel/rad_assert.h>
#ifdef HAVE_GETOPT_H
return FR_IO_REPLY;
}
-static int test_decode(void *packet_ctx, uint8_t *const data, size_t data_len, REQUEST *request)
+static int test_decode(UNUSED void const *packet_ctx, REQUEST *request, uint8_t *const data, size_t data_len)
{
uint32_t number;
return 0;
}
-static ssize_t test_encode(void *packet_ctx, REQUEST *request, uint8_t *const data, size_t data_len)
+static ssize_t test_encode(void const *instance, REQUEST *request, uint8_t *const data, size_t data_len)
{
- MPRINT1("\t\tENCODE >>> request %"PRIu64" - data %p %p size %zd\n", request->number, packet_ctx, data, data_len);
+ MPRINT1("\t\tENCODE >>> request %"PRIu64" - data %p %p size %zd\n", request->number, instance, data, data_len);
return data_len;
}
static fr_io_op_t transport = {
.name = "worker-test",
.default_message_size = 4096,
- .decode = test_decode,
- .encode = test_encode,
.nak = test_nak,
};
fr_channel_event_t ce;
pthread_attr_t attr;
fr_schedule_worker_t *sw;
- fr_io_t io = { .ctx = NULL, .op = &transport };
+ fr_io_t io = { .ctx = NULL, .op = &transport, .encode = test_encode, .decode = test_decode };
struct kevent events[MAX_KEVENTS];
ctx = talloc_init("master");