extern pid_t radius_pid;
extern int dont_fork;
extern int check_config;
-extern void force_log_reopen(void);
extern char *debug_condition;
/*
#ifdef WITH_PROXY
static pthread_mutex_t proxy_mutex;
static rad_listen_t *proxy_listener_list = NULL;
+static int proxy_no_new_sockets = FALSE;
#endif
#define PTHREAD_MUTEX_LOCK if (have_children) pthread_mutex_lock
*/
#define PTHREAD_MUTEX_LOCK(_x)
#define PTHREAD_MUTEX_UNLOCK(_x)
-int thread_pool_addrequest(REQUEST *request, RAD_REQUEST_FUNP fun)
-{
- radius_handle_request(request, fun);
- return 1;
-}
#endif
/*
static fr_packet_list_t *proxy_list = NULL;
static void remove_from_proxy_hash(REQUEST *request);
+static void check_for_zombie_home_server(REQUEST *request);
#else
#define remove_from_proxy_hash(foo)
#endif
tv->tv_usec += usec_delay;
if (tv->tv_usec > USEC) {
- tv->tv_usec -= USEC;
- tv->tv_sec++;
+ tv->tv_sec += tv->tv_usec / USEC;
+ tv->tv_usec %= USEC;
}
}
fr_packet_list_yank(pl, request->packet);
request->in_request_hash = FALSE;
+ /*
+ * FIXME: Move this to a "statistics" thread?
+ * Or (short term) add a mutex lock around it.
+ */
request_stats_final(request);
#ifdef WITH_TCP
#endif
}
+static void ev_request_free(REQUEST **prequest)
+{
+ REQUEST *request;
+
+ if (!prequest || !*prequest) return;
+
+ request = *prequest;
+
+#ifdef WITH_COA
+ if (request->coa) {
+ /*
+ * Divorce the child from the parent first,
+ * then clean up the child.
+ */
+ request->coa->parent = NULL;
+ ev_request_free(&request->coa);
+ }
+
+ /*
+ * Divorce the parent from the child, and leave the
+ * parent still alive.
+ */
+ if (request->parent && (request->parent->coa == request)) {
+ request->parent->coa = NULL;
+ }
+#endif
+
+ if (request->ev) fr_event_delete(el, &request->ev);
+#ifdef WITH_PROXY
+ if (request->in_proxy_hash) remove_from_proxy_hash(request);
+#endif
+ if (request->in_request_hash) remove_from_request_hash(request);
+
+ request_free(prequest);
+}
+
#ifdef WITH_PROXY
static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply)
{
}
request = fr_packet2myptr(REQUEST, proxy, proxy_p);
-
- if (!request) {
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- return NULL;
- }
-
- request->num_proxied_responses++;
-
- /*
- * Catch the most common case of everything working
- * correctly.
- */
- if (request->num_proxied_requests == request->num_proxied_responses) {
- remove_from_proxy_hash(request);
- }
+ request->num_proxied_responses++; /* needs to be protected by lock */
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
#endif /* WITH_PROXY */
-static void ev_request_free(REQUEST **prequest)
-{
- REQUEST *request;
-
- if (!prequest || !*prequest) return;
-
- request = *prequest;
-
-#ifdef WITH_COA
- if (request->coa) {
- /*
- * Divorce the child from the parent first,
- * then clean up the child.
- */
- request->coa->parent = NULL;
- ev_request_free(&request->coa);
- }
-
- /*
- * Divorce the parent from the child, and leave the
- * parent still alive.
- */
- if (request->parent && (request->parent->coa == request)) {
- request->parent->coa = NULL;
- }
-#endif
-
- if (request->ev) fr_event_delete(el, &request->ev);
-#ifdef WITH_PROXY
- if (request->in_proxy_hash) remove_from_proxy_hash(request);
-#endif
- if (request->in_request_hash) remove_from_request_hash(request);
-
- request_free(prequest);
-}
-
#ifdef WITH_TCP
static int remove_all_requests(void *ctx, void *data)
{
#ifdef WITH_PROXY
-static int proxy_id_alloc(REQUEST *request, RADIUS_PACKET *packet)
-{
- void *proxy_listener;
-
- if (fr_packet_list_id_alloc(proxy_list, request->home_server->proto,
- packet, &proxy_listener)) {
- request->proxy_listener = proxy_listener;
- return 1;
- }
-
- if (!proxy_new_listener(request->home_server, 0)) {
- RDEBUG2("ERROR: Failed to create a new socket for proxying requests.");
- return 0;
- }
-
- if (!fr_packet_list_id_alloc(proxy_list, request->home_server->proto,
- packet, &proxy_listener)) {
- RDEBUG2("ERROR: Failed allocating Id for new socket when proxying requests.");
- return 0;
- }
-
- request->proxy_listener = proxy_listener;
- return 1;
-}
-
-
-static int insert_into_proxy_hash(REQUEST *request, int retransmit)
+static int insert_into_proxy_hash(REQUEST *request)
{
char buf[128];
+ int rcode, tries;
+ void *proxy_listener;
rad_assert(request->proxy != NULL);
rad_assert(proxy_list != NULL);
+ tries = 1;
+retry:
PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ rcode = fr_packet_list_id_alloc(proxy_list,
+ request->home_server->proto,
+ request->proxy, &proxy_listener);
+ request->num_proxied_requests = 1;
+ request->num_proxied_responses = 0;
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+ if (!rcode) {
+ if (proxy_no_new_sockets) return 0;
- if (!retransmit) {
- if (!proxy_id_alloc(request, request->proxy)) {
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ /*
+ * Also locks the proxy mutex, so we have to call
+ * it with the mutex unlocked. Some systems
+ * don't support recursive mutexes.
+ */
+ if (!proxy_new_listener(request->home_server, 0)) {
+ radlog(L_ERR, "Failed to create a new socket for proxying requests.");
return 0;
}
- } else {
- RADIUS_PACKET packet;
+ request->proxy->src_port = 0; /* Use any new socket */
-#ifdef WITH_TCP
- rad_assert(request->home_server->proto != IPPROTO_TCP);
-#endif
-
- packet = *request->proxy;
-
- if (!proxy_id_alloc(request, &packet)) {
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ tries++;
+ if (tries > 2) {
+ RDEBUG2("ERROR: Failed allocating Id for new socket when proxying requests.");
return 0;
}
-
- /*
- * Yank the request, free the old Id, and
- * remember the new Id.
- */
- fr_packet_list_yank(proxy_list, request->proxy);
- fr_packet_list_id_free(proxy_list, request->proxy);
- *request->proxy = packet;
+
+ goto retry;
}
+ request->proxy_listener = proxy_listener;
+
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
if (!fr_packet_list_insert(proxy_list, &request->proxy)) {
fr_packet_list_id_free(proxy_list, request->proxy);
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- RDEBUG2("ERROR: Failed to insert entry into proxy list");
+ radlog(L_PROXY, "Failed to insert entry into proxy list.");
return 0;
}
rad_assert(request->magic == REQUEST_MAGIC);
rad_assert(request->proxy != NULL);
- if (!fr_event_now(el, &now)) gettimeofday(&now, NULL);
+ fr_event_now(el, &now);
request->when = request->proxy_when;
#ifdef WITH_COA
#endif
timercmp(&now, &request->when, >)) {
if (request->packet) {
- RDEBUG2("Cleaning up request %d ID %d with timestamp +%d",
- request->number, request->packet->id,
+ RDEBUG2("Cleaning up request packet ID %d with timestamp +%d",
+ request->packet->id,
(unsigned int) (request->timestamp - fr_start_time));
} else {
- RDEBUG2("Cleaning up request %d with timestamp +%d",
- request->number,
+ RDEBUG2("Cleaning up request with timestamp +%d",
(unsigned int) (request->timestamp - fr_start_time));
}
REQUEST *request = ctx;
rad_assert(request->magic == REQUEST_MAGIC);
+ remove_from_request_hash(request);
+ /*
+ * If it's still queued (waiting for a thread to pick it
+ * up) OR, it's running AND there's still a child thread
+ * handling it, THEN delay some more.
+ */
if ((request->child_state == REQUEST_QUEUED) ||
- (request->child_state == REQUEST_RUNNING)) {
- request->delay += (request->delay >> 1);
- tv_add(&request->when, request->delay);
+ ((request->child_state == REQUEST_RUNNING) &&
+ (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0))) {
- RDEBUG2("Child is still stuck for request %d", request->number);
+ /*
+ * Cap delay at max_request_time
+ */
+ if (request->delay < (USEC * request->root->max_request_time)) {
+ request->delay += (request->delay >> 1);
+ radlog_request(L_INFO, 0, request, "WARNING: Child is hung in component %s module %s.",
+ request->component, request->module);
+ } else {
+ request->delay = USEC * request->root->max_request_time;
+ RDEBUG2("WARNING: Child is hung after \"max_request_time\" for request %u",
+ request->number);
+ }
+ tv_add(&request->when, request->delay);
INSERT_EVENT(wait_for_child_to_die, request);
return;
}
- RDEBUG2("Child is responsive for request %d", request->number);
- remove_from_request_hash(request);
+ RDEBUG2("Child is finally responsive");
#ifdef WITH_PROXY
if (request->proxy) {
}
#endif
- RDEBUG2("Cleaning up request %d ID %d with timestamp +%d",
- request->number, request->packet->id,
+ RDEBUG2("Cleaning up request packet ID %d with timestamp +%d",
+ request->packet->id,
(unsigned int) (request->timestamp - fr_start_time));
ev_request_free(&request);
rad_assert(request->magic == REQUEST_MAGIC);
rad_assert(request->child_state == REQUEST_REJECT_DELAY);
- RDEBUG2("Sending delayed reject for request %d", request->number);
+ RDEBUG2("Sending delayed reject");
DEBUG_PACKET(request, request->reply, 1);
*/
if (home->ev) fr_event_delete(el, &home->ev);
- radlog(L_INFO, "PROXY: Marking home server %s port %d alive again... we have no idea if it really is alive or not.",
+ radlog(L_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.ipaddr,
buffer, sizeof(buffer)),
home->port);
home->num_received_pings = 0;
- RDEBUG2("No response to status check %d from home server %s port %d",
+ radlog(L_ERR, "No response to status check %d for home server %s port %d",
request->number,
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
request->proxy->dst_port);
+ check_for_zombie_home_server(request);
+
wait_for_proxy_id_to_expire(request);
}
+/*
+ * Note that we don't care what the value of the code field is.
+ * If the response has a valid (src ip/port, dst ip/port), id,
+ * and correctly signed Message-Authenticator, that's good
+ * enough.
+ */
static void received_response_to_ping(REQUEST *request)
{
home_server *home;
home->num_received_pings++;
- RDEBUG2("Received response to status check %d (%d in current sequence)",
+ radlog(L_PROXY, "Received response to status check %d (%d in current sequence)",
request->number, home->num_received_pings);
/*
RDEBUG2("Hmm... no event for home server. Oh well.");
}
- radlog(L_INFO, "PROXY: Marking home server %s port %d alive",
+ radlog(L_PROXY, "Marking home server %s port %d alive",
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
rad_assert(request->proxy_listener == NULL);
- if (!insert_into_proxy_hash(request, FALSE)) {
- RDEBUG2("ERROR: Failed inserting status check %d into proxy hash. Discarding it.",
+ if (!insert_into_proxy_hash(request)) {
+ radlog(L_PROXY, "Failed to insert status check %d into proxy list. Discarding it.",
request->number);
ev_request_free(&request);
return;
int previous_state = home->state;
char buffer[128];
- radlog(L_INFO, "PROXY: Marking home server %s port %d as dead.",
+ radlog(L_PROXY, "Marking home server %s port %d as dead.",
inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
buffer, sizeof(buffer)),
home->port);
request->child_state = REQUEST_QUEUED;
rad_assert(request->proxy != NULL);
- if (!thread_pool_addrequest(request, virtual_server_handler)) {
- request->child_state = REQUEST_DONE;
- }
+ thread_pool_addrequest(request, virtual_server_handler);
#ifdef HAVE_PTHREAD_H
/*
request->child_state = REQUEST_RUNNING;
if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
- dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Authentication");
+ dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Authentication");
} else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
- dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Accounting");
+ dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Accounting");
#ifdef WITH_COA
/*
request->packet->code &= 0xff; /* restore it */
if (request->proxy->code == PW_COA_REQUEST) {
- dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-CoA");
+ dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-CoA");
} else if (request->proxy->code == PW_DISCONNECT_REQUEST) {
- dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Disconnect");
+ dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Disconnect");
} else {
return 0;
}
return 0;
}
- if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail");
+ if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail");
if (!dval) {
- pairdelete(&request->config_items, PW_POST_PROXY_TYPE);
+ pairdelete(&request->config_items, PW_POST_PROXY_TYPE, 0);
return 0;
}
- vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
+ vp = pairfind(request->config_items, PW_POST_PROXY_TYPE, 0);
if (!vp) vp = radius_paircreate(request, &request->config_items,
- PW_POST_PROXY_TYPE, PW_TYPE_INTEGER);
+ PW_POST_PROXY_TYPE, 0, PW_TYPE_INTEGER);
vp->vp_integer = dval->value;
rad_assert(request->proxy_reply == NULL);
#endif
check_for_zombie_home_server(request);
+ home = request->home_server;
+
/*
* The default as of 2.1.7 is to allow requests to
* fail-over to a backup home server when this one does
* well.
*/
if (home->no_response_fail) {
- radlog(L_ERR, "Rejecting request %d (proxy Id %d) due to lack of any response from home server %s port %d",
- request->number, request->proxy->id,
+ radlog_request(L_ERR, 0, request, "Rejecting request (proxy Id %d) due to lack of any response from home server %s port %d",
+ request->proxy->id,
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
request->proxy->dst_port);
post_proxy_fail_handler(request);
+ } else {
+ /*
+ * Enforce max_request_time.
+ *
+ * We fail over to another backup home server
+ * when the client re-transmits the request. If
+ * the client doesn't re-transmit, no fail-over
+ * occurs.
+ */
+ rad_assert(request->ev == NULL);
+ request->child_state = REQUEST_RUNNING;
+ wait_a_bit(request);
}
- home = request->home_server;
-
/*
* Don't touch request due to race conditions
*/
}
#endif
- if (home->state == HOME_STATE_IS_DEAD) {
- rad_assert(home->ev != NULL); /* or it will never wake up */
+ /*
+ * If it's not alive, don't try to make it a zombie.
+ */
+ if (home->state != HOME_STATE_ALIVE) {
+ /*
+ * Don't check home->ev due to race conditions.
+ */
return;
}
/*
- * Enable the zombie period when we notice that the home
- * server hasn't responded. We do NOT back-date the start
- * of the zombie period.
+ * 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.
*/
- if (home->state == HOME_STATE_ALIVE) {
- home->state = HOME_STATE_ZOMBIE;
- home->zombie_period_start = now;
-
- radlog(L_ERR, "PROXY: Marking home server %s port %d as zombie (it looks like it is dead).",
- inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- home->port);
-
- /*
- * Start pinging the home server.
- */
- ping_home_server(home);
+ if ((home->last_packet + ((home->zombie_period + 3) / 4)) >= now.tv_sec) {
+ return;
}
+
+ /*
+ * Enable the zombie period when we notice that the home
+ * server hasn't responded for a while. We back-date the
+ * zombie period to when we last received a response from
+ * the home server.
+ */
+ home->state = HOME_STATE_ZOMBIE;
+
+ home->zombie_period_start.tv_sec = home->last_packet;
+ home->zombie_period_start.tv_sec = USEC / 2;
+
+ fr_event_delete(el, &home->ev);
+ home->currently_outstanding = 0;
+ home->num_received_pings = 0;
+
+ radlog(L_PROXY, "Marking home server %s port %d as zombie (it looks like it is dead).",
+ inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ home->port);
+
+ /*
+ * Start pinging the home server.
+ */
+ ping_home_server(home);
}
#endif
rad_assert(request->magic == REQUEST_MAGIC);
+#ifdef HAVE_PTHREAD_H
/*
* The socket was closed. Tell the request that
* there is no point in continuing.
if (request->listener->status != RAD_LISTEN_STATUS_KNOWN) {
goto stop_processing;
}
+#endif
#ifdef WITH_COA
/*
switch (request->child_state) {
case REQUEST_QUEUED:
case REQUEST_RUNNING:
+ /*
+ * If we're not thread-capable, OR we're capable,
+ * but have been told to run without threads,
+ * complain when the requests is queued for a
+ * thread, or running in a child thread.
+ */
+#ifdef HAVE_PTHREAD_H
+ if (!have_children)
+#endif
+ {
+ rad_assert("We do not have threads, but the request is marked as queued or running in a child thread" == NULL);
+ break;
+ }
+
+#ifdef HAVE_PTHREAD_H
+ /*
+ * If we have threads, wait for the child thread
+ * to stop.
+ */
when = request->received;
when.tv_sec += request->root->max_request_time;
* Request still has more time. Continue
* waiting.
*/
- if (timercmp(&now, &when, <) ||
- ((request->listener->type == RAD_LISTEN_DETAIL) &&
- (request->child_state == REQUEST_QUEUED))) {
+ if (timercmp(&now, &when, <)) {
if (request->delay < (USEC / 10)) {
request->delay = USEC / 10;
}
request->delay += request->delay >> 1;
-#ifdef WITH_DETAIL
/*
- * Cap wait at some sane value for detail
- * files.
+ * Cap delays at something reasonable.
*/
- if ((request->listener->type == RAD_LISTEN_DETAIL) &&
- (request->delay > (request->root->max_request_time * USEC))) {
+ if (request->delay > (request->root->max_request_time * USEC)) {
request->delay = request->root->max_request_time * USEC;
}
-#endif
request->when = now;
tv_add(&request->when, request->delay);
break;
}
-#if defined(HAVE_PTHREAD_H) || defined(WITH_PROXY)
+ stop_processing:
+ request->master_state = REQUEST_STOP_PROCESSING;
+
/*
* A child thread MAY still be running on the
* request. Ask the thread to stop working on
* the request.
*/
- if (have_children) {
- /* FIXME: kill unresponsive children? */
-
- /*
- * Print this error message ONLY if
- * there's a child currently processing
- * the request. As we don't have thread
- * locks here, there may be race
- * conditions on this check. But it's
- * just an error message, so that's OK.
- */
- if (!pthread_equal(request->child_pid, NO_SUCH_CHILD_PID)) {
- radlog(L_ERR, "WARNING: Unresponsive child for request %d, in module %s component %s",
- request->number,
- request->module ? request->module : "<server core>",
- request->component ? request->component : "<server core>");
- }
+ if (have_children &&
+ (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0)) {
+ radlog(L_ERR, "WARNING: Unresponsive child for request %u, in component %s module %s",
+ request->number,
+ request->component ? request->component : "<server core>",
+ request->module ? request->module : "<server core>");
- stop_processing:
- request->master_state = REQUEST_STOP_PROCESSING;
-
- request->delay = USEC / 4;
- tv_add(&request->when, request->delay);
- callback = wait_for_child_to_die;
- break;
}
-#else /* no child threads */
- stop_processing:
+
+ request->delay = USEC;
+ tv_add(&request->when, request->delay);
+ callback = wait_for_child_to_die;
+ break;
#endif
/*
- * Else there are no child threads. We probably
- * should have just marked the request as 'done'
- * elsewhere, like in the post-proxy-fail
- * handler. But doing that would involve
- * checking for max_request_time in multiple
- * places, so this may be simplest.
- */
- request->child_state = REQUEST_DONE;
- /* FALL-THROUGH */
-
- /*
* Mark the request as no longer running,
* and clean it up.
*/
request->child_pid = NO_SUCH_CHILD_PID;
#endif
-#ifdef WTH_COA
+#ifdef WITH_COA
/*
* This is a CoA request. It's been divorced
* from everything else, so we clean it up now.
return;
}
#endif
- request_stats_final(request);
cleanup_delay(request);
return;
#ifdef HAVE_PTHREAD_H
request->child_pid = NO_SUCH_CHILD_PID;
#endif
- request_stats_final(request);
case REQUEST_PROXIED:
rad_assert(request->next_callback != NULL);
* mode, with no threads...
*/
if (!callback) {
- RDEBUG("WARNING: Internal sanity check failed in event handler for request %d: Discarding the request!", request->number);
+ RDEBUG("WARNING: Internal sanity check failed in event handler: Discarding the request!");
ev_request_free(&request);
return;
}
{
VALUE_PAIR *vp;
- vp = pairfind(packet->vps, PW_EVENT_TIMESTAMP);
+ vp = pairfind(packet->vps, PW_EVENT_TIMESTAMP, 0);
if (!vp) return 0;
vp->vp_date = when;
}
if (update_event_timestamp(request->proxy, now.tv_sec)) {
- if (!insert_into_proxy_hash(request, TRUE)) {
- DEBUG("ERROR: Failed re-inserting CoA request into proxy hash.");
+ /*
+ * Keep a copy of the old Id so that the
+ * re-transmitted request doesn't re-use the old
+ * Id.
+ */
+ RADIUS_PACKET old = *request->proxy;
+ home_server *home = request->home_server;
+ rad_listen_t *listener = request->proxy_listener;
+
+ /*
+ * Don't free the old Id on error.
+ */
+ if (!insert_into_proxy_hash(request)) {
+ radlog(L_PROXY,"Failed to insert retransmission of CoA request into proxy list.");
return;
}
- request->num_proxied_requests = 0;
- request->num_proxied_responses = 0;
+ /*
+ * Now that we have a new Id, free the old one
+ * and update the various statistics.
+ */
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ fr_packet_list_yank(proxy_list, &old);
+ fr_packet_list_id_free(proxy_list, &old);
+ if (home) home->currently_outstanding--;
+#ifdef WITH_TCP
+ if (listener) listener->count--;
+#endif
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+ } else { /* FIXME: protect by a mutex? */
+ request->num_proxied_requests++;
}
- request->num_proxied_requests++;
request->num_coa_requests++; /* is NOT reset by code 3 lines above! */
request->proxy_listener->send(request->proxy_listener,
rad_assert(!request->in_proxy_hash);
rad_assert(request->proxy_reply == NULL);
- vp = pairfind(request->config_items, PW_SEND_COA_REQUEST);
- if (!vp && request->coa) vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST);
+ /*
+ * Check whether we want to originate one, or cancel one.
+ */
+ vp = pairfind(request->config_items, PW_SEND_COA_REQUEST, 0);
+ if (!vp && request->coa) {
+ vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0);
+ }
+
if (vp) {
if (vp->vp_integer == 0) {
ev_request_free(&request->coa);
return 1; /* success */
}
-
- if (!request->coa) request_alloc_coa(request);
- if (!request->coa) return 0;
}
+ if (!request->coa) request_alloc_coa(request);
+ if (!request->coa) return 0;
+
coa = request->coa;
/*
* src_ipaddr will be set up in proxy_encode.
*/
memset(&ipaddr, 0, sizeof(ipaddr));
- vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS);
+ vp = pairfind(coa->proxy->vps, PW_PACKET_DST_IP_ADDRESS, 0);
if (vp) {
ipaddr.af = AF_INET;
ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
} else if ((vp = pairfind(coa->proxy->vps,
- PW_PACKET_DST_IPV6_ADDRESS)) != NULL) {
+ PW_PACKET_DST_IPV6_ADDRESS, 0)) != NULL) {
ipaddr.af = AF_INET6;
ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
} else if ((vp = pairfind(coa->proxy->vps,
- PW_HOME_SERVER_POOL)) != NULL) {
+ PW_HOME_SERVER_POOL, 0)) != NULL) {
coa->home_pool = home_pool_byname(vp->vp_strvalue,
HOME_TYPE_COA);
if (!coa->home_pool) {
} else if (!coa->home_server) {
int port = PW_COA_UDP_PORT;
- vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT);
+ vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT, 0);
if (vp) port = vp->vp_integer;
- coa->home_server = home_server_find(&ipaddr, port);
+ coa->home_server = home_server_find(&ipaddr, port, IPPROTO_UDP);
if (!coa->home_server) {
RDEBUG2("WARNING: Unknown destination %s:%d for CoA request.",
inet_ntop(ipaddr.af, &ipaddr.ipaddr,
}
}
- vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE);
+ vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE, 0);
if (vp) {
switch (vp->vp_integer) {
case PW_COA_REQUEST:
/*
* Call the pre-proxy routines.
*/
- vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
+ vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0);
if (vp) {
RDEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue);
pre_proxy_type = vp->vp_integer;
coa->proxy->dst_ipaddr = coa->home_server->ipaddr;
coa->proxy->dst_port = coa->home_server->port;
- if (!insert_into_proxy_hash(coa, FALSE)) {
- DEBUG("ERROR: Failed inserting CoA request into proxy hash.");
+ if (!insert_into_proxy_hash(coa)) {
+ radlog(L_PROXY, "Failed to insert CoA request into proxy list.");
goto fail;
}
* another thread may have picked it up. Don't
* touch it!
*/
- request->num_proxied_requests = 1;
- request->num_proxied_responses = 0;
request->child_pid = NO_SUCH_CHILD_PID;
update_event_timestamp(request->proxy, request->proxy_when.tv_sec);
* Run the packet through the post-proxy stage,
* BEFORE playing games with the attributes.
*/
- vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
+ vp = pairfind(request->config_items, PW_POST_PROXY_TYPE, 0);
if (vp) {
RDEBUG2(" Found Post-Proxy-Type %s", vp->vp_strvalue);
post_proxy_type = vp->vp_integer;
* the reply. These include Proxy-State
* attributes from us and remote server.
*/
- pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
+ pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE, 0);
/*
* Add the attributes left in the proxy
*/
if (request->packet->dst_port == 0) {
request->username = pairfind(request->packet->vps,
- PW_USER_NAME);
+ PW_USER_NAME, 0);
request->password = pairfind(request->packet->vps,
- PW_USER_PASSWORD);
+ PW_USER_PASSWORD, 0);
return 1;
}
* Put the decoded packet into it's proper place.
*/
if (request->proxy_reply != NULL) {
+ rcode = request->proxy_listener->decode(request->proxy_listener, request);
+ DEBUG_PACKET(request, request->proxy_reply, 0);
+
/*
- * FIXME: For now, we can only proxy RADIUS packets.
- *
- * In order to proxy other packets, we need to
- * somehow cache the "decode" function.
+ * Pro-actively remove it from the proxy hash.
+ * This is later than in 2.1.x, but it means that
+ * the replies are authenticated before being
+ * removed from the hash.
*/
- rcode = rad_decode(request->proxy_reply, request->proxy,
- request->home_server->secret);
- DEBUG_PACKET(request, request->proxy_reply, 0);
+ if ((rcode == 0) &&
+ (request->num_proxied_requests <= request->num_proxied_responses)) {
+ remove_from_proxy_hash(request);
+ }
+
} else
#endif
if (request->packet->vps == NULL) {
}
if (rcode < 0) {
- radlog(L_ERR, "%s Dropping packet without response.", fr_strerror());
+ RDEBUG("%s Dropping packet without response.", fr_strerror());
+ request->reply->offset = -2; /* bad authenticator */
request->child_state = REQUEST_DONE;
return 0;
}
if (!request->username) {
request->username = pairfind(request->packet->vps,
- PW_USER_NAME);
+ PW_USER_NAME, 0);
}
#ifdef WITH_PROXY
if (request->proxy) {
return process_proxy_reply(request);
-#endif
}
+#endif
return 1;
}
struct timeval when;
char buffer[128];
+#ifdef WITH_COA
+ if (request->coa) {
+ RDEBUG("WARNING: Cannot proxy and originate CoA packets at the same time. Cancelling CoA request");
+ ev_request_free(&request->coa);
+ }
+#endif
+
if (request->home_server->server) {
- RDEBUG("ERROR: Cannot perform real proxying to a virtual server.");
+ RDEBUG("ERROR: Cannot proxy to a virtual server.");
return 0;
}
- if (!insert_into_proxy_hash(request, FALSE)) {
- RDEBUG("ERROR: Failed inserting request into proxy hash.");
+ if (!insert_into_proxy_hash(request)) {
+ radlog(L_PROXY, "Failed to insert request into proxy list.");
return 0;
}
}
request->next_callback = no_response_to_proxied_request;
- RDEBUG2("Proxying request %d to home server %s port %d",
- request->number,
+ RDEBUG2("Proxying request to home server %s port %d",
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
* another thread may have picked it up. Don't
* touch it!
*/
- request->num_proxied_requests = 1;
- request->num_proxied_responses = 0;
#ifdef HAVE_PTHREAD_H
request->child_pid = NO_SUCH_CHILD_PID;
#endif
return 0;
}
- realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
+ realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM, 0);
if (!realmpair || (realmpair->length == 0)) {
int pool_type;
- vp = pairfind(request->config_items, PW_HOME_SERVER_POOL);
+ vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0);
if (!vp) return 0;
switch (request->packet->code) {
* requests.
*/
if (realm && (realm->striprealm == TRUE) &&
- (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
+ (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME, 0)) != 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);
+ vp = pairfind(request->proxy->vps, PW_USER_NAME, 0);
if (!vp) {
vp = radius_paircreate(request, NULL,
- PW_USER_NAME, PW_TYPE_STRING);
+ PW_USER_NAME, 0, PW_TYPE_STRING);
rad_assert(vp != NULL); /* handled by above function */
/* Insert at the START of the list */
vp->next = request->proxy->vps;
* anymore - we changed it.
*/
if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
- pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
- pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
+ pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0) &&
+ pairfind(request->proxy->vps, PW_CHAP_CHALLENGE, 0) == NULL) {
vp = radius_paircreate(request, &request->proxy->vps,
- PW_CHAP_CHALLENGE, PW_TYPE_OCTETS);
+ PW_CHAP_CHALLENGE, 0, PW_TYPE_OCTETS);
vp->length = AUTH_VECTOR_LEN;
memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
}
* doesn't need it.
*/
vp = radius_paircreate(request, &request->proxy->vps,
- PW_PROXY_STATE, PW_TYPE_OCTETS);
+ PW_PROXY_STATE, 0, PW_TYPE_OCTETS);
snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%d",
request->packet->id);
vp->length = strlen(vp->vp_strvalue);
/*
* Call the pre-proxy routines.
*/
- vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
+ vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0);
if (vp) {
RDEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue);
pre_proxy_type = vp->vp_integer;
}
if (!proxy_request(request)) {
- RDEBUG("ERROR: Failed to proxy request %d", request->number);
+ RDEBUG("ERROR: Failed to proxy request");
return -1;
}
if ((request->master_state == REQUEST_STOP_PROCESSING) ||
(request->parent &&
(request->parent->master_state == REQUEST_STOP_PROCESSING))) {
- RDEBUG2("Request %d was cancelled.", request->number);
+ RDEBUG2("request was cancelled.");
#ifdef HAVE_PTHREAD_H
request->child_pid = NO_SUCH_CHILD_PID;
#endif
*/
if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
(request->reply->code == 0) &&
- ((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
+ ((vp = pairfind(request->config_items, PW_AUTH_TYPE, 0)) != NULL) &&
(vp->vp_integer == PW_AUTHTYPE_REJECT)) {
request->reply->code = PW_AUTHENTICATION_REJECT;
}
* OR we proxied it internally to a virutal server.
*/
}
+
+#ifdef WITH_COA
+ else if (request->proxy && request->coa) {
+ RDEBUG("WARNING: Cannot proxy and originate CoA packets at the same time. Cancelling CoA request");
+ ev_request_free(&request->coa);
+ }
+#endif
#endif
/*
/*
* Copy Proxy-State from the request to the reply.
*/
- vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
+ vp = paircopy2(request->packet->vps, PW_PROXY_STATE, 0);
if (vp) pairadd(&request->reply->vps, vp);
#endif
* Check if the lack of response is intentional.
*/
vp = pairfind(request->config_items,
- PW_RESPONSE_PACKET_TYPE);
+ PW_RESPONSE_PACKET_TYPE, 0);
if (!vp) {
- RDEBUG2("There was no response configured: rejecting request %d",
- request->number);
+ RDEBUG2("There was no response configured: rejecting request");
request->reply->code = PW_AUTHENTICATION_REJECT;
} else if (vp->vp_integer == 256) {
- RDEBUG2("Not responding to request %d",
- request->number);
+ RDEBUG2("Not responding to request");
/*
* Force cleanup after a long
* Post-Auth-Type = Reject
*/
if (request->reply->code == PW_AUTHENTICATION_REJECT) {
- pairdelete(&request->config_items, PW_POST_AUTH_TYPE);
+ pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0);
vp = radius_pairmake(request, &request->config_items,
"Post-Auth-Type", "Reject",
T_OP_SET);
when.tv_sec += request->root->reject_delay;
if (timercmp(&when, &request->next_when, >)) {
- RDEBUG2("Delaying reject of request %d for %d seconds",
- request->number,
+ RDEBUG2("Delaying reject for %d seconds",
request->root->reject_delay);
request->next_when = when;
request->next_callback = reject_delay;
* and it should re-send it.
* If configured, encode, sign, and send.
*/
- if ((request->reply->code != 0) ||
- (request->listener->type == RAD_LISTEN_DETAIL)) {
+ if ((request->reply->code != 0)
+#ifdef WITH_DETAIL
+ || (request->listener->type == RAD_LISTEN_DETAIL)
+#endif
+ ) {
DEBUG_PACKET(request, request->reply, 1);
request->listener->send(request->listener, request);
}
#ifdef WITH_COA
/*
* Now that we've completely processed the request,
- * see if we need to originate a CoA request.
+ * see if we need to originate a CoA request. But ONLY
+ * if it wasn't proxied.
*/
- if (request->coa ||
- (pairfind(request->config_items, PW_SEND_COA_REQUEST) != NULL)) {
+ if (!request->proxy &&
+ (request->packet->code != PW_COA_REQUEST) &&
+ (request->packet->code != PW_DISCONNECT_REQUEST) &&
+ (request->coa ||
+ (pairfind(request->config_items, PW_SEND_COA_REQUEST, 0) != NULL))) {
if (!originated_coa_request(request)) {
RDEBUG2("Do CoA Fail handler here");
}
}
#endif
- RDEBUG2("Finished request %d.", request->number);
+ RDEBUG2("Finished request.");
rad_assert(child_state >= 0);
request->child_state = child_state;
}
-static void received_retransmit(REQUEST *request, const RADCLIENT *client)
-{
#ifdef WITH_PROXY
- char buffer[128];
+static void rad_retransmit_packet(REQUEST *request)
+{
+ char buffer[256];
+
+#ifdef WITH_TCP
+ if (request->home_server->proto == IPPROTO_TCP) {
+ DEBUG2("Suppressing duplicate proxied request to home server %s port %d proto TCP - ID: %d",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port,
+ request->proxy->id);
+ return; /* don't do anything else */
+ }
+#endif
+
+ RDEBUG2("Sending duplicate proxied 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->num_proxied_requests++;
+
+ DEBUG_PACKET(request, request->proxy, 1);
+ request->proxy_listener->send(request->proxy_listener,
+ request);
+}
+
+
+static int rad_retransmit(REQUEST *request)
+{
+ /*
+ * If we've just discovered that the home server
+ * is dead, OR the socket has been closed, look for
+ * another connection to a home server.
+ */
+ if ((request->home_server->state == HOME_STATE_IS_DEAD) ||
+ (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
+ home_server *home;
+
+ remove_from_proxy_hash(request);
+
+ home = home_server_ldb(NULL, request->home_pool, request);
+ if (!home) {
+ RDEBUG2("ERROR: Failed to find live home server for request");
+ no_home_servers:
+ /*
+ * Do post-request processing,
+ * and any insertion of necessary
+ * events.
+ */
+ post_proxy_fail_handler(request);
+ return 1;
+ }
+
+ request->proxy->code = request->packet->code;
+
+ /*
+ * Free the old packet, to force re-encoding
+ */
+ free(request->proxy->data);
+ request->proxy->data = NULL;
+ request->proxy->data_len = 0;
+
+ /*
+ * This request failed over to a virtual
+ * server. Push it back onto the queue
+ * to be processed.
+ */
+ if (request->home_server->server) {
+ proxy_fallback_handler(request);
+ return 1;
+ }
+
+ /*
+ * Try to proxy the request.
+ */
+ if (!proxy_request(request)) {
+ RDEBUG("ERROR: Failed to re-proxy request");
+ goto no_home_servers;
+ }
+ return 1;
+ } /* else the home server is still alive */
+
+ rad_retransmit_packet(request);
+
+ return 1;
+}
#endif
+static void received_retransmit(REQUEST *request, const RADCLIENT *client)
+{
+
RAD_STATS_TYPE_INC(request->listener, total_dup_requests);
RAD_STATS_CLIENT_INC(request->listener, client, total_dup_requests);
discard:
#endif
radlog(L_ERR, "Discarding duplicate request from "
- "client %s port %d - ID: %d due to unfinished request %d",
+ "client %s port %d - ID: %d due to unfinished request %u",
client->shortname,
request->packet->src_port,request->packet->id,
request->number);
check_for_zombie_home_server(request);
/*
- * If we've just discovered that the home server
- * is dead, OR the socket has been closed, look for
- * another connection to a home server.
+ * Home server is still alive, and the proxy
+ * socket is OK. Just re-send the packet.
*/
- if (((request->packet->dst_port != 0) &&
- (request->home_server->state == HOME_STATE_IS_DEAD)) ||
- (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
- home_server *home;
-
- remove_from_proxy_hash(request);
-
- home = home_server_ldb(NULL, request->home_pool, request);
- if (!home) {
- RDEBUG2("Failed to find live home server for request %d", request->number);
- no_home_servers:
- /*
- * Do post-request processing,
- * and any insertion of necessary
- * events.
- */
- post_proxy_fail_handler(request);
- return;
- }
-
- request->proxy->code = request->packet->code;
-
- /*
- * Free the old packet, to force re-encoding
- */
- free(request->proxy->data);
- request->proxy->data = NULL;
- request->proxy->data_len = 0;
-
- /*
- * This request failed over to a virtual
- * server. Push it back onto the queue
- * to be processed.
- */
- if (request->home_server->server) {
- proxy_fallback_handler(request);
- return;
- }
-
- /*
- * Try to proxy the request.
- */
- if (!proxy_request(request)) {
- RDEBUG("ERROR: Failed to re-proxy request %d", request->number);
- goto no_home_servers;
- }
-
- /*
- * This code executes in the main server
- * thread, so there's no need for locking.
- */
- rad_assert(request->next_callback != NULL);
- INSERT_EVENT(request->next_callback, request);
- request->next_callback = NULL;
- return;
- } /* else the home server is still alive */
-
-#ifdef WITH_TCP
- if (request->home_server->proto == IPPROTO_TCP) {
- DEBUG2("Suppressing duplicate proxied request to home server %s port %d proto TCP - ID: %d",
- inet_ntop(request->proxy->dst_ipaddr.af,
- &request->proxy->dst_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- request->proxy->dst_port,
- request->proxy->id);
+ if ((request->home_server->state != HOME_STATE_IS_DEAD) &&
+ (request->proxy_listener->status == RAD_LISTEN_STATUS_KNOWN)) {
+ rad_retransmit_packet(request);
break;
}
-#endif
- RDEBUG2("Sending duplicate proxied 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->num_proxied_requests++;
+ /*
+ * Otherwise, we need to fail over to another
+ * home server, and possibly run "post-proxy-type
+ * fail". Add an event waiting for the child to
+ * have a result.
+ */
+ INSERT_EVENT(wait_a_bit, request);
- DEBUG_PACKET(request, request->proxy, 1);
- request->proxy_listener->send(request->proxy_listener,
- request);
+ request->priority = RAD_LISTEN_PROXY;
+ thread_pool_addrequest(request, rad_retransmit);
break;
#endif
const RADCLIENT *client)
{
radlog(L_ERR, "Received conflicting packet from "
- "client %s port %d - ID: %d due to unfinished request %d. Giving up on old request.",
+ "client %s port %d - ID: %d due to unfinished request %u. Giving up on old request.",
client->shortname,
request->packet->src_port, request->packet->id,
request->number);
*/
if ((request->reply->code != 0) &&
request->reply->data) {
- radlog(L_INFO, "WARNING: Allowing fast client %s port %d - ID: %d for recent request %d.",
+ radlog(L_INFO, "WARNING: Allowing fast client %s port %d - ID: %d for recent request %u.",
client->shortname,
packet->src_port, packet->id,
request->number);
*/
if (timercmp(&when, &request->received, <)) {
radlog(L_ERR, "Discarding conflicting packet from "
- "client %s port %d - ID: %d due to recent request %d.",
+ "client %s port %d - ID: %d due to recent request %u.",
client->shortname,
packet->src_port, packet->id,
request->number);
/*
* We may want to quench the new request.
*/
- if ((listener->type != RAD_LISTEN_DETAIL) &&
+ if (
+#ifdef WITH_DETAIL
+ (listener->type != RAD_LISTEN_DETAIL) &&
+#endif
!can_handle_new_request(packet, client, root)) {
return 0;
}
if ((request->reply = rad_alloc(0)) == NULL) {
radlog(L_ERR, "No memory");
- exit(1);
+ return 0;
}
request->listener = listener;
* Remember the request in the list.
*/
if (!fr_packet_list_insert(pl, &request->packet)) {
- radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", request->number);
+ radlog(L_ERR, "Failed to insert request %u in the list of live requests: discarding", request->number);
ev_request_free(&request);
return 0;
}
REQUEST *request;
/*
- * Also removes from the proxy hash if responses == requests
+ * Lookup *without* removal. In versions prior to 2.2.0,
+ * this did lookup *and* removal. That method allowed
+ * attackers to spoof replies that caused entries to be
+ * removed from the proxy hash prior to validation.
*/
request = lookup_in_proxy_hash(packet);
}
/*
- * We haven't replied to the NAS, but we have seen an
- * earlier reply from the home server. Ignore this packet,
- * as we're likely still processing the previous reply.
+ * There's a reply: discard it if it's a conflicting one.
*/
if (request->proxy_reply) {
+ /*
+ * ? The home server gave us a new proxy
+ * reply which doesn't match the old
+ * one. Delete it.
+ */
if (memcmp(request->proxy_reply->vector,
packet->vector,
- sizeof(request->proxy_reply->vector)) == 0) {
- RDEBUG2("Discarding duplicate reply from host %s port %d - ID: %d for request %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id,
- request->number);
- } else {
- /*
- * ? The home server gave us a new proxy
- * reply which doesn't match the old
- * one. Delete it.
- */
+ sizeof(request->proxy_reply->vector)) != 0) {
RDEBUG2("Ignoring conflicting proxy reply");
- }
+
- /* assert that there's an event queued for request? */
+ /* assert that there's an event queued for request? */
+ return NULL;
+ } /* else it had previously passed verification */
+
+ /*
+ * Verify the packet before doing ANYTHING with
+ * it. This means we're doing more MD5 checks in
+ * the server core. However, we can fix that by
+ * moving to multiple threads listening on
+ * sockets.
+ *
+ * We do this AFTER looking the request up in the
+ * hash, and AFTER checking if we saw a previous
+ * request. This helps minimize the DoS effect
+ * of people attacking us with spoofed packets.
+ *
+ * FIXME: move the "read from proxy socket" code
+ * into one (or more) threads. Have it read from
+ * the socket, do the validation, and write a
+ * pointer to the packet into a pipe? Or queue it
+ * to the main server?
+ */
+ } else if (rad_verify(packet, request->proxy,
+ request->home_server->secret) != 0) {
+ DEBUG("Ignoring spoofed proxy reply. Signature is invalid");
return NULL;
}
/*
- * Verify the packet before doing ANYTHING with it. This
- * means we're doing more MD5 checks in the server core.
- * However, we can fix that by moving to multiple threads
- * listening on sockets.
- *
- * We do this AFTER looking the request up in the hash,
- * and AFTER vhecking if we saw a previous request. This
- * helps minimize the DoS effect of people attacking us
- * with spoofed packets.
+ * Check (again) if it's a duplicate reply. We do this
+ * after deleting the packet from the proxy hash.
*/
- if (rad_verify(packet, request->proxy,
- request->home_server->secret) != 0) {
- DEBUG("Ignoring spoofed proxy reply. Signature is invalid");
- return NULL;
+ if (request->proxy_reply) {
+ RDEBUG2("Discarding duplicate reply from host %s port %d - ID: %d",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ packet->src_port, packet->id);
}
gettimeofday(&now, NULL);
/*
+ * "ping" packets have a different algorithm for marking
+ * a home server alive. They also skip all of the CoA,
+ * etc. checks.
+ */
+ if (!request->packet) {
+ request->proxy_reply = packet;
+#ifdef WITH_TCP
+ rad_assert(request->home_server != NULL);
+ if (request->home_server->proto != IPPROTO_TCP)
+#endif
+ received_response_to_ping(request);
+ request->proxy_reply = NULL; /* caller will free it */
+ ev_request_free(&request);
+ return NULL;
+ }
+
+ /*
* Maybe move this earlier in the decision process?
* Having it here means that late or duplicate proxy
* replies no longer get the home server marked as
* receive a packet? Setting this here means that we
* mark it alive on *any* packet, even if it's lost all
* of the *other* packets in the last 10s.
+ *
+ * This behavior could be configurable.
*/
request->home_server->state = HOME_STATE_ALIVE;
+ request->home_server->last_packet = now.tv_sec;
#ifdef WITH_COA
/*
request->parent->coa = NULL;
request->parent = NULL;
+ /*
+ * The proxied packet was different from the
+ * original packet, AND the proxied packet was
+ * a CoA: allow it.
+ */
+ } else if ((request->packet->code != request->proxy->code) &&
+ ((request->proxy->code == PW_COA_REQUEST) ||
+ (request->proxy->code == PW_DISCONNECT_REQUEST))) {
+ /*
+ * It's already divorced: do nothing.
+ */
+
} else
/*
* Skip the next set of checks, as the original
RDEBUG2("Ignoring proxy reply that arrived after we sent a reply to the NAS");
return NULL;
}
-
+
#ifdef WITH_STATS
/*
* The average includes our time to receive packets and
case REQUEST_REJECT_DELAY:
case REQUEST_CLEANUP_DELAY:
case REQUEST_DONE:
- radlog(L_ERR, "Reply from home server %s port %d - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
+ radlog(L_ERR, "Reply from home server %s port %d - ID: %d arrived too late for request %u. Try increasing 'retry_delay' or 'max_request_time'",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
buffer, sizeof(buffer)),
}
#endif
- /*
- * There's no incoming request, so it's a proxied packet
- * we originated.
- */
- if (!request->packet) {
- received_response_to_ping(request);
- request->proxy_reply = NULL; /* caller will free it */
- ev_request_free(&request);
- return NULL;
- }
-
request->child_state = REQUEST_QUEUED;
request->when = now;
request->delay = USEC;
}
#endif
-void event_new_fd(rad_listen_t *this)
+int event_new_fd(rad_listen_t *this)
{
char buffer[1024];
- if (this->status == RAD_LISTEN_STATUS_KNOWN) return;
+ if (this->status == RAD_LISTEN_STATUS_KNOWN) return 1;
this->print(this, buffer, sizeof(buffer));
if (just_started) {
DEBUG("Listening on %s", buffer);
} else {
- DEBUG2(" ... adding new socket %s", buffer);
+ radlog(L_INFO, " ... adding new socket %s", buffer);
}
#ifdef WITH_PROXY
sock->proto,
&sock->other_ipaddr, sock->other_port,
this)) {
- radlog(L_ERR, "Fatal error adding socket: %s",
- fr_strerror());
- exit(1);
+ 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.
+ */
+ radlog(L_ERR, "Failed adding proxy socket: %s",
+ fr_strerror());
+ return 0;
}
if (sock->home) {
* Set up the first poll interval.
*/
event_poll_detail(this);
- return;
+ return 1;
}
#endif
FD_MUTEX_LOCK(&fd_mutex);
if (!fr_event_fd_insert(el, 0, this->fd,
event_socket_handler, this)) {
- radlog(L_ERR, "Failed remembering handle for proxy socket!");
+ radlog(L_ERR, "Failed adding event handler for proxy socket!");
exit(1);
}
FD_MUTEX_UNLOCK(&fd_mutex);
this->status = RAD_LISTEN_STATUS_KNOWN;
- return;
+ return 1;
}
/*
fr_event_fd_delete(el, 0, this->fd);
FD_MUTEX_UNLOCK(&fd_mutex);
+#ifdef WITH_TCP
+ /*
+ * We track requests using this socket only for
+ * TCP. For UDP, we don't currently close
+ * sockets.
+ */
#ifdef WITH_PROXY
if (this->type != RAD_LISTEN_PROXY)
#endif
goto finish;
}
}
-#endif
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
/*
* Re-open the socket, pointing it to /dev/null.
*/
}
+#ifdef WITH_TCP
/*
* Called ONLY from the main thread. On the following
* conditions:
*/
if (this->status == RAD_LISTEN_STATUS_CLOSED) {
int count = this->count;
+
+#ifdef WITH_DETAIL
rad_assert(this->type != RAD_LISTEN_DETAIL);
+#endif
#ifdef WITH_PROXY
/*
rad_panic("Failed to insert event");
}
- return;
+ return 1;
}
/*
listen_socket_t *sock = this->data;
rad_assert(this->count == 0);
- DEBUG2(" ... closing socket %s", buffer);
+ radlog(L_INFO, " ... closing socket %s", buffer);
/*
* Remove it from the list of live FD's. Note
*/
listen_free(&this);
}
+#endif /* WITH_TCP */
+
+ return 1;
}
static void handle_signal_self(int flag)
{
if ((flag & (RADIUS_SIGNAL_SELF_EXIT | RADIUS_SIGNAL_SELF_TERM)) != 0) {
if ((flag & RADIUS_SIGNAL_SELF_EXIT) != 0) {
+ radlog(L_INFO, "Received TERM signal");
fr_event_loop_exit(el, 1);
} else {
fr_event_loop_exit(el, 2);
time_t when;
static time_t last_hup = 0;
- DEBUG("Received HUP signal.");
-
when = time(NULL);
if ((int) (when - last_hup) < 5) {
radlog(L_INFO, "Ignoring HUP (less than 5s since last one)");
return;
}
+
+ radlog(L_INFO, "Received HUP signal.");
+
last_hup = when;
fr_event_loop_exit(el, 0x80);
}
#endif
+#ifdef WITH_TCP
#ifdef WITH_PROXY
/*
- * Add event handlers for idle timeouts && maximum lifetime.XXX
+ * Add event handlers for idle timeouts && maximum lifetime.
*/
if ((flag & RADIUS_SIGNAL_SELF_NEW_FD) != 0) {
struct timeval when;
void *fun = NULL;
- if (!fr_event_now(el, &now)) gettimeofday(&now, NULL);
+ fr_event_now(el, &now);
PTHREAD_MUTEX_LOCK(&proxy_mutex);
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
-#endif
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
}
#ifndef WITH_SELF_PIPE
xel = xel;
- if (listener->fd < 0) rad_panic("Socket was closed on us!");
+ if (
+#ifdef WITH_DETAIL
+ (listener->type != RAD_LISTEN_DETAIL) &&
+#endif
+ (listener->fd < 0)) {
+ char buffer[256];
+
+ listener->print(listener, buffer, sizeof(buffer));
+ radlog(L_ERR, "FATAL: Asked to read from closed socket: %s",
+ buffer);
+
+ rad_panic("Socket was closed on us!");
+ _exit(1);
+ }
if (!listener->recv(listener, &fun, &request)) return;
- if (!thread_pool_addrequest(request, fun)) {
- request->child_state = REQUEST_DONE;
- }
-}
+ rad_assert(fun != NULL);
+ rad_assert(request != NULL);
-typedef struct listen_detail_t {
- fr_event_t *ev;
-} listen_detail_t;
+ thread_pool_addrequest(request, fun);
+}
+#ifdef WITH_DETAIL
/*
* This function is called periodically to see if this detail
* file is available for reading.
*/
static void event_poll_detail(void *ctx)
{
- int rcode, delay;
- RAD_REQUEST_FUNP fun;
- REQUEST *request;
+ int delay;
rad_listen_t *this = ctx;
struct timeval when;
listen_detail_t *detail = this->data;
rad_assert(this->type == RAD_LISTEN_DETAIL);
- /*
- * Try to read something.
- *
- * FIXME: This does poll AND receive.
- */
- rcode = this->recv(this, &fun, &request);
- if (rcode != 0) {
- rad_assert(fun != NULL);
- rad_assert(request != NULL);
-
- if (!thread_pool_addrequest(request, fun)) {
- request->child_state = REQUEST_DONE;
- }
- }
+ event_socket_handler(el, this->fd, this);
- if (!fr_event_now(el, &now)) gettimeofday(&now, NULL);
+ fr_event_now(el, &now);
when = now;
/*
exit(1);
}
}
-
+#endif
static void event_status(struct timeval *wake)
{
}
if (!wake) {
- DEBUG("Ready to process requests.");
+ radlog(L_INFO, "Ready to process requests.");
} else if ((wake->tv_sec != 0) ||
(wake->tv_usec >= 100000)) {
#ifdef WITH_PROXY
if (mainconfig.proxy_requests) {
- pthread_mutexattr_t attr;
-
/*
* Create the tree for managing proxied requests and
* responses.
if (!proxy_list) return 0;
#ifdef HAVE_PTHREAD_H
- pthread_mutexattr_init(&attr);
-
-#ifdef PTHREAD_MUTEX_RECURSIVE
- if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) < 0) {
- radlog(L_ERR, "FATAL: Failed to set type for proxy mutex: %s",
- strerror(errno));
- exit(1);
- }
-#endif
-
if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
strerror(errno));
exit(1);
}
-
- pthread_mutexattr_destroy(&attr);
#endif
}
#endif
- /*
- * Just before we spawn the child threads, force the log
- * subsystem to re-open the log file for every write.
- */
- if (spawn_flag) force_log_reopen();
-
#ifdef HAVE_PTHREAD_H
#ifndef __MINGW32__
NO_SUCH_CHILD_PID = (pthread_t ) (0);
if (check_config) {
DEBUG("%s: #### Skipping IP addresses and Ports ####",
mainconfig.name);
+ if (listen_init(cs, &head) < 0) {
+ fflush(NULL);
+ exit(1);
+ }
return 1;
}
* referenced from anywhere else. Remove them first.
*/
if (proxy_list) {
- PTHREAD_MUTEX_LOCK(&proxy_mutex);
fr_packet_list_walk(proxy_list, NULL, proxy_hash_cb);
- PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
fr_packet_list_free(proxy_list);
proxy_list = NULL;
}