From: Alan T. DeKok Date: Sun, 15 Apr 2012 09:47:22 +0000 (+0200) Subject: Move connection limiting code to its own data structure X-Git-Tag: release_3_0_0_beta0~232 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dbed3b907a5b5c4e35a1a043c3c3fba3d0243eb9;p=thirdparty%2Ffreeradius-server.git Move connection limiting code to its own data structure So that it can be shared among home servers, clients, and listeners. Enable tcp socket timers for incoming packets, too. This uses the same code as for outgoing home servers. --- diff --git a/raddb/clients.conf b/raddb/clients.conf index 1af7f1d2d48..f1d1f3c3e0f 100644 --- a/raddb/clients.conf +++ b/raddb/clients.conf @@ -120,15 +120,6 @@ client localhost { # allowed values: yes, no require_message_authenticator = no - # - # Limit the number of TCP connections that this client is - # allowed to have open to us. This configuration entry - # is ignored for UDP sockets. - # - # This entry is the mirror of the "max_connections" entry - # in the home server configuration. - max_connections = 16 - # # The short name is used as an alias for the fully qualified # domain name, or the IP address. @@ -187,6 +178,40 @@ client localhost { # client. For an example of a coa home server or pool, # see raddb/sites-available/originate-coa # coa_server = coa + + # + # Connection limiting for clients using "proto = tcp". + # + # This section is ignored for clients sending UDP traffic + # + limit { + # + # Limit the number of simultaneous TCP connections from a client + # + # The default is 16. + # Setting this to 0 means "no limit" + max_connections = 16 + + # The per-socket "max_requests" option does not exist. + + # + # The lifetime, in seconds, of a TCP connection. After + # this lifetime, the connection will be closed. + # + # Setting this to 0 means "forever". + lifetime = 0 + + # + # The idle timeout, in seconds, of a TCP connection. + # If no packets have been received over the connection for + # this time, the connection will be closed. + # + # Setting this to 0 means "no timeout". + # + # We STRONGLY RECOMMEND that you set an idle timeout. + # + idle_timeout = 30 + } } # IPv6 Client diff --git a/raddb/radiusd.conf.in b/raddb/radiusd.conf.in index bca5c863232..584fc34856e 100644 --- a/raddb/radiusd.conf.in +++ b/raddb/radiusd.conf.in @@ -258,6 +258,40 @@ listen { # See clients.conf for the configuration of "per_socket_clients". # # clients = per_socket_clients + + # + # Connection limiting for sockets with "proto = tcp". + # + # This section is ignored for other kinds of sockets. + # + limit { + # + # Limit the number of simultaneous TCP connections to the socket + # + # The default is 16. + # Setting this to 0 means "no limit" + max_connections = 16 + + # The per-socket "max_requests" option does not exist. + + # + # The lifetime, in seconds, of a TCP connection. After + # this lifetime, the connection will be closed. + # + # Setting this to 0 means "forever". + lifetime = 0 + + # + # The idle timeout, in seconds, of a TCP connection. + # If no packets have been received over the connection for + # this time, the connection will be closed. + # + # Setting this to 0 means "no timeout". + # + # We STRONGLY RECOMMEND that you set an idle timeout. + # + idle_timeout = 30 + } } # This second "listen" section is for listening on the accounting diff --git a/src/include/radiusd.h b/src/include/radiusd.h index f247cfab98b..bf2b0898f4a 100644 --- a/src/include/radiusd.h +++ b/src/include/radiusd.h @@ -157,8 +157,7 @@ typedef struct radclient { int proto; #ifdef WITH_TCP - int max_connections; - int num_connections; + fr_socket_limit_t limit; #endif #ifdef WITH_DYNAMIC_CLIENTS @@ -418,9 +417,8 @@ typedef struct listen_socket_t { time_t opened; fr_event_t *ev; - /* for clients connecting to the server */ - int max_connections; - int num_connections; + fr_socket_limit_t limit; + struct listen_socket_t *parent; RADCLIENT *client; diff --git a/src/include/realms.h b/src/include/realms.h index 74e13ea0cbd..91cc4d20fa8 100644 --- a/src/include/realms.h +++ b/src/include/realms.h @@ -31,6 +31,15 @@ extern "C" { #define HOME_STATE_ZOMBIE (1) #define HOME_STATE_IS_DEAD (2) +typedef struct fr_socket_limit_t { + int max_connections; + int num_connections; + int max_requests; + int num_requests; + int lifetime; + int idle_timeout; +} fr_socket_limit_t; + typedef struct home_server { const char *name; @@ -44,11 +53,7 @@ typedef struct home_server { int type; /* auth/acct */ int proto; - int max_connections; - int num_connections; /* protected by proxy mutex */ - int max_requests; /* for one connection */ - int lifetime; - int idle_timeout; + fr_socket_limit_t limit; fr_ipaddr_t src_ipaddr; /* preferred source IP address */ diff --git a/src/main/client.c b/src/main/client.c index 1be7da81b23..0444cde9786 100644 --- a/src/main/client.c +++ b/src/main/client.c @@ -502,6 +502,21 @@ static struct in6_addr cl_ip6addr; static char *hs_proto = NULL; #endif +#ifdef WITH_TCP +static CONF_PARSER limit_config[] = { + { "max_connections", PW_TYPE_INTEGER, + offsetof(home_server, limit.max_connections), NULL, "16" }, + + { "lifetime", PW_TYPE_INTEGER, + offsetof(home_server, limit.lifetime), NULL, "0" }, + + { "idle_timeout", PW_TYPE_INTEGER, + offsetof(home_server, limit.idle_timeout), NULL, "30" }, + + { NULL, -1, 0, NULL, NULL } /* end the list */ +}; +#endif + static const CONF_PARSER client_config[] = { { "ipaddr", PW_TYPE_IPADDR, 0, &cl_ip4addr, NULL }, @@ -531,8 +546,8 @@ static const CONF_PARSER client_config[] = { #ifdef WITH_TCP { "proto", PW_TYPE_STRING_PTR, 0, &hs_proto, NULL }, - { "max_connections", PW_TYPE_INTEGER, - offsetof(RADCLIENT, max_connections), 0, "16" }, + + { "limit", PW_TYPE_SUBSECTION, 0, NULL, (const void *) limit_config }, #endif #ifdef WITH_DYNAMIC_CLIENTS @@ -770,6 +785,17 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server) } #endif +#ifdef WITH_TCP + if ((c->proto == IPPROTO_TCP) || (c->proto == IPPROTO_IP)) { + if ((c->limit.idle_timeout > 0) && (c->limit.idle_timeout < 5)) + c->limit.idle_timeout = 5; + if ((c->limit.lifetime > 0) && (c->limit.lifetime < 5)) + c->limit.lifetime = 5; + if ((c->limit.lifetime > 0) && (c->limit.idle_timeout > c->limit.lifetime)) + c->limit.idle_timeout = 0; + } +#endif + return c; } diff --git a/src/main/dhcpd.c b/src/main/dhcpd.c index 3f6325cf4c7..690152fb63a 100644 --- a/src/main/dhcpd.c +++ b/src/main/dhcpd.c @@ -410,6 +410,8 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this) RADCLIENT *client; CONF_PAIR *cp; + // LOOK UP DNS DICTIONARY ENTRIES. DIE IF THEY'RE NOT FOUND + rcode = common_socket_parse(cs, this); if (rcode != 0) return rcode; diff --git a/src/main/listen.c b/src/main/listen.c index f51cafe523f..4bbf940a7f8 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -471,11 +471,11 @@ static int dual_tcp_recv(rad_listen_t *listener) /* * Decrement the number of connections. */ - if (sock->parent->num_connections > 0) { - sock->parent->num_connections--; + if (sock->parent->limit.num_connections > 0) { + sock->parent->limit.num_connections--; } - if (sock->client->num_connections > 0) { - sock->client->num_connections--; + if (sock->client->limit.num_connections > 0) { + sock->client->limit.num_connections--; } /* @@ -588,21 +588,21 @@ static int dual_tcp_accept(rad_listen_t *listener) } /* - * Enforce max_connectionsx on client && listen section. + * Enforce max_connections on client && listen section. */ - if ((client->max_connections != 0) && - (client->max_connections == client->num_connections)) { + if ((client->limit.max_connections != 0) && + (client->limit.max_connections == client->limit.num_connections)) { /* * FIXME: Print client IP/port, and server IP/port. */ - radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->max_connections); + radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->limit.max_connections); close(newfd); return 0; } sock = listener->data; - if ((sock->max_connections != 0) && - (sock->max_connections == sock->num_connections)) { + if ((sock->limit.max_connections != 0) && + (sock->limit.max_connections == sock->limit.num_connections)) { /* * FIXME: Print client IP/port, and server IP/port. */ @@ -610,8 +610,8 @@ static int dual_tcp_accept(rad_listen_t *listener) close(newfd); return 0; } - client->num_connections++; - sock->num_connections++; + client->limit.num_connections++; + sock->limit.num_connections++; /* * Add the new listener. @@ -635,6 +635,25 @@ static int dual_tcp_accept(rad_listen_t *listener) sock->client = client; sock->opened = sock->last_packet = time(NULL); + /* + * Set the limits. The defaults are the parent limits. + * Client limits on max_connections are enforced dynamically. + * Set the MINIMUM of client/socket idle timeout or lifetime. + */ + memcpy(&sock->limit, &sock->parent->limit, sizeof(sock->limit)); + + if (client->limit.idle_timeout && + ((sock->limit.idle_timeout == 0) || + (client->limit.idle_timeout < sock->limit.idle_timeout))) { + sock->limit.idle_timeout = client->limit.idle_timeout; + } + + if (client->limit.lifetime && + ((sock->limit.lifetime == 0) || + (client->limit.lifetime < sock->limit.lifetime))) { + sock->limit.lifetime = client->limit.lifetime; + } + this->fd = newfd; this->status = RAD_LISTEN_STATUS_INIT; this->recv = dual_tcp_recv; @@ -838,6 +857,20 @@ static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize) extern int check_config; /* radiusd.c */ +#ifdef WITH_TCP +static CONF_PARSER limit_config[] = { + { "max_connections", PW_TYPE_INTEGER, + offsetof(listen_socket_t, limit.max_connections), NULL, "16" }, + + { "lifetime", PW_TYPE_INTEGER, + offsetof(listen_socket_t, limit.lifetime), NULL, "0" }, + + { "idle_timeout", PW_TYPE_INTEGER, + offsetof(listen_socket_t, limit.idle_timeout), NULL, "30" }, + + { NULL, -1, 0, NULL, NULL } /* end the list */ +}; +#endif /* * Parse an authentication or accounting socket. @@ -921,10 +954,15 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) } else if (strcmp(proto, "tcp") == 0) { sock->proto = IPPROTO_TCP; - rcode = cf_item_parse(cs, "max_connections", PW_TYPE_INTEGER, - &sock->max_connections, "64"); + rcode = cf_section_parse(cf_section_sub_find(cs, "limit"), sock, limit_config); if (rcode < 0) return -1; + if ((sock->limit.idle_timeout > 0) && (sock->limit.idle_timeout < 5)) + sock->limit.idle_timeout = 5; + if ((sock->limit.lifetime > 0) && (sock->limit.lifetime < 5)) + sock->limit.lifetime = 5; + if ((sock->limit.lifetime > 0) && (sock->limit.idle_timeout > sock->limit.lifetime)) + sock->limit.idle_timeout = 0; } else { cf_log_err(cf_sectiontoitem(cs), "Unknown proto name \"%s\"", proto); @@ -2405,10 +2443,10 @@ int proxy_new_listener(home_server *home, int src_port) if (!home) return 0; - if ((home->max_connections > 0) && - (home->num_connections >= home->max_connections)) { + if ((home->limit.max_connections > 0) && + (home->limit.num_connections >= home->limit.max_connections)) { DEBUG("WARNING: Home server has too many open connections (%d)", - home->max_connections); + home->limit.max_connections); return 0; } diff --git a/src/main/process.c b/src/main/process.c index 2301385bfd2..710534d8097 100644 --- a/src/main/process.c +++ b/src/main/process.c @@ -1443,14 +1443,29 @@ static void tcp_socket_timer(void *ctx) listen_socket_t *sock = listener->data; struct timeval end, now; char buffer[256]; + fr_socket_limit_t *limit; fr_event_now(el, &now); + switch (listener->type) { + case RAD_LISTEN_PROXY: + limit = &sock->home->limit; + break; + + case RAD_LISTEN_AUTH: + case RAD_LISTEN_ACCT: + limit = &sock->limit; + break; + + default: + return; + } + /* * If we enforce a lifetime, do it now. */ - if (sock->home->lifetime) { - end.tv_sec = sock->opened + sock->home->lifetime; + if (limit->lifetime > 0) { + end.tv_sec = sock->opened + limit->lifetime; end.tv_usec = 0; if (timercmp(&end, &now, <=)) { @@ -1471,10 +1486,11 @@ static void tcp_socket_timer(void *ctx) /* * Enforce an idle timeout. */ - if (sock->home->idle_timeout > 0) { + if (limit->idle_timeout > 0) { struct timeval idle; - idle.tv_sec = sock->last_packet + sock->home->idle_timeout; + rad_assert(sock->last_packet != 0); + idle.tv_sec = sock->last_packet + limit->idle_timeout; idle.tv_usec = 0; if (timercmp(&idle, &now, <=)) { @@ -3423,6 +3439,8 @@ int event_new_fd(rad_listen_t *this) this->print(this, buffer, sizeof(buffer)); if (this->status == RAD_LISTEN_STATUS_INIT) { + listen_socket_t *sock = this->data; + if (just_started) { DEBUG("Listening on %s", buffer); } else { @@ -3436,8 +3454,6 @@ int event_new_fd(rad_listen_t *this) * added to the packet list. */ if (this->type == RAD_LISTEN_PROXY) { - listen_socket_t *sock = this->data; - PTHREAD_MUTEX_LOCK(&proxy_mutex); if (!fr_packet_list_socket_add(proxy_list, this->fd, sock->proto, @@ -3461,14 +3477,14 @@ int event_new_fd(rad_listen_t *this) } if (sock->home) { - sock->home->num_connections++; + sock->home->limit.num_connections++; #ifdef HAVE_PTHREAD_H /* * If necessary, add it to the list of * new proxy listeners. */ - if (sock->home->lifetime || sock->home->idle_timeout) { + if (sock->home->limit.lifetime || sock->home->limit.idle_timeout) { this->next = proxy_listener_list; proxy_listener_list = this; } @@ -3484,7 +3500,7 @@ int event_new_fd(rad_listen_t *this) * contention. */ if (sock->home) { - if (sock->home->lifetime || sock->home->idle_timeout) { + if (sock->home->limit.lifetime || sock->home->limit.idle_timeout) { radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD); } } @@ -3507,6 +3523,26 @@ int event_new_fd(rad_listen_t *this) } #endif +#ifdef WITH_TCP + /* + * Add timers to child sockets, if necessary. + */ + if (sock->proto == IPPROTO_TCP && sock->opened && + (sock->limit.lifetime || sock->limit.idle_timeout)) { + struct timeval when; + + ASSERT_MASTER; + + when.tv_sec = sock->opened + 1; + when.tv_usec = 0; + + if (!fr_event_insert(el, tcp_socket_timer, this, &when, + &(sock->ev))) { + rad_panic("Failed to insert event"); + } + } +#endif + FD_MUTEX_LOCK(&fd_mutex); if (!fr_event_fd_insert(el, 0, this->fd, event_socket_handler, this)) { @@ -3722,7 +3758,7 @@ finish: fr_strerror()); exit(1); } - if (sock->home) sock->home->num_connections--; + if (sock->home) sock->home->limit.num_connections--; PTHREAD_MUTEX_UNLOCK(&proxy_mutex); } #endif @@ -3835,6 +3871,13 @@ static void handle_signal_self(int flag) when = now; + /* + * Sockets should only be added to the + * proxy_listener_list if they have limits. + * + */ + rad_assert(sock->home->limit.lifetime || sock->home->limit.idle_timeout); + if (!fr_event_insert(el, tcp_socket_timer, this, &when, &(sock->ev))) { rad_panic("Failed to insert event"); diff --git a/src/main/realms.c b/src/main/realms.c index fd8e012f6e0..8f2966cab6e 100644 --- a/src/main/realms.c +++ b/src/main/realms.c @@ -304,16 +304,16 @@ void realms_free(void) #ifdef WITH_PROXY static CONF_PARSER limit_config[] = { { "max_connections", PW_TYPE_INTEGER, - offsetof(home_server, max_connections), NULL, "16" }, + offsetof(home_server, limit.max_connections), NULL, "16" }, { "max_requests", PW_TYPE_INTEGER, - offsetof(home_server,max_requests), NULL, "0" }, + offsetof(home_server, limit.max_requests), NULL, "0" }, { "lifetime", PW_TYPE_INTEGER, - offsetof(home_server,lifetime), NULL, "0" }, + offsetof(home_server, limit.lifetime), NULL, "0" }, { "idle_timeout", PW_TYPE_INTEGER, - offsetof(home_server,idle_timeout), NULL, "0" }, + offsetof(home_server, limit.idle_timeout), NULL, "0" }, { NULL, -1, 0, NULL, NULL } /* end the list */ }; @@ -811,21 +811,21 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs) if (home->coa_mrd > 60 ) home->coa_mrd = 60; #endif - if (home->max_connections > 1024) home->max_connections = 1024; + if (home->limit.max_connections > 1024) home->limit.max_connections = 1024; #ifdef WITH_TCP /* * UDP sockets can't be connection limited. */ - if (home->proto != IPPROTO_TCP) home->max_connections = 0; + if (home->proto != IPPROTO_TCP) home->limit.max_connections = 0; #endif - if ((home->idle_timeout > 0) && (home->idle_timeout < 5)) - home->idle_timeout = 5; - if ((home->lifetime > 0) && (home->lifetime < 5)) - home->lifetime = 5; - if ((home->lifetime > 0) && (home->idle_timeout > home->lifetime)) - home->idle_timeout = 0; + if ((home->limit.idle_timeout > 0) && (home->limit.idle_timeout < 5)) + home->limit.idle_timeout = 5; + if ((home->limit.lifetime > 0) && (home->limit.lifetime < 5)) + home->limit.lifetime = 5; + if ((home->limit.lifetime > 0) && (home->limit.idle_timeout > home->limit.lifetime)) + home->limit.idle_timeout = 0; tls = cf_item_parent(cf_sectiontoitem(cs)); if (strcmp(cf_section_name1(tls), "server") == 0) { diff --git a/src/main/tls_listen.c b/src/main/tls_listen.c index e8537891f8b..91eeec8dce7 100644 --- a/src/main/tls_listen.c +++ b/src/main/tls_listen.c @@ -80,11 +80,11 @@ static void tls_socket_close(rad_listen_t *listener) /* * Decrement the number of connections. */ - if (sock->parent->num_connections > 0) { - sock->parent->num_connections--; + if (sock->parent->limit.num_connections > 0) { + sock->parent->limit.num_connections--; } - if (sock->client->num_connections > 0) { - sock->client->num_connections--; + if (sock->client->limit.num_connections > 0) { + sock->client->limit.num_connections--; } }