📄 request_list.c
字号:
/* * request_list.c Hide the handling of the REQUEST list from * the main server. * * Version: $Id: request_list.c,v 1.40.2.2 2004/06/01 14:35:55 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2003-2004 The FreeRADIUS server project */static const char rcsid[] = "$Id: request_list.c,v 1.40.2.2 2004/06/01 14:35:55 aland Exp $";#include "autoconf.h"#include "libradius.h"#include <stdlib.h>#include <string.h>#include "radiusd.h"#include "rad_assert.h"#include "request_list.h"#include "radius_snmp.h"/* * We keep the incoming requests in an array, indexed by ID. * * Each array element contains a linked list of containers of * active requests, a count of the number of requests, and a time * at which the first request in the list must be serviced. * * Note that we ALSO keep a tree view of the same data, below. * Both views are needed for the server to work optimally. */typedef struct REQNODE { struct REQNODE *prev, *next; REQUEST *req;} REQNODE;typedef struct REQUESTINFO { REQNODE *first_request; REQNODE *last_request; int request_count; time_t last_cleaned_list;} REQUESTINFO;static REQUESTINFO request_list[256];/* * Remember the next request at which we start walking * the list. */static REQUEST *last_request = NULL;/* * It MAY make more sense here to key off of the packet ID, just * like the request_list. Then again, saving another 8 lookups * (on average) isn't much of a problem. * * The "request_cmp" function keys off of the packet ID first, * so the first 8 layers of the tree will be the fanned-out * tree for packet ID's. */static rbtree_t *request_tree;#ifdef HAVE_PTHREAD_Hstatic pthread_mutex_t proxy_mutex;#else/* * This is easier than ifdef's throughout the code. */#define pthread_mutex_lock(_x)#define pthread_mutex_unlock(_x)#endif/* * We keep track of packets we're proxying, keyed by * source socket, and destination ip/port, and Id. */static rbtree_t *proxy_tree;/* * We keep track of free/used Id's, by destination ip/port. * * We need a different tree than above, because this one is NOT * keyed by Id. Instead, we use this one to allocate Id's. */static rbtree_t *proxy_id_tree;/* * We keep the proxy FD's here. The RADIUS Id's are marked * "allocated" per Id, via a bit per proxy FD. */static int proxy_fds[32];/* * We can use 256 RADIUS Id's per dst ipaddr/port, per server * socket. So, to allocate them, we key off of dst ipaddr/port, * and then search the RADIUS Id's, looking for an unused socket. * * We do NOT key off of socket fd's, here, either. Instead, * we look for a free Id from a sockfd, any sockfd. */typedef struct proxy_id_t { uint32_t dst_ipaddr; int dst_port; /* * FIXME: Allocate more proxy sockets when this gets full. */ int index; uint32_t mask; /* of FD's we know about. */ uint32_t id[1]; /* really id[256] */} proxy_id_t;/* * Find a matching entry in the proxy ID tree. */static int proxy_id_cmp(const void *one, const void *two){ const proxy_id_t *a = one; const proxy_id_t *b = two; /* * The following comparisons look weird, but it's * the only way to make the comparisons work. */ if (a->dst_ipaddr < b->dst_ipaddr) return -1; if (a->dst_ipaddr > b->dst_ipaddr) return +1; if (a->dst_port < b->dst_port) return -1; if (a->dst_port > b->dst_port) return +1; /* * Everything's equal. Say so. */ return 0;}/* * Compare two REQUEST data structures, based on a number * of criteria. */static int request_cmp(const void *one, const void *two){ const REQUEST *a = one; const REQUEST *b = two; /* * The following comparisons look weird, but it's * the only way to make the comparisons work. */ /* * If the packets didn't arrive on the same socket, * they're not identical, no matter what their src/dst * ip/ports say. */ if (a->packet->sockfd < b->packet->sockfd) return -1; if (a->packet->sockfd > b->packet->sockfd) return +1; if (a->packet->id < b->packet->id) return -1; if (a->packet->id > b->packet->id) return +1; if (a->packet->code < b->packet->code) return -1; if (a->packet->code > b->packet->code) return +1; if (a->packet->src_ipaddr < b->packet->src_ipaddr) return -1; if (a->packet->src_ipaddr > b->packet->src_ipaddr) return +1; if (a->packet->src_port < b->packet->src_port) return -1; if (a->packet->src_port > b->packet->src_port) return +1; /* * Hmm... we may be listening on IPADDR_ANY, in which case * the destination IP is important, too. */ if (a->packet->dst_ipaddr < b->packet->dst_ipaddr) return -1; if (a->packet->dst_ipaddr > b->packet->dst_ipaddr) return +1; if (a->packet->dst_port < b->packet->dst_port) return -1; if (a->packet->dst_port > b->packet->dst_port) return +1; /* * Everything's equal. Say so. */ return 0;}/* * Compare two REQUEST data structures, based on a number * of criteria, for proxied packets. */static int proxy_cmp(const void *one, const void *two){ const REQUEST *a = one; const REQUEST *b = two; rad_assert(a->magic == REQUEST_MAGIC); rad_assert(b->magic == REQUEST_MAGIC); rad_assert(a->proxy != NULL); rad_assert(b->proxy != NULL); /* * The following code looks unreasonable, but it's * the only way to make the comparisons work. */ if (a->proxy->sockfd < b->proxy->sockfd) return -1; if (a->proxy->sockfd > b->proxy->sockfd) return +1; if (a->proxy->id < b->proxy->id) return -1; if (a->proxy->id > b->proxy->id) return +1; /* * We've got to check packet codes, too. But * this should be done later, by someone else... */ if (a->proxy->dst_ipaddr < b->proxy->dst_ipaddr) return -1; if (a->proxy->dst_ipaddr > b->proxy->dst_ipaddr) return +1; if (a->proxy->dst_port < b->proxy->dst_port) return -1; if (a->proxy->dst_port > b->proxy->dst_port) return +1; /* * FIXME: Check the Proxy-State attribute, too. * This will help cut down on duplicates. */ /* * Everything's equal. Say so. */ return 0;}/* * Initialize the request list. */int rl_init(void){ /* * Initialize the request_list[] array. */ memset(request_list, 0, sizeof(request_list)); request_tree = rbtree_create(request_cmp, NULL, 0); if (!request_tree) { rad_assert("FAIL" == NULL); } /* * Create the tree for managing proxied requests and * responses. */ proxy_tree = rbtree_create(proxy_cmp, NULL, 1); if (!proxy_tree) { rad_assert("FAIL" == NULL); } /* * Create the tree for allocating proxy ID's. */ proxy_id_tree = rbtree_create(proxy_id_cmp, NULL, 0); if (!proxy_id_tree) { rad_assert("FAIL" == NULL); }#ifdef HAVE_PTHREAD_H /* * For now, always create the mutex. * * Later, we can only create it if there are multiple threads. */ if (pthread_mutex_init(&proxy_mutex, NULL) != 0) { radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s", strerror(errno)); exit(1); }#endif /* * The Id allocation table is done by bits, so we have * 32 bits per Id. These bits indicate which entry * in the proxy_fds array is used for that Id. * * This design allows 256*32 = 8k requests to be * outstanding to a home server, before something goes * wrong. */ { int i; rad_listen_t *listener; /* * Mark the Fd's as unused. */ for (i = 0; i < 32; i++) proxy_fds[i] = -1; for (listener = mainconfig.listen; listener != NULL; listener = listener->next) { if (listener->type == RAD_LISTEN_PROXY) { proxy_fds[listener->fd & 0x1f] = listener->fd; break; } } } return 1;}/* * Delete a request from the proxy trees. */static void rl_delete_proxy(REQUEST *request, rbnode_t *node){ proxy_id_t myid, *entry; rad_assert(node != NULL); rbtree_delete(proxy_tree, node); myid.dst_ipaddr = request->proxy->dst_ipaddr; myid.dst_port = request->proxy->dst_port; /* * Find the Id in the array of allocated Id's, * and delete it. */ entry = rbtree_finddata(proxy_id_tree, &myid); if (entry) { int i; DEBUG3(" proxy: de-allocating %08x:%d %d", entry->dst_ipaddr, entry->dst_port, request->proxy->id); /* * Find the proxy socket associated with this * Id. We loop over all 32 proxy fd's, but we * partially index by proxy fd's, which means * that we almost always break out of the loop * quickly. */ for (i = 0; i < 32; i++) { int offset; offset = (request->proxy->sockfd + i) & 0x1f; if (proxy_fds[offset] == request->proxy->sockfd) { entry->id[request->proxy->id] &= ~(1 << offset); break; } } /* else die horribly? */ } else { /* * Hmm... not sure what to do here. */ DEBUG3(" proxy: FAILED TO FIND %08x:%d %d", myid.dst_ipaddr, myid.dst_port, request->proxy->id); }}/* * Delete a particular request. */void rl_delete(REQUEST *request){ int id; REQNODE *prev, *next; prev = ((REQNODE *) request->container)->prev; next = ((REQNODE *) request->container)->next; id = request->packet->id; /* * Update the last request we touched. * * This is so the periodic "walk & clean list" * function, below, doesn't walk over all requests * all of the time. Rather, it tries to amortize * the cost... */ if (last_request == request) { last_request = rl_next(last_request); } if (prev == NULL) { request_list[id].first_request = next; } else { prev->next = next; } if (next == NULL) { request_list[id].last_request = prev; } else { next->prev = prev; } free(request->container);#ifdef WITH_SNMP /* * Update the SNMP statistics. * * Note that we do NOT do this in rad_respond(), * as that function is called from child threads. * Instead, we update the stats when a request is * deleted, because only the main server thread calls * this function... */ if (mainconfig.do_snmp) { switch (request->reply->code) { case PW_AUTHENTICATION_ACK: rad_snmp.auth.total_responses++; rad_snmp.auth.total_access_accepts++; break; case PW_AUTHENTICATION_REJECT: rad_snmp.auth.total_responses++; rad_snmp.auth.total_access_rejects++; break; case PW_ACCESS_CHALLENGE: rad_snmp.auth.total_responses++; rad_snmp.auth.total_access_challenges++; break; case PW_ACCOUNTING_RESPONSE: rad_snmp.acct.total_responses++; break; default: break; } }#endif /* * Delete the request from the tree. */ { rbnode_t *node; node = rbtree_find(request_tree, request); rad_assert(node != NULL); rbtree_delete(request_tree, node); /* * If there's a proxied packet, and we're still * waiting for a reply, then delete the packet * from the list of outstanding proxied requests. */ if (request->proxy && (request->proxy_outstanding > 0)) { pthread_mutex_lock(&proxy_mutex); node = rbtree_find(proxy_tree, request); rl_delete_proxy(request, node); pthread_mutex_unlock(&proxy_mutex); } } request_free(&request); request_list[id].request_count--;}/* * Add a request to the request list. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -