]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Move connection limiting code to its own data structure
authorAlan T. DeKok <aland@freeradius.org>
Sun, 15 Apr 2012 09:47:22 +0000 (11:47 +0200)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 15 Apr 2012 10:32:30 +0000 (12:32 +0200)
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.

raddb/clients.conf
raddb/radiusd.conf.in
src/include/radiusd.h
src/include/realms.h
src/main/client.c
src/main/dhcpd.c
src/main/listen.c
src/main/process.c
src/main/realms.c
src/main/tls_listen.c

index 1af7f1d2d4847bf1fb04edf0e071e842ec7cba2a..f1d1f3c3e0f9466d5eca0323aec974523c5dc7f0 100644 (file)
@@ -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
index bca5c863232679347dd0962bcc9118b9e9e10f6e..584fc34856ef1cb1ce772bbd41ab99c0040ae72c 100644 (file)
@@ -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
index f247cfab98b98134e7fc9c998320460cd4918fed..bf2b0898f4a99648c827941750b01c6afa21f1d9 100644 (file)
@@ -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;
 
index 74e13ea0cbdbf72db053f59d5f624b2fc10ac5f9..91cc4d20fa85bbe00d5964c7c371a8f5fcc39acd 100644 (file)
@@ -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 */
 
index 1be7da81b2345386326fa1b7eb84c69acc954e43..0444cde97863a20a4b3f05a8e735ae052602dc85 100644 (file)
@@ -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;
 }
 
index 3f6325cf4c70e589898ddd1cb8e9804b99650c8e..690152fb63a42446d2818daf93a3d721da673ec9 100644 (file)
@@ -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;
 
index f51cafe523f6ed0665cb22fa9297cf5355ed8df5..4bbf940a7f8ee1fd8ef49a364a1814314f997d27 100644 (file)
@@ -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;
        }
 
index 2301385bfd28d7a017976befdc41b1970383995a..710534d8097688dc71a041f67247c2edcb580408 100644 (file)
@@ -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");
index fd8e012f6e00af05208e979c25d10ead7abf2194..8f2966cab6ed5b5b991d86de64d52c3594934d37 100644 (file)
@@ -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) {
index e8537891f8b5e7ea0b83d10d8b27d4929ccb7753..91eeec8dce737e784971d42eee8e89c3c4a1e5af 100644 (file)
@@ -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--;
                }
        }