2 * event.c Server event handling
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2007 The FreeRADIUS server project
21 * Copyright 2007 Alan DeKok <aland@deployingradius.com>
24 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/modules.h>
30 #include <freeradius-devel/event.h>
31 #include <freeradius-devel/radius_snmp.h>
33 #include <freeradius-devel/rad_assert.h>
35 #define USEC (1000000)
38 * Ridiculous amounts of local state.
40 static lrad_event_list_t *el = NULL;
41 static lrad_packet_list_t *pl = NULL;
42 static int request_num_counter = 0;
43 static struct timeval now;
44 static time_t start_time;
45 static int have_children;
48 static pthread_mutex_t proxy_mutex;
50 #define PTHREAD_MUTEX_LOCK if (have_children) pthread_mutex_lock
51 #define PTHREAD_MUTEX_UNLOCK if (have_children) pthread_mutex_unlock
54 * This is easier than ifdef's throughout the code.
56 #define PTHREAD_MUTEX_LOCK(_x)
57 #define PTHREAD_MUTEX_UNLOCK(_x)
60 #define INSERT_EVENT(_function, _ctx) if (!lrad_event_insert(el, _function, _ctx, &((_ctx)->when), &((_ctx)->ev))) { _rad_panic(__FILE__, __LINE__, "Failed to insert event"); }
62 static lrad_packet_list_t *proxy_list = NULL;
65 * We keep the proxy FD's here. The RADIUS Id's are marked
66 * "allocated" per Id, via a bit per proxy FD.
68 static int proxy_fds[32];
69 static rad_listen_t *proxy_listeners[32];
71 static void request_post_handler(REQUEST *request);
72 static void wait_a_bit(void *ctx);
74 static void NEVER_RETURNS _rad_panic(const char *file, unsigned int line,
77 radlog(L_ERR, "]%s:%d] %s", file, line, msg);
81 #define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
84 static void tv_add(struct timeval *tv, int usec_delay)
86 if (usec_delay > USEC) {
87 tv->tv_sec += usec_delay / USEC;
90 tv->tv_usec += usec_delay;
92 if (tv->tv_usec > USEC) {
98 static VALUE_PAIR * radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
99 const char *attribute, const char *value)
103 request = request; /* -Wunused */
105 vp = pairmake(attribute, value, T_OP_SET);
107 radlog(L_ERR, "No memory!");
108 rad_assert("No memory" == NULL);
118 static void snmp_inc_counters(REQUEST *request)
120 if (!mainconfig.do_snmp) return;
122 if (request->master_state == REQUEST_COUNTED) return;
124 if ((request->listener->type != RAD_LISTEN_AUTH) &&
125 (request->listener->type != RAD_LISTEN_ACCT)) return;
128 * Update the SNMP statistics.
130 * Note that we do NOT do this in a child thread.
131 * Instead, we update the stats when a request is
132 * deleted, because only the main server thread calls
133 * this function, which makes it thread-safe.
135 switch (request->reply->code) {
136 case PW_AUTHENTICATION_ACK:
137 rad_snmp.auth.total_responses++;
138 rad_snmp.auth.total_access_accepts++;
139 if (request->client) request->client->auth->accepts++;
142 case PW_AUTHENTICATION_REJECT:
143 rad_snmp.auth.total_responses++;
144 rad_snmp.auth.total_access_rejects++;
145 if (request->client) request->client->auth->rejects++;
148 case PW_ACCESS_CHALLENGE:
149 rad_snmp.auth.total_responses++;
150 rad_snmp.auth.total_access_challenges++;
151 if (request->client) request->client->auth->challenges++;
154 case PW_ACCOUNTING_RESPONSE:
155 rad_snmp.acct.total_responses++;
156 if (request->client) request->client->auth->responses++;
160 * No response, it must have been a bad
164 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
165 rad_snmp.auth.total_bad_authenticators++;
166 if (request->client) request->client->auth->bad_authenticators++;
174 request->master_state = REQUEST_COUNTED;
177 #define snmp_inc_counters(_x)
181 static void remove_from_request_hash(REQUEST *request)
183 if (!request->in_request_hash) return;
185 lrad_packet_list_yank(pl, request->packet);
186 request->in_request_hash = FALSE;
188 snmp_inc_counters(request);
192 static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply)
194 RADIUS_PACKET **proxy_p;
197 PTHREAD_MUTEX_LOCK(&proxy_mutex);
198 proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
201 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
205 request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
208 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
212 request->num_proxied_responses++;
215 * Catch the most common case of everything working
218 if (request->num_proxied_requests == request->num_proxied_responses) {
219 lrad_packet_list_yank(proxy_list, request->proxy);
220 lrad_packet_list_id_free(proxy_list, request->proxy);
221 request->in_proxy_hash = FALSE;
225 * On the FIRST reply, decrement the count of outstanding
226 * requests. Note that this is NOT the count of sent
227 * packets, but whether or not the home server has
230 if (!request->proxy_reply &&
231 request->home_server->currently_outstanding) {
232 request->home_server->currently_outstanding--;
235 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
241 static void remove_from_proxy_hash(REQUEST *request)
243 if (!request->in_proxy_hash) return;
245 PTHREAD_MUTEX_LOCK(&proxy_mutex);
246 lrad_packet_list_yank(proxy_list, request->proxy);
247 lrad_packet_list_id_free(proxy_list, request->proxy);
250 * The home server hasn't replied, but we've given up on
251 * this request. Don't count this request against the
254 if (!request->proxy_reply &&
255 request->home_server->currently_outstanding) {
256 request->home_server->currently_outstanding--;
259 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
261 request->in_proxy_hash = FALSE;
265 static int insert_into_proxy_hash(REQUEST *request)
270 rad_assert(request->proxy != NULL);
271 rad_assert(proxy_list != NULL);
273 request->proxy->sockfd = -1;
275 PTHREAD_MUTEX_LOCK(&proxy_mutex);
277 request->home_server->currently_outstanding++;
278 request->home_server->total_requests_sent++;
281 * On overflow, back up to ~0.
283 if (!request->home_server->total_requests_sent) {
284 request->home_server->total_requests_sent--;
287 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
289 rad_listen_t *proxy_listener;
292 * Allocate a new proxy fd. This function adds it
293 * into the list of listeners.
295 proxy_listener = proxy_new_listener();
296 if (!proxy_listener) {
297 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
298 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
306 proxy = proxy_listener->fd;
307 for (i = 0; i < 32; i++) {
308 DEBUG2("PROXY %d %d", i, proxy_fds[(proxy + i) & 0x1f]);
311 * Found a free entry. Save the socket,
312 * and remember where we saved it.
314 if (proxy_fds[(proxy + i) & 0x1f] == -1) {
315 found = (proxy + i) & 0x1f;
316 proxy_fds[found] = proxy;
317 proxy_listeners[found] = proxy_listener;
321 rad_assert(found >= 0);
323 if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
324 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
325 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
326 return 0; /* leak proxy_listener */
330 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
331 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
332 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
336 rad_assert(request->proxy->sockfd >= 0);
339 * FIXME: Hack until we get rid of rad_listen_t, and put
340 * the information into the packet_list.
343 for (i = 0; i < 32; i++) {
344 if (proxy_fds[i] == request->proxy->sockfd) {
351 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
352 DEBUG2("ERROR: All sockets are full.");
356 rad_assert(proxy_fds[proxy] != -1);
357 rad_assert(proxy_listeners[proxy] != NULL);
358 request->proxy_listener = proxy_listeners[proxy];
360 if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
361 lrad_packet_list_id_free(proxy_list, request->proxy);
362 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
363 DEBUG2("ERROR: Failed to insert entry into proxy list");
367 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
369 DEBUG3(" proxy: allocating destination %s port %d - Id %d",
370 inet_ntop(request->proxy->dst_ipaddr.af,
371 &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
372 request->proxy->dst_port,
375 request->in_proxy_hash = TRUE;
382 * Called as BOTH an event, and in-line from other functions.
384 static void wait_for_proxy_id_to_expire(void *ctx)
386 REQUEST *request = ctx;
387 home_server *home = request->home_server;
389 rad_assert(request->magic == REQUEST_MAGIC);
390 rad_assert(request->proxy != NULL);
392 request->when = request->proxy_when;
393 request->when.tv_sec += home->response_window;
395 if ((request->num_proxied_requests == request->num_proxied_responses) ||
396 timercmp(&now, &request->when, >)) {
397 if (request->packet) {
398 DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
399 request->number, request->packet->id,
400 (unsigned int) (request->timestamp - start_time));
402 DEBUG2("Cleaning up request %d with timestamp +%d",
404 (unsigned int) (request->timestamp - start_time));
406 lrad_event_delete(el, &request->ev);
407 remove_from_proxy_hash(request);
408 remove_from_request_hash(request);
409 request_free(&request);
413 INSERT_EVENT(wait_for_proxy_id_to_expire, request);
417 static void wait_for_child_to_die(void *ctx)
419 REQUEST *request = ctx;
421 rad_assert(request->magic == REQUEST_MAGIC);
423 if ((request->child_state == REQUEST_QUEUED) |
424 (request->child_state == REQUEST_RUNNING)) {
425 request->delay += (request->delay >> 1);
426 tv_add(&request->when, request->delay);
428 DEBUG2("Child is still stuck for request %d", request->number);
430 INSERT_EVENT(wait_for_child_to_die, request);
434 DEBUG2("Child is finally responsive for request %d", request->number);
435 remove_from_request_hash(request);
437 if (request->proxy) {
438 wait_for_proxy_id_to_expire(request);
442 request_free(&request);
446 static void cleanup_delay(void *ctx)
448 REQUEST *request = ctx;
450 rad_assert(request->magic == REQUEST_MAGIC);
451 rad_assert((request->child_state == REQUEST_CLEANUP_DELAY) ||
452 (request->child_state == REQUEST_DONE));
454 remove_from_request_hash(request);
456 if (request->proxy && request->in_proxy_hash) {
457 wait_for_proxy_id_to_expire(request);
461 DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
462 request->number, request->packet->id,
463 (unsigned int) (request->timestamp - start_time));
465 lrad_event_delete(el, &request->ev);
466 request_free(&request);
470 static void reject_delay(void *ctx)
472 REQUEST *request = ctx;
474 rad_assert(request->magic == REQUEST_MAGIC);
475 rad_assert(request->child_state == REQUEST_REJECT_DELAY);
477 DEBUG2("Sending delayed reject for request %d", request->number);
479 request->listener->send(request->listener, request);
481 request->when.tv_sec += mainconfig.cleanup_delay;
482 request->child_state = REQUEST_CLEANUP_DELAY;
484 INSERT_EVENT(cleanup_delay, request);
488 static void revive_home_server(void *ctx)
490 home_server *home = ctx;
492 home->state = HOME_STATE_ALIVE;
493 DEBUG2("Marking home server alive again... we have no idea if it really is alive or not.");
494 home->currently_outstanding = 0;
498 static void no_response_to_ping(void *ctx)
500 REQUEST *request = ctx;
501 home_server *home = request->home_server;
504 home->num_received_pings = 0;
506 DEBUG2("No response to status check %d from home server %s port %d",
508 inet_ntop(request->proxy->dst_ipaddr.af,
509 &request->proxy->dst_ipaddr.ipaddr,
510 buffer, sizeof(buffer)),
511 request->proxy->dst_port);
513 wait_for_proxy_id_to_expire(request);
517 static void received_response_to_ping(REQUEST *request)
519 home_server *home = request->home_server;
522 home->num_received_pings++;
524 DEBUG2("Received response to status check %d (%d in current sequence)",
525 request->number, home->num_received_pings);
527 if (home->num_received_pings < home->num_pings_to_alive) {
528 wait_for_proxy_id_to_expire(request);
532 DEBUG2("Marking home server %s port %d alive",
533 inet_ntop(request->proxy->dst_ipaddr.af,
534 &request->proxy->dst_ipaddr.ipaddr,
535 buffer, sizeof(buffer)),
536 request->proxy->dst_port);
538 if (!lrad_event_delete(el, &home->ev)) {
539 DEBUG2("Hmm... no event for home server, WTF?");
542 if (!lrad_event_delete(el, &request->ev)) {
543 DEBUG2("Hmm... no event for request, WTF?");
546 wait_for_proxy_id_to_expire(request);
548 home->state = HOME_STATE_ALIVE;
549 home->currently_outstanding = 0;
553 static void ping_home_server(void *ctx)
556 home_server *home = ctx;
560 if (home->state == HOME_STATE_ALIVE) {
561 radlog(L_INFO, "Suspicious proxy state... continuing");
565 request = request_alloc();
566 request->number = request_num_counter++;
568 request->proxy = rad_alloc(1);
569 rad_assert(request->proxy != NULL);
571 gettimeofday(&request->when, NULL);
572 home->when = request->when;
574 if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
575 request->proxy->code = PW_STATUS_SERVER;
577 radius_pairmake(request, &request->proxy->vps,
578 "Message-Authenticator", "0x00");
580 } else if (home->type == HOME_TYPE_AUTH) {
581 request->proxy->code = PW_AUTHENTICATION_REQUEST;
583 radius_pairmake(request, &request->proxy->vps,
584 "User-Name", home->ping_user_name);
585 radius_pairmake(request, &request->proxy->vps,
586 "User-Password", home->ping_user_password);
587 radius_pairmake(request, &request->proxy->vps,
588 "Service-Type", "Authenticate-Only");
589 radius_pairmake(request, &request->proxy->vps,
590 "Message-Authenticator", "0x00");
593 request->proxy->code = PW_ACCOUNTING_REQUEST;
595 radius_pairmake(request, &request->proxy->vps,
596 "User-Name", home->ping_user_name);
597 radius_pairmake(request, &request->proxy->vps,
598 "Acct-Status-Type", "Stop");
599 radius_pairmake(request, &request->proxy->vps,
600 "Acct-Session-Id", "00000000");
601 vp = radius_pairmake(request, &request->proxy->vps,
602 "Event-Timestamp", "0");
603 vp->vp_date = now.tv_sec;
606 radius_pairmake(request, &request->proxy->vps,
607 "NAS-Identifier", "Status Check. Are you alive?");
609 request->proxy->dst_ipaddr = home->ipaddr;
610 request->proxy->dst_port = home->port;
611 request->home_server = home;
613 rad_assert(request->proxy_listener == NULL);
615 if (!insert_into_proxy_hash(request)) {
616 DEBUG2("Failed inserting status check %d into proxy hash. Discarding it.",
618 request_free(&request);
621 rad_assert(request->proxy_listener != NULL);
622 request->proxy_listener->send(request->proxy_listener,
625 request->next_callback = NULL;
626 request->child_state = REQUEST_PROXIED;
627 request->when.tv_sec += home->ping_timeout;;
629 INSERT_EVENT(no_response_to_ping, request);
632 * Add +/- 2s of jitter, as suggested in RFC 3539
633 * and in the Issues and Fixes draft.
635 home->when.tv_sec += home->ping_interval - 2;
637 jitter = lrad_rand();
638 jitter ^= (jitter >> 10);
639 jitter &= ((1 << 23) - 1); /* 22 bits of 1 */
641 tv_add(&home->when, jitter);
644 INSERT_EVENT(ping_home_server, home);
648 static void check_for_zombie_home_server(REQUEST *request)
654 home = request->home_server;
656 if (home->state != HOME_STATE_ZOMBIE) return;
658 when = home->zombie_period_start;
659 when.tv_sec += home->zombie_period;
661 if (timercmp(&now, &when, <)) {
666 * It's been a zombie for too long, mark it as
669 DEBUG2("FAILURE: Marking home server %s port %d as dead.",
670 inet_ntop(request->proxy->dst_ipaddr.af,
671 &request->proxy->dst_ipaddr.ipaddr,
672 buffer, sizeof(buffer)),
673 request->proxy->dst_port);
674 home->state = HOME_STATE_IS_DEAD;
675 home->num_received_pings = 0;
676 home->when = request->when;
678 if (home->ping_check != HOME_PING_CHECK_NONE) {
679 rad_assert((home->ping_check == HOME_PING_CHECK_STATUS_SERVER) ||
680 (home->ping_user_name != NULL));
681 home->when.tv_sec += home->ping_interval;
683 INSERT_EVENT(ping_home_server, home);
685 home->when.tv_sec += home->revive_interval;
687 INSERT_EVENT(revive_home_server, home);
692 static int setup_post_proxy_fail(REQUEST *request)
694 DICT_VALUE *dval = NULL;
697 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
698 dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Authentication");
700 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
701 dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail-Accounting");
707 if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, "Fail");
710 pairdelete(&request->config_items, PW_POST_PROXY_TYPE);
714 vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
715 if (!vp) vp = radius_paircreate(request, &request->config_items,
716 PW_POST_PROXY_TYPE, PW_TYPE_INTEGER);
717 vp->vp_integer = dval->value;
719 rad_assert(request->proxy_reply == NULL);
725 static int null_handler(UNUSED REQUEST *request)
730 static void post_proxy_fail_handler(REQUEST *request)
733 * Not set up to run Post-Proxy-Type = Fail.
735 * Mark the request as still running, and figure out what
738 if (!setup_post_proxy_fail(request)) {
739 request->child_state = REQUEST_RUNNING;
740 request_post_handler(request);
745 * Re-queue the request.
747 request->child_state = REQUEST_QUEUED;
752 * There is a post-proxy-type of fail. We run
753 * the request through the pre/post proxy
754 * handlers, just like it was a real proxied
755 * request. However, we set the per-request
756 * handler to NULL, as we don't want to do
759 request->priority = 0;
760 rad_assert(request->proxy != NULL);
761 thread_pool_addrequest(request, null_handler);
766 /* maybe check this against wait_for_proxy_id_to_expire? */
767 static void no_response_to_proxied_request(void *ctx)
769 REQUEST *request = ctx;
773 rad_assert(request->magic == REQUEST_MAGIC);
774 rad_assert(request->child_state == REQUEST_PROXIED);
776 radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
778 inet_ntop(request->proxy->dst_ipaddr.af,
779 &request->proxy->dst_ipaddr.ipaddr,
780 buffer, sizeof(buffer)),
781 request->proxy->dst_port);
783 check_for_zombie_home_server(request);
785 home = request->home_server;
787 post_proxy_fail_handler(request);
790 * Don't touch request due to race conditions
792 if (home->state == HOME_STATE_IS_DEAD) {
793 rad_assert(home->ev != NULL); /* or it will never wake up */
798 * Enable the zombie period when we notice that the home
799 * server hasn't responded. We also back-date the start
800 * of the zombie period to when the proxied request was
803 if (home->state == HOME_STATE_ALIVE) {
804 DEBUG2("WARNING: Marking home server %s port %d as zombie (it looks like it is dead).",
805 inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
806 buffer, sizeof(buffer)),
808 home->state = HOME_STATE_ZOMBIE;
809 home->zombie_period_start = now;
810 home->zombie_period_start.tv_sec -= home->response_window;
816 static void wait_a_bit(void *ctx)
819 REQUEST *request = ctx;
820 lrad_event_callback_t callback = NULL;
822 rad_assert(request->magic == REQUEST_MAGIC);
824 switch (request->child_state) {
826 case REQUEST_RUNNING:
827 when = request->received;
828 when.tv_sec += mainconfig.max_request_time;
830 if (timercmp(&now, &when, <)) {
831 callback = wait_a_bit;
833 /* FIXME: kill unresponsive children? */
834 radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d, in module %s component %s",
835 (unsigned long)request->child_pid, request->number,
836 request->module ? request->module : "<server core>",
837 request->component ? request->component : "<server core>");
839 request->master_state = REQUEST_STOP_PROCESSING;
841 request->delay = USEC / 2;
842 tv_add(&request->when, request->delay);
843 callback = wait_for_child_to_die;
845 request->delay += request->delay >> 1;
848 case REQUEST_REJECT_DELAY:
849 case REQUEST_CLEANUP_DELAY:
850 request->child_pid = NO_SUCH_CHILD_PID;
851 snmp_inc_counters(request);
853 case REQUEST_PROXIED:
854 rad_assert(request->next_callback != NULL);
855 rad_assert(request->next_callback != wait_a_bit);
857 request->when = request->next_when;
858 callback = request->next_callback;
859 request->next_callback = NULL;
863 * Mark the request as no longer running,
867 request->child_pid = NO_SUCH_CHILD_PID;
868 snmp_inc_counters(request);
869 cleanup_delay(request);
873 rad_panic("Internal sanity check failure");
877 INSERT_EVENT(callback, request);
881 static int request_pre_handler(REQUEST *request)
885 rad_assert(request->magic == REQUEST_MAGIC);
886 rad_assert(request->packet != NULL);
887 rad_assert(request->packet->dst_port != 0);
889 request->child_state = REQUEST_RUNNING;
892 * Don't decode the packet if it's an internal "fake"
893 * request. Instead, just return so that the caller can
896 if (request->packet->dst_port == 0) {
897 request->username = pairfind(request->packet->vps,
899 request->password = pairfind(request->packet->vps,
905 * Put the decoded packet into it's proper place.
907 if (request->proxy_reply != NULL) {
908 rcode = request->proxy_listener->decode(request->proxy_listener,
910 } else if (request->packet->vps == NULL) {
911 rcode = request->listener->decode(request->listener, request);
918 radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);
919 request->child_state = REQUEST_DONE;
923 if (!request->proxy) {
924 request->username = pairfind(request->packet->vps,
928 int post_proxy_type = 0;
932 * Delete any reply we had accumulated until now.
934 pairfree(&request->reply->vps);
937 * Run the packet through the post-proxy stage,
938 * BEFORE playing games with the attributes.
940 vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
942 DEBUG2(" Found Post-Proxy-Type %s", vp->vp_strvalue);
943 post_proxy_type = vp->vp_integer;
945 rcode = module_post_proxy(post_proxy_type, request);
948 * There may NOT be a proxy reply, as we may be
949 * running Post-Proxy-Type = Fail.
951 if (request->proxy_reply) {
953 * Delete the Proxy-State Attributes from
954 * the reply. These include Proxy-State
955 * attributes from us and remote server.
957 pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
960 * Add the attributes left in the proxy
961 * reply to the reply list.
963 pairadd(&request->reply->vps, request->proxy_reply->vps);
964 request->proxy_reply->vps = NULL;
967 * Free proxy request pairs.
969 pairfree(&request->proxy->vps);
973 default: /* Don't do anything */
975 case RLM_MODULE_FAIL:
976 /* FIXME: debug print stuff */
977 request->child_state = REQUEST_DONE;
980 case RLM_MODULE_HANDLED:
981 /* FIXME: debug print stuff */
982 request->child_state = REQUEST_DONE;
992 * Do state handling when we proxy a request.
994 static int proxy_request(REQUEST *request)
999 if (!insert_into_proxy_hash(request)) {
1000 DEBUG("Error: Failed inserting request into proxy hash.");
1004 request->proxy_listener->encode(request->proxy_listener, request);
1006 when = request->received;
1007 when.tv_sec += mainconfig.max_request_time;
1009 gettimeofday(&request->proxy_when, NULL);
1011 request->next_when = request->proxy_when;
1012 request->next_when.tv_sec += request->home_server->response_window;
1014 rad_assert(request->home_server->response_window > 0);
1016 if (timercmp(&when, &request->next_when, <)) {
1017 request->next_when = when;
1019 request->next_callback = no_response_to_proxied_request;
1021 DEBUG2("Proxying request %d to home server %s port %d",
1023 inet_ntop(request->proxy->dst_ipaddr.af,
1024 &request->proxy->dst_ipaddr.ipaddr,
1025 buffer, sizeof(buffer)),
1026 request->proxy->dst_port);
1029 * Note that we set proxied BEFORE sending the packet.
1031 * Once we send it, the request is tainted, as
1032 * another thread may have picked it up. Don't
1035 request->num_proxied_requests = 1;
1036 request->num_proxied_responses = 0;
1037 request->child_pid = NO_SUCH_CHILD_PID;
1038 request->child_state = REQUEST_PROXIED;
1039 request->proxy_listener->send(request->proxy_listener,
1045 * Return 1 if we did proxy it, or the proxy attempt failed
1046 * completely. Either way, the caller doesn't touch the request
1047 * any more if we return 1.
1049 static int successfully_proxied_request(REQUEST *request)
1052 int pre_proxy_type = 0;
1053 VALUE_PAIR *realmpair;
1054 VALUE_PAIR *strippedname;
1058 REALM *realm = NULL;
1061 realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
1062 if (!realmpair || (realmpair->length == 0)) {
1066 realmname = (char *) realmpair->vp_strvalue;
1068 realm = realm_find(realmname);
1070 DEBUG2("ERROR: Cannot proxy to unknown realm %s", realmname);
1075 * Figure out which pool to use.
1077 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
1078 pool = realm->auth_pool;
1080 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
1081 pool = realm->acct_pool;
1084 rad_panic("Internal sanity check failed");
1088 DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
1093 home = home_server_ldb(realmname, pool, request);
1095 DEBUG2("ERROR: Failed to find live home server for realm %s",
1099 request->home_pool = pool;
1102 * Remember that we sent the request to a Realm.
1104 pairadd(&request->packet->vps,
1105 pairmake("Realm", realmname, T_OP_EQ));
1108 * We read the packet from a detail file, AND it came from
1109 * the server we're about to send it to. Don't do that.
1111 if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
1112 (request->listener->type == RAD_LISTEN_DETAIL) &&
1113 (home->ipaddr.af == AF_INET) &&
1114 (request->packet->src_ipaddr.af == AF_INET) &&
1115 (home->ipaddr.ipaddr.ip4addr.s_addr == request->packet->src_ipaddr.ipaddr.ip4addr.s_addr)) {
1116 DEBUG2(" rlm_realm: Packet came from realm %s, proxy cancelled", realmname);
1121 * Allocate the proxy packet, only if it wasn't already
1122 * allocated by a module. This check is mainly to support
1123 * the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
1125 * In those cases, the EAP module creates a "fake"
1126 * request, and recursively passes it through the
1127 * authentication stage of the server. The module then
1128 * checks if the request was supposed to be proxied, and
1129 * if so, creates a proxy packet from the TUNNELED request,
1130 * and not from the EAP request outside of the tunnel.
1132 * The proxy then works like normal, except that the response
1133 * packet is "eaten" by the EAP module, and encapsulated into
1136 if (!request->proxy) {
1137 if ((request->proxy = rad_alloc(TRUE)) == NULL) {
1138 radlog(L_ERR|L_CONS, "no memory");
1143 * Copy the request, then look up name and
1144 * plain-text password in the copy.
1146 * Note that the User-Name attribute is the
1147 * *original* as sent over by the client. The
1148 * Stripped-User-Name attribute is the one hacked
1149 * through the 'hints' file.
1151 request->proxy->vps = paircopy(request->packet->vps);
1155 * Strip the name, if told to.
1157 * Doing it here catches the case of proxied tunneled
1160 if (realm->striprealm == TRUE &&
1161 (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
1163 * If there's a Stripped-User-Name attribute in
1164 * the request, then use THAT as the User-Name
1165 * for the proxied request, instead of the
1168 * This is done by making a copy of the
1169 * Stripped-User-Name attribute, turning it into
1170 * a User-Name attribute, deleting the
1171 * Stripped-User-Name and User-Name attributes
1172 * from the vps list, and making the new
1173 * User-Name the head of the vps list.
1175 vp = pairfind(request->proxy->vps, PW_USER_NAME);
1177 vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
1179 radlog(L_ERR|L_CONS, "no memory");
1182 /* Insert at the START of the list */
1183 vp->next = request->proxy->vps;
1184 request->proxy->vps = vp;
1186 memcpy(vp->vp_strvalue, strippedname->vp_strvalue,
1187 sizeof(vp->vp_strvalue));
1188 vp->length = strippedname->length;
1191 * Do NOT delete Stripped-User-Name.
1196 * If there is no PW_CHAP_CHALLENGE attribute but
1197 * there is a PW_CHAP_PASSWORD we need to add it
1198 * since we can't use the request authenticator
1199 * anymore - we changed it.
1201 if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
1202 pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
1203 vp = radius_paircreate(request, &request->proxy->vps,
1204 PW_CHAP_CHALLENGE, PW_TYPE_OCTETS);
1205 vp->length = AUTH_VECTOR_LEN;
1206 memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
1210 * The RFC's say we have to do this, but FreeRADIUS
1213 vp = radius_paircreate(request, &request->proxy->vps,
1214 PW_PROXY_STATE, PW_TYPE_OCTETS);
1215 sprintf(vp->vp_strvalue, "%d", request->packet->id);
1216 vp->length = strlen(vp->vp_strvalue);
1219 * Should be done BEFORE inserting into proxy hash, as
1220 * pre-proxy may use this information, or change it.
1222 request->proxy->code = request->packet->code;
1223 request->proxy->dst_ipaddr = home->ipaddr;
1224 request->proxy->dst_port = home->port;
1225 request->home_server = home;
1228 * Call the pre-proxy routines.
1230 vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
1232 DEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue);
1233 pre_proxy_type = vp->vp_integer;
1235 rcode = module_pre_proxy(pre_proxy_type, request);
1237 case RLM_MODULE_FAIL:
1238 case RLM_MODULE_INVALID:
1239 case RLM_MODULE_NOTFOUND:
1240 case RLM_MODULE_USERLOCK:
1242 /* FIXME: debug print failed stuff */
1245 case RLM_MODULE_REJECT:
1246 case RLM_MODULE_HANDLED:
1250 * Only proxy the packet if the pre-proxy code succeeded.
1252 case RLM_MODULE_NOOP:
1254 case RLM_MODULE_UPDATED:
1259 * If it's a fake request, don't send the proxy
1260 * packet. The outer tunnel session will take
1261 * care of doing that.
1263 if (request->packet->dst_port == 0) {
1264 request->home_server = NULL;
1268 if (!proxy_request(request)) {
1269 DEBUG("ERROR: Failed to proxy request %d", request->number);
1277 static void request_post_handler(REQUEST *request)
1279 int child_state = -1;
1280 struct timeval when;
1283 if ((request->master_state == REQUEST_STOP_PROCESSING) ||
1285 (request->parent->master_state == REQUEST_STOP_PROCESSING))) {
1286 DEBUG2("Request %d was cancelled.", request->number);
1287 request->child_pid = NO_SUCH_CHILD_PID;
1288 request->child_state = REQUEST_DONE;
1292 if (request->child_state != REQUEST_RUNNING) {
1293 rad_panic("Internal sanity check failed");
1296 if ((request->reply->code == 0) &&
1297 ((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
1298 (vp->vp_integer == PW_AUTHTYPE_REJECT)) {
1299 request->reply->code = PW_AUTHENTICATION_REJECT;
1302 if (mainconfig.proxy_requests &&
1304 (request->reply->code == 0) &&
1305 (request->packet->code != PW_STATUS_SERVER)) {
1306 int rcode = successfully_proxied_request(request);
1308 if (rcode == 1) return;
1311 * Failed proxying it (dead home servers, etc.)
1312 * Run it through Post-Proxy-Type = Fail, and
1313 * respond to the request.
1315 * Note that we're in a child thread here, so we
1316 * do NOT re-schedule the request. Instead, we
1317 * do what we would have done, which is run the
1318 * pre-handler, a NULL request handler, and then
1321 if ((rcode < 0) && setup_post_proxy_fail(request)) {
1322 request_pre_handler(request);
1326 * Else we weren't supposed to proxy it.
1331 * Fake requests don't get encoded or signed. The caller
1332 * also requires the reply VP's, so we don't free them
1335 if (request->packet->dst_port == 0) {
1336 /* FIXME: DEBUG going to the next request */
1337 request->child_pid = NO_SUCH_CHILD_PID;
1338 request->child_state = REQUEST_DONE;
1343 * Copy Proxy-State from the request to the reply.
1345 vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
1346 if (vp) pairadd(&request->reply->vps, vp);
1349 * Access-Requests get delayed or cached.
1351 if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
1352 gettimeofday(&request->next_when, NULL);
1354 if (request->reply->code == 0) {
1356 * Check if the lack of response is intentional.
1358 vp = pairfind(request->config_items,
1359 PW_RESPONSE_PACKET_TYPE);
1360 if (!vp || (vp->vp_integer != 256)) {
1361 DEBUG2("There was no response configured: rejecting request %d",
1363 request->reply->code = PW_AUTHENTICATION_REJECT;
1365 DEBUG2("Not responding to request %d",
1371 * Run rejected packets through
1373 * Post-Auth-Type = Reject
1375 if (request->reply->code == PW_AUTHENTICATION_REJECT) {
1376 vp = pairmake("Post-Auth-Type", "Reject", T_OP_SET);
1378 pairdelete(&request->config_items, PW_POST_AUTH_TYPE);
1379 pairadd(&request->config_items, vp);
1380 rad_postauth(request);
1381 } /* else no Reject section defined */
1384 * If configured, delay Access-Reject packets.
1386 * If mainconfig.reject_delay = 0, we discover
1387 * that we have to send the packet now.
1389 when = request->received;
1390 when.tv_sec += mainconfig.reject_delay;
1392 if (timercmp(&when, &request->next_when, >)) {
1393 DEBUG2("Delaying reject of request %d for %d seconds",
1395 mainconfig.reject_delay);
1396 request->next_when = when;
1397 request->next_callback = reject_delay;
1398 request->child_pid = NO_SUCH_CHILD_PID;
1399 request->child_state = REQUEST_REJECT_DELAY;
1404 request->next_when.tv_sec += mainconfig.cleanup_delay;
1405 request->next_callback = cleanup_delay;
1406 child_state = REQUEST_CLEANUP_DELAY;
1408 } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
1409 request->next_callback = NULL; /* just to be safe */
1410 child_state = REQUEST_DONE;
1413 * FIXME: Status-Server should probably not be
1416 } else if (request->packet->code == PW_STATUS_SERVER) {
1417 request->next_callback = NULL;
1418 child_state = REQUEST_DONE;
1421 rad_panic("Unknown packet type");
1425 * Suppress "no reply" packets here, unless we're reading
1426 * from the "detail" file. In that case, we've got to
1427 * tell the detail file handler that the request is dead,
1428 * and it should re-send it.
1429 * If configured, encode, sign, and send.
1431 if ((request->reply->code != 0) ||
1432 (request->listener->type == RAD_LISTEN_DETAIL)) {
1433 request->listener->send(request->listener, request);
1437 * Clean up. These are no longer needed.
1439 pairfree(&request->config_items);
1441 pairfree(&request->packet->vps);
1442 request->username = NULL;
1443 request->password = NULL;
1445 pairfree(&request->reply->vps);
1447 if (request->proxy) {
1448 pairfree(&request->proxy->vps);
1450 if (request->proxy_reply) {
1451 pairfree(&request->proxy_reply->vps);
1455 * We're not tracking responses from the home
1456 * server, we can therefore free this memory in
1459 if (!request->in_proxy_hash) {
1460 rad_free(&request->proxy);
1461 rad_free(&request->proxy_reply);
1462 request->home_server = NULL;
1466 DEBUG2("Finished request %d state %d", request->number, child_state);
1468 request->child_state = child_state;
1472 static void received_retransmit(REQUEST *request, const RADCLIENT *client)
1476 RAD_SNMP_TYPE_INC(request->listener, total_dup_requests);
1477 RAD_SNMP_CLIENT_INC(request->listener, client, dup_requests);
1479 switch (request->child_state) {
1480 case REQUEST_QUEUED:
1481 case REQUEST_RUNNING:
1483 radlog(L_ERR, "Discarding duplicate request from "
1484 "client %s port %d - ID: %d due to unfinished request %d",
1486 request->packet->src_port,request->packet->id,
1490 case REQUEST_PROXIED:
1492 * We're not supposed to have duplicate
1493 * accounting packets. The other states handle
1494 * duplicates fine (discard, or send duplicate
1495 * reply). But we do NOT want to retransmit an
1496 * accounting request here, because that would
1497 * involve updating the Acct-Delay-Time, and
1498 * therefore changing the packet Id, etc.
1500 * Instead, we just discard the packet. We may
1501 * eventually respond, or the client will send a
1502 * new accounting packet.
1504 if (request->packet->code == PW_ACCOUNTING_REQUEST) {
1508 check_for_zombie_home_server(request);
1511 * If we've just discovered that the home server is
1512 * dead, send the packet to another one.
1514 if ((request->packet->dst_port != 0) &&
1515 (request->home_server->state == HOME_STATE_IS_DEAD)) {
1518 remove_from_proxy_hash(request);
1520 home = home_server_ldb(NULL, request->home_pool, request);
1522 DEBUG2("Failed to find live home server for request %d", request->number);
1525 * Do post-request processing,
1526 * and any insertion of necessary
1529 post_proxy_fail_handler(request);
1533 request->proxy->code = request->packet->code;
1534 request->proxy->dst_ipaddr = home->ipaddr;
1535 request->proxy->dst_port = home->port;
1536 request->home_server = home;
1539 * Free the old packet, to force re-encoding
1541 free(request->proxy->data);
1542 request->proxy->data = NULL;
1543 request->proxy->data_len = 0;
1546 * Try to proxy the request.
1548 if (!proxy_request(request)) {
1549 DEBUG("ERROR: Failed to re-proxy request %d", request->number);
1550 goto no_home_servers;
1554 * This code executes in the main server
1555 * thread, so there's no need for locking.
1557 rad_assert(request->next_callback != NULL);
1558 INSERT_EVENT(request->next_callback, request);
1559 request->next_callback = NULL;
1561 } /* else the home server is still alive */
1563 DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
1564 inet_ntop(request->proxy->dst_ipaddr.af,
1565 &request->proxy->dst_ipaddr.ipaddr,
1566 buffer, sizeof(buffer)),
1567 request->proxy->dst_port,
1568 request->proxy->id);
1569 request->num_proxied_requests++;
1570 request->proxy_listener->send(request->proxy_listener,
1574 case REQUEST_REJECT_DELAY:
1575 DEBUG2("Waiting to send Access-Reject "
1576 "to client %s port %d - ID: %d",
1578 request->packet->src_port, request->packet->id);
1581 case REQUEST_CLEANUP_DELAY:
1583 DEBUG2("Sending duplicate reply "
1584 "to client %s port %d - ID: %d",
1586 request->packet->src_port, request->packet->id);
1587 request->listener->send(request->listener, request);
1593 static void received_conflicting_request(REQUEST *request,
1594 const RADCLIENT *client)
1596 radlog(L_ERR, "Received conflicting packet from "
1597 "client %s port %d - ID: %d due to unfinished request %d. Giving up on old request.",
1599 request->packet->src_port, request->packet->id,
1603 * Nuke it from the request hash, so we can receive new
1606 remove_from_request_hash(request);
1608 switch (request->child_state) {
1610 * It's queued or running. Tell it to stop, and
1611 * wait for it to do so.
1613 case REQUEST_QUEUED:
1614 case REQUEST_RUNNING:
1615 request->master_state = REQUEST_STOP_PROCESSING;
1616 request->delay += request->delay >> 1;
1618 tv_add(&request->when, request->delay);
1620 INSERT_EVENT(wait_for_child_to_die, request);
1624 * It's in some other state, and therefore also
1625 * in the event queue. At some point, the
1626 * child will notice, and we can then delete it.
1629 rad_assert(request->ev != NULL);
1635 static int can_handle_new_request(RADIUS_PACKET *packet,
1639 * Count the total number of requests, to see if
1640 * there are too many. If so, return with an
1643 if (mainconfig.max_requests) {
1644 int request_count = lrad_packet_list_num_elements(pl);
1647 * This is a new request. Let's see if
1648 * it makes us go over our configured
1651 if (request_count > mainconfig.max_requests) {
1652 radlog(L_ERR, "Dropping request (%d is too many): "
1653 "from client %s port %d - ID: %d", request_count,
1655 packet->src_port, packet->id);
1656 radlog(L_INFO, "WARNING: Please check the %s file.\n"
1657 "\tThe value for 'max_requests' is probably set too low.\n", mainconfig.radiusd_conf);
1659 } /* else there were a small number of requests */
1660 } /* else there was no configured limit for requests */
1663 * FIXME: Add per-client checks. If one client is sending
1664 * too many packets, start discarding them.
1666 * We increment the counters here, and decrement them
1667 * when the response is sent... somewhere in this file.
1671 * FUTURE: Add checks for system load. If the system is
1672 * busy, start dropping requests...
1674 * We can probably keep some statistics ourselves... if
1675 * there are more requests coming in than we can handle,
1676 * start dropping some.
1683 int received_request(rad_listen_t *listener,
1684 RADIUS_PACKET *packet, REQUEST **prequest,
1687 RADIUS_PACKET **packet_p;
1688 REQUEST *request = NULL;
1690 packet_p = lrad_packet_list_find(pl, packet);
1692 request = lrad_packet2myptr(REQUEST, packet, packet_p);
1693 rad_assert(request->in_request_hash);
1695 if ((request->packet->data_len == packet->data_len) &&
1696 (memcmp(request->packet->vector, packet->vector,
1697 sizeof(packet->vector)) == 0)) {
1698 received_retransmit(request, client);
1703 * The new request is different from the old one,
1704 * but maybe the old is finished. If so, delete
1707 switch (request->child_state) {
1708 struct timeval when;
1711 gettimeofday(&when, NULL);
1715 * If the cached request was received
1716 * within the last second, then we
1717 * discard the NEW request instead of the
1718 * old one. This will happen ONLY when
1719 * the client is severely broken, and is
1720 * sending conflicting packets very
1723 if (timercmp(&when, &request->received, <)) {
1724 radlog(L_ERR, "Discarding conflicting packet from "
1725 "client %s port %d - ID: %d due to recent request %d.",
1727 packet->src_port, packet->id,
1732 received_conflicting_request(request, client);
1736 case REQUEST_REJECT_DELAY:
1737 case REQUEST_CLEANUP_DELAY:
1738 request->child_state = REQUEST_DONE;
1740 cleanup_delay(request);
1749 * We may want to quench the new request.
1751 if (!can_handle_new_request(packet, client)) {
1756 * Create and initialize the new request.
1758 request = request_alloc(); /* never fails */
1760 if ((request->reply = rad_alloc(0)) == NULL) {
1761 radlog(L_ERR, "No memory");
1765 request->listener = listener;
1766 request->client = client;
1767 request->packet = packet;
1768 request->packet->timestamp = request->timestamp;
1769 request->number = request_num_counter++;
1770 request->priority = listener->type;
1773 * Set virtual server identity
1775 if (client->server) {
1776 request->server = client->server;
1777 } else if (listener->server) {
1778 request->server = listener->server;
1780 request->server = NULL;
1784 * Remember the request in the list.
1786 if (!lrad_packet_list_insert(pl, &request->packet)) {
1787 radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", request->number);
1788 request_free(&request);
1791 request->in_request_hash = TRUE;
1794 * The request passes many of our sanity checks.
1795 * From here on in, if anything goes wrong, we
1796 * send a reject message, instead of dropping the
1801 * Build the reply template from the request.
1804 request->reply->sockfd = request->packet->sockfd;
1805 request->reply->dst_ipaddr = request->packet->src_ipaddr;
1806 request->reply->src_ipaddr = request->packet->dst_ipaddr;
1807 request->reply->dst_port = request->packet->src_port;
1808 request->reply->src_port = request->packet->dst_port;
1809 request->reply->id = request->packet->id;
1810 request->reply->code = 0; /* UNKNOWN code */
1811 memcpy(request->reply->vector, request->packet->vector,
1812 sizeof(request->reply->vector));
1813 request->reply->vps = NULL;
1814 request->reply->data = NULL;
1815 request->reply->data_len = 0;
1817 request->master_state = REQUEST_ACTIVE;
1818 request->child_state = REQUEST_QUEUED;
1819 request->next_callback = NULL;
1821 gettimeofday(&request->received, NULL);
1822 request->timestamp = request->received.tv_sec;
1823 request->when = request->received;
1825 request->delay = USEC / 10;
1827 tv_add(&request->when, request->delay);
1829 INSERT_EVENT(wait_a_bit, request);
1831 *prequest = request;
1836 REQUEST *received_proxy_response(RADIUS_PACKET *packet)
1842 if (!home_server_find(&packet->src_ipaddr, packet->src_port)) {
1843 radlog(L_ERR, "Ignoring request from unknown home server %s port %d",
1844 inet_ntop(packet->src_ipaddr.af,
1845 &packet->src_ipaddr.ipaddr,
1846 buffer, sizeof(buffer)),
1853 * Also removes from the proxy hash if responses == requests
1855 request = lookup_in_proxy_hash(packet);
1858 radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s port %d - ID %d",
1859 inet_ntop(packet->src_ipaddr.af,
1860 &packet->src_ipaddr.ipaddr,
1861 buffer, sizeof(buffer)),
1862 packet->src_port, packet->id);
1867 home = request->home_server;
1869 gettimeofday(&now, NULL);
1870 home->state = HOME_STATE_ALIVE;
1872 if (request->reply && request->reply->code != 0) {
1873 DEBUG2("We already replied to this request. Discarding response from home server.");
1879 * We had previously received a reply, so we don't need
1880 * to do anything here.
1882 if (request->proxy_reply) {
1883 if (memcmp(request->proxy_reply->vector,
1885 sizeof(request->proxy_reply->vector)) == 0) {
1886 DEBUG2("Discarding duplicate reply from home server %s port %d - ID: %d for request %d",
1887 inet_ntop(packet->src_ipaddr.af,
1888 &packet->src_ipaddr.ipaddr,
1889 buffer, sizeof(buffer)),
1890 packet->src_port, packet->id,
1894 * ? The home server gave us a new proxy
1895 * reply, which doesn't match the old
1898 DEBUG2("Ignoring conflicting proxy reply");
1901 /* assert that there's an event queued for request? */
1906 switch (request->child_state) {
1907 case REQUEST_QUEUED:
1908 case REQUEST_RUNNING:
1909 rad_panic("Internal sanity check failed for child state");
1912 case REQUEST_REJECT_DELAY:
1913 case REQUEST_CLEANUP_DELAY:
1915 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'",
1916 inet_ntop(packet->src_ipaddr.af,
1917 &packet->src_ipaddr.ipaddr,
1918 buffer, sizeof(buffer)),
1919 packet->src_port, packet->id,
1921 /* assert that there's an event queued for request? */
1925 case REQUEST_PROXIED:
1929 request->proxy_reply = packet;
1933 * Perform RTT calculations, as per RFC 2988 (for TCP).
1934 * Note that we do so only if we sent one request, and
1935 * received one response. If we sent two requests, we
1936 * have no idea if the response is for the first, or for
1937 * the second request/
1939 if (request->num_proxied_requests == 1) {
1941 home_server *home = request->home_server;
1943 rtt = now.tv_sec - request->proxy_when.tv_sec;
1946 rtt -= request->proxy_when.tv_usec;
1948 if (!home->has_rtt) {
1949 home->has_rtt = TRUE;
1952 home->rttvar = rtt / 2;
1955 home->rttvar -= home->rttvar >> 2;
1956 home->rttvar += (home->srtt - rtt);
1957 home->srtt -= home->srtt >> 3;
1958 home->srtt += rtt >> 3;
1961 home->rto = home->srtt;
1962 if (home->rttvar > (USEC / 4)) {
1963 home->rto += home->rttvar * 4;
1971 * There's no incoming request, so it's a proxied packet
1974 if (!request->packet) {
1975 received_response_to_ping(request);
1979 request->child_state = REQUEST_QUEUED;
1980 request->when = now;
1981 request->delay = USEC / 10;
1982 request->priority = RAD_LISTEN_PROXY;
1983 tv_add(&request->when, request->delay);
1986 * Wait a bit will take care of max_request_time
1988 INSERT_EVENT(wait_a_bit, request);
1995 * Externally-visibly functions.
1997 int radius_event_init(int spawn_flag)
2003 el = lrad_event_list_create();
2006 pl = lrad_packet_list_create(0);
2009 request_num_counter = 0;
2012 * Move all of the thread calls to this file?
2014 * It may be best for the mutexes to be in this file...
2016 have_children = spawn_flag;
2018 if (mainconfig.proxy_requests) {
2020 rad_listen_t *listener;
2023 * Create the tree for managing proxied requests and
2026 proxy_list = lrad_packet_list_create(1);
2027 if (!proxy_list) return 0;
2029 #ifdef HAVE_PTHREAD_H
2030 if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
2031 radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
2038 * Mark the Fd's as unused.
2040 for (i = 0; i < 32; i++) proxy_fds[i] = -1;
2044 for (listener = mainconfig.listen;
2046 listener = listener->next) {
2047 if (listener->type == RAD_LISTEN_PROXY) {
2049 * FIXME: This works only because we
2050 * start off with one proxy socket.
2052 rad_assert(proxy_fds[listener->fd & 0x1f] == -1);
2053 rad_assert(proxy_listeners[listener->fd & 0x1f] == NULL);
2055 proxy_fds[listener->fd & 0x1f] = listener->fd;
2056 proxy_listeners[listener->fd & 0x1f] = listener;
2057 if (!lrad_packet_list_socket_add(proxy_list, listener->fd)) {
2064 if (mainconfig.proxy_requests) rad_assert(i >= 0);
2067 if (thread_pool_init(spawn_flag) < 0) {
2075 static int request_hash_cb(void *ctx, void *data)
2077 ctx = ctx; /* -Wunused */
2078 REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
2080 rad_assert(request->in_proxy_hash == FALSE);
2082 lrad_event_delete(el, &request->ev);
2083 remove_from_request_hash(request);
2084 request_free(&request);
2090 static int proxy_hash_cb(void *ctx, void *data)
2092 ctx = ctx; /* -Wunused */
2093 REQUEST *request = lrad_packet2myptr(REQUEST, proxy, data);
2095 lrad_packet_list_yank(proxy_list, request->proxy);
2096 request->in_proxy_hash = FALSE;
2098 if (!request->in_request_hash) {
2099 lrad_event_delete(el, &request->ev);
2100 request_free(&request);
2107 void radius_event_free(void)
2110 * FIXME: Stop all threads, or at least check that
2111 * they're all waiting on the semaphore, and the queues
2116 * There are requests in the proxy hash that aren't
2117 * referenced from anywhere else. Remove them first.
2120 PTHREAD_MUTEX_LOCK(&proxy_mutex);
2121 lrad_packet_list_walk(proxy_list, NULL, proxy_hash_cb);
2122 PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
2123 lrad_packet_list_free(proxy_list);
2127 lrad_packet_list_walk(pl, NULL, request_hash_cb);
2129 lrad_packet_list_free(pl);
2132 lrad_event_list_free(el);
2135 int radius_event_process(struct timeval **pptv)
2138 struct timeval when;
2142 if (lrad_event_list_num_elements(el) == 0) {
2147 gettimeofday(&now, NULL);
2151 rcode = lrad_event_run(el, &when);
2152 } while (rcode == 1);
2154 gettimeofday(&now, NULL);
2156 if ((when.tv_sec == 0) && (when.tv_usec == 0)) {
2157 if (lrad_event_list_num_elements(el) == 0) {
2161 rad_panic("Internal sanity check failed");
2163 } else if (timercmp(&now, &when, >)) {
2164 DEBUG3("Event in the past... compensating");
2169 when.tv_sec -= now.tv_sec;
2170 when.tv_usec -= now.tv_usec;
2171 if (when.tv_usec < 0) {
2173 when.tv_usec += USEC;
2181 void radius_handle_request(REQUEST *request, RAD_REQUEST_FUNP fun)
2183 if (request_pre_handler(request)) {
2184 rad_assert(fun != NULL);
2185 rad_assert(request != NULL);
2187 if (request->server) DEBUG("server %s {",
2191 if (request->server) DEBUG("} # server %s",
2194 request_post_handler(request);
2197 DEBUG2("Going to the next request");