# 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) CC_HINT(nonnull);
-static int request_proxy(REQUEST *request, int retransmit) CC_HINT(nonnull);
+static int request_proxy(REQUEST *request) CC_HINT(nonnull);
STATE_MACHINE_DECL(request_ping) CC_HINT(nonnull);
STATE_MACHINE_DECL(request_response_delay) CC_HINT(nonnull);
if (!packet) return;
if (!RDEBUG_ENABLED) return;
+#ifdef WITH_DETAIL
+ /*
+ * Don't print IP addresses for detail files.
+ */
+ if (request->listener &&
+ (request->listener->type == RAD_LISTEN_DETAIL)) return;
+
+#endif
/*
* Client-specific debugging re-prints the input
* packet into the client log.
{
VERIFY_REQUEST(request);
+ rad_assert(request->home_server != NULL);
+
if (request->client) {
/*
* The client hasn't set the response window. Return
}
}
- rad_assert(request->home_server != NULL);
return &request->home_server->response_window;
}
* 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
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;
}
return;
} /* else it's time to clean up */
- request_done(request, REQUEST_DONE);
+ request_done(request, FR_ACTION_DONE);
break;
default:
switch (action) {
case FR_ACTION_DUP:
- ERROR("(%u) Discarding duplicate request from "
+ RDEBUG("(%u) Discarding duplicate request from "
"client %s port %d - ID: %u due to delayed response",
request->number, request->client->shortname,
request->packet->src_port,request->packet->id);
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 (request->packet->dst_port == 0) {
RDEBUG("Finished internally proxied request.");
- NO_CHILD_THREAD;
- request->child_state = REQUEST_DONE;
+ FINAL_STATE(REQUEST_DONE);
return;
}
(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
* Don't print a reply if there's none to send.
*/
if (request->reply->code != 0) {
+ 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>";
-
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);
}
}
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;
}
* up the post proxy fail
* handler.
*/
- if (request_proxy(request, 0) < 0) goto req_finished;
+ if (request_proxy(request) < 0) goto req_finished;
} else
#endif
{
#ifdef WITH_ACCOUNTING
if (listener->type != RAD_LISTEN_DETAIL)
#endif
+
+#ifdef WITH_TCP
{
sock = listener->data;
sock->last_packet = now.tv_sec;
-#ifdef WITH_TCP
packet->proto = sock->proto;
-#endif
}
+#endif
/*
* Skip everything if required.
ERROR("No memory");
return NULL;
}
- request->reply = rad_alloc(request, false);
+ request->reply = rad_alloc_reply(request, packet);
if (!request->reply) {
ERROR("No memory");
talloc_free(request);
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;
#ifdef WITH_PROXY
/*
- * Add +/- 2s of jitter, as suggested in RFC 3539
- * and in RFC 5080.
- */
-static void add_jitter(struct timeval *when)
-{
- uint32_t jitter;
-
- when->tv_sec -= 2;
-
- jitter = fr_rand();
- jitter ^= (jitter >> 10);
- jitter &= ((1 << 22) - 1); /* 22 bits of 1 */
-
- /*
- * Add in ~ (4 * USEC) of jitter.
- */
- tv_add(when, jitter);
-}
-
-/*
* Called by socket_del to remove requests with this socket
*/
static int eol_proxy_listener(void *ctx, void *data)
}
#ifdef WITH_TCP
- rad_assert(request->proxy_listener != NULL);
- request->proxy_listener->count--;
+ if (request->proxy_listener) {
+ request->proxy_listener->count--;
+ }
#endif
request->proxy_listener = NULL;
}
#ifdef WITH_COA
- if (request->packet->code == request->proxy->code)
+ if (request->packet->code == request->proxy->code) {
/*
* Don't run the next bit if we originated a CoA
* packet, after receiving an Access-Request or
*/
#endif
- /*
- * There may NOT be a proxy reply, as we may be
- * running Post-Proxy-Type = Fail.
- */
- if (reply) {
- 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.
+ * There may NOT be a proxy reply, as we may be
+ * running Post-Proxy-Type = Fail.
*/
- fr_pair_delete_by_num(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
- }
+ if (reply) {
+ 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.
+ */
+ fr_pair_delete_by_num(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
+ } else {
+ vp = fr_pair_find_by_num(request->config, PW_RESPONSE_PACKET_TYPE, 0, TAG_ANY);
+ if (vp && (vp->vp_integer != 256)) {
+ request->proxy_reply = rad_alloc_reply(request, request->proxy);
+ request->proxy_reply->code = vp->vp_integer;
+ }
+ }
+#ifdef WITH_COA
+ }
+#endif
switch (rcode) {
default: /* Don't do anything */
break;
}
request = fr_packet2myptr(REQUEST, proxy, proxy_p);
- request->num_proxied_responses++; /* needs to be protected by lock */
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
* Status-Server packets don't count as real packets.
*/
if (request->proxy->code != PW_CODE_STATUS_SERVER) {
+#ifdef WITH_TCP
listen_socket_t *sock = request->proxy_listener->data;
- request->home_server->last_packet_recv = now.tv_sec;
sock->last_packet = now.tv_sec;
+#endif
+ request->home_server->last_packet_recv = now.tv_sec;
}
+ request->num_proxied_responses++;
+
/*
* If we have previously seen a reply, ignore the
* duplicate.
#ifdef WITH_ACCOUNTING
case PW_CODE_ACCOUNTING_REQUEST:
+ proxy_acct_stats.last_packet = packet->timestamp.tv_sec;
+
request->proxy_listener->stats.total_responses++;
proxy_acct_stats.last_packet = packet->timestamp.tv_sec;
break;
request->server = old_server;
} else {
+ char buffer[128];
+
+ RDEBUG2("Starting proxy to home server %s port %d",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+
rcode = process_pre_proxy(pre_proxy_type, request);
}
}
-static int request_proxy(REQUEST *request, int retransmit)
+static int request_proxy(REQUEST *request)
{
char buffer[128];
}
- gettimeofday(&request->proxy_retransmit, NULL);
- if (!retransmit) {
- request->proxy->timestamp = request->proxy_retransmit;
- }
- request->home_server->last_packet_sent = request->proxy_retransmit.tv_sec;
+ gettimeofday(&request->proxy->timestamp, NULL);
+ request->home_server->last_packet_sent = request->proxy->timestamp.tv_sec;
/*
* Encode the packet before we do anything else.
/*
* 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;
/*
#ifdef WITH_ACCOUNTING
/*
- * Update the Acct-Delay-Time attribute.
+ * Update the Acct-Delay-Time attribute, since the LAST
+ * time we tried to retransmit this packet.
*/
if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
VALUE_PAIR *vp;
struct timeval now;
gettimeofday(&now, NULL);
- vp->vp_integer += now.tv_sec - request->proxy_retransmit.tv_sec;
+ vp->vp_integer += now.tv_sec - request->proxy->timestamp.tv_sec;
}
}
#endif
request->proxy->data = NULL;
request->proxy->data_len = 0;
- if (request_proxy(request, 1) != 1) goto post_proxy_fail;
+ if (request_proxy(request) != 1) goto post_proxy_fail;
return 1;
}
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
request->proxy->dst_port);
+ remove_from_proxy_hash(request);
break;
case FR_ACTION_PROXY_REPLY:
}
rad_assert(!request->in_request_hash);
+ rad_assert(!request->in_proxy_hash);
rad_assert(request->ev == NULL);
NO_CHILD_THREAD;
request_done(request, FR_ACTION_DONE);
}
/*
+ * Add +/- 2s of jitter, as suggested in RFC 3539
+ * and in RFC 5080.
+ */
+static void add_jitter(struct timeval *when)
+{
+ uint32_t jitter;
+
+ when->tv_sec -= 2;
+
+ jitter = fr_rand();
+ jitter ^= (jitter >> 10);
+ jitter &= ((1 << 22) - 1); /* 22 bits of 1 */
+
+ /*
+ * Add in ~ (4 * USEC) of jitter.
+ */
+ tv_add(when, jitter);
+}
+
+/*
* Called from start of zombie period, OR after control socket
* marks the home server dead.
*/
struct timeval when, now;
if ((home->state == HOME_STATE_ALIVE) ||
-#ifdef WITH_TCP
- (home->proto == IPPROTO_TCP) ||
-#endif
(home->ev != NULL)) {
return;
}
fr_pair_make(request->proxy, &request->proxy->vps,
"Message-Authenticator", "0x00", T_OP_SET);
- } else if (home->type == HOME_TYPE_AUTH) {
+ } else if ((home->type == HOME_TYPE_AUTH) ||
+ (home->type == HOME_TYPE_AUTH_ACCT)) {
request->proxy->code = PW_CODE_ACCESS_REQUEST;
fr_pair_make(request->proxy, &request->proxy->vps,
fr_pair_make(request->proxy, &request->proxy->vps,
"Message-Authenticator", "0x00", T_OP_SET);
- } else {
#ifdef WITH_ACCOUNTING
+ } else if (home->type == HOME_TYPE_ACCT) {
request->proxy->code = PW_CODE_ACCOUNTING_REQUEST;
fr_pair_make(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
- rad_assert("Internal sanity check failed");
#endif
+
+ } else {
+ /*
+ * Unkown home server type.
+ */
+ talloc_free(request);
+ return;
}
vp = fr_pair_make(request->proxy, &request->proxy->vps,
rad_assert((home->state == HOME_STATE_ALIVE) ||
(home->state == HOME_STATE_UNKNOWN));
-#ifdef WITH_TCP
- if (home->proto == IPPROTO_TCP) {
- WARN("Not marking TCP server %s zombie", home->log_name);
- return;
- }
-#endif
-
/*
* We've received a real packet recently. Don't mark the
* server as zombie until we've received NO packets for a
*/
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;
}
home_server_t *home = talloc_get_type_abort(ctx, home_server_t);
char buffer[128];
-#ifdef WITH_TCP
- rad_assert(home->proto != IPPROTO_TCP);
-#endif
-
home->state = HOME_STATE_ALIVE;
home->response_timeouts = 0;
home_trigger(home, "home_server.alive");
int previous_state = home->state;
char buffer[128];
-#ifdef WITH_TCP
- if (home->proto == IPPROTO_TCP) {
- WARN("Not marking TCP server dead");
- return;
- }
-#endif
-
PROXY( "Marking home server %s port %d as dead.",
inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
buffer, sizeof(buffer)),
* More than one retransmit a second is stupid,
* and should be suppressed by the proxy.
*/
- when = request->proxy_retransmit;
+ when = request->proxy->timestamp;
when.tv_sec++;
if (timercmp(&now, &when, <)) {
rad_assert(request->proxy_listener != NULL);
FR_STATS_TYPE_INC(home->stats.total_requests);
home->last_packet_sent = now.tv_sec;
- request->proxy_retransmit = now;
+ request->proxy->timestamp = now;
debug_packet(request, request->proxy, false);
request->proxy_listener->send(request->proxy_listener, request);
break;
* "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) ||
(home->state == HOME_STATE_UNKNOWN))
-#ifdef WITH_TCP
- && (home->proto != IPPROTO_TCP)
-#endif
) {
home->response_timeouts++;
if (home->response_timeouts >= home->max_response_timeouts)
FR_STATS_TYPE_INC(proxy_acct_stats.total_timeouts);
}
#endif
+#ifdef WITH_COA
+ else if (home->type == HOME_TYPE_COA) {
+ if (request->proxy_listener) FR_STATS_TYPE_INC(request->proxy_listener->stats.total_timeouts);
+
+ if (request->packet->code == PW_CODE_COA_REQUEST) {
+ FR_STATS_TYPE_INC(proxy_coa_stats.total_timeouts);
+ } else {
+ FR_STATS_TYPE_INC(proxy_dsc_stats.total_timeouts);
+ }
+ }
+#endif
/*
* There was no response within the window. Stop
}
}
+ if (!main_config.proxy_requests) {
+ RWDEBUG("Cannot originate CoA packets unless 'proxy_requests = yes'");
+ TALLOC_FREE(request->coa);
+ return;
+ }
+
coa = request->coa;
/*
case FR_ACTION_TIMER:
if (request_max_time(request)) break;
+ /*
+ * Don't do fail-over. This is a 3.1 feature.
+ */
+ if (!request->home_server ||
+ (request->home_server->state == HOME_STATE_IS_DEAD) ||
+ !request->proxy_listener ||
+ (request->proxy_listener->status >= RAD_LISTEN_STATUS_EOL)) {
+ request_done(request, FR_ACTION_DONE);
+ break;
+ }
+
coa_retransmit(request);
break;
rad_assert(this->next == NULL);
talloc_free(this);
}
-#endif
#ifdef WITH_PROXY
static int proxy_eol_cb(void *ctx, void *data)
*/
return 0;
}
-#endif
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
static int event_new_fd(rad_listen_t *this)
{
rad_panic("Failed to insert event");
}
}
-#endif
+#endif /* WITH_TCP */
break;
#endif /* WITH_PROXY */
fr_exit(1);
}
}
-#endif
+#endif /* WITH_TCP */
break;
} /* switch over listener types */
}
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
}
-#endif
+#endif /* WITH_PROXY */
/*
* Requests are still using the socket. Wait for
*/
this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
} /* socket is at EOL */
-#endif
+#endif /* WITH_TCP */
/*
* Nuke the socket.
int devnull;
#ifdef WITH_TCP
listen_socket_t *sock = this->data;
-#endif
struct timeval when;
+#endif
/*
* Re-open the socket, pointing it to /dev/null.
}
PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
} else
-#endif
+#endif /* WITH_PROXY */
{
INFO(" ... shutting down socket %s", buffer);
&(sock->ev))) {
rad_panic("Failed to insert event");
}
- }
#endif /* WITH_TCP */
+ }
return 1;
}
#ifndef HAVE_PTHREAD_H
void radius_signal_self(int flag)
{
+ if (flag == RADIUS_SIGNAL_SELF_TERM) {
+ main_config.exiting = true;
+ }
+
return handle_signal_self(flag);
}
ssize_t rcode;
uint8_t buffer[16];
+ if (flag == RADIUS_SIGNAL_SELF_TERM) {
+ main_config.exiting = true;
+ }
+
/*
* The read MUST be non-blocking for this to work.
*/