request_free(&request);
}
+
+static void request_cleanup_delay_init(REQUEST *request, const struct timeval *pnow)
+{
+ struct timeval now, when;
+
+ if (request->packet->code == PW_ACCOUNTING_REQUEST) goto done;
+ if (!request->root->cleanup_delay) goto done;
+
+ if (pnow) {
+ now = *pnow;
+ } else {
+ gettimeofday(&now, NULL);
+ }
+
+ if (request->reply->timestamp.tv_sec == 0) {
+ when = now;
+ } else {
+ when = request->reply->timestamp;
+ }
+ request->delay = request->root->cleanup_delay;
+ when.tv_sec += request->delay;
+
+ /*
+ * Set timer for when we need to clean it up.
+ */
+ if (timercmp(&when, &now, >)) {
+#ifdef DEBUG_STATE_MACHINE
+ if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
+#endif
+ request->process = request_cleanup_delay;
+ STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+ return;
+ }
+
+ /*
+ * Otherwise just clean it up.
+ */
+done:
+ request_done(request, FR_ACTION_DONE);
+}
+
+
/*
* Function to do all time-related events.
*/
#ifdef WITH_ACCOUNTING
if (request->reply->code == PW_ACCOUNTING_RESPONSE) {
- goto done;
+ done:
+ request_done(request, FR_ACTION_DONE);
+ return;
}
#endif
* cleanup_delay even if we don't respond to the NAS, so
* that any retransmit is *not* processed as a new packet.
*/
- if ((request->packet->code != PW_ACCOUNTING_REQUEST) &&
- (request->root->cleanup_delay)) {
- when = request->reply->timestamp;
- request->delay = request->root->cleanup_delay;
- when.tv_sec += request->delay;
-
- /*
- * Set timer for when we need to clean it up.
- */
- if (timercmp(&when, &now, >)) {
-#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
-#endif
- request->process = request_cleanup_delay;
-
- STATE_MACHINE_TIMER(FR_ACTION_TIMER);
- return;
- }
- }
-
-done:
- request_done(request, FR_ACTION_DONE);
+ request_cleanup_delay_init(request, &now);
+ return;
}
static void request_queue_or_run(UNUSED REQUEST *request,
case FR_ACTION_DUP:
if (request->reply->code != 0) {
request->listener->send(request->listener, request);
+ } else {
+ RDEBUG("No reply. Ignoring retransmit.");
}
/*
if (request->home_server &&
request->home_server->currently_outstanding) {
request->home_server->currently_outstanding--;
+
+ /*
+ * If we're NOT sending it packets, then we don't know
+ * if it's alive or dead.
+ */
+ if ((request->home_server->currently_outstanding == 0) &&
+ (request->home_server->state == HOME_STATE_ALIVE)) {
+ request->home_server->state = HOME_STATE_UNKNOWN;
+ request->home_server->last_packet_sent = 0;
+ request->home_server->last_packet_recv = 0;
+ }
}
#ifdef WITH_TCP
if (request->proxy->code != PW_STATUS_SERVER) {
listen_socket_t *sock = request->proxy_listener->data;
- request->home_server->last_packet = now.tv_sec;
+ request->home_server->last_packet_recv = now.tv_sec;
sock->last_packet = now.tv_sec;
}
packet->timestamp = now;
request->priority = RAD_LISTEN_PROXY;
+ /*
+ * We've received a reply. If we hadn't been sending it
+ * packets for a while, just mark it alive.
+ */
+ if (request->home_server->state == HOME_STATE_UNKNOWN) {
+ request->home_server->state = HOME_STATE_ALIVE;
+ }
+
#ifdef WITH_STATS
request->home_server->stats.last_packet = packet->timestamp.tv_sec;
request->proxy_listener->stats.last_packet = packet->timestamp.tv_sec;
#endif
} else {
DEBUG("WARNING: Unknown packet type in Post-Proxy-Type Fail: ignoring");
+ request_cleanup_delay_init(request, NULL);
return 0;
}
if (!dval) {
DEBUG("No Post-Proxy-Type Fail: ignoring");
pairdelete(&request->config_items, PW_POST_PROXY_TYPE, 0);
+ request_cleanup_delay_init(request, NULL);
return 0;
}
return -1;
}
- request->proxy_listener->encode(request->proxy_listener, request);
-
RDEBUG2("Proxying request to home server %s port %d",
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
request->child_pid = NO_SUCH_CHILD_PID;
#endif
FR_STATS_TYPE_INC(request->home_server->stats.total_requests);
+ request->home_server->last_packet_sent = request->proxy_retransmit.tv_sec;
request->proxy_listener->send(request->proxy_listener,
request);
return 1;
ASSERT_MASTER;
- rad_assert(home->state == HOME_STATE_ALIVE);
+ rad_assert((home->state == HOME_STATE_ALIVE) ||
+ (home->state == HOME_STATE_UNKNOWN));
#ifdef WITH_TCP
if (home->proto == IPPROTO_TCP) {
home->state = HOME_STATE_ZOMBIE;
home_trigger(home, "home_server.zombie");
- home->zombie_period_start.tv_sec = home->last_packet;
- home->zombie_period_start.tv_usec = USEC / 2;
+ /*
+ * Back-date the zombie period to when we last expected
+ * to see a response. i.e. when we last sent a request.
+ */
+ if (home->last_packet_sent == 0) {
+ gettimeofday(&home->zombie_period_start, NULL);
+ } else {
+ home->zombie_period_start.tv_sec = home->last_packet_sent;
+ home->zombie_period_start.tv_usec = 0;
+ }
fr_event_delete(el, &home->ev);
home->num_sent_pings = 0;
home->num_received_pings = 0;
- radlog(L_PROXY, "Marking home server %s port %d as zombie (it looks like it is dead).",
+ radlog(L_PROXY, "Marking home server %s port %d as zombie (it has not responded in %d seconds).",
inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
buffer, sizeof(buffer)),
- home->port);
+ home->port, home->response_window);
ping_home_server(home);
}
rad_assert(request->proxy_listener != NULL);;
DEBUG_PACKET(request, request->proxy, 1);
FR_STATS_TYPE_INC(home->stats.total_requests);
+ home->last_packet_sent = now.tv_sec;
request->proxy_listener->send(request->proxy_listener,
request);
break;
case FR_ACTION_TIMER:
/*
- * If we haven't received a packet for a while,
- * mark it as zombie. If the connection is TCP,
- * then another "watchdog timer" function takes
- * care of pings, etc.
+ * If we haven't received any packets for
+ * "response_window", then mark the home server
+ * as zombie.
+ *
+ * If the connection is TCP, then another
+ * "watchdog timer" function takes care of pings,
+ * etc. So we don't need to do it here.
+ *
+ * This check should really be part of a home
+ * server state machine.
*/
- if ((home->state == HOME_STATE_ALIVE) &&
+ if (((home->state == HOME_STATE_ALIVE) ||
+ (home->state == HOME_STATE_UNKNOWN)) &&
#ifdef WITH_TCP
(home->proto != IPPROTO_TCP) &&
#endif
- ((home->last_packet + ((home->zombie_period + 3) / 4)) < now.tv_sec)) {
+ ((home->last_packet_sent + home->response_window) <= now.tv_sec)) {
mark_home_server_zombie(home);
}
+ /*
+ * Wake up "response_window" time in the future.
+ * i.e. when MY packet hasn't received a response.
+ *
+ * Note that we DO NOT mark the home server as
+ * zombie if it doesn't respond to us. It may be
+ * responding to other (better looking) packets.
+ */
when = request->proxy->timestamp;
when.tv_sec += home->response_window;
* that.
*/
if (timercmp(&when, &now, >)) {
+ RDEBUG("Expecting proxy response no later than %d seconds from now", home->response_window);
STATE_MACHINE_TIMER(FR_ACTION_TIMER);
return;
}
+ RDEBUG("No proxy response, giving up on request and marking it done");
+
FR_STATS_TYPE_INC(home->stats.total_timeouts);
if (home->type == HOME_TYPE_AUTH) {
if (request->proxy_listener) FR_STATS_TYPE_INC(request->proxy_listener->stats.total_timeouts);
#endif
/*
- * FIXME: debug log no response to proxied request
- */
-
- /*
- * No response, but we're supposed to do nothing
- * when there's no response. The request is finished.
- */
- if (!home->no_response_fail) {
-#ifdef HAVE_PTHREAD_H
- request->child_pid = NO_SUCH_CHILD_PID;
-#endif
- gettimeofday(&request->reply->timestamp, NULL);
-#ifdef DEBUG_STATE_MACHINE
- if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_DONE);
-#endif
-#ifdef HAVE_PTHREAD_H
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
-#endif
- request->child_state = REQUEST_DONE;
- request_process_timer(request);
- return;
- }
-
- /*
- * Do "fail on no response".
+ * There was no response within the window. Stop
+ * the request. If the client retransmitted, it
+ * may have failed over to another home server.
+ * But that one may be dead, too.
*/
- 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,
+ radlog_request(L_ERR, 0, request, "Failing request due to lack of any response from home server %s port %d",
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
coa->child_state = REQUEST_ACTIVE;
rad_assert(coa->proxy_reply == NULL);
FR_STATS_TYPE_INC(coa->home_server->stats.total_requests);
+ coa->home_server->last_packet_sent = coa->proxy->timestamp.tv_sec;
coa->proxy_listener->send(coa->proxy_listener, coa);
}
request->num_coa_requests++; /* is NOT reset by code 3 lines above! */
FR_STATS_TYPE_INC(request->home_server->stats.total_requests);
+
+ /*
+ * Status servers don't count as real packets sent.
+ */
request->proxy_listener->send(request->proxy_listener,
request);
}
{ "response_window", PW_TYPE_INTEGER,
offsetof(home_server,response_window), NULL, "30" },
- { "no_response_fail", PW_TYPE_BOOLEAN,
- offsetof(home_server,no_response_fail), NULL, NULL },
{ "max_outstanding", PW_TYPE_INTEGER,
offsetof(home_server,max_outstanding), NULL, "65536" },
{ "require_message_authenticator", PW_TYPE_BOOLEAN,
home->name = name2;
home->cs = cs;
-
- /*
- * For zombie period calculations. We want to count
- * zombies from the time when the server starts, instead
- * of from 1970.
- */
- home->last_packet = time(NULL);
+ home->state = HOME_STATE_UNKNOWN;
/*
- * Authentication servers have a default "no_response_fail = 0".
- * Accounting servers have a default "no_response_fail = 1".
- *
- * This is because authentication packets are retried, so
- * they can fail over to another home server. Accounting
- * packets are not retried, so they cannot fail over, and
- * instead should be rejected immediately.
+ * Last packet sent / received are zero.
*/
- home->no_response_fail = 2;
memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
if (strcasecmp(hs_type, "auth") == 0) {
home->type = HOME_TYPE_AUTH;
- if (home->no_response_fail == 2) home->no_response_fail = 0;
} else if (strcasecmp(hs_type, "acct") == 0) {
home->type = HOME_TYPE_ACCT;
- if (home->no_response_fail == 2) home->no_response_fail = 1;
} else if (strcasecmp(hs_type, "auth+acct") == 0) {
home->type = HOME_TYPE_AUTH;
home2->cs = cs;
home2->parent_server = home->parent_server;
- if (home->no_response_fail == 2) home->no_response_fail = 0;
- if (home2->no_response_fail == 2) home2->no_response_fail = 1;
-
if (!rbtree_insert(home_servers_byname, home2)) {
cf_log_err(cf_sectiontoitem(cs),
"Internal error %d adding home server %s.",
/*
* Skip dead home servers.
+ *
+ * Home servers that are unknown, alive, or zombie
+ * are used for proxying.
*/
if (home->state == HOME_STATE_IS_DEAD) {
continue;