2 * request_list.c Hide the handling of the REQUEST list from
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2003-2004,2006 The FreeRADIUS server project
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/autoconf.h>
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/rad_assert.h>
35 #include <freeradius-devel/request_list.h>
36 #include <freeradius-devel/radius_snmp.h>
38 struct request_list_t {
39 lrad_packet_list_t *pl;
43 static pthread_mutex_t proxy_mutex;
46 * This is easier than ifdef's throughout the code.
48 #define pthread_mutex_lock(_x)
49 #define pthread_mutex_unlock(_x)
52 static lrad_packet_list_t *proxy_list = NULL;
55 * We keep the proxy FD's here. The RADIUS Id's are marked
56 * "allocated" per Id, via a bit per proxy FD.
58 static int proxy_fds[32];
59 static rad_listen_t *proxy_listeners[32];
62 * Initialize the request list.
64 request_list_t *rl_init(void)
66 request_list_t *rl = rad_malloc(sizeof(*rl));
69 * Initialize the request_list[] array.
71 memset(rl, 0, sizeof(*rl));
73 rl->pl = lrad_packet_list_create(0);
75 rad_assert("FAIL" == NULL);
81 int rl_init_proxy(void)
84 * Hacks, so that multiple users can call rl_init,
85 * and it won't get excited.
87 * FIXME: Move proxy stuff to another struct entirely.
89 if (proxy_list) return 0;
92 * Create the tree for managing proxied requests and
95 proxy_list = lrad_packet_list_create(1);
97 rad_assert("FAIL" == NULL);
100 #ifdef HAVE_PTHREAD_H
102 * For now, always create the mutex.
104 * Later, we can only create it if there are multiple threads.
106 if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
107 radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
115 rad_listen_t *listener;
118 * Mark the Fd's as unused.
120 for (i = 0; i < 32; i++) proxy_fds[i] = -1;
122 for (listener = mainconfig.listen;
124 listener = listener->next) {
125 if (listener->type == RAD_LISTEN_PROXY) {
127 * FIXME: This works only because we
128 * start off with one proxy socket.
130 proxy_fds[listener->fd & 0x1f] = listener->fd;
131 proxy_listeners[listener->fd & 0x1f] = listener;
132 lrad_packet_list_socket_add(proxy_list, listener->fd);
141 static int rl_free_entry(void *ctx, void *data)
143 ctx = ctx; /* -Wunused */
144 REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
146 #ifdef HAVE_PTHREAD_H
148 * If someone is processing this request, kill
149 * them, and mark the request as not being used.
151 * FIXME: Move the request to the "dead pool",
152 * and don't kill the thread.
154 if (request->child_pid != NO_SUCH_CHILD_PID) {
155 pthread_kill(request->child_pid, SIGKILL);
156 request->child_pid = NO_SUCH_CHILD_PID;
159 request_free(&request);
166 * Delete everything in the request list.
168 * This should be called only when debugging the server...
170 void rl_deinit(request_list_t *rl)
175 lrad_packet_list_free(proxy_list);
180 * Delete everything in the table, too.
182 lrad_packet_list_walk(rl->pl, NULL, rl_free_entry);
184 lrad_packet_list_free(rl->pl);
187 * Just to ensure no one is using the memory.
189 memset(rl, 0, sizeof(*rl));
195 * Yank a request from the tree, without free'ing it.
197 void rl_yank(request_list_t *rl, REQUEST *request)
201 * Update the SNMP statistics.
203 * Note that we do NOT do this in rad_respond(),
204 * as that function is called from child threads.
205 * Instead, we update the stats when a request is
206 * deleted, because only the main server thread calls
209 if (mainconfig.do_snmp) {
210 switch (request->reply->code) {
211 case PW_AUTHENTICATION_ACK:
212 rad_snmp.auth.total_responses++;
213 rad_snmp.auth.total_access_accepts++;
216 case PW_AUTHENTICATION_REJECT:
217 rad_snmp.auth.total_responses++;
218 rad_snmp.auth.total_access_rejects++;
221 case PW_ACCESS_CHALLENGE:
222 rad_snmp.auth.total_responses++;
223 rad_snmp.auth.total_access_challenges++;
226 case PW_ACCOUNTING_RESPONSE:
227 rad_snmp.acct.total_responses++;
237 * Delete the request from the list.
239 lrad_packet_list_yank(rl->pl, request->packet);
242 * If there's a proxied packet, and we're still
243 * waiting for a reply, then delete the packet
244 * from the list of outstanding proxied requests.
246 if (request->proxy &&
247 (request->proxy_outstanding > 0)) {
248 pthread_mutex_lock(&proxy_mutex);
249 lrad_packet_list_yank(proxy_list, request->proxy);
250 lrad_packet_list_id_free(proxy_list, request->proxy);
251 pthread_mutex_unlock(&proxy_mutex);
257 * Delete a request from the tree.
259 void rl_delete(request_list_t *rl, REQUEST *request)
261 rl_yank(rl, request);
262 request_free(&request);
267 * Add a request to the request list.
269 int rl_add(request_list_t *rl, REQUEST *request)
271 return lrad_packet_list_insert(rl->pl, &request->packet);
275 * Look up a particular request, using:
277 * Request ID, request code, source IP, source port,
279 * Note that we do NOT use the request vector to look up requests.
281 * We MUST NOT have two requests with identical (id/code/IP/port), and
282 * different vectors. This is a serious error!
284 REQUEST *rl_find(request_list_t *rl, RADIUS_PACKET *packet)
286 RADIUS_PACKET **packet_p;
288 packet_p = lrad_packet_list_find(rl->pl, packet);
289 if (!packet_p) return NULL;
291 return lrad_packet2myptr(REQUEST, packet, packet_p);
295 * Add an entry to the proxy tree.
297 * This is the ONLY function in this source file which may be called
298 * from a child thread. It therefore needs mutexes...
300 int rl_add_proxy(REQUEST *request)
305 request->proxy_outstanding = 1;
306 request->proxy->sockfd = -1;
308 pthread_mutex_lock(&proxy_mutex);
310 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
312 rad_listen_t *proxy_listener;
315 * Allocate a new proxy Fd. This function adds it
316 * into the list of listeners.
318 proxy_listener = proxy_new_listener();
319 if (!proxy_listener) {
320 pthread_mutex_unlock(&proxy_mutex);
321 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
329 proxy = proxy_listener->fd;
330 for (i = 0; i < 32; i++) {
332 * Found a free entry. Save the socket,
333 * and remember where we saved it.
335 if (proxy_fds[(proxy + i) & 0x1f] == -1) {
336 found = (proxy + i) & 0x1f;
337 proxy_fds[found] = proxy;
338 proxy_listeners[found] = proxy_listener;
342 rad_assert(found >= 0);
344 if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
345 pthread_mutex_unlock(&proxy_mutex);
346 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
347 return 0; /* leak proxy_listener */
351 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
352 pthread_mutex_unlock(&proxy_mutex);
353 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
359 * FIXME: Hack until we get rid of rad_listen_t, and put
360 * the information into the packet_list.
363 for (i = 0; i < 32; i++) {
364 if (proxy_fds[i] == request->proxy->sockfd) {
369 rad_assert(proxy >= 0);
371 rad_assert(proxy_fds[proxy] != -1);
372 request->proxy_listener = proxy_listeners[proxy];
374 if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
375 pthread_mutex_unlock(&proxy_mutex);
376 DEBUG2("ERROR: Failed to insert entry into proxy list");
380 pthread_mutex_unlock(&proxy_mutex);
382 DEBUG3(" proxy: allocating destination %s port %d - Id %d",
383 inet_ntop(request->proxy->dst_ipaddr.af,
384 &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
385 request->proxy->dst_port,
393 * Look up a particular request, using:
395 * Request Id, request code, source IP, source port,
397 * Note that we do NOT use the request vector to look up requests.
399 * We MUST NOT have two requests with identical (id/code/IP/port), and
400 * different vectors. This is a serious error!
402 REQUEST *rl_find_proxy(RADIUS_PACKET *reply)
404 RADIUS_PACKET **proxy_p;
407 pthread_mutex_lock(&proxy_mutex);
408 proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
411 pthread_mutex_unlock(&proxy_mutex);
415 request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
416 rad_assert(request->proxy_outstanding > 0);
417 request->proxy_outstanding--;
420 * Received all of the replies we expect.
421 * delete it from the managed list.
423 if (request->proxy_outstanding == 0) {
424 lrad_packet_list_yank(proxy_list, request->proxy);
425 lrad_packet_list_id_free(proxy_list, request->proxy);
427 pthread_mutex_unlock(&proxy_mutex);
434 * Return the number of requests in the request list.
436 int rl_num_requests(request_list_t *rl)
438 return lrad_packet_list_num_elements(rl->pl);
445 #define SLEEP_FOREVER (65536)
446 typedef struct rl_walk_t {
454 * Refresh a request, by using cleanup_delay, max_request_time, etc.
456 * When walking over the request list, all of the per-request
457 * magic is done here.
459 static int refresh_request(void *ctx, void *data)
462 rl_walk_t *info = (rl_walk_t *) ctx;
463 child_pid_t child_pid;
464 request_list_t *rl = info->rl;
465 REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
467 rad_assert(request->magic == REQUEST_MAGIC);
469 time_passed = (int) (info->now - request->timestamp);
472 * If the request is marked as a delayed reject, AND it's
473 * time to send the reject, then do so now.
475 if (request->finished &&
476 ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) {
477 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
478 if (time_passed < mainconfig.reject_delay) {
484 * Clear the 'delayed reject' bit, so that we
485 * don't do this again, and fall through to
486 * setting cleanup delay.
488 request->listener->send(request->listener, request);
489 request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
492 * FIXME: Beware interaction with cleanup_delay,
493 * where we might send a reject, and immediately
494 * there-after clean it up!
499 * If the request is finished, THEN
500 * check that more than cleanup_delay seconds have passed
501 * since it was received
502 * OR, if this is a request which had the "don't cache"
503 * option set, then it CANNOT have a duplicate
506 if (request->finished &&
507 ((time_passed >= mainconfig.cleanup_delay) ||
508 ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) {
509 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
511 * Request completed, delete it, and unlink it
512 * from the currently 'alive' list of requests.
515 DEBUG2("Cleaning up request %d ID %d with timestamp %08lx",
516 request->number, request->packet->id,
517 (unsigned long) request->timestamp);
520 * Delete the request.
522 rl_delete(rl, request);
527 * If more than max_request_time has passed since
528 * we received the request, kill it.
530 if (time_passed >= mainconfig.max_request_time) {
533 child_pid = request->child_pid;
534 number = request->number;
537 * There MUST be a RAD_PACKET reply.
539 rad_assert(request->reply != NULL);
542 * If we've tried to proxy the request, and
543 * the proxy server hasn't responded, then
544 * we send a REJECT back to the caller.
546 * For safety, we assert that there is no child
547 * handling the request. If the assertion fails,
548 * it means that we've sent a proxied request to
549 * the home server, and the child thread is still
550 * sitting on the request!
552 if (request->proxy && !request->proxy_reply) {
553 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
555 radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
557 client_name_old(&request->packet->src_ipaddr),
558 request->packet->src_port);
559 request_reject(request, REQUEST_FAIL_HOME_SERVER);
560 request->finished = TRUE;
564 #ifdef DELETE_BLOCKED_REQUESTS
566 * Calling pthread_cancel() without a cancel handler is an
567 * exceedingly bad idea. This code is left here in case
568 * we implement per-module cancel handlers later.
569 * See freeradius-devel archives,
570 * "cancelling requests due to max_request_time"
572 * If implemented, just remove the #ifdef's for DELETE_BLOCKED_REQUESTS
573 * scattered throughout the code (this file and others), and
574 * add the option back to radiusd.conf.in.
576 if (mainconfig.kill_unresponsive_children) {
577 if (child_pid != NO_SUCH_CHILD_PID) {
579 * This request seems to have hung
582 #ifdef HAVE_PTHREAD_H
583 radlog(L_ERR, "Killing unresponsive thread for request %d",
585 pthread_cancel(child_pid);
587 } /* else no proxy reply, quietly fail */
590 * Maybe we haven't killed it. In that
591 * case, print a warning.
595 if ((child_pid != NO_SUCH_CHILD_PID) &&
596 ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) {
597 radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d",
598 (unsigned long)child_pid, number);
601 * Set the option that we've sent a log message,
602 * so that we don't send more than one message
605 request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD;
609 * Send a reject message for the request, mark it
610 * finished, and forget about the child.
612 request_reject(request, REQUEST_FAIL_SERVER_TIMEOUT);
614 request->child_pid = NO_SUCH_CHILD_PID;
616 #ifdef DELETE_BLOCKED_REQUESTS
617 if (mainconfig.kill_unresponsive_children)
618 request->finished = TRUE;
621 } /* else the request is still allowed to be in the queue */
624 * If the request is finished, set the cleanup delay.
626 if (request->finished) {
627 time_passed = mainconfig.cleanup_delay - time_passed;
632 * Accounting request. Don't re-send them, since the NAS
633 * will take care of doing that, and we're not a NAS.
634 * Instead, simply clean them up once we're pretty sure
635 * that the home server won't be responding.
637 if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
638 request->proxy && !request->proxy_reply &&
639 (request->child_pid == NO_SUCH_CHILD_PID) &&
640 ((info->now - request->proxy_start_time) > (mainconfig.proxy_retry_delay * 2))) {
646 * Set reject delay, if appropriate.
648 if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
649 (mainconfig.reject_delay > 0)) {
651 time_passed = mainconfig.reject_delay - time_passed;
654 * This catches a corner case, apparently.
656 if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
657 (time_passed == 0)) goto reject_packet;
658 if (time_passed <= 0) time_passed = 1;
663 * The request is still alive, wake up when it's
666 time_passed = mainconfig.max_request_time - time_passed;
669 if (time_passed < 0) time_passed = 1;
671 if (time_passed < info->sleep_time) {
672 info->sleep_time = time_passed;
680 * Clean up the request list, every so often.
682 * This is done by walking through ALL of the list, and
683 * - marking any requests which are finished, and expired
684 * - killing any processes which are NOT finished after a delay
685 * - deleting any marked requests.
687 * Returns the number of millisends to sleep, before processing
690 int rl_clean_list(request_list_t *rl, time_t now)
695 info.sleep_time = SLEEP_FOREVER;
698 lrad_packet_list_walk(rl->pl, &info, refresh_request);
700 if (info.sleep_time < 0) info.sleep_time = 0;
702 return info.sleep_time;