*/
#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
/*
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
if (!fr_packet_list_insert(proxy_list, &request->proxy)) {
fr_packet_list_id_free(proxy_list, request->proxy);
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
- radlog(L_PROXY, "Failed to insert entry into proxy list");
+ radlog(L_PROXY, "Failed to insert entry into proxy list.");
return 0;
}
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
(pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0))) {
/*
- * Cap delay at five minutes.
+ * Cap delay at max_request_time
*/
- if (request->delay < (USEC * 60 * 5)) {
+ 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 {
- RDEBUG2("Child is still stuck");
+ 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);
}
RDEBUG2("Child is finally responsive");
- remove_from_request_hash(request);
#ifdef WITH_PROXY
if (request->proxy) {
rad_assert(request->proxy_listener == NULL);
if (!insert_into_proxy_hash(request)) {
- radlog(L_PROXY, "Failed inserting status check %d into proxy hash. Discarding it.",
+ radlog(L_PROXY, "Failed to insert status check %d into proxy list. Discarding it.",
request->number);
ev_request_free(&request);
return;
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
- 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;
- 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);
+ 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, and
+ * the request is still running. This is usually
+ * because the request was proxied, and the home
+ * server didn't respond.
+ */
+#ifdef HAVE_PTHREAD_H
+ if (!have_children)
+#endif
+ {
+ goto done;
+ }
+
+#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);
}
stop_processing:
-#if defined(HAVE_PTHREAD_H)
+ request->master_state = REQUEST_STOP_PROCESSING;
+
/*
* A child thread MAY still be running on the
* request. Ask the thread to stop working on
*/
if (have_children &&
(pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0)) {
- request->master_state = REQUEST_STOP_PROCESSING;
+ 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>");
- radlog_request(L_ERR, 0, request, "WARNING: Unresponsive child in module %s component %s",
- request->module ? request->module : "<server core>",
- request->component ? request->component : "<server core>");
-
- request->delay = USEC / 4;
- tv_add(&request->when, request->delay);
- callback = wait_for_child_to_die;
- break;
}
+
+ request->delay = USEC;
+ tv_add(&request->when, request->delay);
+ callback = wait_for_child_to_die;
+ break;
#endif
/*
- * Else no child thread is processing the
- * request. 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.
*/
case REQUEST_DONE:
+ done:
#ifdef HAVE_PTHREAD_H
request->child_pid = NO_SUCH_CHILD_PID;
#endif
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);
{
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;
* Don't free the old Id on error.
*/
if (!insert_into_proxy_hash(request)) {
- radlog(L_PROXY,"Failed re-inserting CoA request into proxy hash.");
+ radlog(L_PROXY,"Failed to insert retransmission of CoA request into proxy list.");
return;
}
/*
* Check whether we want to originate one, or cancel one.
*/
- vp = pairfind(request->config_items, PW_SEND_COA_REQUEST);
+ vp = pairfind(request->config_items, PW_SEND_COA_REQUEST, 0);
if (!vp && request->coa) {
- vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST);
+ vp = pairfind(request->coa->proxy->vps, PW_SEND_COA_REQUEST, 0);
}
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);
+ 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, IPPROTO_UDP);
}
}
- 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_port = coa->home_server->port;
if (!insert_into_proxy_hash(coa)) {
- radlog(L_PROXY, "Failed inserting CoA request into proxy hash.");
+ radlog(L_PROXY, "Failed to insert CoA request into proxy list.");
goto fail;
}
* 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 (!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;
}
#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)) {
- radlog(L_PROXY, "Failed inserting request into proxy hash.");
+ radlog(L_PROXY, "Failed to insert request into proxy list.");
return 0;
}
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 ((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;
}
/*
* 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");
request->reply->code = PW_AUTHENTICATION_REJECT;
* 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);
* 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);
}
* if it wasn't proxied.
*/
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) != NULL))) {
+ (pairfind(request->config_items, PW_SEND_COA_REQUEST, 0) != NULL))) {
if (!originated_coa_request(request)) {
RDEBUG2("Do CoA Fail handler here");
}
}
-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);
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");
- 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");
- 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
/*
* 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;
}
* sockets.
*
* We do this AFTER looking the request up in the
- * hash, and AFTER vhecking if we saw a previous
+ * hash, and AFTER checking if we saw a previous
* request. This helps minimize the DoS effect
* of people attacking us with spoofed packets.
*
}
/*
- * Now that we know it's a good reply, see if we can
- * delete it from the proxy hash. This lets the source
- * ports && Ids be re-used earlier.
- *
- * FIXME: protect by mutex? This is likely less relevant
- * as if we have the reply, the originating thread knows to
- * avoid touching the request. Any retransmits are done from
- * the main server thread (i.e. this thread).
- */
- if (request->num_proxied_requests <= request->num_proxied_responses) {
- remove_from_proxy_hash(request);
- }
-
- /*
* Check (again) if it's a duplicate reply. We do this
* after deleting the packet from the proxy hash.
*/
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.
*/
- if (request->proxy->code != PW_STATUS_SERVER) {
- request->home_server->state = HOME_STATE_ALIVE;
- }
+ request->home_server->state = HOME_STATE_ALIVE;
+ request->home_server->last_packet = now.tv_sec;
#ifdef WITH_COA
/*
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
}
#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;
}
#ifdef WITH_PROXY
else {
- int count = this->count;
+ int count;
/*
* Duplicate code
*/
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
/*
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);
+ 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);
fr_event_now(el, &now);
when = now;
exit(1);
}
}
-
+#endif
static void event_status(struct timeval *wake)
{
if (check_config) {
DEBUG("%s: #### Skipping IP addresses and Ports ####",
mainconfig.name);
+ if (listen_init(cs, &head) < 0) {
+ fflush(NULL);
+ exit(1);
+ }
return 1;
}