23RCSID(
"$Id: 8fd3b9be6e033327bce66dc34c4054afcfd8cc12 $")
25#include <freeradius-devel/io/control.h>
26#include <freeradius-devel/io/listen.h>
27#include <freeradius-devel/io/worker.h>
28#include <freeradius-devel/radius/defs.h>
29#include <freeradius-devel/util/debug.h>
30#include <freeradius-devel/util/inet.h>
31#include <freeradius-devel/util/log.h>
32#include <freeradius-devel/util/md5.h>
33#include <freeradius-devel/util/syserror.h>
44#define MAX_MESSAGES (2048)
45#define MAX_CONTROL_PLANE (1024)
46#define MAX_KEVENTS (10)
47#define MAX_WORKERS (1024)
49#define MPRINT1 if (debug_lvl) printf
50#define MPRINT2 if (debug_lvl > 1) printf
64 struct sockaddr_storage src;
75static char const *
secret =
"testing123";
81 fprintf(stderr,
"usage: radius_test [OPTS]\n");
82 fprintf(stderr,
" -c <control-plane> Size of the control plane queue.\n");
83 fprintf(stderr,
" -i <address>[:port] Set IP address and optional port.\n");
84 fprintf(stderr,
" -q quiet - suppresses worker stats.\n");
85 fprintf(stderr,
" -s <secret> Set shared secret.\n");
86 fprintf(stderr,
" -w N Create N workers. Default is 1.\n");
87 fprintf(stderr,
" -x Debugging mode.\n");
94 MPRINT1(
"\t\tPROCESS --- request %"PRIu64
" action %d\n", request->number, action);
104 request->number = pc->
id;
109 MPRINT1(
"\t\tDECODE <<< request %"PRIu64
" - %p data %p size %zd\n", request->number, pc,
data, data_len);
120 MPRINT1(
"\t\tENCODE >>> request %"PRIu64
" - data %p %p room %zd\n",
121 request->number, pc,
buffer, buffer_len);
141 MPRINT1(
"\t\tNAK !!! request %d - data %p %p size %zd\n", packet[1], instance, packet, packet_len);
147 .name =
"worker-test",
163 MPRINT1(
"\tWorker %d started.\n", sw->
id);
169 fprintf(stderr,
"radius_test: Failed to create the event list\n");
175 fprintf(stderr,
"radius_test: Failed to create the worker\n");
179 MPRINT1(
"\tWorker %d looping.\n", sw->
id);
183 MPRINT1(
"\tWorker %d exiting.\n", sw->
id);
197 fprintf(stderr,
"Failed sending reply: %s\n",
fr_syserror(errno));
210 int rcode, i, num_events, which_worker;
215 pthread_attr_t pthread_attr;
228 fprintf(stderr,
"Failed creating message set\n");
246 fr_perror(
"radius_test: Failed creating socket");
251 fr_perror(
"radius_test: Failed binding to socket");
258 EV_SET(&
events[0],
sockfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
260 fr_perror(
"Failed setting KQ for EVFILT_READ");
267 (void) pthread_attr_init(&pthread_attr);
268 (void) pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_JOINABLE);
283 if (!
workers[i].worker)
continue;
284 if (
workers[i].ch != NULL)
continue;
300 MPRINT1(
"Master created all channels.\n");
306 bool control_plane_signal;
310 MPRINT1(
"Master waiting on events.\n");
313 MPRINT1(
"Master kevent returned %d\n", num_events);
315 if (num_events < 0) {
316 if (errno == EINTR)
continue;
318 fprintf(stderr,
"Failed waiting for kevent: %s\n",
fr_syserror(errno));
322 if (num_events == 0)
continue;
324 control_plane_signal =
false;
331 for (i = 0; i < num_events; i++) {
337 if (
events[i].filter == EVFILT_USER) {
339 control_plane_signal =
true;
350 packet_ctx->
salen =
sizeof(packet_ctx->
src);
357 (
struct sockaddr *) &packet_ctx->
src, &packet_ctx->
salen);
358 MPRINT1(
"Master got packet size %zd\n", data_size);
359 if (data_size <= 20) {
360 MPRINT1(
"Master ignoring packet (data length %zd)\n", data_size);
372 MPRINT1(
"Master ignoring packet code %u\n", packet[0]);
377 if (total_len < 20) {
378 MPRINT1(
"Master ignoring packet (header length %zu)\n", total_len);
381 if (total_len > (
size_t) data_size) {
382 MPRINT1(
"Master ignoring truncated packet (read %zd, says %zu)\n",
383 data_size, total_len);
388 end = packet + data_size;
390 if ((end - attr) < 2)
goto discard;
391 if (attr[0] == 0)
goto discard;
392 if (attr[1] < 2)
goto discard;
393 if ((attr + attr[1]) > end)
goto discard;
400 MPRINT1(
"Master sending packet size %zd to worker %d\n", cd->
m.
data_size, which_worker);
403 packet_ctx->
id = packet[1];
404 memcpy(packet_ctx->
vector, packet + 4, 16);
408 fprintf(stderr,
"Failed sending request: %s\n",
fr_syserror(errno));
418 if (!control_plane_signal)
continue;
422 MPRINT1(
"Master servicing control-plane\n");
430 if (!data_size)
break;
435 MPRINT1(
"Master got channel event %d\n", ce);
439 MPRINT1(
"Master got data ready signal\n");
443 MPRINT1(
"Master SIGNAL WITH NO DATA!\n");
453 sw = fr_channel_master_ctx_get(ch);
456 MPRINT1(
"Master received close ack signal for worker %d\n", sw->
id);
466 fprintf(stderr,
"Master got unexpected CE %d\n", ce);
490 if (!
workers[i].worker) num_outstanding--;
493 if ((now - last_checked) > (
NSEC / 10)) {
494 MPRINT1(
"still num_outstanding %d\n", num_outstanding);
497 }
while (num_outstanding > 0);
511 MPRINT2(
"Master messages used = %d\n", rcode);
521int main(
int argc,
char *argv[])
537 while ((c = getopt(argc, argv,
"c:hi:qs:w:x")) != -1)
switch (c) {
578 argc -= (optind - 1);
579 argv += (optind - 1);
585 setvbuf(stdout, NULL, _IONBF, 0);
static int const char char buffer[256]
size_t default_message_size
Usually maximum message size.
Public structure describing an I/O path for a protocol.
fr_atomic_queue_t * fr_atomic_queue_alloc(TALLOC_CTX *ctx, size_t size)
Create fixed-size atomic queue.
Structure to hold the atomic queue.
#define NEVER_RETURNS
Should be placed before the function return type.
bool fr_channel_recv_reply(fr_channel_t *ch)
Receive a reply message from the channel.
int fr_channel_send_request(fr_channel_t *ch, fr_channel_data_t *cd)
Send a request message into the channel.
fr_channel_event_t fr_channel_service_message(fr_time_t when, fr_channel_t **p_channel, void const *data, size_t data_size)
Service a control-plane message.
int fr_channel_service_kevent(fr_channel_t *ch, fr_control_t *c, UNUSED struct kevent const *kev)
Service a control-plane event.
A full channel, which consists of two ends.
fr_message_t m
the message header
@ FR_CHANNEL_DATA_READY_REQUESTOR
void * packet_ctx
Packet specific context for holding client information, and other proto_* specific information that n...
fr_listen_t * listen
for tracking packet transport, etc.
uint32_t priority
Priority of this packet.
Channel information which is added to a message.
static fr_atomic_queue_t * aq_master
static fr_control_t * control_master
#define FR_CONTROL_ID_CHANNEL
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
@ FR_RADIUS_CODE_ACCESS_REQUEST
RFC2865 - Access-Request.
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port)
uint8_t prefix
Prefix length - Between 0-32 for IPv4 and 0-128 for IPv6.
union fr_ipaddr_t::@130 addr
ssize_t fr_control_message_pop(fr_atomic_queue_t *aq, uint32_t *p_id, void *data, size_t data_size)
Pop control-plane message.
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_atomic_queue_t *aq)
Create a control-plane signaling path.
fr_app_io_t const * app_io
I/O path functions.
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Stores all information relating to an event list.
int fr_log_init_legacy(fr_log_t *log, bool daemonize)
Initialise file descriptors based on logging destination.
@ L_DBG_LVL_MAX
Lowest priority debug messages (-xxxxx | -Xxxx).
fr_md5_update_t fr_md5_update
fr_md5_final_t fr_md5_final
void fr_md5_ctx_free_from_list(fr_md5_ctx_t **ctx)
fr_md5_ctx_t * fr_md5_ctx_alloc_from_list(void)
int fr_message_done(fr_message_t *m)
Mark a message as done.
fr_message_t * fr_message_alloc(fr_message_set_t *ms, fr_message_t *m, size_t actual_packet_size)
Allocate packet data for a message.
int fr_message_set_messages_used(fr_message_set_t *ms)
Count the number of used messages.
void fr_message_set_debug(fr_message_set_t *ms, FILE *fp)
Print debug information about the message set.
void fr_message_set_gc(fr_message_set_t *ms)
Garbage collect the message set.
fr_message_t * fr_message_reserve(fr_message_set_t *ms, size_t reserve_size)
Reserve a message.
fr_message_set_t * fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size)
Create a message set.
A Message set, composed of message headers and ring buffer data.
size_t rb_size
cache-aligned size in the ring buffer
fr_time_t when
when this message was sent
uint8_t * data
pointer to the data in the ring buffer
size_t data_size
size of the data in the ring buffer
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
static TALLOC_CTX * autofree
static int max_control_plane
int main(int argc, char *argv[])
static void * worker_thread(void *arg)
static rlm_rcode_t test_process(UNUSED void const *instance, request_t *request, fr_io_action_t action)
static fr_schedule_worker_t workers[MAX_WORKERS]
static int test_decode(UNUSED void const *instance, request_t *request, uint8_t *const data, size_t data_len)
static ssize_t test_encode(UNUSED void const *instance, request_t *request, uint8_t *buffer, size_t buffer_len)
struct sockaddr_storage src
static size_t test_nak(void const *instance, UNUSED void *packet_ctx, uint8_t *const packet, size_t packet_len, UNUSED uint8_t *reply, UNUSED size_t reply_len)
#define MAX_CONTROL_PLANE
static char const * secret
static fr_ipaddr_t my_ipaddr
static void sig_ignore(int sig)
static fr_app_io_t app_io
static NEVER_RETURNS void usage(void)
static void send_reply(int sockfd, fr_channel_data_t *reply)
static fr_event_list_t * events
rlm_rcode_t
Return codes indicating the result of the module call.
Signals that can be sent to a request.
int fr_socket_server_udp(fr_ipaddr_t const *src_ipaddr, uint16_t *src_port, char const *port_name, bool async)
Open an IPv4/IPv6 unconnected UDP socket.
int fr_socket_bind(int sockfd, char const *ifname, fr_ipaddr_t *src_ipaddr, uint16_t *src_port)
Bind a UDP/TCP v4/v6 socket to a given ipaddr src port, and interface.
#define fr_time()
Allow us to arbitrarily manipulate time.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define talloc_get_type_abort_const
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
int fr_time_start(void)
Initialize the local time.
static fr_event_list_t * el
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
fr_channel_t * fr_worker_channel_create(fr_worker_t *worker, TALLOC_CTX *ctx, fr_control_t *master)
Create a channel to the worker.
fr_worker_t * fr_worker_create(TALLOC_CTX *ctx, fr_event_list_t *el, char const *name, fr_log_t const *logger, fr_log_lvl_t lvl, fr_worker_config_t *config)
Create a worker.
void fr_worker(fr_worker_t *worker)
The main loop and entry point of the stand-alone worker thread.
A worker which takes packets from a master, and processes them.
int id
ID of the worker 0..N.
fr_channel_t * ch
channel for communicating with the worker
pthread_t pthread_id
the thread of this worker
static void master_process(void)
fr_worker_t * worker
the worker data structure
Scheduler specific information for worker threads.