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 REQUEST *request = data;
145 ctx = ctx; /* -Wunused */
147 #ifdef HAVE_PTHREAD_H
149 * If someone is processing this request, kill
150 * them, and mark the request as not being used.
152 * FIXME: Move the request to the "dead pool",
153 * and don't kill the thread.
155 if (request->child_pid != NO_SUCH_CHILD_PID) {
156 pthread_kill(request->child_pid, SIGKILL);
157 request->child_pid = NO_SUCH_CHILD_PID;
160 request_free(&request);
167 * Delete everything in the request list.
169 * This should be called only when debugging the server...
171 void rl_deinit(request_list_t *rl)
176 lrad_packet_list_free(proxy_list);
181 * Delete everything in the table, too.
183 lrad_packet_list_walk(rl->pl, rl_free_entry, NULL);
185 lrad_packet_list_free(rl->pl);
188 * Just to ensure no one is using the memory.
190 memset(rl, 0, sizeof(*rl));
196 * Yank a request from the tree, without free'ing it.
198 void rl_yank(request_list_t *rl, REQUEST *request)
202 * Update the SNMP statistics.
204 * Note that we do NOT do this in rad_respond(),
205 * as that function is called from child threads.
206 * Instead, we update the stats when a request is
207 * deleted, because only the main server thread calls
210 if (mainconfig.do_snmp) {
211 switch (request->reply->code) {
212 case PW_AUTHENTICATION_ACK:
213 rad_snmp.auth.total_responses++;
214 rad_snmp.auth.total_access_accepts++;
217 case PW_AUTHENTICATION_REJECT:
218 rad_snmp.auth.total_responses++;
219 rad_snmp.auth.total_access_rejects++;
222 case PW_ACCESS_CHALLENGE:
223 rad_snmp.auth.total_responses++;
224 rad_snmp.auth.total_access_challenges++;
227 case PW_ACCOUNTING_RESPONSE:
228 rad_snmp.acct.total_responses++;
238 * Delete the request from the list.
240 lrad_packet_list_yank(rl->pl, request->packet);
243 * If there's a proxied packet, and we're still
244 * waiting for a reply, then delete the packet
245 * from the list of outstanding proxied requests.
247 if (request->proxy &&
248 (request->proxy_outstanding > 0)) {
249 pthread_mutex_lock(&proxy_mutex);
250 lrad_packet_list_yank(proxy_list, request->proxy);
251 lrad_packet_list_id_free(proxy_list, request->proxy);
252 pthread_mutex_unlock(&proxy_mutex);
258 * Delete a request from the tree.
260 void rl_delete(request_list_t *rl, REQUEST *request)
262 rl_yank(rl, request);
263 request_free(&request);
268 * Add a request to the request list.
270 int rl_add(request_list_t *rl, REQUEST *request)
272 return lrad_packet_list_insert(rl->pl, &request->packet);
276 * Look up a particular request, using:
278 * Request ID, request code, source IP, source port,
280 * Note that we do NOT use the request vector to look up requests.
282 * We MUST NOT have two requests with identical (id/code/IP/port), and
283 * different vectors. This is a serious error!
285 REQUEST *rl_find(request_list_t *rl, RADIUS_PACKET *packet)
287 RADIUS_PACKET **packet_p;
289 packet_p = lrad_packet_list_find(rl->pl, packet);
290 if (!packet_p) return NULL;
292 return lrad_packet2myptr(REQUEST, packet, packet_p);
296 * Add an entry to the proxy tree.
298 * This is the ONLY function in this source file which may be called
299 * from a child thread. It therefore needs mutexes...
301 int rl_add_proxy(REQUEST *request)
306 request->proxy_outstanding = 1;
307 request->proxy->sockfd = -1;
309 pthread_mutex_lock(&proxy_mutex);
311 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
313 rad_listen_t *proxy_listener;
316 * Allocate a new proxy Fd. This function adds it
317 * into the list of listeners.
319 proxy_listener = proxy_new_listener();
320 if (!proxy_listener) {
321 pthread_mutex_unlock(&proxy_mutex);
322 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
330 proxy = proxy_listener->fd;
331 for (i = 0; i < 32; i++) {
333 * Found a free entry. Save the socket,
334 * and remember where we saved it.
336 if (proxy_fds[(proxy + i) & 0x1f] == -1) {
337 found = (proxy + i) & 0x1f;
338 proxy_fds[found] = proxy;
339 proxy_listeners[found] = proxy_listener;
343 rad_assert(found >= 0);
345 if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
346 pthread_mutex_unlock(&proxy_mutex);
347 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
348 return 0; /* leak proxy_listener */
352 if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
353 pthread_mutex_unlock(&proxy_mutex);
354 DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
360 * FIXME: Hack until we get rid of rad_listen_t, and put
361 * the information into the packet_list.
364 for (i = 0; i < 32; i++) {
365 if (proxy_fds[i] == request->proxy->sockfd) {
370 rad_assert(proxy >= 0);
372 rad_assert(proxy_fds[proxy] != -1);
373 request->proxy_listener = proxy_listeners[proxy];
375 if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
376 pthread_mutex_unlock(&proxy_mutex);
377 DEBUG2("ERROR: Failed to insert entry into proxy list");
381 pthread_mutex_unlock(&proxy_mutex);
383 DEBUG3(" proxy: allocating destination %s port %d - Id %d",
384 inet_ntop(request->proxy->dst_ipaddr.af,
385 &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
386 request->proxy->dst_port,
394 * Look up a particular request, using:
396 * Request Id, request code, source IP, source port,
398 * Note that we do NOT use the request vector to look up requests.
400 * We MUST NOT have two requests with identical (id/code/IP/port), and
401 * different vectors. This is a serious error!
403 REQUEST *rl_find_proxy(RADIUS_PACKET *reply)
405 RADIUS_PACKET **proxy_p;
408 pthread_mutex_lock(&proxy_mutex);
409 proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
412 pthread_mutex_unlock(&proxy_mutex);
416 request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
417 rad_assert(request->proxy_outstanding > 0);
418 request->proxy_outstanding--;
421 * Received all of the replies we expect.
422 * delete it from the managed list.
424 if (request->proxy_outstanding == 0) {
425 lrad_packet_list_yank(proxy_list, request->proxy);
426 lrad_packet_list_id_free(proxy_list, request->proxy);
428 pthread_mutex_unlock(&proxy_mutex);
435 * Return the number of requests in the request list.
437 int rl_num_requests(request_list_t *rl)
439 return lrad_packet_list_num_elements(rl->pl);
446 #define SLEEP_FOREVER (65536)
447 typedef struct rl_walk_t {
455 * Refresh a request, by using cleanup_delay, max_request_time, etc.
457 * When walking over the request list, all of the per-request
458 * magic is done here.
460 static int refresh_request(void *ctx, void *data)
463 rl_walk_t *info = (rl_walk_t *) ctx;
464 child_pid_t child_pid;
465 request_list_t *rl = info->rl;
466 REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
468 rad_assert(request->magic == REQUEST_MAGIC);
470 time_passed = (int) (info->now - request->timestamp);
473 * If the request is marked as a delayed reject, AND it's
474 * time to send the reject, then do so now.
476 if (request->finished &&
477 ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) {
478 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
479 if (time_passed < mainconfig.reject_delay) {
485 * Clear the 'delayed reject' bit, so that we
486 * don't do this again, and fall through to
487 * setting cleanup delay.
489 request->listener->send(request->listener, request);
490 request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
493 * FIXME: Beware interaction with cleanup_delay,
494 * where we might send a reject, and immediately
495 * there-after clean it up!
500 * If the request is finished, THEN
501 * check that more than cleanup_delay seconds have passed
502 * since it was received
503 * OR, if this is a request which had the "don't cache"
504 * option set, then it CANNOT have a duplicate
507 if (request->finished &&
508 ((time_passed >= mainconfig.cleanup_delay) ||
509 ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) {
510 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
512 * Request completed, delete it, and unlink it
513 * from the currently 'alive' list of requests.
516 DEBUG2("Cleaning up request %d ID %d with timestamp %08lx",
517 request->number, request->packet->id,
518 (unsigned long) request->timestamp);
521 * Delete the request.
523 rl_delete(rl, request);
528 * If more than max_request_time has passed since
529 * we received the request, kill it.
531 if (time_passed >= mainconfig.max_request_time) {
534 child_pid = request->child_pid;
535 number = request->number;
538 * There MUST be a RAD_PACKET reply.
540 rad_assert(request->reply != NULL);
543 * If we've tried to proxy the request, and
544 * the proxy server hasn't responded, then
545 * we send a REJECT back to the caller.
547 * For safety, we assert that there is no child
548 * handling the request. If the assertion fails,
549 * it means that we've sent a proxied request to
550 * the home server, and the child thread is still
551 * sitting on the request!
553 if (request->proxy && !request->proxy_reply) {
554 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
556 radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
558 client_name_old(&request->packet->src_ipaddr),
559 request->packet->src_port);
560 request_reject(request, REQUEST_FAIL_HOME_SERVER);
561 request->finished = TRUE;
565 if (mainconfig.kill_unresponsive_children) {
566 if (child_pid != NO_SUCH_CHILD_PID) {
568 * This request seems to have hung
571 #ifdef HAVE_PTHREAD_H
572 radlog(L_ERR, "Killing unresponsive thread for request %d",
574 pthread_cancel(child_pid);
576 } /* else no proxy reply, quietly fail */
579 * Maybe we haven't killed it. In that
580 * case, print a warning.
582 } else if ((child_pid != NO_SUCH_CHILD_PID) &&
583 ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) {
584 radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d",
585 (unsigned long)child_pid, number);
588 * Set the option that we've sent a log message,
589 * so that we don't send more than one message
592 request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD;
596 * Send a reject message for the request, mark it
597 * finished, and forget about the child.
599 request_reject(request, REQUEST_FAIL_SERVER_TIMEOUT);
601 request->child_pid = NO_SUCH_CHILD_PID;
603 if (mainconfig.kill_unresponsive_children)
604 request->finished = TRUE;
606 } /* else the request is still allowed to be in the queue */
609 * If the request is finished, set the cleanup delay.
611 if (request->finished) {
612 time_passed = mainconfig.cleanup_delay - time_passed;
617 * Accounting request. Don't re-send them, since the NAS
618 * will take care of doing that, and we're not a NAS.
619 * Instead, simply clean them up once we're pretty sure
620 * that the home server won't be responding.
622 if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
623 request->proxy && !request->proxy_reply &&
624 (request->child_pid == NO_SUCH_CHILD_PID) &&
625 ((info->now - request->proxy_start_time) > (mainconfig.proxy_retry_delay * 2))) {
631 * Set reject delay, if appropriate.
633 if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
634 (mainconfig.reject_delay > 0)) {
636 time_passed = mainconfig.reject_delay - time_passed;
639 * This catches a corner case, apparently.
641 if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
642 (time_passed == 0)) goto reject_packet;
643 if (time_passed <= 0) time_passed = 1;
648 * The request is still alive, wake up when it's
651 time_passed = mainconfig.max_request_time - time_passed;
654 if (time_passed < 0) time_passed = 1;
656 if (time_passed < info->sleep_time) {
657 info->sleep_time = time_passed;
665 * Clean up the request list, every so often.
667 * This is done by walking through ALL of the list, and
668 * - marking any requests which are finished, and expired
669 * - killing any processes which are NOT finished after a delay
670 * - deleting any marked requests.
672 * Returns the number of millisends to sleep, before processing
675 int rl_clean_list(request_list_t *rl, time_t now)
680 info.sleep_time = SLEEP_FOREVER;
683 lrad_packet_list_walk(rl->pl, &info, refresh_request);
685 if (info.sleep_time < 0) info.sleep_time = 0;
687 return info.sleep_time;