23RCSID(
"$Id: 3c2c6a5c5c16782519381bc226f376e366c3bfc5 $")
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/md5.h>
31#include <freeradius-devel/util/syserror.h>
40#define MAX_MESSAGES (2048)
41#define MAX_CONTROL_PLANE (1024)
42#define MAX_KEVENTS (10)
43#define MAX_WORKERS (1024)
45#define MPRINT1 if (debug_lvl) printf
46#define MPRINT2 if (debug_lvl > 1) printf
60 struct sockaddr_storage src;
71static char const *
secret =
"testing123";
77 fprintf(stderr,
"usage: radius_test [OPTS]\n");
78 fprintf(stderr,
" -c <control-plane> Size of the control plane queue.\n");
79 fprintf(stderr,
" -i <address>[:port] Set IP address and optional port.\n");
80 fprintf(stderr,
" -q quiet - suppresses worker stats.\n");
81 fprintf(stderr,
" -s <secret> Set shared secret.\n");
82 fprintf(stderr,
" -w N Create N workers. Default is 1.\n");
83 fprintf(stderr,
" -x Debugging mode.\n");
90 MPRINT1(
"\t\tPROCESS --- request %"PRIu64
" action %d\n", request->number, action);
100 request->number = pc->
id;
105 MPRINT1(
"\t\tDECODE <<< request %"PRIu64
" - %p data %p size %zd\n", request->number, pc,
data, data_len);
116 MPRINT1(
"\t\tENCODE >>> request %"PRIu64
" - data %p %p room %zd\n",
117 request->number, pc,
buffer, buffer_len);
137 MPRINT1(
"\t\tNAK !!! request %d - data %p %p size %zd\n", packet[1], instance, packet, packet_len);
143 .name =
"worker-test",
159 MPRINT1(
"\tWorker %d started.\n", sw->
id);
165 fprintf(stderr,
"radius_test: Failed to create the event list\n");
171 fprintf(stderr,
"radius_test: Failed to create the worker\n");
175 MPRINT1(
"\tWorker %d looping.\n", sw->
id);
179 MPRINT1(
"\tWorker %d exiting.\n", sw->
id);
193 fprintf(stderr,
"Failed sending reply: %s\n",
fr_syserror(errno));
206 int rcode, i, num_events, which_worker;
211 pthread_attr_t pthread_attr;
224 fprintf(stderr,
"Failed creating message set\n");
242 fr_perror(
"radius_test: Failed creating socket");
247 fr_perror(
"radius_test: Failed binding to socket");
254 EV_SET(&
events[0],
sockfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
256 fr_perror(
"Failed setting KQ for EVFILT_READ");
263 (void) pthread_attr_init(&pthread_attr);
264 (void) pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_JOINABLE);
279 if (!
workers[i].worker)
continue;
280 if (
workers[i].ch != NULL)
continue;
296 MPRINT1(
"Master created all channels.\n");
302 bool control_plane_signal;
306 MPRINT1(
"Master waiting on events.\n");
309 MPRINT1(
"Master kevent returned %d\n", num_events);
311 if (num_events < 0) {
312 if (errno == EINTR)
continue;
314 fprintf(stderr,
"Failed waiting for kevent: %s\n",
fr_syserror(errno));
318 if (num_events == 0)
continue;
320 control_plane_signal =
false;
327 for (i = 0; i < num_events; i++) {
333 if (
events[i].filter == EVFILT_USER) {
335 control_plane_signal =
true;
346 packet_ctx->
salen =
sizeof(packet_ctx->
src);
353 (
struct sockaddr *) &packet_ctx->
src, &packet_ctx->
salen);
354 MPRINT1(
"Master got packet size %zd\n", data_size);
355 if (data_size <= 20) {
356 MPRINT1(
"Master ignoring packet (data length %zd)\n", data_size);
368 MPRINT1(
"Master ignoring packet code %u\n", packet[0]);
373 if (total_len < 20) {
374 MPRINT1(
"Master ignoring packet (header length %zu)\n", total_len);
377 if (total_len > (
size_t) data_size) {
378 MPRINT1(
"Master ignoring truncated packet (read %zd, says %zu)\n",
379 data_size, total_len);
384 end = packet + data_size;
386 if ((end - attr) < 2)
goto discard;
387 if (attr[0] == 0)
goto discard;
388 if (attr[1] < 2)
goto discard;
389 if ((attr + attr[1]) > end)
goto discard;
396 MPRINT1(
"Master sending packet size %zd to worker %d\n", cd->
m.
data_size, which_worker);
399 packet_ctx->
id = packet[1];
400 memcpy(packet_ctx->
vector, packet + 4, 16);
404 fprintf(stderr,
"Failed sending request: %s\n",
fr_syserror(errno));
414 if (!control_plane_signal)
continue;
418 MPRINT1(
"Master servicing control-plane\n");
426 if (!data_size)
break;
431 MPRINT1(
"Master got channel event %d\n", ce);
435 MPRINT1(
"Master got data ready signal\n");
439 MPRINT1(
"Master SIGNAL WITH NO DATA!\n");
449 sw = fr_channel_master_ctx_get(ch);
452 MPRINT1(
"Master received close ack signal for worker %d\n", sw->
id);
462 fprintf(stderr,
"Master got unexpected CE %d\n", ce);
486 if (!
workers[i].worker) num_outstanding--;
489 if ((now - last_checked) > (
NSEC / 10)) {
490 MPRINT1(
"still num_outstanding %d\n", num_outstanding);
493 }
while (num_outstanding > 0);
507 MPRINT2(
"Master messages used = %d\n", rcode);
517int main(
int argc,
char *argv[])
533 while ((c = getopt(argc, argv,
"c:hi:qs:w:x")) != -1)
switch (c) {
574 argc -= (optind - 1);
575 argv += (optind - 1);
581 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.
static TALLOC_CTX * autofree
#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.
#define FR_CONTROL_ID_CHANNEL
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_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::@137 addr
fr_control_t * fr_control_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_atomic_queue_t *aq, size_t num_callbacks)
Create a control-plane signaling path.
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_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).
void fr_md5_ctx_free_from_list(fr_md5_ctx_t **ctx)
Free function for MD5 digest ctx.
fr_md5_ctx_t * fr_md5_ctx_alloc_from_list(void)
Allocation function for MD5 digest context.
#define fr_md5_final(_out, _ctx)
Finalise the ctx, producing the digest.
#define fr_md5_update(_ctx, _in, _inlen)
Ingest plaintext into the digest.
fr_message_set_t * fr_message_set_create(TALLOC_CTX *ctx, int num_messages, size_t message_size, size_t ring_buffer_size, bool unlimited_size)
Create a message set.
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_gc(fr_message_set_t *ms)
Garbage collect the message set.
void fr_message_set_debug(FILE *fp, fr_message_set_t *ms)
Print debug information about the message set.
fr_message_t * fr_message_reserve(fr_message_set_t *ms, size_t reserve_size)
Reserve a message.
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 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.
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.
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
pthread ID of the worker
static void master_process(void)
fr_worker_t * worker
the worker data structure
Scheduler specific information for worker threads.