From: Arran Cudbard-Bell Date: Wed, 14 Jun 2017 22:20:28 +0000 (-0400) Subject: Commit 95% of the new module framework changes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6b71efcb3748372c597ca962861067a050b57877;p=thirdparty%2Ffreeradius-server.git Commit 95% of the new module framework changes It doesn't work yet, but the old tests pass and i'm tired. --- diff --git a/src/include/cf_parse.h b/src/include/cf_parse.h index 9dbc045dd84..688f1815786 100644 --- a/src/include/cf_parse.h +++ b/src/include/cf_parse.h @@ -75,6 +75,8 @@ typedef void _mismatch_size; //!< Dummy type used to indicate FR_TYPE_*/C type 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. @@ -178,6 +180,10 @@ _Generic((_ct), \ _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) \ @@ -267,6 +273,7 @@ _Generic((_ct), \ * user-supplied variables. */ #define FR_TYPE_SUBSECTION 102 +#define FR_TYPE_VOID 103 /** @name #CONF_PARSER type flags * diff --git a/src/include/dl.h b/src/include/dl.h index 77f98753c2f..b6d6818e83b 100644 --- a/src/include/dl.h +++ b/src/include/dl.h @@ -142,12 +142,23 @@ struct dl_module { 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); @@ -156,12 +167,15 @@ int dl_symbol_free_cb_register(char const *symbol, dl_free_t func, void *ctx); 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 diff --git a/src/include/modules.h b/src/include/modules.h index a241fe1d487..1867b72f642 100644 --- a/src/include/modules.h +++ b/src/include/modules.h @@ -204,9 +204,11 @@ rlm_rcode_t process_send_coa(int type, REQUEST *request); #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. diff --git a/src/include/protocol.h b/src/include/protocol.h index e4b41f60502..1da22c693f7 100644 --- a/src/include/protocol.h +++ b/src/include/protocol.h @@ -39,6 +39,14 @@ typedef int (*rad_listen_parse_t)(CONF_SECTION *, rad_listen_t *); 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, @@ -77,73 +85,6 @@ typedef struct rad_protocol_t { #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 diff --git a/src/lib/io/application.h b/src/lib/io/application.h index 939e2366972..1d6c6664e2f 100644 --- a/src/lib/io/application.h +++ b/src/lib/io/application.h @@ -39,21 +39,21 @@ typedef struct fr_schedule_t fr_schedule_t; */ 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) * @@ -63,12 +63,10 @@ typedef struct { 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 @@ -77,20 +75,12 @@ typedef int (*fr_app_subtype_instantiate_t)(fr_conf_section_t *cs); 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. @@ -98,7 +88,9 @@ typedef int (*fr_app_io_instantiate_t)(fr_conf_section_t *io_cs, void *instance) 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; diff --git a/src/lib/io/io.h b/src/lib/io/io.h index eaf929ce9e8..58282ff9d01 100644 --- a/src/lib/io/io.h +++ b/src/lib/io/io.h @@ -31,7 +31,6 @@ RCSIDH(transport_h, "$Id$") #include #include #include -//#include #ifdef __cplusplus extern "C" { @@ -78,20 +77,20 @@ typedef struct fr_channel_t fr_channel_t; * * 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. * @@ -103,15 +102,16 @@ typedef int (*fr_io_get_fd_t)(void *io_ctx); * 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. * @@ -123,15 +123,16 @@ typedef int (*fr_io_decode_t)(void *io_ctx, uint8_t *const data, size_t data_len * 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. * @@ -148,17 +149,52 @@ typedef ssize_t (*fr_io_encode_t)(void *io_ctx, REQUEST *request, uint8_t *buffe * 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. @@ -170,7 +206,7 @@ typedef size_t (*fr_io_nak_t)(void const *io_ctx, uint8_t *const packet, size_t * 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. @@ -183,14 +219,15 @@ typedef size_t (*fr_io_nak_t)(void const *io_ctx, uint8_t *const packet, size_t * 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. * @@ -199,12 +236,12 @@ typedef ssize_t (*fr_io_data_t)(void *io_ctx, uint8_t *buffer, size_t buffer_len * 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. * @@ -219,51 +256,17 @@ typedef struct fr_io_op_t { 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 diff --git a/src/lib/io/listen.h b/src/lib/io/listen.h index abca1ecfac9..0b09ca50a37 100644 --- a/src/lib/io/listen.h +++ b/src/lib/io/listen.h @@ -13,8 +13,8 @@ * 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 #include @@ -25,13 +25,13 @@ 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 diff --git a/src/lib/io/network.c b/src/lib/io/network.c index 4d946b38885..b60e4e7c830 100644 --- a/src/lib/io/network.c +++ b/src/lib/io/network.c @@ -33,6 +33,7 @@ RCSID("$Id$") #include #include #include +#include typedef struct fr_network_worker_t { int heap_id; //!< workers are in a heap @@ -361,7 +362,7 @@ static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, void *ctx) * 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"); @@ -776,7 +777,7 @@ void fr_network(fr_network_t *nr) * 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; diff --git a/src/lib/io/schedule.h b/src/lib/io/schedule.h index 4e82d760470..4521e5499cd 100644 --- a/src/lib/io/schedule.h +++ b/src/lib/io/schedule.h @@ -36,14 +36,14 @@ extern "C" { 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 } diff --git a/src/lib/io/worker.c b/src/lib/io/worker.c index 0ab8d92e8db..30bf73af153 100644 --- a/src/lib/io/worker.c +++ b/src/lib/io/worker.c @@ -64,6 +64,7 @@ RCSID("$Id$") #include #include #include +#include /** * Track things by priority and time. @@ -413,7 +414,7 @@ static void fr_worker_send_reply(fr_worker_t *worker, REQUEST *request, size_t s 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; @@ -695,7 +696,7 @@ static REQUEST *fr_worker_get_request(fr_worker_t *worker, fr_time_t now) * * 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); diff --git a/src/main/cf_parse.c b/src/main/cf_parse.c index f8865315cfa..dd879116a0a 100644 --- a/src/main/cf_parse.c +++ b/src/main/cf_parse.c @@ -846,22 +846,23 @@ static void cf_section_parse_warn(CONF_SECTION *cs) * @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); @@ -874,7 +875,12 @@ static int cf_subsection_parse(TALLOC_CTX *ctx, void *out, CONF_SECTION *cs, 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. @@ -921,10 +927,22 @@ static int cf_subsection_parse(TALLOC_CTX *ctx, void *out, CONF_SECTION *cs, 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) { @@ -972,24 +990,22 @@ int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs) */ 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 diff --git a/src/main/dl.c b/src/main/dl.c index 603494a60c3..df97c6c2700 100644 --- a/src/main/dl.c +++ b/src/main/dl.c @@ -390,15 +390,15 @@ static void *dl_by_name(char const *name) /** 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; @@ -767,6 +767,49 @@ dl_t const *dl_module(CONF_SECTION *conf, dl_t const *parent, char const *name, 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 * */ diff --git a/src/main/mainconfig.c b/src/main/mainconfig.c index 30046b57465..4bc030253ca 100644 --- a/src/main/mainconfig.c +++ b/src/main/mainconfig.c @@ -893,6 +893,7 @@ do {\ * 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; /* diff --git a/src/main/modules.c b/src/main/modules.c index 20415a81b07..29dd86bfbc0 100644 --- a/src/main/modules.c +++ b/src/main/modules.c @@ -891,7 +891,7 @@ static module_instance_t *module_bootstrap(CONF_SECTION *modules, CONF_SECTION * /* * 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; } diff --git a/src/main/radiusd.c b/src/main/radiusd.c index 2478b87d57c..b1077618ded 100644 --- a/src/main/radiusd.c +++ b/src/main/radiusd.c @@ -560,7 +560,12 @@ int main(int argc, char *argv[]) /* * 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 diff --git a/src/main/unit_test_module.c b/src/main/unit_test_module.c index 5bd98d7f44a..afd2995add2 100644 --- a/src/main/unit_test_module.c +++ b/src/main/unit_test_module.c @@ -896,7 +896,7 @@ int main(int argc, char *argv[]) /* * 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); diff --git a/src/main/virtual_servers.c b/src/main/virtual_servers.c index fda4a27b11d..223e26a0b06 100644 --- a/src/main/virtual_servers.c +++ b/src/main/virtual_servers.c @@ -32,6 +32,8 @@ RCSID("$Id$") #include #include #include +#include +#include static int default_component_results[MOD_COUNT] = { RLM_MODULE_REJECT, /* AUTH */ @@ -49,6 +51,87 @@ static int default_component_results[MOD_COUNT] = { #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) @@ -236,44 +319,6 @@ rlm_rcode_t process_send_coa(int send_coa_type, 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 @@ -358,8 +403,7 @@ static int virtual_servers_compile(CONF_SECTION *cs) 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) { @@ -443,6 +487,44 @@ static int virtual_servers_compile(CONF_SECTION *cs) 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; @@ -503,189 +585,166 @@ static bool virtual_server_define_types(CONF_SECTION *cs, rlm_components_t comp) 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; diff --git a/src/modules/proto_radius/proto_radius.c b/src/modules/proto_radius/proto_radius.c index 76ceb551c7c..8c6750e89f9 100644 --- a/src/modules/proto_radius/proto_radius.c +++ b/src/modules/proto_radius/proto_radius.c @@ -1,8 +1,4 @@ /* - * 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 @@ -16,399 +12,330 @@ * 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 */ +/** + * $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 -#include #include +#include #include +#include #include #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 }; diff --git a/src/modules/proto_radius/proto_radius.h b/src/modules/proto_radius/proto_radius.h index b156e6b0648..4d9449a315b 100644 --- a/src/modules/proto_radius/proto_radius.h +++ b/src/modules/proto_radius/proto_radius.h @@ -24,15 +24,4 @@ * @copyright 2017 Alan DeKok */ -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 */ diff --git a/src/modules/proto_radius/proto_radius_status.c b/src/modules/proto_radius/proto_radius_status.c index eef289bee6b..f3908b4afd8 100644 --- a/src/modules/proto_radius/proto_radius_status.c +++ b/src/modules/proto_radius/proto_radius_status.c @@ -28,6 +28,7 @@ #include #include #include +#include #include static fr_io_final_t mod_process(REQUEST *request, UNUSED fr_io_action_t action) @@ -227,7 +228,7 @@ static int mod_compile_section(CONF_SECTION *server_cs, char const *name1, char /* * 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; diff --git a/src/modules/proto_radius/proto_radius_udp.c b/src/modules/proto_radius/proto_radius_udp.c index 7794dc37659..55cfdca3c22 100644 --- a/src/modules/proto_radius/proto_radius_udp.c +++ b/src/modules/proto_radius/proto_radius_udp.c @@ -27,9 +27,22 @@ #include #include #include +#include #include #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; @@ -46,20 +59,11 @@ typedef struct { 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) }, @@ -72,16 +76,22 @@ static const CONF_PARSER udp_listen_conf[] = { 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; @@ -89,47 +99,97 @@ static ssize_t mod_read(void *ctx, uint8_t *buffer, size_t buffer_len) /* * 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) @@ -162,38 +222,7 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance) 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; } @@ -203,9 +232,9 @@ static int mod_open(void *instance) * @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; } @@ -214,7 +243,7 @@ extern fr_app_io_t proto_radius_udp; 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, diff --git a/src/modules/rlm_cache/rlm_cache.c b/src/modules/rlm_cache/rlm_cache.c index 6b1b9003c95..e048afb36c2 100644 --- a/src/modules/rlm_cache/rlm_cache.c +++ b/src/modules/rlm_cache/rlm_cache.c @@ -954,7 +954,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) 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; diff --git a/src/modules/rlm_eap/rlm_eap.c b/src/modules/rlm_eap/rlm_eap.c index cdc6a3158f0..0d4243c101c 100644 --- a/src/modules/rlm_eap/rlm_eap.c +++ b/src/modules/rlm_eap/rlm_eap.c @@ -105,7 +105,7 @@ int eap_method_instantiate(rlm_eap_method_t **out, rlm_eap_t *inst, eap_type_t n * 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; } diff --git a/src/modules/rlm_pap/rlm_pap.c b/src/modules/rlm_pap/rlm_pap.c index e489a433a1b..52964d2acac 100644 --- a/src/modules/rlm_pap/rlm_pap.c +++ b/src/modules/rlm_pap/rlm_pap.c @@ -506,8 +506,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, UNUSED void *t } 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; } diff --git a/src/modules/rlm_sql/rlm_sql.c b/src/modules/rlm_sql/rlm_sql.c index b16b87e4e29..6b5b0709af3 100644 --- a/src/modules/rlm_sql/rlm_sql.c +++ b/src/modules/rlm_sql/rlm_sql.c @@ -1091,7 +1091,7 @@ static int mod_bootstrap(CONF_SECTION *conf, void *instance) * 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; diff --git a/src/tests/util/radius1_test.c b/src/tests/util/radius1_test.c index a61562f7f0f..fe15c5d51af 100644 --- a/src/tests/util/radius1_test.c +++ b/src/tests/util/radius1_test.c @@ -24,6 +24,7 @@ RCSID("$Id$") #include #include +#include #include #include #include @@ -102,9 +103,9 @@ static fr_io_final_t test_process(REQUEST *request, fr_io_action_t action) } -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; @@ -116,10 +117,10 @@ static int test_decode(void *packet_ctx, uint8_t *const data, size_t data_len, R 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); @@ -139,9 +140,9 @@ static ssize_t test_encode(void *packet_ctx, REQUEST *request, uint8_t *buffer, 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; } @@ -149,8 +150,6 @@ static size_t test_nak(void const *packet_ctx, uint8_t *const packet, size_t pac static fr_io_op_t op = { .name = "worker-test", .default_message_size = 4096, - .decode = test_decode, - .encode = test_encode, .nak = test_nak, }; @@ -215,7 +214,7 @@ static void master_process(TALLOC_CTX *ctx) 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"); diff --git a/src/tests/util/radius_schedule_test.c b/src/tests/util/radius_schedule_test.c index 48600c69d36..68cd8b2a18a 100644 --- a/src/tests/util/radius_schedule_test.c +++ b/src/tests/util/radius_schedule_test.c @@ -23,6 +23,7 @@ RCSID("$Id$") #include +#include #include #include #include @@ -76,9 +77,9 @@ static fr_io_final_t test_process(REQUEST *request, fr_io_action_t action) 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; @@ -89,10 +90,10 @@ static int test_decode(void *ctx, uint8_t *const data, size_t data_len, REQUEST 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); @@ -136,7 +137,7 @@ static int test_open(void *ctx) 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); @@ -156,7 +157,7 @@ static ssize_t test_read(void *ctx, uint8_t *buffer, size_t buffer_len) } -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); @@ -173,7 +174,7 @@ static ssize_t test_write(void *ctx, uint8_t *buffer, size_t buffer_len) 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); @@ -186,8 +187,6 @@ static fr_io_op_t op = { .open = test_open, .read = test_read, .write = test_write, - .decode = test_decode, - .encode = test_encode, .fd = test_fd, .nak = test_nak }; @@ -211,7 +210,7 @@ int main(int argc, char *argv[]) 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); diff --git a/src/tests/util/worker_test.c b/src/tests/util/worker_test.c index 21a7a805529..53ed5da7abb 100644 --- a/src/tests/util/worker_test.c +++ b/src/tests/util/worker_test.c @@ -24,6 +24,7 @@ RCSID("$Id$") #include #include +#include #include #ifdef HAVE_GETOPT_H @@ -82,7 +83,7 @@ static fr_io_final_t test_process(REQUEST *request, fr_io_action_t action) 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; @@ -98,9 +99,9 @@ static int test_decode(void *packet_ctx, uint8_t *const data, size_t data_len, R 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; } @@ -123,8 +124,6 @@ static size_t test_nak(void const *packet_ctx, uint8_t *const packet, size_t pac static fr_io_op_t transport = { .name = "worker-test", .default_message_size = 4096, - .decode = test_decode, - .encode = test_encode, .nak = test_nak, }; @@ -170,7 +169,7 @@ static void master_process(void) 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");