#ifdef DEBUG_STATE_MACHINE
# define TRACE_STATE_MACHINE \
-if (debug_flag) do { \
+if (rad_debug_lvl) do { \
struct timeval debug_tv; \
gettimeofday(&debug_tv, NULL); \
debug_tv.tv_sec -= fr_start_time; \
# define TRACE_STATE_MACHINE {}
#endif
+static NEVER_RETURNS void _rad_panic(char const *file, unsigned int line, char const *msg)
+{
+ ERROR("%s[%u]: %s", file, line, msg);
+ fr_exit_now(1);
+}
+
+#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
+
/** 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 CC_HINT(nonnull) _x(REQUEST *request, int action)
+#define STATE_MACHINE_DECL(_x) static void _x(REQUEST *request, int action)
static void request_timer(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.
* @param action to perform when we resume processing the request.
*/
-static inline void state_machine_timer(REQUEST *request, struct timeval *when, fr_state_action_t *action)
+static inline void state_machine_timer(char const *file, int line, REQUEST *request,
+ struct timeval *when, fr_state_action_t action)
{
request->timer_action = action;
- fr_event_insert(el, request_timer, request, when, &request->ev);
+ if (!fr_event_insert(el, request_timer, request, when, &request->ev)) {
+ _rad_panic(file, line, "Failed to insert event");
+ }
}
/** @copybrief state_machine_timer
*
* @param _x the action to perform when we resume processing the request.
*/
-#define STATE_MACHINE_TIMER(_x) state_machine_timer(request, &when, _x)
+#define STATE_MACHINE_TIMER(_x) state_machine_timer(__FILE__, __LINE__, request, &when, _x)
/*
* We need a different VERIFY_REQUEST macro in process.c
# 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);
/*
static int request_num_counter = 1;
#ifdef WITH_PROXY
-static int request_will_proxy(REQUEST *request);
-static int request_proxy(REQUEST *request, int retransmit);
-STATE_MACHINE_DECL(proxy_wait_for_reply);
-STATE_MACHINE_DECL(proxy_no_reply);
-STATE_MACHINE_DECL(proxy_running);
-static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply);
-static void remove_from_proxy_hash(REQUEST *request);
-static void remove_from_proxy_hash_nl(REQUEST *request, bool yank);
-static int insert_into_proxy_hash(REQUEST *request);
+static int request_will_proxy(REQUEST *request) CC_HINT(nonnull);
+static int request_proxy(REQUEST *request, int retransmit) 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_running) CC_HINT(nonnull);
+STATE_MACHINE_DECL(request_done) 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);
+
+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 REQUEST *request_setup(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PACKET *packet,
RADCLIENT *client, RAD_REQUEST_FUNP fun);
+static int request_pre_handler(REQUEST *request, UNUSED int action) CC_HINT(nonnull);
-STATE_MACHINE_DECL(request_response_delay);
-STATE_MACHINE_DECL(request_cleanup_delay);
-STATE_MACHINE_DECL(request_running);
#ifdef WITH_COA
-static void request_coa_originate(REQUEST *request);
-STATE_MACHINE_DECL(coa_running);
-STATE_MACHINE_DECL(coa_wait_for_reply);
-STATE_MACHINE_DECL(coa_no_reply);
-static void coa_separate(REQUEST *request);
-#define COA_SEPARATE if (request->coa) coa_separate(request->coa);
+static void request_coa_originate(REQUEST *request) CC_HINT(nonnull);
+STATE_MACHINE_DECL(coa_wait_for_reply) CC_HINT(nonnull);
+STATE_MACHINE_DECL(coa_no_reply) CC_HINT(nonnull);
+STATE_MACHINE_DECL(coa_running) CC_HINT(nonnull);
+static void coa_separate(REQUEST *request) CC_HINT(nonnull);
+# define COA_SEPARATE if (request->coa) coa_separate(request->coa);
#else
-#define COA_SEPARATE
+# define COA_SEPARATE
#endif
#define CHECK_FOR_STOP do { if (request->master_state == REQUEST_STOP_PROCESSING) {request_done(request, FR_ACTION_DONE);return;}} while (0)
#define INSERT_EVENT(_function, _ctx) if (!fr_event_insert(el, _function, _ctx, &((_ctx)->when), &((_ctx)->ev))) { _rad_panic(__FILE__, __LINE__, "Failed to insert event"); }
-static NEVER_RETURNS void _rad_panic(char const *file, unsigned int line, char const *msg)
-{
- ERROR("[%s:%d] %s", file, line, msg);
-#ifndef NDEBUG
- rad_assert(0 == 1);
-#endif
- fr_exit(1);
-}
-
-#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
-
static void tv_add(struct timeval *tv, int usec_delay)
{
if (usec_delay >= USEC) {
* This really belongs in a utility library
*/
if (is_radius_code(packet->code)) {
- RDEBUG("%s %s Id %i from %s:%i to %s:%i length %zu",
+ RDEBUG("%s %s Id %i from %s%s%s:%i to %s%s%s:%i length %zu",
received ? "Received" : "Sent",
fr_packet_codes[packet->code],
packet->id,
+ packet->src_ipaddr.af == AF_INET6 ? "[" : "",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
src_ipaddr, sizeof(src_ipaddr)),
+ packet->src_ipaddr.af == AF_INET6 ? "]" : "",
packet->src_port,
+ packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
inet_ntop(packet->dst_ipaddr.af,
&packet->dst_ipaddr.ipaddr,
dst_ipaddr, sizeof(dst_ipaddr)),
+ packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
packet->dst_port,
packet->data_len);
} else {
- RDEBUG("%s code %i Id %i from %s:%i to %s:%i length %zu",
+ RDEBUG("%s code %u Id %i from %s%s%s:%i to %s%s%s:%i length %zu\n",
received ? "Received" : "Sent",
packet->code,
packet->id,
+ packet->src_ipaddr.af == AF_INET6 ? "[" : "",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
src_ipaddr, sizeof(src_ipaddr)),
+ packet->src_ipaddr.af == AF_INET6 ? "]" : "",
packet->src_port,
+ packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
inet_ntop(packet->dst_ipaddr.af,
&packet->dst_ipaddr.ipaddr,
dst_ipaddr, sizeof(dst_ipaddr)),
+ packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
packet->dst_port,
packet->data_len);
}
#endif
-/*
- * Only ever called from the master thread.
+/** 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
*/
-STATE_MACHINE_DECL(request_done)
+static void request_done(REQUEST *request, int action)
{
struct timeval now, when;
* and wait for the master thread timer to clean us up.
*/
if (!we_are_master()) {
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
+ FINAL_STATE(REQUEST_DONE);
return;
}
#endif
#endif
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
+ if (rad_debug_lvl) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
request->number, __FUNCTION__,
child_state_names[request->child_state],
child_state_names[REQUEST_DONE]);
* If we're the last one, remove the listener now.
*/
if ((request->listener->count == 0) &&
- (request->listener->status == RAD_LISTEN_STATUS_EOL)) {
- request->listener->status = RAD_LISTEN_STATUS_REMOVE_NOW;
+ (request->listener->status >= RAD_LISTEN_STATUS_FROZEN)) {
event_new_fd(request->listener);
}
}
VERIFY_REQUEST(request);
- if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) goto done;
+ /*
+ * 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;
-#ifdef WITH_DETAIL
/*
- * If the packets are from the detail file, we can clean them up now.
+ * Accounting packets shouldn't be retransmitted. They
+ * should always be updated with Acct-Delay-Time.
*/
- if (request->listener->type == RAD_LISTEN_DETAIL) goto done;
+#ifdef WITH_ACCOUNTING
+ if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) goto done;
+#endif
+
+#ifdef WITH_DHCP
+ if (request->listener->type == RAD_LISTEN_DHCP) goto done;
+#endif
+
+#ifdef WITH_VMPS
+ if (request->listener->type == RAD_LISTEN_VQP) goto done;
#endif
if (!request->root->cleanup_delay) goto done;
*/
if (timercmp(&when, &now, >)) {
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
+ if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
#endif
request->process = request_cleanup_delay;
- request->child_state = 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.
*/
- if (we_are_master()) {
- STATE_MACHINE_TIMER(FR_ACTION_TIMER);
- } else {
- NO_CHILD_THREAD;
- }
+ request->child_state = REQUEST_CLEANUP_DELAY;
+#ifdef HAVE_PTHREAD_H
+ rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
+#endif
+ STATE_MACHINE_TIMER(FR_ACTION_TIMER);
return;
}
/*
* Enforce max_request_time.
*/
-static void request_max_time(REQUEST *request)
+static bool request_max_time(REQUEST *request)
{
struct timeval now, when;
rad_assert(request->magic == REQUEST_MAGIC);
if (request->child_state == REQUEST_DONE) {
done:
request_done(request, FR_ACTION_DONE);
- return;
+ return true;
}
/*
tv_add(&when, request->delay);
request->delay += request->delay >> 1;
STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+ return false;
}
static void request_queue_or_run(REQUEST *request,
*/
if (request->master_state == REQUEST_STOP_PROCESSING) {
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s M-%s causes C-%s-> C-%s\t********\n",
+ if (rad_debug_lvl) printf("(%u) ********\tSTATE %s M-%s causes C-%s-> C-%s\t********\n",
request->number, __FUNCTION__,
master_state_names[request->master_state],
child_state_names[request->child_state],
request->component, request->module);
}
-STATE_MACHINE_DECL(request_cleanup_delay)
+
+/** 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, int action)
{
struct timeval when, now;
if (timercmp(&when, &now, >)) {
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
+ if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
#endif
STATE_MACHINE_TIMER(FR_ACTION_TIMER);
return;
}
}
-STATE_MACHINE_DECL(request_response_delay)
+
+/** 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, int action)
{
struct timeval when, now;
case FR_ACTION_TIMER:
fr_event_now(el, &now);
- rad_assert(request->response_delay.tv_sec > 0);
-
/*
* See if it's time to send the reply. If not,
* we wait some more.
if (timercmp(&when, &now, >)) {
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_response_delay");
+ if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_response_delay");
#endif
STATE_MACHINE_TIMER(FR_ACTION_TIMER);
return;
}
-static int CC_HINT(nonnull) request_pre_handler(REQUEST *request, UNUSED int action)
+static int request_pre_handler(REQUEST *request, UNUSED int action)
{
int rcode;
* process it.
*/
if (request->packet->dst_port == 0) {
- request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
- request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+ request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
return 1;
}
}
if (!request->username) {
- request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
}
return 1;
}
-STATE_MACHINE_DECL(request_finish)
+
+/** 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, int action)
{
VALUE_PAIR *vp;
/*
* Override the response code if a control:Response-Packet-Type attribute is present.
*/
- vp = pairfind(request->config, PW_RESPONSE_PACKET_TYPE, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->config, PW_RESPONSE_PACKET_TYPE, 0, TAG_ANY);
if (vp) {
if (vp->vp_integer == 256) {
RDEBUG2("Not responding to request");
*/
else if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
if (request->reply->code == 0) {
- vp = pairfind(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
-
+ vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
if (!vp || (vp->vp_integer != 5)) {
RDEBUG2("There was no response configured: "
"rejecting request");
/*
* Copy Proxy-State from the request to the reply.
*/
- vp = paircopy_by_num(request->reply, request->packet->vps,
+ vp = fr_pair_list_copy_by_num(request->reply, request->packet->vps,
PW_PROXY_STATE, 0, TAG_ANY);
- if (vp) pairadd(&request->reply->vps, vp);
+ if (vp) fr_pair_add(&request->reply->vps, vp);
/*
* Call Post-Auth for Access-Request packets.
rad_postauth(request);
}
+#ifdef WITH_COA
+ /*
+ * Maybe originate a CoA request.
+ */
+ if ((action == FR_ACTION_RUN) && !request->proxy && request->coa) {
+ request_coa_originate(request);
+ }
+#endif
/*
* Clean up. These are no longer needed.
*/
if (request->packet->dst_port == 0) {
RDEBUG("Finished internally proxied request.");
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
+ FINAL_STATE(REQUEST_DONE);
return;
}
*/
if (request->listener->type == RAD_LISTEN_DETAIL) {
request->simul_max = 1;
- request->listener->send(request->listener, request);
+
/*
* But only print the reply if there is one.
*/
if (request->reply->code != 0) {
debug_packet(request, request->reply, false);
}
+
+ request->listener->send(request->listener, request);
goto done;
}
#endif
(request->root->reject_delay.tv_sec > 0)) {
request->response_delay = request->root->reject_delay;
+ vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY);
+ if (vp) {
+ if (vp->vp_integer <= 10) {
+ request->response_delay.tv_sec = vp->vp_integer;
+ } else {
+ request->response_delay.tv_sec = 10;
+ }
+ request->response_delay.tv_usec = 0;
+ } else {
+ vp = fr_pair_find_by_num(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY_USEC, 0, TAG_ANY);
+ if (vp) {
+ if (vp->vp_integer <= 10 * USEC) {
+ request->response_delay.tv_sec = vp->vp_integer / USEC;
+ request->response_delay.tv_usec = vp->vp_integer % 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
/*
* Send the reply.
*/
- if (request->response_delay.tv_sec == 0) {
- rad_assert(request->response_delay.tv_usec == 0);
+ 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->send(request->listener, request);
+ if (rad_debug_lvl && request->state &&
+ (request->reply->code == PW_CODE_ACCESS_ACCEPT)) {
+ if (!fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY)) {
+ RWDEBUG2("Unused attributes found in &session-state:");
+ }
+ }
+
debug_packet(request, request->reply, false);
+ request->listener->send(request->listener, request);
}
done:
RDEBUG2("Finished request");
- request->component = "<core>";
- request->module = "<done>";
-
-#ifdef WITH_ACCOUNTING
- /*
- * Accounting packets can be cleaned up now.
- */
- if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
- return;
- }
-#endif
-
-#ifdef WITH_DETAIL
- /*
- * If the packets are from the detail file, we can clean them up now.
- */
- if (request->listener->type == RAD_LISTEN_DETAIL) {
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
- return;
- }
-#endif
-
-#ifdef WITH_COA
- /*
- * If we've originated this CoA request, it gets
- * cleaned up now.
- */
- if (request->proxy &&
- ((request->proxy->code == PW_CODE_COA_REQUEST) ||
- (request->proxy->code == PW_CODE_DISCONNECT_REQUEST)) &&
- (request->packet->code != request->proxy->code)) {
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
- return;
- }
-#endif
-
- /*
- * Clean up the request.
- */
request_cleanup_delay_init(request);
} else {
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->component = "<core>";
- request->module = "<delay>";
request->process = request_response_delay;
- NO_CHILD_THREAD;
- request->child_state = REQUEST_RESPONSE_DELAY;
+
+ FINAL_STATE(REQUEST_RESPONSE_DELAY);
}
}
-STATE_MACHINE_DECL(request_running)
+/** 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, int action)
{
VERIFY_REQUEST(request);
switch (action) {
case FR_ACTION_TIMER:
COA_SEPARATE;
- request_max_time(request);
+ (void) request_max_time(request);
break;
case FR_ACTION_DUP:
case FR_ACTION_RUN:
if (!request_pre_handler(request, action)) {
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s failed in pre-handler C-%s -> C-%s\t********\n",
+ if (rad_debug_lvl) printf("(%u) ********\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
-
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
+ FINAL_STATE(REQUEST_DONE);
break;
}
if ((action == FR_ACTION_RUN) &&
request_will_proxy(request)) {
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tWill Proxy\t********\n", request->number);
+ if (rad_debug_lvl) printf("(%u) ********\tWill Proxy\t********\n", request->number);
#endif
/*
* If this fails, it
#endif
{
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tFinished\t********\n", request->number);
-#endif
-
-#ifdef WITH_COA
- /*
- * Maybe originate a CoA request.
- */
- if ((action == FR_ACTION_RUN) && request->coa) {
- request_coa_originate(request);
- }
+ if (rad_debug_lvl) printf("(%u) ********\tFinished\t********\n", request->number);
#endif
#ifdef WITH_PROXY
{
sock = listener->data;
sock->last_packet = now.tv_sec;
+
+#ifdef WITH_TCP
+ packet->proto = sock->proto;
+#endif
}
/*
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
#endif
request->listener->decode(request->listener, request);
- request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
- request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
+ request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
fun(request);
request->master_state = REQUEST_ACTIVE;
request->child_state = REQUEST_RUNNING;
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
+ if (rad_debug_lvl) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
request->number, __FUNCTION__,
child_state_names[request->child_state],
child_state_names[REQUEST_RUNNING]);
#endif
-#ifdef HAVE_PTHREAD_H
- request->child_pid = NO_SUCH_CHILD_PID;
-#endif
request->handle = fun;
NO_CHILD_THREAD;
ASSERT_MASTER;
- fr_event_now(el, &now);
-
if (listener->status != RAD_LISTEN_STATUS_KNOWN) return;
+ fr_event_now(el, &now);
+
switch (listener->type) {
#ifdef WITH_PROXY
case RAD_LISTEN_PROXY:
do_close:
- listener->status = RAD_LISTEN_STATUS_EOL;
+#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)) {
+ ERROR("Fatal error freezing socket: %s", fr_strerror());
+ fr_exit(1);
+ }
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ }
+#endif
+
+ /*
+ * Mark the socket as "don't use if at all possible".
+ */
+ listener->status = RAD_LISTEN_STATUS_FROZEN;
event_new_fd(listener);
return;
}
* the mutex. This guarantees that when another thread
* grabs the mutex, the "not in hash" flag is correct.
*/
- RDEBUG3("proxy: request is no longer in proxy hash");
}
static void remove_from_proxy_hash(REQUEST *request)
* Delete any reply we had accumulated until now.
*/
RDEBUG2("Clearing existing &reply: attributes");
- pairfree(&request->reply->vps);
+ fr_pair_list_free(&request->reply->vps);
/*
* Run the packet through the post-proxy stage,
* BEFORE playing games with the attributes.
*/
- vp = pairfind(request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
if (vp) {
post_proxy_type = vp->vp_integer;
/*
* Create config:Post-Proxy-Type
*/
if (dval) {
- vp = radius_paircreate(request, &request->config, PW_POST_PROXY_TYPE, 0);
+ vp = radius_pair_create(request, &request->config, PW_POST_PROXY_TYPE, 0);
vp->vp_integer = dval->value;
}
}
* running Post-Proxy-Type = Fail.
*/
if (reply) {
- pairadd(&request->reply->vps, paircopy(request->reply, reply->vps));
+ fr_pair_add(&request->reply->vps, fr_pair_list_copy(request->reply, reply->vps));
/*
* Delete the Proxy-State Attributes from
* the reply. These include Proxy-State
* attributes from us and remote server.
*/
- pairdelete(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+ fr_pair_delete_by_num(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
}
switch (rcode) {
return 1;
}
+static void mark_home_server_alive(REQUEST *request, home_server_t *home)
+{
+ char buffer[128];
+
+ home->state = HOME_STATE_ALIVE;
+ home->response_timeouts = 0;
+ exec_trigger(request, home->cs, "home_server.alive", false);
+ home->currently_outstanding = 0;
+ home->num_sent_pings = 0;
+ home->num_received_pings = 0;
+ gettimeofday(&home->revive_time, NULL);
+
+ fr_event_delete(el, &home->ev);
+
+ RPROXY("Marking home server %s port %d alive",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+}
+
+
int request_proxy_reply(RADIUS_PACKET *packet)
{
RADIUS_PACKET **proxy_p;
if (!proxy_p) {
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- PROXY("No outstanding request was found for reply from host %s port %d - ID %u",
+ PROXY("No outstanding request was found for %s packet from host %s port %d - ID %u",
+ fr_packet_codes[packet->code],
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
buffer, sizeof(buffer)),
#endif
/*
- * We've received a reply. If we hadn't been sending it
- * packets for a while, just mark it alive.
+ * 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 (request->home_server->state == HOME_STATE_UNKNOWN) {
- request->home_server->state = HOME_STATE_ALIVE;
- request->home_server->response_timeouts = 0;
+ if ((request->home_server->state == HOME_STATE_UNKNOWN) ||
+ (request->home_server->state == HOME_STATE_ZOMBIE)) {
+ mark_home_server_alive(request, request->home_server);
}
/*
if (request->proxy->code == PW_CODE_ACCESS_REQUEST) {
dval = dict_valbyname(PW_POST_PROXY_TYPE, 0,
"Fail-Authentication");
-
+#ifdef WITH_ACCOUNTING
} else if (request->proxy->code == PW_CODE_ACCOUNTING_REQUEST) {
dval = dict_valbyname(PW_POST_PROXY_TYPE, 0,
"Fail-Accounting");
+#endif
+
#ifdef WITH_COA
} else if (request->proxy->code == PW_CODE_COA_REQUEST) {
dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-CoA");
if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail");
if (!dval) {
- pairdelete(&request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
+ fr_pair_delete_by_num(&request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
return 0;
}
- vp = pairfind(request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
- if (!vp) vp = radius_paircreate(request, &request->config,
+ vp = fr_pair_find_by_num(request->config, PW_POST_PROXY_TYPE, 0, TAG_ANY);
+ if (!vp) vp = radius_pair_create(request, &request->config,
PW_POST_PROXY_TYPE, 0);
vp->vp_integer = dval->value;
return 1;
}
-STATE_MACHINE_DECL(proxy_no_reply)
+
+/** 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 -> done [ label = "TIMER >= timeout" ];
+ * }
+ * \enddot
+ */
+static void proxy_no_reply(REQUEST *request, int action)
{
VERIFY_REQUEST(request);
break;
case FR_ACTION_TIMER:
- request_max_time(request);
+ (void) request_max_time(request);
break;
case FR_ACTION_PROXY_REPLY:
}
}
-STATE_MACHINE_DECL(proxy_running)
+/** 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, int action)
{
VERIFY_REQUEST(request);
break;
case FR_ACTION_TIMER:
- request_max_time(request);
+ (void) request_max_time(request);
break;
case FR_ACTION_RUN:
}
}
+/** 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->config. If any of the following attributes are found, the
+ * request may be proxied.
+ *
+ * The key attributes are:
+ * - PW_PROXY_TO_REALM - Specifies a realm the request should be proxied to.
+ * - PW_HOME_SERVER_POOL - Specifies a specific home server pool to proxy to.
+ * - PW_PACKET_DST_IP_ADDRESS - Specifies a specific IPv4 home server to proxy to.
+ * - PW_PACKET_DST_IPV6_ADDRESS - Specifies a specific IPv6 home server to proxy to.
+ *
+ * Certain packet types such as #PW_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 error.
+ */
static int request_will_proxy(REQUEST *request)
{
int rcode, pre_proxy_type = 0;
*/
if (request->reply->code != 0) return 0;
- vp = pairfind(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY);
if (vp) {
realm = realm_find2(vp->vp_strvalue);
if (!realm) {
return 0;
}
- } else if ((vp = pairfind(request->config, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
+ } else if ((vp = fr_pair_find_by_num(request->config, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
int pool_type;
switch (request->packet->code) {
/*
* Send it directly to a home server (i.e. NAS)
*/
- } else if (((vp = pairfind(request->config, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY)) != NULL) ||
- ((vp = pairfind(request->config, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL)) {
- VALUE_PAIR *port;
+ } else if (((vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY)) != NULL) ||
+ ((vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL)) {
uint16_t dst_port;
fr_ipaddr_t dst_ipaddr;
dst_ipaddr.prefix = 128;
}
- port = pairfind(request->config, PW_PACKET_DST_PORT, 0, TAG_ANY);
- if (!port) {
- dst_port = PW_COA_UDP_PORT;
+ vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_PORT, 0, TAG_ANY);
+ if (!vp) {
+ if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
+ dst_port = PW_AUTH_UDP_PORT;
+
+#ifdef WITH_ACCOUNTING
+ } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
+ dst_port = PW_ACCT_UDP_PORT;
+#endif
+
+#ifdef WITH_COA
+ } else if ((request->packet->code == PW_CODE_COA_REQUEST) ||
+ (request->packet->code == PW_CODE_DISCONNECT_REQUEST)) {
+ dst_port = PW_COA_UDP_PORT;
+#endif
+ } else { /* shouldn't happen for RADIUS... */
+ return 0;
+ }
+
} else {
dst_port = vp->vp_integer;
}
if (!home) {
char buffer[256];
- WARN("No such CoA home server %s port %u",
+ WARN("No such home server %s port %u",
inet_ntop(dst_ipaddr.af, &dst_ipaddr.ipaddr, buffer, sizeof(buffer)),
(unsigned int) dst_port);
return 0;
}
- goto do_home;
+ /*
+ * The home server is alive (or may be alive).
+ * Send the packet to the IP.
+ */
+ if (home->state != HOME_STATE_IS_DEAD) goto do_home;
+
+ /*
+ * The home server is dead. If you wanted
+ * fail-over, you should have proxied to a pool.
+ * Sucks to be you.
+ */
+
+ return 0;
} else {
return 0;
/*
* Remember that we sent the request to a Realm.
*/
- if (realmname) pairmake_packet("Realm", realmname, T_OP_EQ);
+ if (realmname) pair_make_request("Realm", realmname, T_OP_EQ);
/*
* Strip the name, if told to.
* requests.
*/
if (realm && (realm->strip_realm == true) &&
- (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY)) != NULL) {
+ (strippedname = fr_pair_find_by_num(request->proxy->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY)) != NULL) {
/*
* If there's a Stripped-User-Name attribute in
* the request, then use THAT as the User-Name
* from the vps list, and making the new
* User-Name the head of the vps list.
*/
- vp = pairfind(request->proxy->vps, PW_USER_NAME, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->proxy->vps, PW_USER_NAME, 0, TAG_ANY);
if (!vp) {
vp_cursor_t cursor;
- vp = radius_paircreate(NULL, NULL,
+ vp = radius_pair_create(NULL, NULL,
PW_USER_NAME, 0);
rad_assert(vp != NULL); /* handled by above function */
/* Insert at the START of the list */
fr_cursor_merge(&cursor, request->proxy->vps);
request->proxy->vps = vp;
}
- pairstrcpy(vp, strippedname->vp_strvalue);
+ fr_pair_value_strcpy(vp, strippedname->vp_strvalue);
/*
* Do NOT delete Stripped-User-Name.
* anymore - we changed it.
*/
if ((request->packet->code == PW_CODE_ACCESS_REQUEST) &&
- pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
- pairfind(request->proxy->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
- vp = radius_paircreate(request->proxy, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
- pairmemcpy(vp, request->packet->vector, sizeof(request->packet->vector));
+ fr_pair_find_by_num(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
+ fr_pair_find_by_num(request->proxy->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
+ vp = radius_pair_create(request->proxy, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
+ fr_pair_value_memcpy(vp, request->packet->vector, sizeof(request->packet->vector));
}
/*
* The RFC's say we have to do this, but FreeRADIUS
* doesn't need it.
*/
- vp = radius_paircreate(request->proxy, &request->proxy->vps, PW_PROXY_STATE, 0);
- pairsprintf(vp, "%u", request->packet->id);
+ vp = radius_pair_create(request->proxy, &request->proxy->vps, PW_PROXY_STATE, 0);
+ fr_pair_value_sprintf(vp, "%u", request->packet->id);
/*
* Should be done BEFORE inserting into proxy hash, as
/*
* Call the pre-proxy routines.
*/
- vp = pairfind(request->config, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->config, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
if (vp) {
DICT_VALUE const *dval = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
/* Must be a validation issue */
} else {
rcode = process_pre_proxy(pre_proxy_type, request);
}
+
switch (rcode) {
case RLM_MODULE_FAIL:
case RLM_MODULE_INVALID:
case RLM_MODULE_NOOP:
case RLM_MODULE_OK:
case RLM_MODULE_UPDATED:
- break;
+ return 1;
}
+}
- return 1;
+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->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_flag || !we_are_master());
+
+ fake = request_alloc_fake(request);
+
+ fake->packet->vps = fr_pair_list_copy(fake->packet, request->packet->vps);
+ talloc_free(request->proxy);
+
+ fake->server = request->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 = talloc_steal(request, fake->packet);
+ fake->packet = NULL;
+ request->proxy_reply = talloc_steal(request, 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(REQUEST *request, int retransmit)
{
char buffer[128];
*
* So, we have some horrible hacks to get around that.
*/
- if (request->home_server->server) {
- REQUEST *fake;
-
- if (request->packet->dst_port == 0) {
- WARN("Cannot proxy an internal request");
- return 0;
- }
-
- DEBUG("Proxying to virtual server %s",
- request->home_server->server);
-
- /*
- * Packets to virtual serrers 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(retransmit == 0);
- rad_assert(!spawn_flag || !we_are_master());
-
- fake = request_alloc_fake(request);
-
- fake->packet->vps = paircopy(fake->packet, request->packet->vps);
- talloc_free(request->proxy);
-
- fake->server = request->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 = talloc_steal(request, fake->packet);
- fake->packet = NULL;
- request->proxy_reply = talloc_steal(request, 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);
- }
-
- /*
- * Just do the work here, rather than trying to
- * run the "decode proxy reply" stuff...
- */
- process_proxy_reply(request, request->proxy_reply);
-
- /*
- * If we have a reply, run it through the handler.
- */
- if (request->proxy_reply) {
- request->handle(request); /* to do more post-proxy stuff */
- }
-
- return -1; /* so we call request_finish */
- }
+ if (request->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)) {
- ERROR("Failed to insert request into the proxy list");
+ RPROXY("Failed to insert request into the proxy list");
return -1;
}
rad_assert(request->proxy->id >= 0);
- if (debug_flag) {
+ if (rad_debug_lvl) {
struct timeval *response_window;
response_window = request_response_window(request);
/*
* 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 = "";
NO_CHILD_THREAD;
/*
}
return 0;
}
- home_server_update_request(home, request);
-
- if (!insert_into_proxy_hash(request)) {
- RPROXY("Failed to insert retransmission into the proxy list");
- goto post_proxy_fail;
- }
-
- /*
- * Free the old packet, to force re-encoding
- */
- talloc_free(request->proxy->data);
- request->proxy->data = NULL;
- request->proxy->data_len = 0;
#ifdef WITH_ACCOUNTING
/*
if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
VALUE_PAIR *vp;
- vp = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
- if (!vp) vp = radius_paircreate(request->proxy,
+ vp = fr_pair_find_by_num(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
+ if (!vp) vp = radius_pair_create(request->proxy,
&request->proxy->vps,
PW_ACCT_DELAY_TIME, 0);
if (vp) {
}
#endif
+ /*
+ * May have failed over to a "fallback" virtual server.
+ * If so, run that instead of doing proxying to a real
+ * server.
+ */
+ if (home->server) {
+ request->home_server = home;
+ TALLOC_FREE(request->proxy);
+
+ (void) proxy_to_virtual_server(request);
+ return 0;
+ }
+
+ home_server_update_request(home, request);
+
+ if (!insert_into_proxy_hash(request)) {
+ RPROXY("Failed to insert retransmission into the proxy list");
+ goto post_proxy_fail;
+ }
+
+ /*
+ * Free the old packet, to force re-encoding
+ */
+ talloc_free(request->proxy->data);
+ request->proxy->data = NULL;
+ request->proxy->data_len = 0;
+
if (request_proxy(request, 1) != 1) goto post_proxy_fail;
return 1;
}
-STATE_MACHINE_DECL(request_ping)
+
+/** Ping a home server.
+ *
+ */
+static void request_ping(REQUEST *request, int action)
{
home_server_t *home = request->home_server;
char buffer[128];
switch (action) {
case FR_ACTION_TIMER:
- ERROR("No response to status check %d for home server %s port %d",
+ ERROR("No response to status check %d ID %u for home server %s port %d",
request->number,
+ request->proxy->id,
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
rad_assert(request->in_proxy_hash);
request->home_server->num_received_pings++;
- RPROXY("Received response to status check %d (%d in current sequence)",
- request->number, home->num_received_pings);
+ RPROXY("Received response to status check %d ID %u (%d in current sequence)",
+ request->number, request->proxy->id, home->num_received_pings);
/*
* Remove the request from any hashes
* Mark it alive and delete any outstanding
* pings.
*/
- home->state = HOME_STATE_ALIVE;
- home->response_timeouts = 0;
- exec_trigger(request, home->cs, "home_server.alive", false);
- home->currently_outstanding = 0;
- home->num_sent_pings = 0;
- home->num_received_pings = 0;
- gettimeofday(&home->revive_time, NULL);
-
- fr_event_delete(el, &home->ev);
-
- RPROXY("Marking home server %s port %d alive",
- inet_ntop(request->proxy->dst_ipaddr.af,
- &request->proxy->dst_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- request->proxy->dst_port);
+ mark_home_server_alive(request, home);
break;
default:
*/
if (home->ping_check == HOME_PING_CHECK_NONE) {
if (home->state == HOME_STATE_ZOMBIE) {
- when = home->zombie_period_start;
- when.tv_sec += home->zombie_period;
+ home->when = home->zombie_period_start;
+ home->when.tv_sec += home->zombie_period;
INSERT_EVENT(ping_home_server, home);
}
if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
request->proxy->code = PW_CODE_STATUS_SERVER;
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"Message-Authenticator", "0x00", T_OP_SET);
} else if (home->type == HOME_TYPE_AUTH) {
request->proxy->code = PW_CODE_ACCESS_REQUEST;
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"User-Name", home->ping_user_name, T_OP_SET);
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"User-Password", home->ping_user_password, T_OP_SET);
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"Service-Type", "Authenticate-Only", T_OP_SET);
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"Message-Authenticator", "0x00", T_OP_SET);
} else {
#ifdef WITH_ACCOUNTING
request->proxy->code = PW_CODE_ACCOUNTING_REQUEST;
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"User-Name", home->ping_user_name, T_OP_SET);
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"Acct-Status-Type", "Stop", T_OP_SET);
- pairmake(request->proxy, &request->proxy->vps,
+ fr_pair_make(request->proxy, &request->proxy->vps,
"Acct-Session-Id", "00000000", T_OP_SET);
- vp = pairmake(request->proxy, &request->proxy->vps,
+ vp = fr_pair_make(request->proxy, &request->proxy->vps,
"Event-Timestamp", "0", T_OP_SET);
vp->vp_date = now.tv_sec;
#else
#endif
}
- vp = pairmake(request->proxy, &request->proxy->vps,
+ vp = fr_pair_make(request->proxy, &request->proxy->vps,
"NAS-Identifier", "", T_OP_SET);
if (vp) {
- pairsprintf(vp, "Status Check %u. Are you alive?",
+ fr_pair_value_sprintf(vp, "Status Check %u. Are you alive?",
home->num_sent_pings);
}
+#ifdef WITH_TCP
+ request->proxy->proto = home->proto;
+#endif
request->proxy->src_ipaddr = home->src_ipaddr;
request->proxy->dst_ipaddr = home->ipaddr;
request->proxy->dst_port = home->port;
request->home_server = home;
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
+ if (rad_debug_lvl) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
child_state_names[request->child_state],
child_state_names[REQUEST_DONE]);
- if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_ping");
+ if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_ping");
#endif
#ifdef HAVE_PTHREAD_H
rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
home->num_sent_pings++;
rad_assert(request->proxy_listener != NULL);
+ debug_packet(request, request->proxy, false);
request->proxy_listener->send(request->proxy_listener,
request);
*/
start = now->tv_sec - ((home->zombie_period + 3) / 4);
if (home->last_packet_recv >= start) {
- DEBUG("Recieved reply from home server %d seconds ago. Might not be zombie.",
+ DEBUG("Received reply from home server %d seconds ago. Might not be zombie.",
(int) (now->tv_sec - home->last_packet_recv));
return;
}
}
}
-STATE_MACHINE_DECL(proxy_wait_for_reply)
+/** 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_window" ];
+ * proxy_wait_for_reply -> timer [ label = "TIMER < max_request_time" ];
+ * proxy_wait_for_reply -> proxy_running [ label = "PROXY_REPLY" arrowhead = "none"];
+ * proxy_wait_for_reply -> done [ label = "TIMER >= max_request_time" ];
+ * }
+ * \enddot
+ */
+static void proxy_wait_for_reply(REQUEST *request, int action)
{
struct timeval now, when;
struct timeval *response_window = NULL;
*/
if (request->home_server->server) return;
+ /*
+ * 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_KNOWN)) {
+ (request->proxy_listener->status >= RAD_LISTEN_STATUS_EOL)) {
request_proxy_anew(request);
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->dst_ipaddr.af,
* get a new ID.
*/
if ((request->packet->code == PW_CODE_ACCOUNTING_REQUEST) &&
- pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY)) {
+ fr_pair_find_by_num(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY)) {
request_proxy_anew(request);
return;
}
FR_STATS_TYPE_INC(home->stats.total_requests);
home->last_packet_sent = now.tv_sec;
request->proxy_retransmit = now;
- request->proxy_listener->send(request->proxy_listener, request);
debug_packet(request, request->proxy, false);
+ request->proxy_listener->send(request->proxy_listener, request);
break;
case FR_ACTION_TIMER:
#ifdef WITH_TCP
if (!request->proxy_listener ||
- (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
+ (request->proxy_listener->status >= RAD_LISTEN_STATUS_EOL)) {
remove_from_proxy_hash(request);
when = request->packet->timestamp;
}
#endif /* WITH_PROXY */
+
/***********************************************************************
*
* CoA code
/*
* Check whether we want to originate one, or cancel one.
*/
- vp = pairfind(request->config, PW_SEND_COA_REQUEST, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->config, PW_SEND_COA_REQUEST, 0, TAG_ANY);
if (!vp) {
- vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0, TAG_ANY);
}
if (vp) {
* src_ipaddr will be set up in proxy_encode.
*/
memset(&ipaddr, 0, sizeof(ipaddr));
- vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY);
if (vp) {
ipaddr.af = AF_INET;
ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
ipaddr.prefix = 32;
- } else if ((vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL) {
+ } else if ((vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_DST_IPV6_ADDRESS, 0, TAG_ANY)) != NULL) {
ipaddr.af = AF_INET6;
ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
ipaddr.prefix = 128;
- } else if ((vp = pairfind(coa->proxy->vps, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
+ } else if ((vp = fr_pair_find_by_num(coa->proxy->vps, PW_HOME_SERVER_POOL, 0, TAG_ANY)) != NULL) {
coa->home_pool = home_pool_byname(vp->vp_strvalue,
HOME_TYPE_COA);
if (!coa->home_pool) {
} else if (!coa->home_server) {
uint16_t port = PW_COA_UDP_PORT;
- vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
if (vp) port = vp->vp_integer;
coa->home_server = home_server_find(&ipaddr, port, IPPROTO_UDP);
}
}
- vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(coa->proxy->vps, PW_PACKET_TYPE, 0, TAG_ANY);
if (vp) {
switch (vp->vp_integer) {
case PW_CODE_COA_REQUEST:
coa->packet = rad_copy_packet(coa, request->packet);
coa->reply = rad_copy_packet(coa, request->reply);
- coa->config = paircopy(coa, request->config);
+ coa->config = fr_pair_list_copy(coa, request->config);
coa->num_coa_requests = 0;
coa->handle = null_handler;
coa->number = request->number; /* it's associated with the same request */
/*
* Call the pre-proxy routines.
*/
- vp = pairfind(request->config, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
+ vp = fr_pair_find_by_num(request->config, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
if (vp) {
DICT_VALUE const *dval = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
/* Must be a validation issue */
debug_packet(coa, coa->proxy, false);
#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
+ if (rad_debug_lvl) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
child_state_names[request->child_state],
child_state_names[REQUEST_PROXIED]);
#endif
coa->child_pid = NO_SUCH_CHILD_PID;
#endif
+ if (we_are_master()) coa_separate(request->coa);
+
/*
* And send the packet.
*/
{
uint32_t delay, frac;
struct timeval now, when, mrd;
+ char buffer[128];
VERIFY_REQUEST(request);
fr_event_now(el, &now);
- /*
- * FIXME: Enforce max_request_time
- */
-
if (request->delay == 0) {
/*
* Implement re-transmit algorithm as per RFC 5080
*/
if (request->home_server->coa_mrc &&
(request->num_coa_requests >= request->home_server->coa_mrc)) {
- char buffer[128];
-
RERROR("Failing request - originate-coa ID %u, due to lack of any response from coa server %s port %d",
request->proxy->id,
inet_ntop(request->proxy->dst_ipaddr.af,
FR_STATS_TYPE_INC(request->home_server->stats.total_requests);
+ RDEBUG2("Sending duplicate CoA request to home server %s port %d - ID: %d",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port,
+ request->proxy->id);
+
request->proxy_listener->send(request->proxy_listener,
request);
}
-STATE_MACHINE_DECL(coa_wait_for_reply)
+
+/** Wait for a reply after originating a CoA a request.
+ *
+ * Retransmit the proxied packet, or time out and go to
+ * coa_no_reply. Mark the home server unresponsive, etc.
+ *
+ * If we do receive a reply, we transition to coa_running.
+ *
+ * \dot
+ * digraph coa_wait_for_reply {
+ * coa_wait_for_reply;
+ *
+ * coa_wait_for_reply -> coa_no_reply [ label = "TIMER >= response_window" ];
+ * coa_wait_for_reply -> timer [ label = "TIMER < max_request_time" ];
+ * coa_wait_for_reply -> coa_running [ label = "PROXY_REPLY" arrowhead = "none"];
+ * coa_wait_for_reply -> done [ label = "TIMER >= max_request_time" ];
+ * }
+ * \enddot
+ */
+static void coa_wait_for_reply(REQUEST *request, int action)
{
VERIFY_REQUEST(request);
ASSERT_MASTER;
CHECK_FOR_STOP;
+ if (request->parent) coa_separate(request);
+
switch (action) {
case FR_ACTION_TIMER:
- if (request->parent) coa_separate(request);
+ if (request_max_time(request)) break;
coa_retransmit(request);
break;
case FR_ACTION_PROXY_REPLY:
- if (request->parent) coa_separate(request);
-
request_queue_or_run(request, coa_running);
break;
(void) talloc_steal(NULL, request);
request->parent->coa = NULL;
request->parent = NULL;
+
+ if (we_are_master()) {
+ request->delay = 0;
+ coa_retransmit(request);
+ }
}
-STATE_MACHINE_DECL(coa_no_reply)
+
+/** Process a request after the CoA has timed out.
+ *
+ * Run the packet through Post-Proxy-Type Fail
+ *
+ * \dot
+ * digraph coa_no_reply {
+ * coa_no_reply;
+ *
+ * coa_no_reply -> dup [ label = "DUP", arrowhead = "none" ];
+ * coa_no_reply -> timer [ label = "TIMER < max_request_time" ];
+ * coa_no_reply -> coa_reply_too_late [ label = "PROXY_REPLY" arrowhead = "none"];
+ * coa_no_reply -> process_proxy_reply [ label = "RUN" ];
+ * coa_no_reply -> done [ label = "TIMER >= timeout" ];
+ * }
+ * \enddot
+ */
+static void coa_no_reply(REQUEST *request, int action)
{
char buffer[128];
switch (action) {
case FR_ACTION_TIMER:
- request_max_time(request);
+ (void) request_max_time(request);
break;
case FR_ACTION_PROXY_REPLY: /* too late! */
}
}
-STATE_MACHINE_DECL(coa_running)
+
+/** Process the request after receiving a coa reply.
+ *
+ * Throught the post-proxy section, and the through the handler
+ * function.
+ *
+ * \dot
+ * digraph coa_running {
+ * coa_running;
+ *
+ * coa_running -> timer [ label = "TIMER < max_request_time" ];
+ * coa_running -> process_proxy_reply [ label = "RUN" ];
+ * coa_running -> done [ label = "TIMER >= timeout" ];
+ * }
+ * \enddot
+ */
+static void coa_running(REQUEST *request, int action)
{
VERIFY_REQUEST(request);
switch (action) {
case FR_ACTION_TIMER:
- request_max_time(request);
+ (void) request_max_time(request);
break;
case FR_ACTION_RUN:
int argval;
#endif
- if (debug_flag == 0) {
+ if (rad_debug_lvl == 0) {
if (just_started) {
INFO("Ready to process requests");
just_started = false;
rad_assert(sock != NULL);
if (just_started) {
DEBUG("Listening on %s", buffer);
+ } else {
+ INFO(" ... adding new socket %s", buffer);
+ }
#ifdef WITH_PROXY
- } else if (this->type == RAD_LISTEN_PROXY) {
+ if (!just_started && (this->type == RAD_LISTEN_PROXY)) {
home_server_t *home;
-
+
home = sock->home;
if (!home || !home->limit.max_connections) {
INFO(" ... adding new socket %s", buffer);
}
#endif
- } else {
- INFO(" ... adding new socket %s", buffer);
}
switch (this->type) {
#ifdef WITH_TCP
/*
- * Stop using this socket, if at all possible.
+ * 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) {
+ struct timeval when;
+ listen_socket_t *sock = this->data;
+
+ /*
+ * Try again to clean up the socket in 30
+ * seconds.
+ */
+ gettimeofday(&when, NULL);
+ when.tv_sec += 30;
+
+ ASSERT_MASTER;
+ if (!fr_event_insert(el,
+ (fr_event_callback_t) event_new_fd,
+ this, &when, &sock->ev)) {
+ rad_panic("Failed to insert event");
+ }
+
+ return 1;
+ }
+
+ fr_event_fd_delete(el, 0, this->fd);
+ this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
+ }
+
+ /*
+ * The socket has had a catastrophic error. Close it.
*/
if (this->status == RAD_LISTEN_STATUS_EOL) {
/*
#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.
+ * Tell all requests using this socket that the socket is dead.
*/
if (this->type == RAD_LISTEN_PROXY) {
PTHREAD_MUTEX_LOCK(&proxy_mutex);
fr_exit(1);
}
- fr_packet_list_walk(proxy_list, this, proxy_eol_cb);
+ if (this->count > 0) {
+ fr_packet_list_walk(proxy_list, this, proxy_eol_cb);
+ }
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
#endif
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;
+ if (!home_servers_udp) return;
+
+ /*
+ * We passed "-i" on the command line. Use that address
+ * family for the proxy socket.
+ */
+ if (main_config.myip.af != AF_UNSPEC) {
+ create_default_proxy_listener(main_config.myip.af);
+ 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
int radius_event_start(CONF_SECTION *cs, bool have_children)
{
request_num_counter = 0;
#ifdef WITH_PROXY
- if (main_config.proxy_requests) {
+ if (main_config.proxy_requests && !check_config) {
/*
* Create the tree for managing proxied requests and
* responses.
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.
- */
+ * At this point, no one has any business *ever* going
+ * back to root uid.
+ */
rad_suid_down_permanent();
return 1;