#include <freeradius-devel/rad_assert.h>
-#ifdef HAVE_STDATOMIC_H
-# include <stdatomic.h>
-#else
-# include <freeradius-devel/stdatomic.h>
-#endif
-
#include <signal.h>
#include <fcntl.h>
static fr_event_timer_t *sd_watchdog_ev;
#endif
-static bool spawn_workers = false;
-static bool just_started = true;
-time_t fr_start_time = (time_t)-1;
-static rbtree_t *packet_list = NULL;
-static fr_event_list_t *event_list = NULL;
-
-static void mark_home_server_alive(REQUEST *request, home_server_t *home);
-
-fr_event_list_t *process_global_event_list(UNUSED event_corral_t hint) {
- /* Currently we do not run a second event loop for modules. */
- return event_list;
-}
-
-static char const *action_codes[] = {
- "INVALID",
- "run",
- "done",
- "dup",
- "timer",
-#ifdef WITH_PROXY
- "proxy-reply"
-#endif
-};
-
-#ifdef DEBUG_STATE_MACHINE
-static char const *master_state_names[REQUEST_MASTER_NUM_STATES] = {
- "?",
- "active",
- "stop-processing",
- "counted"
-};
-
-static char const *child_state_names[REQUEST_CHILD_NUM_STATES] = {
- "?",
- "queued",
- "running",
- "proxied",
- "reject-delay",
- "cleanup-delay",
- "done"
-};
-
-void request_trace_state_machine(REQUEST *request)
-{
- struct timeval debug_tv;
- gettimeofday(&debug_tv, NULL);
- debug_tv.tv_sec -= fr_start_time;
- printf("(%" PRIu64 ") %d.%06d ********\tSTATE %s action %s live M-%s C-%s\t********\n",
- request->number, (int) debug_tv.tv_sec, (int) debug_tv.tv_usec,
- __FUNCTION__, action_codes[action], master_state_names[request->master_state],
- child_state_names[request->child_state]);
-}
-#endif
-
-
-#define rad_panic(_x, ...) radlog_fatal("%s[%u]: " _x, __FILE__, __LINE__, ## __VA_ARGS__)
-
-/** Declare a state in the state machine
- *
- * Expands to the start of a function definition for a given state.
- *
- * @param _x the name of the state.
- */
-#define STATE_MACHINE_DECL(_x) static void _x(REQUEST *request, fr_state_action_t action)
-
-static void request_timer(fr_event_list_t *eel, struct timeval *now, void *ctx);
-
-/** Insert #REQUEST back into the event heap, to continue executing at a future time
- *
- * @param file the state machine timer call occurred in.
- * @param line the state machine timer call occurred on.
- * @param request to set add the timer event for.
- * @param when the event should fine.
- */
-static inline void state_machine_timer(char const *file, int line, REQUEST *request,
- struct timeval *when)
-{
- if (fr_event_timer_insert(event_list, request_timer, request, when, &request->ev) < 0) {
- radlog_fatal("%s[%u]: Failed to insert event: %s", file, line, fr_strerror());
- }
-}
-
-/** @copybrief state_machine_timer
- *
- * @param _x the action to perform when we resume processing the request.
- */
-#define STATE_MACHINE_TIMER state_machine_timer(__FILE__, __LINE__, request, &when)
-
-/*
- * We need a different VERIFY_REQUEST macro in process.c
- * To avoid the race conditions with the master thread
- * checking the REQUEST whilst it's being worked on by
- * the child.
- */
-#if defined(WITH_VERIFY_PTR)
-# undef VERIFY_REQUEST
-# define VERIFY_REQUEST(_x) if (pthread_equal(pthread_self(), _x->child_pid) != 0) verify_request(__FILE__, __LINE__, _x)
-#endif
-
-/**
- * @section request_timeline
- *
- * Time sequence of a request
- * @code
- *
- * RQ-----------------P=============================Y-J-C
- * ::::::::::::::::::::::::::::::::::::::::::::::::::::::::M
- * @endcode
- *
- * - R: received. Duplicate detection is done, and request is
- * cached.
- *
- * - Q: Request is placed onto a queue for child threads to pick up.
- * If there are no child threads, the request goes immediately
- * to P.
- *
- * - P: Processing the request through the modules.
- *
- * - Y: Reply is ready. Rejects MAY be delayed here. All other
- * replies are sent immediately.
- *
- * - J: Reject is sent "response_delay" after the reply is ready.
- *
- * - C: For Access-Requests, After "cleanup_delay", the request is
- * deleted. Accounting-Request packets go directly from Y to C.
- *
- * - M: Max request time. If the request hits this timer, it is
- * forcibly stopped.
- *
- * Other considerations include duplicate and conflicting
- * packets. When a dupicate packet is received, it is ignored
- * until we've reached Y, as no response is ready. If the reply
- * is a reject, duplicates are ignored until J, when we're ready
- * to send the reply. In between the reply being sent (Y or J),
- * and C, the server responds to duplicates by sending the cached
- * reply.
- *
- * Conflicting packets are sent in 2 situations.
- *
- * The first is in between R and Y. In that case, we consider
- * it as a hint that we're taking too long, and the NAS has given
- * up on the request. We then behave just as if the M timer was
- * reached, and we discard the current request. This allows us
- * to process the new one.
- *
- * The second case is when we're at Y, but we haven't yet
- * finished processing the request. This is a race condition in
- * the threading code (avoiding locks is faster). It means that
- * a thread has actually encoded and sent the reply, and that the
- * NAS has responded with a new packet. The server can then
- * safely mark the current request as "OK to delete", and behaves
- * just as if the M timer was reached. This usually happens only
- * in high-load situations.
- *
- * Duplicate packets are sent when the NAS thinks we're taking
- * too long, and wants a reply. From R-Y, duplicates are
- * ignored. From Y-J (for Access-Rejects), duplicates are also
- * ignored. From Y-C, duplicates get a duplicate reply. *And*,
- * they cause the "cleanup_delay" time to be extended. This
- * extension means that we're more likely to send a duplicate
- * reply (if we have one), or to suppress processing the packet
- * twice if we didn't reply to it.
- *
- * All functions in this file should be thread-safe, and should
- * assume thet the REQUEST structure is being accessed
- * simultaneously by the main thread, and by the child worker
- * threads. This means that timers, etc. cannot be updated in
- * the child thread.
- *
- * Instead, the master thread periodically calls request->process
- * with action TIMER. It's up to the individual functions to
- * determine how to handle that. They need to check if they're
- * being called from a child thread or the master, and then do
- * different things based on that.
- */
-#ifdef WITH_PROXY
-static fr_packet_list_t *proxy_list = NULL;
-static TALLOC_CTX *proxy_ctx = NULL;
-#endif
-
-#ifdef WITH_PROXY
-static pthread_mutex_t proxy_mutex;
-static bool proxy_no_new_sockets = false;
-#endif
-
-#define pthread_mutex_lock if (spawn_workers) pthread_mutex_lock
-#define pthread_mutex_unlock if (spawn_workers) pthread_mutex_unlock
-
-static pthread_t NO_SUCH_CHILD_PID;
-#define NO_CHILD_THREAD request->child_pid = NO_SUCH_CHILD_PID
-
-static bool we_are_master(void)
-{
- if (spawn_workers &&
- (pthread_equal(pthread_self(), NO_SUCH_CHILD_PID) == 0)) {
- return false;
- }
-
- return true;
-}
-
-/*
- * Assertions are debug checks.
- */
-# ifndef NDEBUG
-# define ASSERT_MASTER if (!we_are_master()) rad_panic("We are not master")
-# endif
-
-#ifndef ASSERT_MASTER
-# define ASSERT_MASTER
-#endif
-
-/*
- * Make state transitions simpler.
- */
-#define FINAL_STATE(_x) NO_CHILD_THREAD; request->component = "<" #_x ">"; request->module = ""; request->child_state = _x
-
-
-static int event_new_fd(rad_listen_t *this);
-
-/*
- * We need mutexes around the event FD list *only* in certain
- * cases.
- */
-#if (defined(WITH_PROXY) || defined(WITH_TCP))
-static rad_listen_t *new_listeners = NULL;
-
-static pthread_mutex_t fd_mutex;
-# define FD_MUTEX_LOCK if (spawn_workers) pthread_mutex_lock
-# define FD_MUTEX_UNLOCK if (spawn_workers) pthread_mutex_unlock
-
-void radius_update_listener(rad_listen_t *this)
-{
- /*
- * Just do it ourselves.
- */
- if (we_are_master()) {
- event_new_fd(this);
- return;
- }
-
- FD_MUTEX_LOCK(&fd_mutex);
-
- /*
- * If it's already in the list, don't add it again.
- */
- if (this->next) {
- FD_MUTEX_UNLOCK(&fd_mutex);
- return;
- }
-
- /*
- * Otherwise, add it to the list
- */
- this->next = new_listeners;
- new_listeners = this;
- FD_MUTEX_UNLOCK(&fd_mutex);
- radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD);
-}
-#else
-void radius_update_listener(rad_listen_t *this)
-{
- /*
- * No threads. Just insert it.
- */
- event_new_fd(this);
-}
-/*
- * This is easier than ifdef's throughout the code.
- */
-# define FD_MUTEX_LOCK(_x)
-# define FD_MUTEX_UNLOCK(_x)
-#endif
-
-/** Session sequence number
- *
- * Unique for the lifetime of the process (or at least until it wraps).
- */
-static atomic_uint_fast64_t request_number_counter = ATOMIC_VAR_INIT(1);
-
-#ifdef WITH_PROXY
-static int request_will_proxy(REQUEST *request) CC_HINT(nonnull);
-static int request_proxy_send(REQUEST *request) CC_HINT(nonnull);
-STATE_MACHINE_DECL(request_ping) CC_HINT(nonnull);
-
-STATE_MACHINE_DECL(request_response_delay) CC_HINT(nonnull);
-STATE_MACHINE_DECL(request_cleanup_delay) CC_HINT(nonnull);
-STATE_MACHINE_DECL(request_queued) CC_HINT(nonnull);
-STATE_MACHINE_DECL(request_running) CC_HINT(nonnull);
-STATE_MACHINE_DECL(request_done) CC_HINT(nonnull);
-
-STATE_MACHINE_DECL(proxy_queued) CC_HINT(nonnull);
-STATE_MACHINE_DECL(proxy_no_reply) CC_HINT(nonnull);
-STATE_MACHINE_DECL(proxy_running) CC_HINT(nonnull);
-STATE_MACHINE_DECL(proxy_wait_for_reply) CC_HINT(nonnull);
-STATE_MACHINE_DECL(proxy_wait_for_id) CC_HINT(nonnull);
-
-static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply) CC_HINT(nonnull (1));
-static void remove_from_proxy_hash(REQUEST *request) CC_HINT(nonnull);
-static void remove_from_proxy_hash_nl(REQUEST *request, bool yank) CC_HINT(nonnull);
-static int insert_into_proxy_hash(REQUEST *request) CC_HINT(nonnull);
-#endif
-
-static int request_pre_handler(REQUEST *request, UNUSED fr_state_action_t action) CC_HINT(nonnull);
-
-#define CHECK_FOR_STOP do { if (request->master_state == REQUEST_STOP_PROCESSING) {action = FR_ACTION_DONE;}} while (0)
-#define CHECK_FOR_PROXY_CANCELLED do { if (!request->proxy->listener) {action = FR_ACTION_DONE;}} while (0)
-
-
-#undef USEC
-#define USEC (1000000)
-
-#define INSERT_EVENT(_function, _ctx) \
- if (fr_event_timer_insert(event_list, _function, _ctx, &((_ctx)->when), &((_ctx)->ev)) < 0) { \
- radlog_fatal("%s[%u]: %s", __FILE__, __LINE__, fr_strerror()); \
- }
-
-static void tv_add(struct timeval *tv, int usec_delay)
-{
- if (usec_delay >= USEC) {
- tv->tv_sec += usec_delay / USEC;
- usec_delay %= USEC;
- }
- tv->tv_usec += usec_delay;
-
- if (tv->tv_usec >= USEC) {
- tv->tv_sec += tv->tv_usec / USEC;
- tv->tv_usec %= USEC;
- }
-}
-
-/***********************************************************************
- *
- * Start of RADIUS server state machine.
- *
- ***********************************************************************/
-
-static struct timeval *request_response_window(REQUEST *request)
-{
- VERIFY_REQUEST(request);
- rad_assert(request->proxy != NULL);
- rad_assert(request->proxy->home_server != NULL);
-
- if (request->client) {
- /*
- * The client hasn't set the response window. Return
- * either the home server one, if set, or the global one.
- */
- if (!fr_timeval_isset(&request->client->response_window)) {
- return &request->proxy->home_server->response_window;
- }
-
- if (fr_timeval_cmp(&request->client->response_window,
- &request->proxy->home_server->response_window) < 0) {
- return &request->client->response_window;
- }
- }
-
- return &request->proxy->home_server->response_window;
-}
-
-/*
- * Determine initial request processing delay.
- */
-static int request_init_delay(REQUEST *request)
-{
- int delay;
-
- VERIFY_REQUEST(request);
-
- /* Allow client response window to lower initial delay */
- if (fr_timeval_isset(&request->client->response_window) &&
- fr_timeval_cmp(&main_config.init_delay, &request->client->response_window) > 0) {
- delay = request->client->response_window.tv_sec * USEC;
- delay += request->client->response_window.tv_usec;
-
- delay >>= 1;
-
- return delay;
- }
-
- return (int)request->root->init_delay.tv_sec * USEC +
- (int)request->root->init_delay.tv_usec;
-}
-
-/*
- * Callback for ALL timer events related to the request.
- */
-static void request_timer(UNUSED fr_event_list_t *eel, UNUSED struct timeval *now, void *ctx)
-{
- REQUEST *request = talloc_get_type_abort(ctx, REQUEST);
-#ifdef DEBUG_STATE_MACHINE
- fr_state_action_t action = FR_ACTION_TIMER;
-#endif
-
- TRACE_STATE_MACHINE;
-
- request->process(request, FR_ACTION_TIMER);
-}
-
-/*
- * Wrapper for talloc pools. If there's no parent, just free the
- * request. If there is a parent, free the parent INSTEAD of the
- * request.
- */
-void request_free(REQUEST *request)
-{
- void *ptr;
-
- rad_assert(request->ev == NULL);
- rad_assert(!request->in_request_hash);
- rad_assert(!request->in_proxy_hash);
-
- if ((request->options & RAD_REQUEST_OPTION_CTX) == 0) {
- talloc_free(request);
- return;
- }
-
- ptr = talloc_parent(request);
- rad_assert(ptr != NULL);
- talloc_free(ptr);
-}
-
-
-#ifdef WITH_PROXY
-static void proxy_reply_too_late(REQUEST *request)
-{
- char buffer[INET6_ADDRSTRLEN];
-
- RDEBUG2("Reply from home server %s port %d - ID: %d arrived too late. "
- "Try increasing 'retry_delay' or 'max_request_time'",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port, request->proxy->packet->id);
-}
-#endif
-
-
-static void request_dup_extract(REQUEST *request)
-{
- if (!request->in_request_hash) return;
-
- if (!rbtree_deletebydata(packet_list, &request->packet)) {
- rad_assert(0 == 1);
- }
- request->in_request_hash = false;
-}
-
-
-/*
- * If the child is still running, wait for it to be finished.
- */
-bool request_thread_active(REQUEST *request)
-{
- struct timeval when, now;
-
- if (!spawn_workers) return false;
-
- if (!we_are_master()) return true;
-
- if (request->child_state > REQUEST_RUNNING) return false;
-
- gettimeofday(&now, NULL);
- when = now;
- if (request->delay < (USEC / 3)) request->delay = USEC / 3;
- tv_add(&when, request->delay);
- request->delay += request->delay >> 1;
- if (request->delay > (10 * USEC)) request->delay = 10 * USEC;
-
- STATE_MACHINE_TIMER;
- return true;
-}
-
-
-void request_thread_done(REQUEST *request)
-{
- request->child_state = REQUEST_DONE;
- NO_CHILD_THREAD;
-}
-
-/*
- * Delete a request.
- */
-void request_delete(REQUEST *request)
-{
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
- rad_assert(!request->in_request_hash);
- rad_assert(request->heap_id == -1);
-#ifdef WITH_PROXY
- rad_assert(!request->in_proxy_hash);
-#endif
-
- if (request->el) {
- fr_event_timer_delete(request->el, &request->ev);
- } else {
- fr_event_timer_delete(event_list, &request->ev);
- }
-
- /*
- * @todo: do final states for TCP sockets, too?
- */
- request_stats_final(request);
-#ifdef WITH_TCP
- if (request->listener) {
- request->listener->count--;
-
- /*
- * If we're the last one, remove the listener now.
- */
- if ((request->listener->count == 0) &&
- (request->listener->status >= RAD_LISTEN_STATUS_FROZEN)) {
- event_new_fd(request->listener);
- }
- }
-#endif
-
- if (request->packet) {
- RDEBUG2("Cleaning up request packet ID %u with timestamp +%d",
- request->packet->id,
- (unsigned int) (request->packet->timestamp.tv_sec - fr_start_time));
- } /* else don't print anything */
-
- request_free(request);
-}
-
-
-/** Mark a request DONE and clean it up.
- *
- * When a request is DONE, it can have ties to a number of other
- * portions of the server. The request hash, proxy hash, events,
- * child threads, etc. This function takes care of either cleaning
- * up the request, or managing the timers to wait for the ties to be
- * removed.
- *
- * \dot
- * digraph done {
- * done -> done [ label = "still running" ];
- * }
- * \enddot
- */
-static void request_done(REQUEST *request, fr_state_action_t action)
-{
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
-
- /*
- * Force this no matter what.
- */
- request->master_state = REQUEST_STOP_PROCESSING;
- request->process = request_done;
- request->component = NULL;
- request->module = NULL;
-
-
-#ifdef WITH_DETAIL
- /*
- * Tell the detail listener that we're done.
- */
- if (request->listener &&
- (request->listener->type == RAD_LISTEN_DETAIL) &&
- ((request->options & RAD_REQUEST_OPTION_DETAIL) == 0)) {
- request->options |= RAD_REQUEST_OPTION_DETAIL;
- request->listener->send(request->listener,
- request);
- }
-#endif
-
- /*
- * If called from a child thread, mark ourselves as done,
- * and wait for the master thread timer to clean us up.
- */
- if (!we_are_master()) {
- FINAL_STATE(REQUEST_DONE);
- return;
- }
-
- switch (action) {
- case FR_ACTION_DUP:
-#ifdef WITH_DETAIL
- if (!rad_cond_assert(request->listener != NULL)) return;
-#endif
- if (request->reply->code != 0) {
- request->listener->send(request->listener, request);
- } else {
- RDEBUG("No reply. Ignoring retransmit");
- }
- /* @fixme: increment cleanup_delay */
- break;
-
- /*
- * Mark the request as done.
- */
- case FR_ACTION_DONE:
- /*
- * If the child is still running, leave it alone.
- */
- if (spawn_workers && (request->child_state <= REQUEST_RUNNING)) {
- break;
- }
-
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tSTATE %s C-%s -> C-%s\t********\n",
- request->number, __FUNCTION__,
- child_state_names[request->child_state],
- child_state_names[REQUEST_DONE]);
-#endif
- request->child_state = REQUEST_DONE;
- break;
-
- /*
- * Called when the child is taking too long to
- * finish. We've already marked it "please
- * stop", so we don't complain any more.
- */
- case FR_ACTION_TIMER:
- break;
-
-#ifdef WITH_PROXY
- case FR_ACTION_PROXY_REPLY:
- proxy_reply_too_late(request);
- break;
-#endif
-
- default:
- break;
- }
-
- /*
- * Remove it from the request hash.
- */
- request_dup_extract(request);
-
- /*
- * If there's no children, we can mark the request as done.
- */
- if (!spawn_workers) request->child_state = REQUEST_DONE;
-
- if (request_thread_active(request)) return;
-
- request_delete(request);
-}
-
-
-static void request_cleanup_delay_init(REQUEST *request)
-{
- struct timeval now, when;
-
- VERIFY_REQUEST(request);
-
- /*
- * Do cleanup delay ONLY for RADIUS packets from a real
- * client. Everything else just gets cleaned up
- * immediately.
- */
- if (request->packet->dst_port == 0) goto done;
-
- /*
- * Only Access-Requests get cleanup_delay. Everything
- * else gets cleaned up immediately.
- */
- if (request->packet->code != FR_CODE_ACCESS_REQUEST) goto done;
-
- if (!request->root->cleanup_delay) goto done;
-
- gettimeofday(&now, NULL);
-
- rad_assert(request->reply->timestamp.tv_sec != 0);
- when = request->reply->timestamp;
-
- request->delay = request->root->cleanup_delay;
- when.tv_sec += request->delay;
-
- /*
- * Set timer for when we need to clean it up.
- */
- if (fr_timeval_cmp(&when, &now) > 0) {
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
-#endif
- request->process = request_cleanup_delay;
-
- if (!we_are_master()) {
- FINAL_STATE(REQUEST_CLEANUP_DELAY);
- return;
- }
-
- /*
- * Update this if we can, otherwise let the timers pick it up.
- */
- request->child_state = REQUEST_CLEANUP_DELAY;
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
- STATE_MACHINE_TIMER;
- return;
- }
-
- /*
- * Otherwise just clean it up.
- */
-done:
- request_done(request, FR_ACTION_DONE);
-}
-
-
-/*
- * Enforce max_request_time.
- */
-bool request_max_time(REQUEST *request)
-{
- struct timeval now, when;
- rad_assert(request->magic == REQUEST_MAGIC);
-#ifdef DEBUG_STATE_MACHINE
- fr_state_action_t action = FR_ACTION_TIMER;
-#endif
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- ASSERT_MASTER;
-
- /*
- * The child thread has acknowledged it's done.
- * Transition to the DONE state.
- *
- * If the request was marked STOP, then the "check for
- * stop" macro already took care of it.
- */
- if (request->child_state == REQUEST_DONE) {
- done:
- request->master_state = REQUEST_STOP_PROCESSING;
- request->process(request, FR_ACTION_DONE);
- return true;
- }
-
- /*
- * The request is still running. Enforce max_request_time.
- */
- fr_event_list_time(&now, event_list);
- when = request->packet->timestamp;
- when.tv_sec += request->root->max_request_time;
-
- /*
- * Taking too long: tell it to die.
- */
- if (fr_timeval_cmp(&now, &when) >= 0) {
- /*
- * If there's a child thread processing it,
- * complain.
- */
- if (spawn_workers &&
- (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0)) {
- ERROR("Unresponsive child for request %" PRIu64 ", in component %s module %s",
- request->number,
- request->component ? request->component : "<core>",
- request->module ? request->module : "<core>");
- trigger_exec(request, NULL, "server.thread.unresponsive", true, NULL);
- }
-
- /*
- * Tell the request that it's done.
- */
- goto done;
- }
-
- /*
- * Sleep for some more. We HOPE that the child will
- * become responsive at some point in the future. We do
- * this by adding 50% to the current timer.
- */
- when = now;
- tv_add(&when, request->delay);
- request->delay += request->delay >> 1;
- STATE_MACHINE_TIMER;
- return false;
-}
-
-void request_thread(REQUEST *request, fr_request_process_t process)
-{
-#ifdef DEBUG_STATE_MACHINE
- fr_state_action_t action = FR_ACTION_TIMER;
-#endif
- struct timeval when;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- ASSERT_MASTER;
-
- request->process = process;
-
- /*
- * (re) set the initial delay.
- */
- request->delay = request_init_delay(request);
- gettimeofday(&when, NULL);
- tv_add(&when, request->delay);
- request->delay += request->delay >> 1;
-
- STATE_MACHINE_TIMER;
-
- request_enqueue(request);
-}
-
-
-static void request_dup_msg(REQUEST *request)
-{
- ERROR("(%" PRIu64 ") Ignoring duplicate packet from "
- "client %s port %d - ID: %u due to unfinished request "
- "in component %s module %s",
- request->number, request->client->shortname,
- request->packet->src_port,request->packet->id,
- request->component, request->module);
-}
-
-
-/** Sit on a request until it's time to clean it up.
- *
- * A NAS may not see a response from the server. When the NAS
- * retransmits, we want to be able to send a cached reply back. The
- * alternative is to re-process the packet, which does bad things for
- * EAP, among others.
- *
- * IF we do see a NAS retransmit, we extend the cleanup delay,
- * because the NAS might miss our cached reply.
- *
- * Otherwise, once we reach cleanup_delay, we transition to DONE.
- *
- * \dot
- * digraph cleanup_delay {
- * cleanup_delay;
- * send_reply [ label = "send_reply\nincrease cleanup delay" ];
- *
- * cleanup_delay -> send_reply [ label = "DUP" ];
- * send_reply -> cleanup_delay;
- * cleanup_delay -> proxy_reply_too_late [ label = "PROXY_REPLY", arrowhead = "none" ];
- * cleanup_delay -> cleanup_delay [ label = "TIMER < timeout" ];
- * cleanup_delay -> done [ label = "TIMER >= timeout" ];
- * }
- * \enddot
- */
-static void request_cleanup_delay(REQUEST *request, fr_state_action_t action)
-{
- struct timeval when, now;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- ASSERT_MASTER;
- CHECK_FOR_STOP;
-
- switch (action) {
- case FR_ACTION_DUP:
- if (request->reply->code != 0) {
- request->listener->send(request->listener, request);
- } else {
- RDEBUG("No reply. Ignoring retransmit");
- }
-
- /*
- * Double the cleanup_delay to catch retransmits.
- */
- when = request->reply->timestamp;
- request->delay += request->delay;
- when.tv_sec += request->delay;
-
- STATE_MACHINE_TIMER;
- break;
-
-#ifdef WITH_PROXY
- case FR_ACTION_PROXY_REPLY:
- proxy_reply_too_late(request);
- break;
-#endif
-
- case FR_ACTION_TIMER:
- fr_event_list_time(&now, event_list);
-
- rad_assert(request->root->cleanup_delay > 0);
-
- when = request->reply->timestamp;
- when.tv_sec += request->root->cleanup_delay;
-
- if (fr_timeval_cmp(&when, &now) > 0) {
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
-#endif
- STATE_MACHINE_TIMER;
- return;
- } /* else it's time to clean up */
- /* FALL-THROUGH */
-
- case FR_ACTION_DONE:
- request_done(request, FR_ACTION_DONE);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-
-/** Sit on a request until it's time to respond to it.
- *
- * For security reasons, rejects (and maybe some other) packets are
- * delayed for a while before we respond. This delay means that
- * badly behaved NASes don't hammer the server with authentication
- * attempts.
- *
- * Otherwise, once we reach response_delay, we send the reply, and
- * transition to cleanup_delay.
- *
- * \dot
- * digraph response_delay {
- * response_delay -> proxy_reply_too_late [ label = "PROXY_REPLY", arrowhead = "none" ];
- * response_delay -> response_delay [ label = "DUP, TIMER < timeout" ];
- * response_delay -> send_reply [ label = "TIMER >= timeout" ];
- * send_reply -> cleanup_delay;
- * }
- * \enddot
- */
-static void request_response_delay(REQUEST *request, fr_state_action_t action)
-{
- struct timeval when, now;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- ASSERT_MASTER;
- CHECK_FOR_STOP;
-
- switch (action) {
- case FR_ACTION_DUP:
- ERROR("(%" PRIu64 ") Discarding duplicate request from "
- "client %s port %d - ID: %u due to delayed response",
- request->number, request->client->shortname,
- request->packet->src_port, request->packet->id);
- break;
-
-#ifdef WITH_PROXY
- case FR_ACTION_PROXY_REPLY:
- proxy_reply_too_late(request);
- break;
-#endif
-
- case FR_ACTION_TIMER:
- fr_event_list_time(&now, event_list);
-
- /*
- * See if it's time to send the reply. If not,
- * we wait some more.
- */
- when = request->reply->timestamp;
-
- tv_add(&when, request->response_delay.tv_sec * USEC);
- tv_add(&when, request->response_delay.tv_usec);
-
- if (fr_timeval_cmp(&when, &now) > 0) {
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tNEXT-STATE %s -> %s\n",
- request->number, __FUNCTION__, "request_response_delay");
-#endif
- STATE_MACHINE_TIMER;
- return;
- } /* else it's time to send the reject */
-
- RDEBUG2("Sending delayed response");
- request->listener->debug(request, request->reply, false);
- request->listener->send(request->listener, request);
-
- /*
- * Clean up the request.
- */
- request_cleanup_delay_init(request);
- break;
-
- case FR_ACTION_DONE:
- request_done(request, FR_ACTION_DONE);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-
-extern fr_log_t debug_log;
-
-static int request_pre_handler(REQUEST *request, UNUSED fr_state_action_t action)
-{
- int rcode;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
-
- if (request->master_state == REQUEST_STOP_PROCESSING) return 0;
-
- /*
- * Don't decode the packet if it's an internal "fake"
- * request. Instead, just return so that the caller can
- * process it.
- */
- if (request->packet->dst_port == 0) {
- request->username = fr_pair_find_by_num(request->packet->vps, 0, FR_USER_NAME, TAG_ANY);
- request->password = fr_pair_find_by_num(request->packet->vps, 0, FR_USER_PASSWORD, TAG_ANY);
- return 1;
- }
-
- if (!request->packet->vps) { /* FIXME: check for correct state */
- rcode = request->listener->decode(request->listener, request);
-
-#ifdef WITH_UNLANG
- /*
- * If a specific destination has been set for
- * requests, overwrite the pointer to the default
- * log so output goes there instead.
- *
- * ...but only if there is no debug condition
- * or there is a condition and it matches.
- *
- * All other requests will go to the default log
- * destination with the default verbosity level.
- */
- if ((debug_log.dst != L_DST_NULL) &&
- (!debug_condition || (cond_eval(request, RLM_MODULE_OK, 0, debug_condition) == 1))) {
- request->log.dst = talloc_zero(request, log_dst_t);
- request->log.dst->func = vradlog_request;
- request->log.dst->uctx = &debug_log;
-
- request->log.lvl = req_debug_lvl;
- }
-#endif
-
- request->listener->debug(request, request->packet, true);
- } else {
- rcode = 0;
- }
-
- if (rcode < 0) {
- RATE_LIMIT(INFO("Dropping packet without response because of error: %s", fr_strerror()));
- return 0;
- }
-
- if (!request->username) {
- request->username = fr_pair_find_by_num(request->packet->vps, 0, FR_USER_NAME, TAG_ANY);
- }
-
- return 1;
-}
-
-
-/** Do the final processing of a request before we reply to the NAS.
- *
- * Various cleanups, suppress responses, copy Proxy-State, and set
- * response_delay or cleanup_delay;
- */
-static void request_finish(REQUEST *request, UNUSED fr_state_action_t action)
-{
- VALUE_PAIR *vp;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
-
- if (request->master_state == REQUEST_STOP_PROCESSING) {
- request->process(request, FR_ACTION_DONE);
- return;
- }
-
- /*
- * Override the response code if a control:Response-Packet-Type attribute is present.
- */
- vp = fr_pair_find_by_num(request->control, 0, FR_RESPONSE_PACKET_TYPE, TAG_ANY);
- if (vp) {
- if (vp->vp_uint32 == 256) {
- RDEBUG2("Not responding to request");
- request->reply->code = 0;
- } else {
- request->reply->code = vp->vp_uint32;
- }
- }
- /*
- * Catch Auth-Type := Reject BEFORE proxying the packet.
- */
- else if (request->packet->code == FR_CODE_ACCESS_REQUEST) {
- if (request->reply->code == 0) {
- vp = fr_pair_find_by_num(request->control, 0, FR_AUTH_TYPE, TAG_ANY);
- if (!vp || (vp->vp_uint32 != 5)) {
- RDEBUG2("There was no response configured: "
- "rejecting request");
- }
-
- request->reply->code = FR_CODE_ACCESS_REJECT;
- }
- }
-
- /*
- * Copy Proxy-State from the request to the reply.
- */
- vp = fr_pair_list_copy_by_num(request->reply, request->packet->vps, 0, FR_PROXY_STATE, TAG_ANY);
- if (vp) fr_pair_add(&request->reply->vps, vp);
-
- switch (request->reply->code) {
- case FR_CODE_ACCESS_ACCEPT:
- rad_postauth(request);
- break;
- case FR_CODE_ACCESS_CHALLENGE:
- fr_pair_delete_by_num(&request->control, 0, FR_POST_AUTH_TYPE, TAG_ANY);
- vp = pair_make_config("Post-Auth-Type", "Challenge", T_OP_SET);
- if (vp) rad_postauth(request);
- break;
- default:
- break;
- }
-
- /*
- * Run rejected packets through
- *
- * Post-Auth-Type = Reject
- *
- * We do this separately so ACK and challenge can change the code
- * to reject if a module returns reject.
- */
- if (request->reply->code == FR_CODE_ACCESS_REJECT) {
- fr_pair_delete_by_num(&request->control, 0, FR_POST_AUTH_TYPE, TAG_ANY);
- vp = pair_make_config("Post-Auth-Type", "Reject", T_OP_SET);
- if (vp) rad_postauth(request);
- }
-
- /*
- * Clean up. These are no longer needed.
- */
- gettimeofday(&request->reply->timestamp, NULL);
-
- /*
- * Fake packets get marked as "done", and have the
- * proxy-reply section deal with the reply attributes.
- * We therefore don't free the reply attributes.
- */
- if (request->packet->dst_port == 0) {
- RDEBUG("Finished internally proxied request.");
- FINAL_STATE(REQUEST_DONE);
- return;
- }
-
-#ifdef WITH_DETAIL
- /*
- * Always send the reply to the detail listener.
- */
- if (request->listener->type == RAD_LISTEN_DETAIL) {
- request->options |= RAD_REQUEST_OPTION_DETAIL;
-
- /*
- * But only print the reply if there is one.
- */
- if (request->reply->code != 0) {
- request->listener->debug(request, request->reply, false);
- }
-
- request->listener->send(request->listener, request);
- goto done;
- }
-#endif
-
- /*
- * Ignore all "do not respond" packets.
- * Except for the detail ones, which need to ping
- * the detail file reader so that it will retransmit.
- */
- if (!request->reply->code) {
- RDEBUG("Not sending reply to client.");
- goto done;
- }
-
- /*
- * If it's not in the request hash, we MIGHT not want to
- * send a reply.
- *
- * If duplicate packets are allowed, then then only
- * reason to NOT be in the request hash is because we
- * don't want to send a reply.
- *
- * FIXME: this is crap. The rest of the state handling
- * should use a different field so that we don't have two
- * meanings for it.
- *
- * Otherwise duplicates are forbidden, and the request is
- * SUPPOSED to avoid the request hash.
- *
- * In that case, we need to send a reply.
- */
- if (!request->in_request_hash &&
- !request->listener->nodup) {
- RDEBUG("Suppressing reply to client.");
- goto done;
- }
-
- /*
- * See if we need to delay an Access-Reject packet.
- */
- if ((request->reply->code == FR_CODE_ACCESS_REJECT) &&
- (request->root->reject_delay.tv_sec > 0)) {
- request->response_delay = request->root->reject_delay;
-
- vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY, TAG_ANY);
- if (vp) {
- if (vp->vp_uint32 <= 10) {
- request->response_delay.tv_sec = vp->vp_uint32;
- } else {
- request->response_delay.tv_sec = 10;
- }
- request->response_delay.tv_usec = 0;
- } else {
- vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY_USEC, TAG_ANY);
- if (vp) {
- if (vp->vp_uint32 <= 10 * USEC) {
- request->response_delay.tv_sec = vp->vp_uint32 / USEC;
- request->response_delay.tv_usec = vp->vp_uint32 % USEC;
- } else {
- request->response_delay.tv_sec = 10;
- request->response_delay.tv_usec = 0;
- }
- }
- }
-
-#ifdef WITH_PROXY
- /*
- * If we timed out a proxy packet, don't delay
- * the reject any more.
- */
- if (request->proxy && !request->proxy->reply) {
- request->response_delay.tv_sec = 0;
- request->response_delay.tv_usec = 0;
- }
-#endif
- }
-
- /*
- * Send the reply.
- */
- if ((request->response_delay.tv_sec == 0) &&
- (request->response_delay.tv_usec == 0)) {
-
- /*
- * Don't print a reply if there's none to send.
- */
- if (request->reply->code != 0) {
- request->listener->debug(request, request->reply, false);
- request->listener->send(request->listener, request);
- }
-
- done:
- RDEBUG2("Finished request");
- request_cleanup_delay_init(request);
-
- } else {
- /*
- * Encode and sign it here, so that the master
- * thread can just send the encoded data, which
- * means it does less work.
- */
- RDEBUG2("Delaying response for %d.%06d seconds",
- (int) request->response_delay.tv_sec, (int) request->response_delay.tv_usec);
- request->listener->encode(request->listener, request);
- request->process = request_response_delay;
-
- FINAL_STATE(REQUEST_RESPONSE_DELAY);
- }
-}
-
-/** Process a request from a client.
- *
- * The outcome might be that the request is proxied.
- *
- * \dot
- * digraph running {
- * running -> running [ label = "TIMER < max_request_time" ];
- * running -> done [ label = "TIMER >= max_request_time" ];
- * running -> proxy [ label = "proxied" ];
- * running -> dup [ label = "DUP", arrowhead = "none" ];
- * }
- * \enddot
- */
-static void request_running(REQUEST *request, fr_state_action_t action)
-{
- int ret;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
-
- switch (action) {
- case FR_ACTION_TIMER:
- (void) request_max_time(request);
- break;
-
- case FR_ACTION_DUP:
- request_dup_msg(request);
- break;
-
- case FR_ACTION_RUN:
- if (!request_pre_handler(request, action)) {
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tSTATE %s failed in pre-handler C-%s -> "
- "C-%s\t********\n",
- request->number, __FUNCTION__,
- child_state_names[request->child_state],
- child_state_names[REQUEST_DONE]);
-#endif
- FINAL_STATE(REQUEST_DONE);
- break;
- }
-
- rad_assert(request->handle != NULL);
- ret = request->handle(request);
- if (ret < 0) REDEBUG2("State callback returned error (%i): %s", ret, fr_strerror());
-
-#ifdef WITH_PROXY
- /*
- * We may need to send a proxied request.
- */
- if ((action == FR_ACTION_RUN) &&
- request_will_proxy(request)) {
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tWill Proxy\t********\n", request->number);
-#endif
- /*
- * If this fails, it
- * takes care of setting
- * up the post proxy fail
- * handler.
- */
- if (request_proxy_send(request) < 0) goto req_finished;
- } else
-#endif
- {
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tFinished\t********\n", request->number);
-#endif
-
-#ifdef WITH_PROXY
- req_finished:
-#endif
- request_finish(request, action);
- }
- break;
-
- case FR_ACTION_DONE:
- request_done(request, FR_ACTION_DONE);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-/** Process events while the request is queued.
- *
- * We give different messages on DUP, and on DONE,
- * remove the request from the queue
- *
- * \dot
- * digraph queued {
- * queued -> queued [ label = "TIMER < max_request_time" ];
- * queued -> done [ label = "TIMER >= max_request_time" ];
- * queued -> running [ label = "RUNNING" ];
- * queued -> dup [ label = "DUP", arrowhead = "none" ];
- * }
- * \enddot
- */
-static void request_queued(REQUEST *request, fr_state_action_t action)
-{
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
-
- switch (action) {
- case FR_ACTION_TIMER:
- (void) request_max_time(request);
- break;
-
- case FR_ACTION_DUP:
- ERROR("(%" PRIu64 ") Ignoring duplicate packet from "
- "client %s port %d - ID: %u as request is still queued.",
- request->number, request->client->shortname,
- request->packet->src_port,request->packet->id);
- break;
-
- case FR_ACTION_RUN:
- request->process = request_running;
- request->process(request, action);
- break;
-
- case FR_ACTION_DONE:
- request_queue_extract(request);
- request_dup_extract(request);
- request_delete(request);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-/*
- * See if a new packet is a duplicate of an old one.
- */
-bool request_dup_received(rad_listen_t *listener, rbtree_t *dup_tree, RADCLIENT *client, RADIUS_PACKET *packet)
-{
- RADIUS_PACKET **packet_p;
- rad_child_state_t child_state;
- REQUEST *request;
-
- packet_p = rbtree_finddata(dup_tree, &packet);
- if (!packet_p) return false;
-
- request = fr_packet2myptr(REQUEST, packet, packet_p);
- rad_assert(request->in_request_hash);
-
- child_state = request->child_state;
-
- /*
- * Same src/dst ip/port, length, and
- * authentication vector: must be a duplicate.
- */
- if ((request->packet->data_len == packet->data_len) &&
- (memcmp(request->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0)) {
-
-#ifdef WITH_STATS
- switch (packet->code) {
- case FR_CODE_ACCESS_REQUEST:
- FR_STATS_INC(auth, total_dup_requests);
- break;
-
-#ifdef WITH_ACCOUNTING
- case FR_CODE_ACCOUNTING_REQUEST:
- FR_STATS_INC(acct, total_dup_requests);
- break;
-#endif
-#ifdef WITH_COA
- case FR_CODE_COA_REQUEST:
- FR_STATS_INC(coa, total_dup_requests);
- break;
-
- case FR_CODE_DISCONNECT_REQUEST:
- FR_STATS_INC(dsc, total_dup_requests);
- break;
-#endif
-
- default:
- break;
- }
-#endif /* WITH_STATS */
-
- /*
- * Tell the state machine that there's a
- * duplicate request.
- */
- request->packet->count++;
- request->process(request, FR_ACTION_DUP);
- return true;
- }
-
- /*
- * Mark the old request as done ASAP, and before we log
- * anything. The child may stop processing the request
- * just as we're logging the complaint.
- *
- * If there's no child thread, the request will be marked
- * done immediately. If there is a child thread, it will
- * be notified, and a timer will be set to clean up the
- * request.
- */
- request->process(request, FR_ACTION_DONE);
- request = NULL;
-
- /*
- * It's a new request, not a duplicate. If the old one
- * is done, then we can clean it up.
- */
- if (child_state <= REQUEST_RUNNING) {
- /*
- * The request is still QUEUED or RUNNING. That's a problem.
- */
- ERROR("Received conflicting packet from "
- "client %s port %d - ID: %u due to "
- "unfinished request. Giving up on old request.",
- client->shortname,
- packet->src_port, packet->id);
- }
-
- return false;
-}
-
-
-bool request_limit(rad_listen_t *listener, RADCLIENT *client, RADIUS_PACKET *packet)
-{
- uint32_t count;
- listen_socket_t *sock = NULL;
-
- if (main_config.drop_requests) return true;
-
- /*
- * Quench maximum number of outstanding requests.
- */
- if (main_config.max_requests &&
- ((count = rbtree_num_elements(packet_list)) > main_config.max_requests)) {
- RATE_LIMIT(ERROR("Dropping request (%d is too many): from client %s port %d - ID: %d", count,
- client->shortname,
- packet->src_port, packet->id);
- WARN("Please check the configuration file.\n"
- "\tThe value for 'max_requests' is probably set too low.\n"));
-
- trigger_exec(NULL, NULL, "server.max_requests", true, NULL);
- return true;
- }
-
-#ifdef WITH_ACCOUNTING
- if (listener->type != RAD_LISTEN_DETAIL)
-#endif
- {
- sock = listener->data;
- }
-
- /*
- * Rate-limit the incoming packets
- */
- if (sock && sock->max_rate) {
- uint32_t pps;
-
- pps = rad_pps(&sock->rate_pps_old, &sock->rate_pps_now, &sock->rate_time, &packet->timestamp);
- if (pps > sock->max_rate) {
- DEBUG("Dropping request due to rate limiting");
- return true;
- }
- sock->rate_pps_now++;
- }
-
- return false;
-}
-
-
-int request_receive(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *packet,
- RADCLIENT *client, RAD_REQUEST_FUNP fun)
-{
- REQUEST *request = NULL;
- struct timeval now;
- listen_socket_t *sock = NULL;
-
- VERIFY_PACKET(packet);
-
- /*
- * Set the last packet received.
- */
- now = packet->timestamp;
- rad_assert(packet->timestamp.tv_sec != 0);
-
- listener->old_style = true; /* hack for now */
-
-#ifdef WITH_ACCOUNTING
- if (listener->type != RAD_LISTEN_DETAIL)
-#endif
- {
- sock = listener->data;
- sock->last_packet = now.tv_sec;
-
-#ifdef WITH_TCP
- packet->proto = sock->proto;
-#endif
- }
-
- /*
- * Check for duplicates.
- */
- if (!listener->nodup && request_dup_received(listener, packet_list, client, packet)) return 0;
-
- if (request_limit(listener, client, packet)) return 0;
-
- /*
- * Allocate a pool for the request.
- */
- if (!ctx) {
- ctx = talloc_pool(NULL, main_config.talloc_pool_size);
- if (!ctx) return 0;
- talloc_set_name_const(ctx, "request_receive_pool");
-
- /*
- * The packet is still allocated from a different
- * context, but oh well.
- */
- (void) talloc_steal(ctx, packet);
- }
-
- request = request_setup(ctx, listener, packet, client, fun);
- if (!request) {
- talloc_free(ctx);
- return 1;
- }
-
- /*
- * Remember the request in the list.
- */
- if (!listener->nodup) {
- if (!rbtree_insert(packet_list, &request->packet)) {
- RERROR("Failed to insert request in the list of live requests: discarding it");
- request_queued(request, FR_ACTION_DONE);
- return 1;
- }
-
- request->in_request_hash = true;
- }
-
- /*
- * Otherwise, insert it into the state machine.
- * The child threads will take care of processing it.
- */
- request_thread(request, request_queued);
-
- return 1;
-}
-
-
-REQUEST *request_setup(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *packet,
- RADCLIENT *client, RAD_REQUEST_FUNP fun)
-{
- REQUEST *request;
-
- /*
- * Create and initialize the new request.
- */
- request = request_alloc(ctx);
- if (!request) {
- ERROR("No memory");
- return NULL;
- }
- request->reply = fr_radius_alloc_reply(request, packet);
- if (!request->reply) {
- ERROR("No memory");
- talloc_free(request);
- return NULL;
- }
-
- /*
- * Mark it as a "real" request with a context.
- */
- request->options |= RAD_REQUEST_OPTION_CTX;
-
- request->listener = listener;
- request->client = client;
- request->packet = talloc_steal(request, packet);
- request->number = atomic_fetch_add_explicit(&request_number_counter, 1, memory_order_relaxed);
- request->priority = listener->type;
- if (request->priority >= RAD_LISTEN_MAX) {
- request->priority = RAD_LISTEN_AUTH;
- }
-
- request->master_state = REQUEST_ACTIVE;
- request->child_state = REQUEST_RUNNING;
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tSTATE %s C-%s -> C-%s\t********\n",
- request->number, __FUNCTION__,
- child_state_names[request->child_state],
- child_state_names[REQUEST_RUNNING]);
-#endif
- request->handle = fun;
- NO_CHILD_THREAD;
-
-#ifdef WITH_STATS
- request->listener->stats.last_packet = request->packet->timestamp.tv_sec;
- if (packet->code == FR_CODE_ACCESS_REQUEST) {
- request->client->auth.last_packet = request->packet->timestamp.tv_sec;
- radius_auth_stats.last_packet = request->packet->timestamp.tv_sec;
-#ifdef WITH_ACCOUNTING
- } else if (packet->code == FR_CODE_ACCOUNTING_REQUEST) {
- request->client->acct.last_packet = request->packet->timestamp.tv_sec;
- radius_acct_stats.last_packet = request->packet->timestamp.tv_sec;
-#endif
- }
-#endif /* WITH_STATS */
-
- /*
- * Status-Server packets go to the head of the queue.
- */
- if (request->packet->code == FR_CODE_STATUS_SERVER) request->priority = 0;
-
- /*
- * Set virtual server identity
- */
- if (client->server) {
- request->server_cs = client->server_cs;
- } else {
- request->server_cs = listener->server_cs;
- }
- rad_assert(request->server_cs != NULL);
-
- request->root = &main_config;
-#ifdef WITH_TCP
- request->listener->count++;
-#endif
-
- /*
- * The request passes many of our sanity checks.
- * From here on in, if anything goes wrong, we
- * send a reject message, instead of dropping the
- * packet.
- */
-
- return request;
-}
-
-#ifdef WITH_TCP
-/***********************************************************************
- *
- * TCP Handlers.
- *
- ***********************************************************************/
-
-/*
- * Timer function for all TCP sockets.
- */
-static void tcp_socket_timer(UNUSED fr_event_list_t *eel, struct timeval *now, void *ctx)
-{
- rad_listen_t *listener = talloc_get_type_abort(ctx, rad_listen_t);
- listen_socket_t *sock = listener->data;
- struct timeval end;
- char buffer[256];
- fr_socket_limit_t *limit;
-
- ASSERT_MASTER;
-
- if (listener->status != RAD_LISTEN_STATUS_KNOWN) return;
-
- fr_event_list_time(now, event_list);
-
- switch (listener->type) {
-#ifdef WITH_PROXY
- case RAD_LISTEN_PROXY:
- limit = &sock->home->limit;
- break;
-#endif
-
- case RAD_LISTEN_AUTH:
-#ifdef WITH_ACCOUNTING
- case RAD_LISTEN_ACCT:
-#endif
- limit = &sock->limit;
- break;
-
- default:
- return;
- }
-
- /*
- * If we enforce a lifetime, do it now.
- */
- if (limit->lifetime > 0) {
- end.tv_sec = sock->opened + limit->lifetime;
- end.tv_usec = 0;
-
- if (fr_timeval_cmp(&end, now) <= 0) {
- listener->print(listener, buffer, sizeof(buffer));
- DEBUG("Reached maximum lifetime on socket %s", buffer);
-
- do_close:
-
-#ifdef WITH_PROXY
- /*
- * Proxy sockets get frozen, so that we don't use
- * them for new requests. But we do keep them
- * open to listen for replies to requests we had
- * previously sent.
- */
- if (listener->type == RAD_LISTEN_PROXY) {
- pthread_mutex_lock(&proxy_mutex);
- if (!fr_packet_list_socket_freeze(proxy_list,
- listener->fd)) {
- PERROR("Fatal error freezing socket");
- fr_exit(1);
- }
- pthread_mutex_unlock(&proxy_mutex);
- }
-#endif
-
- /*
- * Mark the socket as don't use, and
- * remove it from the incoming list of
- * FDs.
- */
- listener->status = RAD_LISTEN_STATUS_FROZEN;
- fr_event_fd_delete(event_list, listener->fd);
- event_new_fd(listener); /* mainly set a new timer */
- return;
- }
- } else {
- end = *now;
- end.tv_sec += 3600;
- }
-
- /*
- * Enforce an idle timeout.
- */
- if (limit->idle_timeout > 0) {
- struct timeval idle;
-
- rad_assert(sock->last_packet != 0);
- idle.tv_sec = sock->last_packet + limit->idle_timeout;
- idle.tv_usec = 0;
-
- if (fr_timeval_cmp(&idle, now) <= 0) {
- listener->print(listener, buffer, sizeof(buffer));
- DEBUG("Reached idle timeout on socket %s", buffer);
- goto do_close;
- }
-
- /*
- * Enforce the minimum of idle timeout or lifetime.
- */
- if (fr_timeval_cmp(&idle, &end) < 0) {
- end = idle;
- }
- }
-
- /*
- * Wake up at t + 0.5s. The code above checks if the timers
- * are <= t. This addition gives us a bit of leeway.
- */
- end.tv_usec = USEC / 2;
-
- listener->when = end;
-
- INSERT_EVENT(tcp_socket_timer, listener);
-}
-
-
-#ifdef WITH_PROXY
-/*
- * Add +/- 2s of jitter, as suggested in RFC 3539
- * and in RFC 5080.
- */
-static void add_jitter(struct timeval *when)
-{
- uint32_t jitter;
-
- when->tv_sec -= 2;
-
- jitter = fr_rand();
- jitter ^= (jitter >> 10);
- jitter &= ((1 << 22) - 1); /* 22 bits of 1 */
-
- /*
- * Add in ~ (4 * USEC) of jitter.
- */
- tv_add(when, jitter);
-}
-
-/*
- * Called by socket_del to remove requests with this socket
- */
-static int eol_proxy_listener(void *ctx, void *data)
-{
- rad_listen_t *this = talloc_get_type_abort(ctx, rad_listen_t);
- RADIUS_PACKET **proxy_p = data;
- REQUEST *request;
-
- request = fr_packet2myptr(REQUEST, packet, proxy_p);
-
- VERIFY_REQUEST(request);
- rad_assert(request->parent != NULL);
- rad_assert(request->parent->proxy == request);
- request = request->parent;
- VERIFY_REQUEST(request);
-
- if (request->proxy->listener != this) return 0;
-
-#ifdef WITH_ACCOUNTING
- /*
- * Accounting packets should be deleted immediately.
- * They will never be retransmitted by the client.
- */
- if (request->proxy->packet->code == FR_CODE_ACCOUNTING_REQUEST) {
- RDEBUG("Stopping request due to failed connection to home server");
- request->master_state = REQUEST_STOP_PROCESSING;
- }
-#endif
-
- /*
- * The normal "remove_from_proxy_hash" tries to grab the
- * proxy mutex. We already have it held, so grabbing it
- * again will cause a deadlock. Instead, call the "no
- * lock" version of the function.
- */
- rad_assert(request->in_proxy_hash == true);
- remove_from_proxy_hash_nl(request, false);
-
- /*
- * Don't mark it as DONE. The client can retransmit, and
- * the packet SHOULD be re-proxied somewhere else.
- *
- * Return "2" means that the rbtree code will remove it
- * from the tree, and we don't need to do it ourselves.
- */
- return 2;
-}
-#endif /* WITH_PROXY */
-
-static int eol_listener(void *ctx, void *data)
-{
- rad_listen_t *this = talloc_get_type_abort(ctx, rad_listen_t);
- RADIUS_PACKET **packet_p = data;
- REQUEST *request;
-
- request = fr_packet2myptr(REQUEST, packet, packet_p);
- if (request->listener != this) return 0;
-
- request->master_state = REQUEST_STOP_PROCESSING;
-
- return 0;
-}
-#endif /* WITH_TCP */
-
-#ifdef WITH_PROXY
-/***********************************************************************
- *
- * Proxy handlers for the state machine.
- *
- ***********************************************************************/
-
-/*
- * Called with the proxy mutex held
- */
-static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
-{
- VERIFY_REQUEST(request);
-
- if (!request->in_proxy_hash || !request->proxy->listener) return;
-
- fr_packet_list_id_free(proxy_list, request->proxy->packet, yank);
- request->in_proxy_hash = false;
-
- /*
- * On the FIRST reply, decrement the count of outstanding
- * requests. Note that this is NOT the count of sent
- * packets, but whether or not the home server has
- * responded at all.
- */
- if (request->proxy->home_server &&
- request->proxy->home_server->currently_outstanding) {
- request->proxy->home_server->currently_outstanding--;
-
- /*
- * If we're NOT sending it packets, AND it's been
- * a while since we got a response, then we don't
- * know if it's alive or dead.
- */
- if ((request->proxy->home_server->currently_outstanding == 0) &&
- (request->proxy->home_server->state == HOME_STATE_ALIVE)) {
- struct timeval when, now;
-
- when.tv_sec = request->proxy->home_server->last_packet_recv ;
- when.tv_usec = 0;
-
- fr_timeval_add(&when, request_response_window(request), &when);
- gettimeofday(&now, NULL);
-
- /*
- * last_packet + response_window
- *
- * We *administratively* mark the home
- * server as "unknown" state, because we
- * haven't seen a packet for a while.
- */
- if (fr_timeval_cmp(&now, &when) > 0) {
- request->proxy->home_server->state = HOME_STATE_UNKNOWN;
- request->proxy->home_server->last_packet_sent = 0;
- request->proxy->home_server->last_packet_recv = 0;
- }
- }
- }
-
-#ifdef WITH_TCP
- request->proxy->listener->count--;
-#endif
- request->proxy->listener = NULL;
-
- /*
- * Got from YES in hash, to NO, not in hash while we hold
- * the mutex. This guarantees that when another thread
- * grabs the mutex, the "not in hash" flag is correct.
- */
-}
-
-static void remove_from_proxy_hash(REQUEST *request)
-{
- VERIFY_REQUEST(request);
-
- /*
- * Check this without grabbing the mutex because it's a
- * lot faster that way.
- */
- if (!request->in_proxy_hash) return;
-
- /*
- * The "not in hash" flag is definitive. However, if the
- * flag says that it IS in the hash, there might still be
- * a race condition where it isn't.
- */
- pthread_mutex_lock(&proxy_mutex);
-
- if (!request->in_proxy_hash) {
- pthread_mutex_unlock(&proxy_mutex);
- return;
- }
-
- remove_from_proxy_hash_nl(request, true);
-
- pthread_mutex_unlock(&proxy_mutex);
-}
-
-static int insert_into_proxy_hash(REQUEST *request)
-{
- char buffer[INET6_ADDRSTRLEN];
- int tries;
- bool success = false;
- void *proxy_listener;
-
- VERIFY_REQUEST(request);
-
- rad_assert(request->proxy != NULL);
- rad_assert(request->proxy->home_server != NULL);
- rad_assert(proxy_list != NULL);
-
-
- pthread_mutex_lock(&proxy_mutex);
- proxy_listener = NULL;
- request->proxy->packet->count = 1;
-
- for (tries = 0; tries < 2; tries++) {
- rad_listen_t *this;
- listen_socket_t *sock;
-
- RDEBUG3("proxy: Trying to allocate ID (%d/2)", tries);
- success = fr_packet_list_id_alloc(proxy_list,
- request->proxy->home_server->proto,
- &request->proxy->packet, &proxy_listener);
- if (success) break;
-
- if (tries > 0) continue; /* try opening new socket only once */
-
- if (proxy_no_new_sockets) break;
-
- RDEBUG3("proxy: Trying to open a new listener to the home server");
- this = proxy_new_listener(proxy_ctx, request->proxy->home_server, 0);
- if (!this) {
- pthread_mutex_unlock(&proxy_mutex);
- goto fail;
- }
-
- request->proxy->packet->src_port = 0; /* Use any new socket */
- proxy_listener = this;
-
- sock = this->data;
- if (!fr_packet_list_socket_add(proxy_list, this->fd,
- sock->proto,
- &sock->other_ipaddr, sock->other_port,
- this)) {
-
- proxy_no_new_sockets = true;
-
- pthread_mutex_unlock(&proxy_mutex);
-
- /*
- * This is bad. However, the
- * packet list now supports 256
- * open sockets, which should
- * minimize this problem.
- */
- ERROR("Failed adding proxy socket: %s",
- fr_strerror());
- goto fail;
- }
-
- /*
- * Add it to the event loop. Ensure that we have
- * only one mutex locked at a time.
- */
- pthread_mutex_unlock(&proxy_mutex);
- radius_update_listener(this);
- pthread_mutex_lock(&proxy_mutex);
- }
-
- if (!proxy_listener || !success) {
- pthread_mutex_unlock(&proxy_mutex);
- REDEBUG2("proxy: Failed allocating Id for proxied request");
- fail:
- request->proxy->listener = NULL;
- request->in_proxy_hash = false;
- return 0;
- }
-
- rad_assert(request->proxy->packet->id >= 0);
-
- request->proxy->listener = proxy_listener;
- request->in_proxy_hash = true;
- RDEBUG3("proxy: request is now in proxy hash");
-
- /*
- * Keep track of maximum outstanding requests to a
- * particular home server. 'max_outstanding' is
- * enforced in home_server_ldb(), in realms.c.
- */
- request->proxy->home_server->currently_outstanding++;
-
-#ifdef WITH_TCP
- request->proxy->listener->count++;
-#endif
-
- pthread_mutex_unlock(&proxy_mutex);
-
- RDEBUG3("proxy: allocating destination %s port %d - Id %d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af, &request->proxy->packet->dst_ipaddr.addr, buffer, sizeof(buffer)),
- request->proxy->packet->dst_port,
- request->proxy->packet->id);
-
- return 1;
-}
-
-static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply)
-{
- int rcode;
- VALUE_PAIR *vp;
-
- VERIFY_REQUEST(request);
-
- /*
- * Delete any reply we had accumulated until now.
- */
- RDEBUG2("Clearing existing &reply: attributes");
- fr_pair_list_free(&request->reply->vps);
-
- /*
- * Run the packet through the post-proxy stage,
- * BEFORE playing games with the attributes.
- */
- vp = fr_pair_find_by_num(request->control, 0, FR_POST_PROXY_TYPE, TAG_ANY);
-
- /*
- * If we have a proxy_reply, and it was a reject, or a NAK
- * setup Post-Proxy <type>.
- *
- * If the <type> doesn't have a section, then the Post-Proxy
- * section is ignored.
- */
- if (!vp && reply) {
- fr_dict_enum_t *dval = NULL;
- fr_dict_attr_t const *da = fr_dict_attr_by_num(NULL, 0, FR_POST_PROXY_TYPE);
-
- switch (reply->code) {
- case FR_CODE_ACCESS_REJECT:
- case FR_CODE_DISCONNECT_NAK:
- case FR_CODE_COA_NAK:
- dval = fr_dict_enum_by_alias(NULL, da, fr_packet_codes[reply->code]);
-
- if (dval) {
- vp = radius_pair_create(request, &request->control, FR_POST_PROXY_TYPE, 0);
- fr_value_box_copy(NULL, &vp->data, dval->value);
- }
- break;
-
- default:
- break;
- }
- }
-
- if (vp) RDEBUG2("Found Post-Proxy-Type %s", fr_dict_enum_alias_by_value(NULL, vp->da, &vp->data));
-
- /*
- * Remove it from the proxy hash, if there's no reply, or
- * if we recieved all of the replies.
- */
- if (request->in_proxy_hash &&
- (!reply || (request->proxy->packet->count <= reply->count))) {
- remove_from_proxy_hash(request);
- }
-
- if (request->home_pool && request->home_pool->virtual_server) {
- CONF_SECTION *old_server = request->server_cs;
-
- request->server_cs = virtual_server_find(request->home_pool->virtual_server); /* @fixme 4.0 this shouldn't be necessary! */
-
- RDEBUG2("server %s {", cf_section_name2(request->server_cs));
- RINDENT();
- rcode = process_post_proxy(vp ? vp->vp_uint32 : 0, request);
- REXDENT();
- RDEBUG2("}");
- request->server_cs = old_server;
- } else {
- rcode = process_post_proxy(vp ? vp->vp_uint32 : 0, request);
- }
-
- switch (rcode) {
- default: /* Don't do anything */
- break;
- case RLM_MODULE_FAIL:
- return 0;
-
- case RLM_MODULE_HANDLED:
- return 0;
- }
-
- return 1;
-}
-
-int request_proxy_reply(RADIUS_PACKET *reply)
-{
- RADIUS_PACKET **packet_p;
- REQUEST *request, *proxy;
- struct timeval now;
- char buffer[INET6_ADDRSTRLEN];
-
- VERIFY_PACKET(reply);
-
- pthread_mutex_lock(&proxy_mutex);
- packet_p = fr_packet_list_find_byreply(proxy_list, reply);
-
- if (!packet_p) {
- pthread_mutex_unlock(&proxy_mutex);
- PROXY("No outstanding request was found for %s packet from host %s port %d - ID %u",
- fr_packet_codes[reply->code],
- inet_ntop(reply->src_ipaddr.af,
- &reply->src_ipaddr.addr,
- buffer, sizeof(buffer)),
- reply->src_port, reply->id);
- return 0;
- }
-
- /*
- * The proxied packet is in request->proxy->packet.
- * First, dereference "reply" to find "request->proxy".
- * Then, check request->proxy->parent, which is the one we want.
- */
- proxy = fr_packet2myptr(REQUEST, packet, packet_p);
- VERIFY_REQUEST(proxy);
-
- /*
- * The sent packet is in a proxy REQUEST.
- * We want to get the parent request.
- */
- rad_assert(proxy->parent != NULL);
- rad_assert(proxy->parent->proxy == proxy);
-
- request = proxy->parent;
-
- pthread_mutex_unlock(&proxy_mutex);
-
- VERIFY_REQUEST(request);
-
- /*
- * No reply, BUT the current packet fails verification:
- * ignore it. This does the MD5 calculations in the
- * server core, but I guess we can fix that later.
- */
- if (!proxy->reply && (fr_radius_packet_verify(reply, proxy->packet, proxy->home_server->secret) != 0)) {
- RWDEBUG("Discarding invalid reply from host %s port %d - ID: %d: %s",
- inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.addr, buffer, sizeof(buffer)),
- reply->src_port, reply->id, fr_strerror());
- return 0;
- }
-
- /*
- * The home server sent us a packet which doesn't match
- * something we have: ignore it. This is done only to
- * catch the case of broken systems.
- */
- if (proxy->reply && (memcmp(proxy->reply->vector, reply->vector, sizeof(proxy->reply->vector)) != 0)) {
- RWDEBUG("Discarding conflicting reply from host %s port %d - ID: %d",
- inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.addr, buffer, sizeof(buffer)),
- reply->src_port, reply->id);
- return 0;
- }
-
- /*
- * If we have previously seen a reply, ignore the
- * duplicate.
- */
- if (proxy->reply) {
- proxy->reply->count++;
-
- RWDEBUG("Discarding duplicate reply from host %s port %d - ID: %d",
- inet_ntop(reply->src_ipaddr.af, &reply->src_ipaddr.addr, buffer, sizeof(buffer)),
- reply->src_port, reply->id);
- return 0;
- }
-
- gettimeofday(&now, NULL);
-
- /*
- * Status-Server packets don't count as real packets.
- */
- if (proxy->packet->code != FR_CODE_STATUS_SERVER) {
- listen_socket_t *sock = proxy->listener->data;
-
- proxy->home_server->last_packet_recv = now.tv_sec;
- sock->last_packet = now.tv_sec;
- }
-
- /*
- * Call the state machine to do something useful with the
- * request.
- */
- proxy->reply = talloc_steal(proxy, reply);
- proxy->reply->count++;
- request->priority = RAD_LISTEN_PROXY;
-
-#ifdef WITH_STATS
- if (!proxy->listener) goto global_stats;
-
- /*
- * Update the proxy listener stats here, because only one
- * thread accesses that at a time. The home_server and
- * main proxy_*_stats structures are updated once the
- * request is cleaned up.
- */
- proxy->listener->stats.total_responses++;
-
- proxy->listener->stats.last_packet = reply->timestamp.tv_sec;
-
- switch (proxy->packet->code) {
- case FR_CODE_ACCESS_REQUEST:
- if (proxy->reply->code == FR_CODE_ACCESS_ACCEPT) {
- proxy->listener->stats.total_access_accepts++;
-
- } else if (proxy->reply->code == FR_CODE_ACCESS_REJECT) {
- proxy->listener->stats.total_access_rejects++;
-
- } else if (proxy->reply->code == FR_CODE_ACCESS_CHALLENGE) {
- proxy->listener->stats.total_access_challenges++;
- }
- break;
-
-#ifdef WITH_ACCOUNTING
- case FR_CODE_ACCOUNTING_REQUEST:
- proxy->listener->stats.total_responses++;
- break;
-
-#endif
-
-#ifdef WITH_COA
- case FR_CODE_COA_REQUEST:
- proxy->listener->stats.total_responses++;
- break;
-
- case FR_CODE_DISCONNECT_REQUEST:
- proxy->listener->stats.total_responses++;
- break;
-
-#endif
- default:
- break;
- }
-
-global_stats:
- proxy->home_server->stats.last_packet = reply->timestamp.tv_sec;
-
- switch (proxy->packet->code) {
- case FR_CODE_ACCESS_REQUEST:
- proxy_auth_stats.last_packet = reply->timestamp.tv_sec;
- break;
-
-#ifdef WITH_ACCOUNTING
- case FR_CODE_ACCOUNTING_REQUEST:
- proxy_acct_stats.last_packet = reply->timestamp.tv_sec;
- break;
-
-#endif
-
-#ifdef WITH_COA
- case FR_CODE_COA_REQUEST:
- proxy_coa_stats.last_packet = reply->timestamp.tv_sec;
- break;
-
- case FR_CODE_DISCONNECT_REQUEST:
- proxy_dsc_stats.last_packet = reply->timestamp.tv_sec;
- break;
-
-#endif
- default:
- break;
- }
-#endif
-
- /*
- * If we hadn't been sending the home server packets for
- * a while, just mark it alive. Or, if it was zombie,
- * it's now responded, and is therefore alive.
- */
- if ((proxy->home_server->state == HOME_STATE_UNKNOWN) ||
- (proxy->home_server->state == HOME_STATE_ZOMBIE)) {
- mark_home_server_alive(request, proxy->home_server);
- }
-
- /*
- * Tell the request state machine that we have a proxy
- * reply. Depending on the function, this should either
- * ignore it, or process it.
- */
- request->process(request, FR_ACTION_PROXY_REPLY);
-
- return 1;
-}
-
-
-static int setup_post_proxy_fail(REQUEST *request)
-{
- fr_dict_enum_t const *dval = NULL;
- fr_dict_attr_t const *da = fr_dict_attr_by_num(NULL, 0, FR_POST_PROXY_TYPE);
- VALUE_PAIR *vp;
- char buffer[256];
-
- VERIFY_REQUEST(request);
-
- snprintf(buffer, sizeof(buffer), "Fail-%s", fr_packet_codes[request->proxy->packet->code]);
- dval = fr_dict_enum_by_alias(NULL, da, buffer);
-
- if (!dval) dval = fr_dict_enum_by_alias(NULL, da, "Fail");
-
- if (!dval) {
- fr_pair_delete_by_num(&request->control, 0, FR_POST_PROXY_TYPE, TAG_ANY);
- return 0;
- }
-
- vp = fr_pair_find_by_num(request->control, 0, FR_POST_PROXY_TYPE, TAG_ANY);
- if (!vp) vp = radius_pair_create(request, &request->control,
- FR_POST_PROXY_TYPE, 0);
- fr_value_box_copy(NULL, &vp->data, dval->value);
-
- return 1;
-}
-
-
-/** Wait for the proxy ID to expire.
- *
- * \dot
- * digraph proxy_wait_for_id {
- * proxy_wait_for_id;
- *
- * proxy_wait_for_id -> dup [ label = "DUP", arrowhead = "none" ];
- * proxy_wait_for_id -> timer [ label = "TIMER < max_request_time" ];
- * proxy_wait_for_id -> proxy_reply_too_late [ label = "PROXY_REPLY" arrowhead = "none"];
- * proxy_wait_for_id -> process_proxy_reply [ label = "RUN" ];
- * proxy_wait_for_id -> done [ label = "TIMER >= timeout" ];
- * }
- * \enddot
- */
-static void proxy_wait_for_id(REQUEST *request, fr_state_action_t action)
-{
- struct timeval now, when;
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
-
- /*
- * We don't have an ID allocated, so we can just mark
- * this request as done.
- */
- if (!request->in_proxy_hash) goto done2;
-
- /*
- * We've been called from a child thread. Rely on the timers to call us back...
- */
- if (!we_are_master()) return;
-
- switch (action) {
- case FR_ACTION_DUP:
- request_dup_msg(request);
- break;
-
- case FR_ACTION_TIMER:
- if (request_max_time(request)) goto done;
-
-#ifdef WITH_TCP
- /*
- * TCP home servers don't retransmit. If we have a reply,
- * then we can clean up the ID now.
- */
- if ((request->proxy->home_server->proto == IPPROTO_TCP) &&
- request->proxy->reply && request->proxy->reply->count) {
- goto done;
- }
-#endif
-
- fr_event_list_time(&now, event_list);
- when = request->proxy->packet->timestamp;
-
-#ifdef WITH_COA
- if (((request->proxy->packet->code == FR_CODE_COA_REQUEST) ||
- (request->proxy->packet->code == FR_CODE_DISCONNECT_REQUEST)) &&
- (request->packet->code != request->proxy->packet->code)) {
- when.tv_sec += request->proxy->home_server->coa_mrd;
- } else
-#endif
- fr_timeval_add(&when, request_response_window(request), &when);
-
- /*
- * We may need to keep waiting, if there's no reply, OR
- * there are fewer replies than packets sent.
- */
- if (fr_timeval_cmp(&now, &when) < 0 &&
- (!request->proxy->reply ||
- (request->proxy->packet->count > request->proxy->reply->count))) {
- RDEBUG("Waiting for more responses from the home server");
- STATE_MACHINE_TIMER;
- return;
- }
- goto done;
-
- case FR_ACTION_PROXY_REPLY:
- if (!request->proxy->reply) proxy_reply_too_late(request);
- break;
-
- done:
- case FR_ACTION_DONE:
- remove_from_proxy_hash(request);
- done2:
- request_done(request, FR_ACTION_DONE);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-/** Process a request after the proxy has timed out.
- *
- * Run the packet through Post-Proxy-Type Fail
- *
- * \dot
- * digraph proxy_no_reply {
- * proxy_no_reply;
- *
- * proxy_no_reply -> dup [ label = "DUP", arrowhead = "none" ];
- * proxy_no_reply -> timer [ label = "TIMER < max_request_time" ];
- * proxy_no_reply -> proxy_reply_too_late [ label = "PROXY_REPLY" arrowhead = "none"];
- * proxy_no_reply -> process_proxy_reply [ label = "RUN" ];
- * proxy_no_reply -> proxy_wait_for_id [ label = "TIMER >= timeout" ];
- * }
- * \enddot
- */
-static void proxy_no_reply(REQUEST *request, fr_state_action_t action)
-{
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
- CHECK_FOR_PROXY_CANCELLED;
-
- switch (action) {
- case FR_ACTION_DUP:
- request_dup_msg(request);
- break;
-
- case FR_ACTION_TIMER:
- (void) request_max_time(request);
- break;
-
- case FR_ACTION_PROXY_REPLY:
- proxy_reply_too_late(request);
- break;
-
- case FR_ACTION_RUN:
- if (process_proxy_reply(request, NULL)) {
- VALUE_PAIR *vp;
-
- /*
- * We didn't receive a reply, but maybe
- * post-proxy-type FAIL told us to create
- * one.
- */
- vp = fr_pair_find_by_num(request->control, 0, FR_RESPONSE_PACKET_TYPE, TAG_ANY);
- if (vp && (vp->vp_uint32 != 256)) {
- request->proxy->reply = fr_radius_alloc_reply(request, request->proxy->packet);
- request->proxy->reply->code = vp->vp_uint32;
- fr_pair_delete_by_num(&request->control, 0, FR_RESPONSE_PACKET_TYPE, TAG_ANY);
- }
-
- request->handle(request);
- }
- request_finish(request, action);
- break;
-
- case FR_ACTION_DONE:
- request->process = proxy_wait_for_id;
- request->process(request, action);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-/** Process the request after receiving a proxy reply.
- *
- * Throught the post-proxy section, and the through the handler
- * function.
- *
- * \dot
- * digraph proxy_running {
- * proxy_running;
- *
- * proxy_running -> dup [ label = "DUP", arrowhead = "none" ];
- * proxy_running -> timer [ label = "TIMER < max_request_time" ];
- * proxy_running -> process_proxy_reply [ label = "RUN" ];
- * proxy_running -> done [ label = "TIMER >= timeout" ];
- * }
- * \enddot
- */
-static void proxy_running(REQUEST *request, fr_state_action_t action)
-{
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
- CHECK_FOR_PROXY_CANCELLED;
-
- switch (action) {
- case FR_ACTION_DUP:
- request_dup_msg(request);
- break;
-
- case FR_ACTION_TIMER:
- (void) request_max_time(request);
- break;
-
- case FR_ACTION_RUN:
- if (request->proxy->listener->decode(request->proxy->listener, request) < 0) goto done;
- request->proxy->listener->debug(request, request->proxy->reply, true);
-
- if (process_proxy_reply(request, request->proxy->reply)) {
- VALUE_PAIR *vp;
-
- /*
- * Base the reply to the NAS on the reply from the home server.
- * Except that we don't copy over Proxy-State.
- */
- vp = fr_pair_list_copy(request->reply, request->proxy->reply->vps);
- fr_pair_delete_by_num(&vp, 0, FR_PROXY_STATE, TAG_ANY);
- fr_pair_add(&request->reply->vps, vp);
-
- request->handle(request);
- }
-
- request_finish(request, action);
- break;
-
- done:
- case FR_ACTION_DONE:
- request->process = proxy_wait_for_id;
- request->process(request, action);
- break;
-
- default: /* duplicate proxy replies are suppressed */
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-
-/** Handle events while a proxy packet is in the queue.
- *
- * \dot
- * digraph proxy_queued {
- * proxy_queued;
- *
- * proxy_queued -> timer [ label = "TIMER < max_request_time" ];
- * proxy_queued -> proxy_queued [ label = "PROXY_REPLY" ];
- * proxy_queued -> proxy_queued [ label = "DUP" ];
- * proxy_queued -> proxy_running [ label = "RUN with reply" ];
- * proxy_queued -> proxy_no_reply [ label = "RUN without reply" ];
- * proxy_queued -> done [ label = "TIMER >= timeout" ];
- * }
- * \enddot
- */
-static void proxy_queued(REQUEST *request, fr_state_action_t action)
-{
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
- CHECK_FOR_PROXY_CANCELLED;
-
- switch (action) {
- case FR_ACTION_DUP:
- request_dup_msg(request);
- break;
-
- case FR_ACTION_TIMER:
- (void) request_max_time(request);
- break;
-
- /*
- * We have a proxy reply, but wait for it to be
- * de-queued before doing anything.
- */
- case FR_ACTION_PROXY_REPLY:
- break;
-
- case FR_ACTION_RUN:
- if (request->proxy->reply) {
- request->process = proxy_running;
- request->process(request, FR_ACTION_RUN);
- break;
-
- } else if (setup_post_proxy_fail(request)) {
- request->process = proxy_no_reply;
- request->process(request, FR_ACTION_RUN);
- break;
-
- } else { /* no Post-Proxy-Type fail */
- gettimeofday(&request->reply->timestamp, NULL);
- goto done;
- }
-
- case FR_ACTION_DONE:
- request_queue_extract(request);
- done:
- request->process = proxy_wait_for_id;
- request->process(request, action);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-
-/** Determine if a #REQUEST needs to be proxied, and perform pre-proxy operations
- *
- * Whether a request will be proxied is determined by the attributes present
- * in request->control. If any of the following attributes are found, the
- * request may be proxied.
- *
- * The key attributes are:
- * - FR_PROXY_TO_REALM - Specifies a realm the request should be proxied to.
- * - FR_HOME_SERVER_POOL - Specifies a specific home server pool to proxy to.
- * - FR_PACKET_DST_IP_ADDRESS - Specifies a specific IPv4 home server to proxy to.
- * - FR_PACKET_DST_IPV6_ADDRESS - Specifies a specific IPv6 home server to proxy to.
- *
- * Certain packet types such as #FR_CODE_STATUS_SERVER will never be proxied.
- *
- * If request should be proxied, will:
- * - Add request:Proxy-State
- * - Strip the current username value of its realm (depending on config)
- * - Create a CHAP-Challenge from the original request vector, if one doesn't already
- * exist.
- * - Call the pre-process section in the current server, or in the virtual server
- * associated with the home server pool we're proxying to.
- *
- * @todo A lot of this logic is RADIUS specific, and should be moved out into a protocol
- * specific function.
- *
- * @param request The #REQUEST to evaluate for proxying.
- * @return
- * - 0 if not proxying.
- * - 1 if #REQUEST should be proxied.
- * - -1 on failure.
- */
-static int request_will_proxy(UNUSED REQUEST *request)
-{
- return 0;
-}
-
-static int proxy_to_virtual_server(REQUEST *request)
-{
- REQUEST *fake;
-
- if (request->packet->dst_port == 0) {
- WARN("Cannot proxy an internal request");
- return 0;
- }
-
- DEBUG("Proxying to virtual server %s",
- request->proxy->home_server->server);
-
- /*
- * Packets to virtual servers don't get
- * retransmissions sent to them. And the virtual
- * server is run ONLY if we have no child
- * threads, or we're running in a child thread.
- */
- rad_assert(!spawn_workers || !we_are_master());
-
- fake = request_alloc_fake(request);
-
- fake->packet->vps = fr_pair_list_copy(fake->packet, request->packet->vps);
- TALLOC_FREE(request->proxy->packet);
-
- fake->server_cs = virtual_server_find(request->proxy->home_server->server);
- fake->handle = request->handle;
- fake->process = NULL; /* should never be run for anything */
-
- /*
- * Run the virtual server.
- */
- request_running(fake, FR_ACTION_RUN);
-
- request->proxy = request_alloc_proxy(request);
-
- request->proxy->packet = talloc_steal(request->proxy, fake->packet);
- fake->packet = NULL;
- request->proxy->reply = talloc_steal(request->proxy, fake->reply);
- fake->reply = NULL;
-
- talloc_free(fake);
-
- /*
- * No reply code, toss the reply we have,
- * and do post-proxy-type Fail.
- */
- if (!request->proxy->reply->code) {
- TALLOC_FREE(request->proxy->reply);
- setup_post_proxy_fail(request);
- }
-
- /*
- * Do the proxy reply (if any)
- */
- if (process_proxy_reply(request, request->proxy->reply)) {
- request->handle(request);
- }
-
- return -1; /* so we call request_finish */
-}
-
-
-static int request_proxy_send(REQUEST *request)
-{
- struct timeval *response_window;
- char buffer[INET6_ADDRSTRLEN];
-
- VERIFY_REQUEST(request);
-
- rad_assert(request->parent == NULL);
- rad_assert(request->proxy != NULL);
- rad_assert(request->proxy->home_server != NULL);
-
- /*
- * The request may need sending to a virtual server.
- * This code is more than a little screwed up. The rest
- * of the state machine doesn't handle parent / child
- * relationships well. i.e. if the child request takes
- * too long, the core will mark the *parent* as "stop
- * processing". And the child will continue without
- * knowing anything...
- *
- * So, we have some horrible hacks to get around that.
- */
- if (request->proxy->home_server->server) return proxy_to_virtual_server(request);
-
- /*
- * We're actually sending a proxied packet. Do that now.
- */
- if (!request->in_proxy_hash && !insert_into_proxy_hash(request)) {
- RPROXY("Failed to insert request into the proxy list");
- return -1;
- }
-
- rad_assert(request->proxy->packet->id >= 0);
-
- response_window = request_response_window(request);
-
-#ifdef WITH_TLS
- if (request->proxy->home_server->tls) {
- RDEBUG2("Proxying request to home server %s port %d (TLS) timeout %d.%06d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port,
- (int) response_window->tv_sec, (int) response_window->tv_usec);
- } else
-#endif
- RDEBUG2("Proxying request to home server %s port %d timeout %d.%06d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port,
- (int) response_window->tv_sec, (int) response_window->tv_usec);
-
- request->proxy->response_delay = *response_window;
-
- gettimeofday(&request->proxy->packet->timestamp, NULL);
- request->proxy->home_server->last_packet_sent = request->proxy->packet->timestamp.tv_sec;
-
- /*
- * Encode the packet before we do anything else.
- */
- request->proxy->listener->encode(request->proxy->listener, request);
- request->proxy->listener->debug(request, request->proxy->packet, false);
-
- /*
- * Set the state function, then the state, no child, and
- * send the packet.
- *
- * The order here is different from other state changes
- * due to race conditions with replies from the home
- * server.
- */
- request->process = proxy_wait_for_reply;
- request->child_state = REQUEST_PROXIED;
- request->component = "<REQUEST_PROXIED>";
- request->module = NULL;
- NO_CHILD_THREAD;
-
- /*
- * And send the packet.
- */
- request->proxy->listener->send(request->proxy->listener, request);
- return 1;
-}
-
-/*
- * Proxy the packet as if it was new.
- */
-static int request_proxy_anew(UNUSED REQUEST *request)
-{
- return 0;
-}
-
-
-/** Ping a home server.
- *
- */
-static void request_ping(REQUEST *request, fr_state_action_t action)
-{
- home_server_t *home;
- char buffer[INET6_ADDRSTRLEN];
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- ASSERT_MASTER;
-
- rad_assert(request->proxy != NULL);
- home = request->proxy->home_server;
-
- switch (action) {
- case FR_ACTION_TIMER:
- ERROR("No response to status check %" PRIu64 " ID %u for home server %s port %d",
- request->number,
- request->proxy->packet->id,
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port);
- remove_from_proxy_hash(request);
- break;
-
- case FR_ACTION_PROXY_REPLY:
- rad_assert(request->in_proxy_hash);
-
- request->proxy->home_server->num_received_pings++;
- RPROXY("Received response to status check %" PRIu64 " ID %u (%d in current sequence)",
- request->number, request->proxy->packet->id, home->num_received_pings);
-
- /*
- * Remove the request from any hashes
- */
- fr_event_timer_delete(event_list, &request->ev);
- remove_from_proxy_hash(request);
-
- /*
- * The control socket may have marked the home server as
- * alive. OR, it may have suddenly started responding to
- * requests again. If so, don't re-do the "make alive"
- * work.
- */
- if (home->state == HOME_STATE_ALIVE) break;
-
- /*
- * It's dead, and we haven't received enough ping
- * responses to mark it "alive". Wait a bit.
- *
- * If it's zombie, we mark it alive immediately.
- */
- if ((home->state == HOME_STATE_IS_DEAD) &&
- (home->num_received_pings < home->num_pings_to_alive)) {
- return;
- }
-
- /*
- * Mark it alive and delete any outstanding
- * pings.
- */
- mark_home_server_alive(request, home);
- break;
-
- case FR_ACTION_DONE:
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-
- rad_assert(!request->in_request_hash);
- rad_assert(!request->in_proxy_hash);
- rad_assert(request->ev == NULL);
- NO_CHILD_THREAD;
- request_delete(request);
-}
-
-/*
- * Called from start of zombie period, OR after control socket
- * marks the home server dead.
- */
-static void ping_home_server(UNUSED fr_event_list_t *eel, struct timeval *now, void *ctx)
-{
- home_server_t *home = talloc_get_type_abort(ctx, home_server_t);
- REQUEST *request;
- VALUE_PAIR *vp;
- struct timeval when;
-
- if ((home->state == HOME_STATE_ALIVE) ||
- (home->ev != NULL)) {
- return;
- }
-
- ASSERT_MASTER;
-
- /*
- * We've run out of zombie time. Mark it dead.
- */
- if (home->state == HOME_STATE_ZOMBIE) {
- when = home->zombie_period_start;
- when.tv_sec += home->zombie_period;
-
- if (fr_timeval_cmp(&when, now) < 0) {
- DEBUG("PING: Zombie period is over for home server %s", home->log_name);
- mark_home_server_dead(home, now);
- }
- }
-
- /*
- * We're not supposed to be pinging it. Just wake up
- * when we're supposed to mark it dead.
- */
- if (home->ping_check == HOME_PING_CHECK_NONE) {
- if (home->state == HOME_STATE_ZOMBIE) {
- home->when = home->zombie_period_start;
- home->when.tv_sec += home->zombie_period;
- INSERT_EVENT(ping_home_server, home);
- }
-
- /*
- * Else mark_home_server_dead will set a timer
- * for revive_interval.
- */
- return;
- }
-
- /*
- * Skip Status-Server checks if the NAS is retransmitting
- * packets. If it responds to one of the normal packets,
- * it will be marked "alive".
- */
- if ((home->last_packet_sent + home->ping_timeout) >= now->tv_sec) goto reset_timer;
-
- request = request_alloc(NULL);
- if (!request) return;
- request->number = atomic_fetch_add_explicit(&request_number_counter, 1, memory_order_relaxed);
- NO_CHILD_THREAD;
-
- request->proxy = request_alloc_proxy(request);
-
- request->proxy->packet = fr_radius_alloc(request->proxy, true);
- rad_assert(request->proxy != NULL);
-
- if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
- request->proxy->packet->code = FR_CODE_STATUS_SERVER;
-
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "Message-Authenticator", "0x00", T_OP_SET);
-
- } else if ((home->type == HOME_TYPE_AUTH) ||
- (home->type == HOME_TYPE_AUTH_ACCT)) {
- request->proxy->packet->code = FR_CODE_ACCESS_REQUEST;
-
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "User-Name", home->ping_user_name, T_OP_SET);
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "User-Password", home->ping_user_password, T_OP_SET);
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "Service-Type", "Authenticate-Only", T_OP_SET);
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "Message-Authenticator", "0x00", T_OP_SET);
-
-#ifdef WITH_ACCOUNTING
- } else if (home->type == HOME_TYPE_ACCT) {
- request->proxy->packet->code = FR_CODE_ACCOUNTING_REQUEST;
-
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "User-Name", home->ping_user_name, T_OP_SET);
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "Acct-Status-Type", "Stop", T_OP_SET);
- fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "Acct-Session-Id", "00000000", T_OP_SET);
- vp = fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "Event-Timestamp", "0", T_OP_SET);
- vp->vp_date = now->tv_sec;
-#endif
-
- } else {
- /*
- * Unkown home server type.
- */
- talloc_free(request);
- return;
- }
-
- vp = fr_pair_make(request->proxy->packet, &request->proxy->packet->vps,
- "NAS-Identifier", "", T_OP_SET);
- if (vp) {
- fr_pair_value_snprintf(vp, "Status Check %u. Are you alive?",
- home->num_sent_pings);
- }
-
-#ifdef WITH_TCP
- request->proxy->packet->proto = home->proto;
-#endif
- request->proxy->packet->src_ipaddr = home->src_ipaddr;
- request->proxy->packet->dst_ipaddr = home->ipaddr;
- request->proxy->packet->dst_port = home->port;
- request->proxy->home_server = home;
-#ifdef DEBUG_STATE_MACHINE
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tSTATE %s C-%s -> C-%s\t********\n",
- request->number, __FUNCTION__,
- child_state_names[request->child_state],
- child_state_names[REQUEST_DONE]);
- if (rad_debug_lvl) printf("(%" PRIu64 ") ********\tNEXT-STATE %s -> %s\n",
- request->number, __FUNCTION__, "request_ping");
-#endif
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
-
- request->child_state = REQUEST_PROXIED;
- request->process = request_ping;
-
- rad_assert(request->proxy->listener == NULL);
-
- if (!insert_into_proxy_hash(request)) {
- RPROXY("Failed to insert status check %" PRIu64 " into proxy list. Discarding it.",
- request->number);
-
- rad_assert(!request->in_request_hash);
- rad_assert(!request->in_proxy_hash);
- rad_assert(request->ev == NULL);
- talloc_free(request);
- return;
- }
-
- /*
- * Set up the timer callback.
- */
- when = *now;
- when.tv_sec += home->ping_timeout;
-
- DEBUG("PING: Waiting %u seconds for response to ping",
- home->ping_timeout);
-
- STATE_MACHINE_TIMER;
- home->num_sent_pings++;
-
- rad_assert(request->proxy->listener != NULL);
- request->proxy->listener->debug(request, request->proxy->packet, false);
- request->proxy->listener->send(request->proxy->listener, request);
-
-reset_timer:
- /*
- * Add +/- 2s of jitter, as suggested in RFC 3539
- * and in the Issues and Fixes draft.
- */
- home->when = *now;
- home->when.tv_sec += home->ping_interval;
-
- add_jitter(&home->when);
-
- DEBUG("PING: Next status packet in %u seconds", home->ping_interval);
- INSERT_EVENT(ping_home_server, home);
-}
-
-static void home_trigger(home_server_t *home, char const *trigger)
-{
- REQUEST *request;
-
- request = talloc_zero(NULL, REQUEST);
- request->proxy = request_alloc_proxy(request);
-
- request->proxy->packet = talloc_zero(request->proxy, RADIUS_PACKET);
- request->proxy->packet->dst_ipaddr = home->ipaddr;
- request->proxy->packet->src_ipaddr = home->src_ipaddr;
-
- trigger_exec(request, home->cs, trigger, false, NULL);
- talloc_free(request);
-}
-
-static void mark_home_server_alive(REQUEST *request, home_server_t *home)
-{
- home_state_t old_state;
- char buffer[INET6_ADDRSTRLEN];
-
- old_state = home->state;
-
- home->state = HOME_STATE_ALIVE;
- home->response_timeouts = 0;
- trigger_exec(request, home->cs, "home_server.alive", false, NULL);
- home->currently_outstanding = 0;
- home->num_sent_pings = 0;
- home->num_received_pings = 0;
- gettimeofday(&home->revive_time, NULL);
-
- fr_event_timer_delete(event_list, &home->ev);
-
- if (old_state == HOME_STATE_UNKNOWN) {
- RPROXY("Home server %s port %d went from idle -> alive",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port);
- } else {
- RPROXY("Marking home server %s port %d alive",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port);
- }
-}
-
-
-static void mark_home_server_zombie(home_server_t *home, struct timeval *now, struct timeval *response_window)
-{
- time_t start;
- char buffer[INET6_ADDRSTRLEN];
-
- ASSERT_MASTER;
-
- rad_assert((home->state == HOME_STATE_ALIVE) ||
- (home->state == HOME_STATE_UNKNOWN));
-
- /*
- * We've received a real packet recently. Don't mark the
- * server as zombie until we've received NO packets for a
- * while. The "1/4" of zombie period was chosen rather
- * arbitrarily. It's a balance between too short, which
- * gives quick fail-over and fail-back, or too long,
- * where the proxy still sends packets to an unresponsive
- * home server.
- */
- start = now->tv_sec - ((home->zombie_period + 3) / 4);
- if (home->last_packet_recv >= start) {
- DEBUG("Received reply from home server %d seconds ago. Might not be zombie.",
- (int) (now->tv_sec - home->last_packet_recv));
- return;
- }
-
- home->state = HOME_STATE_ZOMBIE;
-
- /*
- * Set the home server to "zombie", as of the time
- * calculated above.
- */
- home->zombie_period_start.tv_sec = start;
- home->zombie_period_start.tv_usec = USEC / 2;
-
- fr_event_timer_delete(event_list, &home->ev);
-
- home->num_sent_pings = 0;
- home->num_received_pings = 0;
-
- PROXY("Marking home server %s port %d as zombie (it has not responded in %d.%06d seconds).",
- inet_ntop(home->ipaddr.af, &home->ipaddr.addr,
- buffer, sizeof(buffer)),
- home->port, (int) response_window->tv_sec, (int) response_window->tv_usec);
-
- ping_home_server(event_list, now, home);
-}
-
-
-void mark_home_server_dead(home_server_t *home, struct timeval *when)
-{
- int previous_state = home->state;
- char buffer[INET6_ADDRSTRLEN];
-
- PROXY("Marking home server %s port %d as dead",
- inet_ntop(home->ipaddr.af, &home->ipaddr.addr,
- buffer, sizeof(buffer)),
- home->port);
-
- home->state = HOME_STATE_IS_DEAD;
- home_trigger(home, "home_server.dead");
-
- if (home->ping_check != HOME_PING_CHECK_NONE) {
- /*
- * If the control socket marks us dead, start
- * pinging. Otherwise, we already started
- * pinging when it was marked "zombie".
- */
- if (previous_state == HOME_STATE_ALIVE) {
- struct timeval now;
-
- gettimeofday(&now, NULL);
- ping_home_server(event_list, &now, home);
- } else {
- DEBUG("PING: Already pinging home server %s", home->log_name);
- }
-
- } else {
- /*
- * Revive it after a fixed period of time. This
- * is very, very, bad.
- */
- home->when = *when;
- home->when.tv_sec += home->revive_interval;
-
- DEBUG("PING: Reviving home server %s in %u seconds", home->log_name, home->revive_interval);
- ASSERT_MASTER;
- INSERT_EVENT(revive_home_server, home);
- }
-}
-
-
-void revive_home_server(UNUSED fr_event_list_t *eel, UNUSED struct timeval *now, void *ctx)
-{
- home_server_t *home = talloc_get_type_abort(ctx, home_server_t);
- char buffer[INET6_ADDRSTRLEN];
-
- home->state = HOME_STATE_ALIVE;
- home->response_timeouts = 0;
- home_trigger(home, "home_server.alive");
- home->currently_outstanding = 0;
- gettimeofday(&home->revive_time, NULL);
-
- /*
- * Delete any outstanding events.
- */
- ASSERT_MASTER;
- fr_event_timer_delete(event_list, &home->ev);
-
- PROXY("Marking home server %s port %d alive again... we have no idea if it really is alive or not.",
- inet_ntop(home->ipaddr.af, &home->ipaddr.addr, buffer, sizeof(buffer)),
- home->port);
-}
-
-
-static bool proxy_keep_waiting(REQUEST *request, struct timeval *now)
-{
- struct timeval when;
- home_server_t *home = request->proxy->home_server;
- char buffer[INET6_ADDRSTRLEN];
-
-#ifdef WITH_TCP
- if (!request->proxy->listener ||
- (request->proxy->listener->status >= RAD_LISTEN_STATUS_EOL)) {
- remove_from_proxy_hash(request);
-
- when = request->packet->timestamp;
- when.tv_sec += request->root->max_request_time;
-
- if (fr_timeval_cmp(&when, now) > 0) {
- RDEBUG("Waiting for client retransmission in order to do a proxy retransmit");
- STATE_MACHINE_TIMER;
- return true;
- }
- } else
-#endif
- {
- /*
- * Wake up "response_delay" time in the future.
- * i.e. when MY packet hasn't received a response.
- *
- * Note that we DO NOT mark the home server as
- * zombie if it doesn't respond to us. It may be
- * responding to other (better looking) packets.
- */
- when = request->proxy->packet->timestamp;
- fr_timeval_add(&when, &request->proxy->response_delay, &when);
-
- /*
- * Not at the response window. Set the timer for
- * that.
- */
- if (fr_timeval_cmp(&when, now) > 0) {
- struct timeval diff;
- fr_timeval_subtract(&diff, &when, now);
-
- RDEBUG("Expecting proxy response no later than %d.%06d seconds from now",
- (int) diff.tv_sec, (int) diff.tv_usec);
- STATE_MACHINE_TIMER;
- return true;
- }
- }
-
- RDEBUG("No proxy response, giving up on request and marking it done");
-
- /*
- * If we haven't received any packets for
- * "response_delay", then mark the home server
- * as zombie.
- *
- * This check should really be part of a home
- * server state machine.
- */
- if (!home->is_ourself &&
- ((home->state == HOME_STATE_ALIVE) ||
- (home->state == HOME_STATE_UNKNOWN))) {
- home->response_timeouts++;
- if (home->response_timeouts >= home->max_response_timeouts)
- mark_home_server_zombie(home, now, &request->proxy->response_delay);
- }
-
- FR_STATS_TYPE_INC(home->stats.total_timeouts);
- if (home->type == HOME_TYPE_AUTH) {
- if (request->proxy->listener) FR_STATS_TYPE_INC(request->proxy->listener->stats.total_timeouts);
- FR_STATS_TYPE_INC(proxy_auth_stats.total_timeouts);
- }
-#ifdef WITH_ACCT
- else if (home->type == HOME_TYPE_ACCT) {
- if (request->proxy->listener) FR_STATS_TYPE_INC(request->proxy->listener->stats.total_timeouts);
- FR_STATS_TYPE_INC(proxy_acct_stats.total_timeouts);
- }
-#endif
-#ifdef WITH_COA
- else if (home->type == HOME_TYPE_COA) {
- if (request->proxy->listener) FR_STATS_TYPE_INC(request->proxy->listener->stats.total_timeouts);
-
- if (request->packet->code == FR_CODE_COA_REQUEST) {
- FR_STATS_TYPE_INC(proxy_coa_stats.total_timeouts);
- } else {
- FR_STATS_TYPE_INC(proxy_dsc_stats.total_timeouts);
- }
- }
-#endif
-
- /*
- * There was no response within the window. Stop
- * the request. If the client retransmitted, it
- * may have failed over to another home server.
- * But that one may be dead, too.
- *
- * The extra verbose message if we have a username,
- * is extremely useful if the proxy is part of a chain
- * and the final home server, is not the one we're
- * proxying to.
- */
- if (request->username) {
- RERROR("Failing proxied request for user \"%s\", due to lack of any response from home "
- "server %s port %d",
- request->username->vp_strvalue,
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port);
- } else {
- RERROR("Failing proxied request, due to lack of any response from home server %s port %d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port);
- }
-
- return false;
-}
-
-static void proxy_retransmit(REQUEST *request, struct timeval *now)
-{
- struct timeval when;
- home_server_t *home = request->proxy->home_server;
- char buffer[INET6_ADDRSTRLEN];
-
- /*
- * Use a new connection when the home server is
- * dead, or when there's no proxy listener, or
- * when the listener is failed or dead.
- *
- * If the listener is known or frozen, use it for
- * retransmits.
- */
- if ((home->state == HOME_STATE_IS_DEAD) ||
- !request->proxy->listener ||
- (request->proxy->listener->status >= RAD_LISTEN_STATUS_EOL)) {
- request_proxy_anew(request);
- return;
- }
-
- /*
- * More than one retransmit a second is stupid,
- * and should be suppressed by the proxy.
- */
- when = request->proxy->packet->timestamp;
- when.tv_sec++;
-
- if (fr_timeval_cmp(now, &when) < 0) {
- DEBUG2("Suppressing duplicate proxied request (too fast) to home server %s port %d proto TCP - ID: %d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port,
- request->proxy->packet->id);
- return;
- }
-
-#ifdef WITH_ACCOUNTING
- /*
- * If we update the Acct-Delay-Time, we need to
- * get a new ID.
- */
- if ((request->packet->code == FR_CODE_ACCOUNTING_REQUEST) &&
- fr_pair_find_by_num(request->proxy->packet->vps, 0, FR_ACCT_DELAY_TIME, TAG_ANY)) {
- request_proxy_anew(request);
- return;
- }
-#endif
-
- RDEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port,
- request->proxy->packet->id);
- request->proxy->packet->count++;
-
- rad_assert(request->proxy->listener != NULL);
- FR_STATS_TYPE_INC(home->stats.total_requests);
- home->last_packet_sent = now->tv_sec;
- request->proxy->listener->debug(request, request->proxy->packet, false);
- request->proxy->listener->send(request->proxy->listener, request);
-}
-
-/** Wait for a reply after proxying a request.
- *
- * Retransmit the proxied packet, or time out and go to
- * proxy_no_reply. Mark the home server unresponsive, etc.
- *
- * If we do receive a reply, we transition to proxy_running.
- *
- * \dot
- * digraph proxy_wait_for_reply {
- * proxy_wait_for_reply;
- *
- * proxy_wait_for_reply -> retransmit_proxied_request [ label = "DUP", arrowhead = "none" ];
- * proxy_wait_for_reply -> proxy_no_reply [ label = "TIMER >= response_delay" ];
- * proxy_wait_for_reply -> timer [ label = "TIMER < max_request_time" ];
- * proxy_wait_for_reply -> proxy_queued [ label = "PROXY_REPLY" arrowhead = "none"];
- * proxy_wait_for_reply -> done [ label = "TIMER >= max_request_time" ];
- * }
- * \enddot
- */
-static void proxy_wait_for_reply(REQUEST *request, fr_state_action_t action)
-{
- struct timeval now;
- home_server_t *home = request->proxy->home_server;
- char buffer[INET6_ADDRSTRLEN];
-
- VERIFY_REQUEST(request);
-
- TRACE_STATE_MACHINE;
- CHECK_FOR_STOP;
-
- rad_assert(request->packet->code != FR_CODE_STATUS_SERVER);
- rad_assert(request->proxy->home_server != NULL);
-
- fr_event_list_time(&now, event_list);
-
- switch (action) {
- case FR_ACTION_DUP:
- /*
- * We have a reply, ignore the retransmit.
- */
- if (request->proxy->reply) return;
-
- /*
- * The request was proxied to a virtual server.
- * Ignore the retransmit.
- */
- if (request->proxy->home_server->server) return;
-
-#ifdef WITH_TCP
- /*
- * The home server is still alive, but TCP. We
- * rely on TCP to get the request and reply back.
- * So there's no need to retransmit.
- */
- if (home->proto == IPPROTO_TCP) {
- DEBUG2("Suppressing duplicate proxied request (tcp) to home server %s port %d proto TCP - ID: %d",
- inet_ntop(request->proxy->packet->dst_ipaddr.af,
- &request->proxy->packet->dst_ipaddr.addr,
- buffer, sizeof(buffer)),
- request->proxy->packet->dst_port,
- request->proxy->packet->id);
- return;
- }
-#endif
-
- proxy_retransmit(request, &now);
- break;
-
- case FR_ACTION_TIMER:
- if (proxy_keep_waiting(request, &now)) break;
-
- /* FALL-THROUGH */
-
- case FR_ACTION_PROXY_REPLY:
- request_thread(request, proxy_queued);
- break;
-
- case FR_ACTION_DONE:
- request->process = proxy_wait_for_id;
- request->process(request, action);
- break;
-
- default:
- RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
- }
-}
-#endif /* WITH_PROXY */
-
-
-/***********************************************************************
- *
- * End of the State machine. Start of additional helper code.
- *
- ***********************************************************************/
-
-/***********************************************************************
- *
- * Event handlers.
- *
- ***********************************************************************/
-static void event_socket_handler(NDEBUG_UNUSED fr_event_list_t *xel, UNUSED int fd, UNUSED int flags, void *ctx)
-{
- rad_listen_t *listener = talloc_get_type_abort(ctx, rad_listen_t);
-
- rad_assert(xel == event_list);
-
- if (listener->fd < 0) {
- char buffer[256];
+static bool spawn_workers = false;
+static bool just_started = true;
+time_t fr_start_time = (time_t)-1;
+static fr_event_list_t *event_list = NULL;
- listener->print(listener, buffer, sizeof(buffer));
+fr_event_list_t *process_global_event_list(UNUSED event_corral_t hint) {
+ /* Currently we do not run a second event loop for modules. */
+ return event_list;
+}
- rad_panic("FATAL: Asked to read from closed socket (fd %i): %s", listener->fd, buffer);
- }
+#define rad_panic(_x, ...) radlog_fatal("%s[%u]: " _x, __FILE__, __LINE__, ## __VA_ARGS__)
- listener->recv(listener);
+/*
+ * If the child is still running, wait for it to be finished.
+ */
+bool request_thread_active(UNUSED REQUEST *request)
+{
+ rad_assert(0 == 1);
+ return false;
}
-static void event_socket_error(NDEBUG_UNUSED fr_event_list_t *xel, int fd, UNUSED int flags, void *ctx)
+
+void request_thread_done(UNUSED REQUEST *request)
{
- rad_listen_t *listener = talloc_get_type_abort(ctx, rad_listen_t);
+ rad_assert(0 == 1);
+}
- rad_assert(xel == event_list);
+/*
+ * Delete a request.
+ */
+void request_delete(UNUSED REQUEST *request)
+{
+ rad_assert(0 == 1);
+}
- /*
- * No error callback. Just delete the listener.
- */
- if (!listener->error) {
- listener->status = RAD_LISTEN_STATUS_EOL;
- fr_event_fd_delete(event_list, listener->fd);
- return;
- }
+int request_receive(UNUSED TALLOC_CTX *ctx, UNUSED rad_listen_t *listener, UNUSED RADIUS_PACKET *packet,
+ UNUSED RADCLIENT *client, UNUSED RAD_REQUEST_FUNP fun)
+{
+ return 0;
+}
- listener->error(listener, fd);
+REQUEST *request_setup(UNUSED TALLOC_CTX *ctx, UNUSED rad_listen_t *listener, UNUSED RADIUS_PACKET *packet,
+ UNUSED RADCLIENT *client, UNUSED RAD_REQUEST_FUNP fun)
+{
+ rad_assert(0 == 1);
+ return NULL;
}
static int event_status(UNUSED void *ctx, struct timeval *wake)
{
char buffer[1024];
- ASSERT_MASTER;
-
if (this->status == RAD_LISTEN_STATUS_KNOWN) return 1;
this->print(this, buffer, sizeof(buffer));
this->when.tv_sec = sock->opened + 1;
this->when.tv_usec = 0;
- INSERT_EVENT(tcp_socket_timer, this);
}
#endif
break;
this->when.tv_sec = sock->opened + 1;
this->when.tv_usec = 0;
- INSERT_EVENT(tcp_socket_timer, this);
}
#endif
break;
} /* switch over listener types */
- /*
- * All sockets: add the FD to the event handler.
- */
- if (fr_event_fd_insert(event_list, this->fd, event_socket_handler, NULL, event_socket_error, this)) {
- PERROR("Failed adding event handler for socket");
- fr_exit(1);
- }
-
this->status = RAD_LISTEN_STATUS_KNOWN;
return 1;
} /* end of INIT */
-#ifdef WITH_TCP
- /*
- * The socket has reached a timeout. Try to close it.
- */
- if (this->status == RAD_LISTEN_STATUS_FROZEN) {
- /*
- * Requests are still using the socket. Wait for
- * them to finish.
- */
- if (this->count > 0) {
- keep_waiting:
- /*
- * Try again to clean up the socket in a
- * few seconds. seconds.
- */
- gettimeofday(&this->when, NULL);
- this->when.tv_sec += 3;
-
- INSERT_EVENT((fr_event_callback_t) event_new_fd, this);
- return 1;
- }
-
- this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
- }
-
- /*
- * The socket has had a catastrophic error. Close it.
- */
- if (this->status == RAD_LISTEN_STATUS_EOL) {
- int devnull;
-
- /*
- * Remove it from the list of live FD's.
- */
- fr_event_fd_delete(event_list, this->fd);
-
- /*
- * Re-open the socket, pointing it to /dev/null.
- * This means that all writes proceed without
- * blocking, and all reads return "no data".
- *
- * This leaves the socket active, so any child
- * threads won't go insane. But it means that
- * they cannot send or receive any packets.
- *
- * This is EXTRA work in the normal case, when
- * sockets are closed without error. But it lets
- * us have one simple processing method for all
- * sockets.
- */
- devnull = open("/dev/null", O_RDWR);
- if (devnull < 0) {
- ERROR("FATAL failure opening /dev/null: %s", fr_syserror(errno));
- fr_exit(1);
- }
- if (dup2(devnull, this->fd) < 0) {
- ERROR("FATAL failure closing socket: %s", fr_syserror(errno));
- fr_exit(1);
- }
- close(devnull);
-
-#ifdef WITH_PROXY
- /*
- * Tell all requests using this socket that the socket is dead.
- */
- if (this->type == RAD_LISTEN_PROXY) {
- home_server_t *home;
- listen_socket_t *sock = this->data;
-
- home = sock->home;
- if (!home || !home->limit.max_connections) {
- INFO(" ... shutting down socket %s", buffer);
- } else {
- INFO(" ... shutting down socket %s (%u of %u)", buffer,
- home->limit.num_connections, home->limit.max_connections);
- }
-
- pthread_mutex_lock(&proxy_mutex);
- if (!fr_packet_list_socket_freeze(proxy_list,
- this->fd)) {
- PERROR("Fatal error freezing socket");
- fr_exit(1);
- }
-
- fr_packet_list_walk(proxy_list, this, eol_proxy_listener);
- pthread_mutex_unlock(&proxy_mutex);
- } else
-#endif
- {
- INFO(" ... shutting down socket %s", buffer);
-
- /*
- * EOL all requests using this socket.
- *
- * Except for control sockets, which
- * don't have any requests associated
- * with them.
- */
-#ifdef WITH_COMMAND_SOCKET
- if (this->type != RAD_LISTEN_COMMAND)
-#endif
-
- rbtree_walk(packet_list, RBTREE_DELETE_ORDER, eol_listener, this);
- }
-
-
- /*
- * Requests are still using the socket. Wait for
- * them to finish.
- */
- if (this->count > 0) {
- this->status = RAD_LISTEN_STATUS_FROZEN;
- goto keep_waiting;
- }
-
- /*
- * No one is using the socket. We can remove it now.
- */
- this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
- } /* socket is at EOL */
-
- /*
- * Nuke the socket.
- */
- if (this->status == RAD_LISTEN_STATUS_REMOVE_NOW) {
- if (this->count > 0) goto keep_waiting;
-
- fr_event_timer_delete(event_list, &this->ev);
-
- this->print(this, buffer, sizeof(buffer));
- DEBUG("... cleaning up socket %s", buffer);
- listen_free(&this);
- return 1;
- }
-#endif /* WITH_TCP */
-
return 1;
}
+void radius_update_listener(rad_listen_t *this)
+{
+ event_new_fd(this);
+}
+
/*
* Emit a systemd watchdog notification and reschedule the event.
*/
static void handle_signal_self(int flag)
{
- ASSERT_MASTER;
-
if ((flag & (RADIUS_SIGNAL_SELF_EXIT | RADIUS_SIGNAL_SELF_TERM)) != 0) {
if ((flag & RADIUS_SIGNAL_SELF_EXIT) != 0) {
INFO("Signalled to exit");
trigger_exec(NULL, NULL, "server.signal.hup", true, NULL);
fr_event_loop_exit(event_list, 0x80);
}
-
-#if defined(WITH_TCP) && defined(WITH_PROXY)
- /*
- * There are new listeners in the list. Run
- * event_new_fd() on them.
- */
- if ((flag & RADIUS_SIGNAL_SELF_NEW_FD) != 0) {
- rad_listen_t *this, *next;
-
- FD_MUTEX_LOCK(&fd_mutex);
-
- /*
- * FIXME: unlock the mutex before calling
- * event_new_fd()?
- */
- for (this = new_listeners; this != NULL; this = next) {
- next = this->next;
- this->next = NULL;
-
- event_new_fd(this);
- }
-
- new_listeners = NULL;
- FD_MUTEX_UNLOCK(&fd_mutex);
- }
-#endif
}
static int self_pipe[2] = { -1, -1 };
return 1;
}
-static int packet_entry_cmp(void const *one, void const *two)
-{
- RADIUS_PACKET const * const *a = one;
- RADIUS_PACKET const * const *b = two;
-
- return fr_packet_cmp(*a, *b);
-}
-
-#ifdef WITH_PROXY
-/*
- * They haven't defined a proxy listener. Automatically
- * add one for them, with the correct address family.
- */
-static void create_default_proxy_listener(int af)
-{
- uint16_t port = 0;
- home_server_t home;
- listen_socket_t *sock;
- rad_listen_t *this;
-
- memset(&home, 0, sizeof(home));
-
- /*
- * Open a default UDP port
- */
- home.proto = IPPROTO_UDP;
- port = 0;
-
- /*
- * Set the address family.
- */
- home.src_ipaddr.af = af;
- home.ipaddr.af = af;
-
- /*
- * Get the correct listener.
- */
- this = proxy_new_listener(proxy_ctx, &home, port);
- if (!this) {
- fr_exit_now(1);
- }
-
- sock = this->data;
- if (!fr_packet_list_socket_add(proxy_list, this->fd,
- sock->proto,
- &sock->other_ipaddr, sock->other_port,
- this)) {
- ERROR("Failed adding proxy socket");
- fr_exit_now(1);
- }
-
- /*
- * Insert the FD into list of FDs to listen on.
- */
- radius_update_listener(this);
-}
-
-/*
- * See if we automatically need to open a proxy socket.
- */
-static void check_proxy(rad_listen_t *head)
-{
- bool defined_proxy;
- bool has_v4, has_v6;
- rad_listen_t *this;
-
- if (check_config) return;
- if (!main_config.proxy_requests) return;
- if (!head) return;
-
- defined_proxy = has_v4 = has_v6 = false;
-
- /*
- * Figure out if we need to open a proxy socket, and if
- * so, which one.
- */
- for (this = head; this != NULL; this = this->next) {
- listen_socket_t *sock;
-
- switch (this->type) {
- case RAD_LISTEN_PROXY:
- defined_proxy = true;
- break;
-
- case RAD_LISTEN_AUTH:
-#ifdef WITH_ACCT
- case RAD_LISTEN_ACCT:
-#endif
-#ifdef WITH_COA
- case RAD_LISTEN_COA:
-#endif
- sock = this->data;
- if (sock->my_ipaddr.af == AF_INET) has_v4 = true;
- if (sock->my_ipaddr.af == AF_INET6) has_v6 = true;
- break;
-
- default:
- break;
- }
- }
-
- /*
- * Assume they know what they're doing.
- */
- if (defined_proxy) return;
-
- if (has_v4) create_default_proxy_listener(AF_INET);
-
- if (has_v6) create_default_proxy_listener(AF_INET6);
-}
-#endif
-
/** Start the main event loop and initialise the listeners
*
* @param have_children Whether the server is threaded.
* - 0 on success.
* - -1 on failure.
*/
-int radius_event_start(bool have_children)
+int radius_event_start(UNUSED bool have_children)
{
- rad_listen_t *head = NULL;
-
if (fr_start_time != (time_t)-1) return 0;
time(&fr_start_time);
- if (!check_config) {
- /*
- * radius_event_init() must be called first
- */
- rad_assert(event_list);
-
- MEM(packet_list = rbtree_create(NULL, packet_entry_cmp, NULL, RBTREE_FLAG_LOCK));
- }
-
-#ifdef WITH_PROXY
- if (main_config.proxy_requests && !check_config) {
- /*
- * Create the tree for managing proxied requests and
- * responses.
- */
- MEM(proxy_list = fr_packet_list_create(1));
-
- if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
- ERROR("Failed to initialize proxy mutex: %s", fr_syserror(errno));
- return -1;
- }
-
- /*
- * The "init_delay" is set to "response_window".
- * Reset it to half of "response_window" in order
- * to give the event loop enough time to service
- * the event before hitting "response_window".
- */
- main_config.init_delay.tv_usec += (main_config.init_delay.tv_sec & 0x01) * USEC;
- main_config.init_delay.tv_usec >>= 1;
- main_config.init_delay.tv_sec >>= 1;
-
- proxy_ctx = talloc_init("proxy");
- }
-#endif
-
- /*
- * Move all of the thread calls to this file?
- *
- * It may be best for the mutexes to be in this file...
- */
- spawn_workers = have_children;
-
- NO_SUCH_CHILD_PID = pthread_self(); /* not a child thread */
-
if (check_config) {
DEBUG("%s: #### Skipping IP addresses and Ports ####",
main_config.name);
return -1;
}
- DEBUG("%s: #### Opening IP addresses and Ports ####", main_config.name);
-
- /*
- * The server temporarily switches to an unprivileged
- * user very early in the bootstrapping process.
- * However, some sockets MAY require privileged access
- * (bind to device, or to port < 1024, or to raw
- * sockets). Those sockets need to call suid up/down
- * themselves around the functions that need a privileged
- * uid.
- */
- if (listen_init(&head, spawn_workers) < 0) return -1;
-
- main_config.listen = head;
-
-#ifdef WITH_PROXY
- check_proxy(head);
-#endif
-
/*
* At this point, no one has any business *ever* going
* back to root uid.
return 0;
}
-
-#ifdef WITH_PROXY
-static int proxy_delete_cb(UNUSED void *ctx, void *data)
-{
- REQUEST *request = fr_packet2myptr(REQUEST, packet, data);
-
- VERIFY_REQUEST(request);
- rad_assert(request->parent != NULL);
- rad_assert(request->parent->proxy == request);
- request = request->parent;
- VERIFY_REQUEST(request);
-
- request->master_state = REQUEST_STOP_PROCESSING;
-
- if (request->child_state == REQUEST_QUEUED) {
- request_queue_extract(request);
- request->child_state = REQUEST_DONE;
- }
-
- if (request->child_state == REQUEST_RUNNING) return 0;
-
- request->in_proxy_hash = false;
-
- /*
- * Delete it from the list.
- */
- return 2;
-}
-#endif
-
-
-static int request_delete_cb(UNUSED void *ctx, void *data)
-{
- REQUEST *request = fr_packet2myptr(REQUEST, packet, data);
-
- VERIFY_REQUEST(request);
-
- request->master_state = REQUEST_STOP_PROCESSING;
-
- if (request->child_state == REQUEST_QUEUED) {
- request_queue_extract(request);
- request->child_state = REQUEST_DONE;
- }
-
- if (request->child_state == REQUEST_RUNNING) return 0;
-
-#ifdef WITH_PROXY
- rad_assert(request->in_proxy_hash == false);
-#endif
-
- request->in_request_hash = false;
- fr_event_timer_delete(event_list, &request->ev);
-
- if (main_config.talloc_memory_report) {
- RDEBUG2("Cleaning up request packet ID %u with timestamp +%d",
- request->packet->id,
- (unsigned int) (request->packet->timestamp.tv_sec - fr_start_time));
- }
-
- request_free(request);
-
- /*
- * Delete it from the list, and continue;
- */
- return 2;
-}
-
-
void radius_event_free(void)
{
- ASSERT_MASTER;
-
-#ifdef WITH_PROXY
- /*
- * There are requests in the proxy hash that aren't
- * referenced from anywhere else. Remove them first.
- */
- if (proxy_list) {
- fr_packet_list_walk(proxy_list, NULL, proxy_delete_cb);
- }
-#endif
-
- rbtree_walk(packet_list, RBTREE_DELETE_ORDER, request_delete_cb, NULL);
-
- if (spawn_workers) {
- /*
- * Walk the lists again, ensuring that all
- * requests are done.
- */
- if (main_config.talloc_memory_report) {
- int num;
-
-#ifdef WITH_PROXY
- if (proxy_list) {
- fr_packet_list_walk(proxy_list, NULL, proxy_delete_cb);
- num = fr_packet_list_num_elements(proxy_list);
- if (num > 0) {
- ERROR("Proxy list has %d requests still in it.", num);
- }
- }
-#endif
-
- rbtree_walk(packet_list, RBTREE_DELETE_ORDER, request_delete_cb, NULL);
- num = rbtree_num_elements(packet_list);
- if (num > 0) {
- ERROR("Request list has %d requests still in it.", num);
- }
- }
- }
-
- talloc_free(packet_list);
- packet_list = NULL;
-
-#ifdef WITH_PROXY
- fr_packet_list_free(proxy_list);
- proxy_list = NULL;
-
- if (proxy_ctx) talloc_free(proxy_ctx);
-#endif
-
TALLOC_FREE(event_list);
if (debug_condition) talloc_free(debug_condition);