merge in changes from 2.0 branch; fix byte counters in accounting records, add gigawords
[l2tpns.git] / l2tpns.c
index 28eca3b..1489318 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -4,7 +4,7 @@
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.96 2005/05/07 08:17:25 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.107 2005/06/02 11:32:30 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -90,7 +90,7 @@ uint32_t eth_tx = 0;
 static uint32_t ip_pool_size = 1;      // Size of the pool of addresses used for dynamic address allocation.
 time_t time_now = 0;                   // Current time in seconds since epoch.
 static char time_now_string[64] = {0}; // Current time as a string.
-static char main_quit = 0;             // True if we're in the process of exiting.
+char main_quit = 0;                    // True if we're in the process of exiting.
 linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
@@ -113,6 +113,7 @@ config_descriptt config_values[] = {
        CONFIG("radius_interim", radius_interim, INT),
        CONFIG("radius_secret", radiussecret, STRING),
        CONFIG("radius_authtypes", radius_authtypes_s, STRING),
+       CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL),
        CONFIG("bind_address", bind_address, IPv4),
        CONFIG("peer_address", peer_address, IPv4),
        CONFIG("send_garp", send_garp, BOOL),
@@ -121,7 +122,6 @@ config_descriptt config_values[] = {
        CONFIG("accounting_dir", accounting_dir, STRING),
        CONFIG("setuid", target_uid, INT),
        CONFIG("dump_speed", dump_speed, BOOL),
-       CONFIG("cleanup_interval", cleanup_interval, INT),
        CONFIG("multi_read_count", multi_read_count, INT),
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
        CONFIG("lock_pages", lock_pages, BOOL),
@@ -131,6 +131,7 @@ config_descriptt config_values[] = {
        CONFIG("cluster_interface", cluster_interface, STRING),
        CONFIG("cluster_hb_interval", cluster_hb_interval, INT),
        CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT),
+       CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT),
        CONFIG("ipv6_prefix", ipv6_prefix, IPv6),
        { NULL, 0, 0, 0 },
 };
@@ -189,11 +190,15 @@ static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int
 static tunnelidt new_tunnel(void);
 static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len);
 
-// return internal time (10ths since process startup)
-static clockt now(void)
+// on slaves, alow BGP to withdraw cleanly before exiting
+#define QUIT_DELAY     5
+
+// return internal time (10ths since process startup), set f if given
+static clockt now(double *f)
 {
        struct timeval t;
        gettimeofday(&t, 0);
+       if (f) *f = t.tv_sec + t.tv_usec / 1000000.0;
        return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1;
 }
 
@@ -203,7 +208,7 @@ static clockt now(void)
 clockt backoff(uint8_t try)
 {
        if (try > 5) try = 5;                  // max backoff
-       return now() + 10 * (1 << try);
+       return now(NULL) + 10 * (1 << try);
 }
 
 
@@ -297,6 +302,16 @@ void _log_hex(int level, const char *title, const char *data, int maxsize)
        }
 }
 
+// update a counter, accumulating 2^32 wraps
+void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta)
+{
+       uint32_t new = *counter + delta;
+       if (new < *counter)
+               (*wrap)++;
+
+       *counter = new;
+}
+
 // initialise the random generator
 static void initrandom(char *source)
 {
@@ -1076,11 +1091,13 @@ static void processipout(uint8_t * buf, int len)
        if (sp->snoop_ip && sp->snoop_port)
                snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port);
 
-       sp->cout += len; // byte count
-       sp->total_cout += len; // byte count
+       increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
+       sp->cout_delta += len;
        sp->pout++;
        udp_tx += len;
+
        sess_local[s].cout += len;      // To send to master..
+       sess_local[s].pout++;
 }
 
 // process outgoing (to tunnel) IPv6
@@ -1185,11 +1202,13 @@ static void processipv6out(uint8_t * buf, int len)
        if (sp->snoop_ip && sp->snoop_port)
                snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port);
 
-       sp->cout += len; // byte count
-       sp->total_cout += len; // byte count
+       increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
+       sp->cout_delta += len;
        sp->pout++;
        udp_tx += len;
+
        sess_local[s].cout += len;      // To send to master..
+       sess_local[s].pout++;
 }
 
 //
@@ -1235,11 +1254,13 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
        if (sp->snoop_ip && sp->snoop_port)
                snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port);
 
-       sp->cout += len; // byte count
-       sp->total_cout += len; // byte count
+       increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
+       sp->cout_delta += len;
        sp->pout++;
        udp_tx += len;
+
        sess_local[s].cout += len;      // To send to master..
+       sess_local[s].pout++;
 }
 
 // add an AVP (16 bit)
@@ -1323,11 +1344,11 @@ static void controlnull(tunnelidt t)
 }
 
 // add a control message to a tunnel, and send if within window
-static void controladd(controlt * c, tunnelidt t, sessionidt s)
+static void controladd(controlt * c, tunnelidt t, sessionidt far)
 {
        *(uint16_t *) (c->buf + 2) = htons(c->length); // length
        *(uint16_t *) (c->buf + 4) = htons(tunnel[t].far); // tunnel
-       *(uint16_t *) (c->buf + 6) = htons(s ? session[s].far : 0); // session
+       *(uint16_t *) (c->buf + 6) = htons(far); // session
        *(uint16_t *) (c->buf + 8) = htons(tunnel[t].ns); // sequence
        tunnel[t].ns++;              // advance sequence
        // link in message in to queue
@@ -1518,7 +1539,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error)
                        control16(c, 1, result, 1);
 
                control16(c, 14, s, 1);   // assigned session (our end)
-               controladd(c, session[s].tunnel, s); // send the message
+               controladd(c, session[s].tunnel, session[s].far); // send the message
        }
 
        if (!session[s].die)
@@ -1544,7 +1565,7 @@ void sendipcp(tunnelidt t, sessionidt s)
 
        if (!r)
        {
-               sessionshutdown(s, "No free RADIUS sessions for IPCP");
+               sessionshutdown(s, "No free RADIUS sessions for IPCP", 3, 0);
                return;
        }
 
@@ -1566,7 +1587,7 @@ void sendipcp(tunnelidt t, sessionidt s)
        if (!q) return;
 
        *q = ConfigReq;
-       q[1] = r << RADIUS_SHIFT;                    // ID, dont care, we only send one type of request
+       q[1] = r >> RADIUS_SHIFT;                    // ID, dont care, we only send one type of request
        *(uint16_t *) (q + 2) = htons(10);
        q[4] = 3;
        q[5] = 6;
@@ -1588,7 +1609,7 @@ void sendipcp(tunnelidt t, sessionidt s)
                if (!q) return;
 
                *q = ConfigReq;
-               q[1] = r << RADIUS_SHIFT;               // ID, don't care, we
+               q[1] = r >> RADIUS_SHIFT;               // ID, don't care, we
                                                        // only send one type
                                                        // of request
                *(uint16_t *) (q + 2) = htons(14);
@@ -1801,12 +1822,11 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                uint16_t message = 0xFFFF;      // message type
                uint8_t fatal = 0;
                uint8_t mandatory = 0;
-               uint8_t chap = 0;               // if CHAP being used
+               uint8_t authtype = 0;           // proxy auth type
                uint16_t asession = 0;          // assigned session
                uint32_t amagic = 0;            // magic number
                uint8_t aflags = 0;             // flags from last LCF
                uint16_t version = 0x0100;      // protocol version (we handle 0.0 as well and send that back just in case)
-               int requestchap = 0;            // do we request PAP instead of original CHAP request?
                char called[MAXTEL] = "";       // called number
                char calling[MAXTEL] = "";      // calling number
 
@@ -2187,7 +2207,11 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        {
                                                uint16_t atype = ntohs(*(uint16_t *)b);
                                                LOG(4, s, t, "   Proxy Auth Type %d (%s)\n", atype, auth_type(atype));
-                                               requestchap = (atype == 2);
+                                               if (atype == 2)
+                                                       authtype = AUTHCHAP;
+                                               else if (atype == 3)
+                                                       authtype = AUTHPAP;
+
                                                break;
                                        }
                                case 30:    // Proxy Authentication Name
@@ -2224,8 +2248,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                {
                                                        if (*p == 5 && p[1] == 6) // Magic-Number
                                                                amagic = ntohl(*(uint32_t *) (p + 2));
-                                                       else if (*p == 3 && p[1] == 5 && *(uint16_t *) (p + 2) == htons(PPPCHAP) && p[4] == 5) // Authentication-Protocol
-                                                               chap = 1;
+                                                       else if (*p == 3 && p[1] == 4 && *(uint16_t *) (p + 2) == htons(PPPPAP)) // Authentication-Protocol (PAP)
+                                                               authtype = AUTHPAP;
+                                                       else if (*p == 3 && p[1] == 5 && *(uint16_t *) (p + 2) == htons(PPPCHAP) && p[4] == 5) // Authentication-Protocol (CHAP)
+                                                               authtype = AUTHCHAP;
                                                        else if (*p == 7) // Protocol-Field-Compression
                                                                aflags |= SESSIONPFC;
                                                        else if (*p == 8) // Address-and-Control-Field-Compression
@@ -2272,7 +2298,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                controls(c, 7, tunnel[t].hostname, 1); // host name (TBA)
                                                if (chapresponse) controlb(c, 13, chapresponse, 16, 1); // Challenge response
                                                control16(c, 9, t, 1); // assigned tunnel
-                                               controladd(c, t, s); // send the resply
+                                               controladd(c, t, 0); // send the resply
                                        }
                                        tunnel[t].state = TUNNELOPENING;
                                        break;
@@ -2300,16 +2326,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        // TBA
                                        break;
                                case 10:      // ICRQ
-                                       if (!sessionfree)
-                                       {
-                                               STAT(session_overflow);
-                                               LOG(1, 0, t, "No free sessions\n");
-                                               return;
-                                       }
-                                       else
+                                       if (sessionfree)
                                        {
                                                uint16_t r;
-                                               controlt *c;
 
                                                s = sessionfree;
                                                sessionfree = session[s].next;
@@ -2319,28 +2338,40 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                        config->cluster_highest_sessionid = s;
 
                                                // make a RADIUS session
-                                               if (!(r = radiusnew(s)))
+                                               if ((r = radiusnew(s)))
                                                {
-                                                       LOG(1, s, t, "No free RADIUS sessions for ICRQ\n");
-                                                       sessionclear(s);
-                                                       return;
+                                                       controlt *c = controlnew(11); // sending ICRP
+                                                       session[s].opened = time_now;
+                                                       session[s].tunnel = t;
+                                                       session[s].far = asession;
+                                                       session[s].last_packet = time_now;
+                                                       LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far);
+                                                       control16(c, 14, s, 1); // assigned session
+                                                       controladd(c, t, asession); // send the reply
+
+                                                       strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1);
+                                                       strncpy(session[s].called, called, sizeof(session[s].called) - 1);
+                                                       strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1);
+                                                       STAT(session_created);
+                                                       break;
                                                }
 
-                                               c = controlnew(11); // sending ICRP
-                                               session[s].opened = time_now;
-                                               session[s].tunnel = t;
-                                               session[s].far = asession;
-                                               session[s].last_packet = time_now;
-                                               LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far);
-                                               control16(c, 14, s, 1); // assigned session
-                                               controladd(c, t, s); // send the reply
-
-                                               strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1);
-                                               strncpy(session[s].called, called, sizeof(session[s].called) - 1);
-                                               strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1);
-                                               STAT(session_created);
+
+                                               LOG(1, s, t, "No free RADIUS sessions for ICRQ\n");
+                                               sessionclear(s);
                                        }
-                                       break;
+                                       else
+                                       {
+                                               STAT(session_overflow);
+                                               LOG(1, 0, t, "No free sessions\n");
+                                       }
+
+                                       {
+                                               controlt *c = controlnew(14); // CDN
+                                               control16(c, 1, 4, 1); // temporary lack of resources
+                                               controladd(c, session[s].tunnel, asession); // send the message
+                                       }
+                                       return;
                                case 11:      // ICRP
                                        // TBA
                                        break;
@@ -2350,9 +2381,12 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        session[s].l2tp_flags = aflags; // set flags received
                                        LOG(3, s, t, "Magic %X Flags %X\n", amagic, aflags);
                                        controlnull(t); // ack
-                                       // In CHAP state, request PAP instead
-                                       if (requestchap)
-                                               initlcp(t, s);
+                                       // proxy authentication type is not supported
+                                       if (!(config->radius_authtypes & authtype))
+                                               authtype = config->radius_authprefer;
+
+                                       // start LCP
+                                       sendlcp(t, s, authtype);
                                        break;
                                case 14:      // CDN
                                        controlnull(t); // ack
@@ -2537,42 +2571,61 @@ static void processtun(uint8_t * buf, int len)
        // Else discard.
 }
 
-//
-// Maximum number of actions to complete.
-// This is to avoid sending out too many packets
-// at once.
-#define MAX_ACTIONS 500
-
-static int regular_cleanups(void)
+// Handle retries, timeouts.  Runs every 1/10th sec, want to ensure
+// that we look at the whole of the tunnel, radius and session tables
+// every second
+static void regular_cleanups(double period)
 {
-       static sessionidt s = 0;        // Next session to check for actions on.
-       tunnelidt t;
-       int count=0,i;
-       uint16_t r;
-       static clockt next_acct = 0;
-       static clockt next_shut_acct = 0;
+       // Next tunnel, radius and session to check for actions on.
+       static tunnelidt t = 0;
+       static int r = 0;
+       static sessionidt s = 0;
+
+       int t_actions = 0;
+       int r_actions = 0;
+       int s_actions = 0;
+
+       int t_slice;
+       int r_slice;
+       int s_slice;
+
+       int i;
        int a;
 
-       LOG(3, 0, 0, "Begin regular cleanup\n");
+       // divide up tables into slices based on the last run
+       t_slice = config->cluster_highest_tunnelid  * period;
+       r_slice = (MAXRADIUS - 1)                   * period;
+       s_slice = config->cluster_highest_sessionid * period;
 
-       for (r = 1; r < MAXRADIUS; r++)
-       {
-               if (!radius[r].state)
-                       continue;
-               if (radius[r].retry)
-               {
-                       if (radius[r].retry <= TIME)
-                               radiusretry(r);
-               } else
-                       radius[r].retry = backoff(radius[r].try+1);     // Is this really needed? --mo
-       }
-       for (t = 1; t <= config->cluster_highest_tunnelid; t++)
+       if (t_slice < 1)
+           t_slice = 1;
+       else if (t_slice > config->cluster_highest_tunnelid)
+           t_slice = config->cluster_highest_tunnelid;
+
+       if (r_slice < 1)
+           r_slice = 1;
+       else if (r_slice > (MAXRADIUS - 1))
+           r_slice = MAXRADIUS - 1;
+
+       if (s_slice < 1)
+           s_slice = 1;
+       else if (s_slice > config->cluster_highest_sessionid)
+           s_slice = config->cluster_highest_sessionid;
+
+       LOG(4, 0, 0, "Begin regular cleanup (last %f seconds ago)\n", period);
+
+       for (i = 0; i < t_slice; i++)
        {
+               t++;
+               if (t > config->cluster_highest_tunnelid)
+                       t = 1;
+
                // check for expired tunnels
                if (tunnel[t].die && tunnel[t].die <= TIME)
                {
                        STAT(tunnel_timeout);
                        tunnelkill(t, "Expired");
+                       t_actions++;
                        continue;
                }
                // check for message resend
@@ -2592,6 +2645,8 @@ static int regular_cleanups(void)
                                                tunnelsend(c->buf, c->length, t);
                                                c = c->next;
                                        }
+
+                               t_actions++;
                        }
                }
                // Send hello
@@ -2600,6 +2655,7 @@ static int regular_cleanups(void)
                        controlt *c = controlnew(6); // sending HELLO
                        controladd(c, t, 0); // send the message
                        LOG(3, 0, t, "Sending HELLO message\n");
+                       t_actions++;
                }
 
                // Check for tunnel changes requested from the CLI
@@ -2610,13 +2666,28 @@ static int regular_cleanups(void)
                        {
                                LOG(2, 0, t, "Dropping tunnel by CLI\n");
                                tunnelshutdown(t, "Requested by administrator", 1, 0, 0);
+                               t_actions++;
                        }
                }
+       }
 
+       for (i = 0; i < r_slice; i++)
+       {
+               r++;
+               if (r >= MAXRADIUS)
+                       r = 1;
+
+               if (!radius[r].state)
+                       continue;
+
+               if (radius[r].retry <= TIME)
+               {
+                       radiusretry(r);
+                       r_actions++;
+               }
        }
 
-       count = 0;
-       for (i = 1; i <= config->cluster_highest_sessionid; i++)
+       for (i = 0; i < s_slice; i++)
        {
                s++;
                if (s > config->cluster_highest_sessionid)
@@ -2631,7 +2702,7 @@ static int regular_cleanups(void)
                        if (session[s].die <= TIME)
                        {
                                sessionkill(s, "Expired");
-                               if (++count >= MAX_ACTIONS) break;
+                               s_actions++;
                        }
                        continue;
                }
@@ -2641,6 +2712,7 @@ static int regular_cleanups(void)
                        // IPCP has not completed yet. Resend
                        LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
                        sendipcp(session[s].tunnel, s);
+                       s_actions++;
                }
 
                // Drop sessions who have not responded within IDLE_TIMEOUT seconds
@@ -2648,7 +2720,7 @@ static int regular_cleanups(void)
                {
                        sessionshutdown(s, "No response to LCP ECHO requests.", 3, 0);
                        STAT(session_timeout);
-                       if (++count >= MAX_ACTIONS) break;
+                       s_actions++;
                        continue;
                }
 
@@ -2668,7 +2740,7 @@ static int regular_cleanups(void)
                        LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n",
                                        (int)(time_now - session[s].last_packet));
                        tunnelsend(b, 24, session[s].tunnel); // send it
-                       if (++count >= MAX_ACTIONS) break;
+                       s_actions++;
                }
 
                // Check for actions requested from the CLI
@@ -2682,6 +2754,7 @@ static int regular_cleanups(void)
                                LOG(2, s, session[s].tunnel, "Dropping session by CLI\n");
                                sessionshutdown(s, "Requested by administrator.", 3, 0);
                                a = 0; // dead, no need to check for other actions
+                               s_actions++;
                        }
 
                        if (a & CLI_SESS_NOSNOOP)
@@ -2689,6 +2762,7 @@ static int regular_cleanups(void)
                                LOG(2, s, session[s].tunnel, "Unsnooping session by CLI\n");
                                session[s].snoop_ip = 0;
                                session[s].snoop_port = 0;
+                               s_actions++;
                                send++;
                        }
                        else if (a & CLI_SESS_SNOOP)
@@ -2699,6 +2773,7 @@ static int regular_cleanups(void)
 
                                session[s].snoop_ip = cli_session_actions[s].snoop_ip;
                                session[s].snoop_port = cli_session_actions[s].snoop_port;
+                               s_actions++;
                                send++;
                        }
 
@@ -2706,6 +2781,7 @@ static int regular_cleanups(void)
                        {
                                LOG(2, s, session[s].tunnel, "Un-throttling session by CLI\n");
                                throttle_session(s, 0, 0);
+                               s_actions++;
                                send++;
                        }
                        else if (a & CLI_SESS_THROTTLE)
@@ -2715,6 +2791,7 @@ static int regular_cleanups(void)
                                    cli_session_actions[s].throttle_out);
 
                                throttle_session(s, cli_session_actions[s].throttle_in, cli_session_actions[s].throttle_out);
+                               s_actions++;
                                send++;
                        }
 
@@ -2722,6 +2799,7 @@ static int regular_cleanups(void)
                        {
                                LOG(2, s, session[s].tunnel, "Un-filtering session by CLI\n");
                                filter_session(s, 0, 0);
+                               s_actions++;
                                send++;
                        }
                        else if (a & CLI_SESS_FILTER)
@@ -2731,13 +2809,12 @@ static int regular_cleanups(void)
                                    cli_session_actions[s].filter_out);
 
                                filter_session(s, cli_session_actions[s].filter_in, cli_session_actions[s].filter_out);
+                               s_actions++;
                                send++;
                        }
 
                        if (send)
                                cluster_send_session(s);
-
-                       if (++count >= MAX_ACTIONS) break;
                }
 
                // RADIUS interim accounting
@@ -2758,38 +2835,14 @@ static int regular_cleanups(void)
 
                        radiussend(r, RADIUSINTERIM);
                        sess_local[s].last_interim = time_now;
-
-                       if (++count >= MAX_ACTIONS)
-                               break;
+                       s_actions++;
                }
        }
 
-       if (*config->accounting_dir)
-       {
-               if (next_acct <= TIME)
-               {
-                       // Dump accounting data
-                       next_acct = TIME + ACCT_TIME;
-                       next_shut_acct = TIME + ACCT_SHUT_TIME;
-                       dump_acct_info(1);
-               }
-               else if (next_shut_acct <= TIME)
-               {
-                       // Dump accounting data for shutdown sessions
-                       next_shut_acct = TIME + ACCT_SHUT_TIME;
-                       if (shut_acct_n)
-                               dump_acct_info(0);
-               }
-       }
-
-       if (count >= MAX_ACTIONS)
-               return 1;       // Didn't finish!
-
-       LOG(3, 0, 0, "End regular cleanup (%d actions), next in %d seconds\n", count, config->cleanup_interval);
-       return 0;
+       LOG(4, 0, 0, "End regular cleanup: checked %d/%d/%d tunnels/radius/sessions; %d/%d/%d actions\n",
+               t_slice, r_slice, s_slice, t_actions, r_actions, s_actions);
 }
 
-
 //
 // Are we in the middle of a tunnel update, or radius
 // requests??
@@ -2797,8 +2850,39 @@ static int regular_cleanups(void)
 static int still_busy(void)
 {
        int i;
+       static time_t stopped_bgp = 0;
        static clockt last_talked = 0;
        static clockt start_busy_wait = 0;
+
+       if (!config->cluster_iam_master)
+       {
+#ifdef BGP
+               if (bgp_configured)
+               {
+                       if (!stopped_bgp)
+                       {
+                               LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY);
+
+                               for (i = 0; i < BGP_NUM_PEERS; i++)
+                                       if (bgp_peers[i].state == Established)
+                                               bgp_stop(&bgp_peers[i]);
+
+                               stopped_bgp = time_now;
+
+                               // we don't want to become master
+                               cluster_send_ping(0);
+
+                               return 1;
+                       }
+
+                       if (time_now < (stopped_bgp + QUIT_DELAY))
+                               return 1;
+               }
+#endif /* BGP */
+
+               return 0;
+       }
+
        if (start_busy_wait == 0)
                start_busy_wait = TIME;
 
@@ -2850,7 +2934,6 @@ static void mainloop(void)
        uint8_t buf[65536];
        struct timeval to;
        clockt next_cluster_ping = 0;   // send initial ping immediately
-       time_t next_clean = time_now + config->cleanup_interval;
 
        LOG(4, 0, 0, "Beginning of main loop.  udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
                udpfd, tunfd, cluster_sockfd, controlfd);
@@ -2875,6 +2958,7 @@ static void mainloop(void)
                fd_set w;
                int bgp_set[BGP_NUM_PEERS];
 #endif /* BGP */
+               int more = 0;
 
                if (config->reload_config)
                {
@@ -2913,7 +2997,7 @@ static void mainloop(void)
 
                STAT(select_called);
 
-               TIME = now();
+               TIME = now(NULL);
                if (n < 0)
                {
                        if (errno == EINTR ||
@@ -3040,6 +3124,7 @@ static void mainloop(void)
                                        config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
 
                                STAT(multi_read_exceeded);
+                               more++;
                        }
                }
 
@@ -3062,9 +3147,11 @@ static void mainloop(void)
                                next_cluster_ping = TIME + config->cluster_hb_interval;
                }
 
+               if (!config->cluster_iam_master)
+                       continue;
+
                        // Run token bucket filtering queue..
                        // Only run it every 1/10th of a second.
-                       // Runs on all machines both master and slave.
                {
                        static clockt last_run = 0;
                        if (last_run != TIME)
@@ -3074,20 +3161,42 @@ static void mainloop(void)
                        }
                }
 
-               /* Handle timeouts. Make sure that this gets run anyway, even if there was
-                * something to read, else under load this will never actually run....
-                *
-                */
-               if (config->cluster_iam_master && next_clean <= time_now)
+                       // Handle timeouts, retries etc.
                {
-                       if (regular_cleanups())
+                       static double last_clean = 0;
+                       double this_clean;
+                       double diff;
+
+                       TIME = now(&this_clean);
+                       diff = this_clean - last_clean;
+
+                       // Run during idle time (after we've handled
+                       // all incoming packets) or every 1/10th sec
+                       if (!more || diff > 0.1)
                        {
-                               // Did it finish?
-                               next_clean = time_now + 1 ;     // Didn't finish. Check quickly.
+                               regular_cleanups(diff);
+                               last_clean = this_clean;
                        }
-                       else
+               }
+
+               if (*config->accounting_dir)
+               {
+                       static clockt next_acct = 0;
+                       static clockt next_shut_acct = 0;
+
+                       if (next_acct <= TIME)
+                       {
+                               // Dump accounting data
+                               next_acct = TIME + ACCT_TIME;
+                               next_shut_acct = TIME + ACCT_SHUT_TIME;
+                               dump_acct_info(1);
+                       }
+                       else if (next_shut_acct <= TIME)
                        {
-                               next_clean = time_now + config->cleanup_interval; // Did. Move to next interval.
+                               // Dump accounting data for shutdown sessions
+                               next_shut_acct = TIME + ACCT_SHUT_TIME;
+                               if (shut_acct_n)
+                                       dump_acct_info(0);
                        }
                }
        }
@@ -3102,6 +3211,7 @@ static void mainloop(void)
 
        //
        // Important!!! We MUST not process any packets past this point!
+       LOG(1, 0, 0, "Clean shutdown complete\n");
 }
 
 static void stripdomain(char *host)
@@ -3186,6 +3296,7 @@ static void initdata(int optdebug, char *optconfig)
        config->debug = optdebug;
        config->num_tbfs = MAXTBFS;
        config->rl_rate = 28; // 28kbps
+       config->cluster_master_min_adv = 1;
        strcpy(config->random_device, RANDOMDEVICE);
 
        log_stream = stderr;
@@ -3575,7 +3686,7 @@ void snoop_send_packet(char *packet, uint16_t size, in_addr_t destination, uint1
 
 static int dump_session(FILE **f, sessiont *s)
 {
-       if (!s->opened || !s->ip || !(s->cin || s->cout) || !*s->user || s->walled_garden)
+       if (!s->opened || !s->ip || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
                return 1;
 
        if (!*f)
@@ -3609,11 +3720,10 @@ static int dump_session(FILE **f, sessiont *s)
                s->user,                                                // username
                fmtaddr(htonl(s->ip), 0),                               // ip
                (s->throttle_in || s->throttle_out) ? 2 : 1,            // qos
-               (uint32_t) s->cin,                                      // uptxoctets
-               (uint32_t) s->cout);                                    // downrxoctets
+               (uint32_t) s->cin_delta,                                // uptxoctets
+               (uint32_t) s->cout_delta);                              // downrxoctets
 
-       s->pin = s->cin = 0;
-       s->pout = s->cout = 0;
+       s->cin_delta = s->cout_delta = 0;
 
        return 1;
 }
@@ -3784,14 +3894,6 @@ int main(int argc, char *argv[])
 
        mainloop();
 
-#ifdef BGP
-       /* try to shut BGP down cleanly; with luck the sockets will be
-          writable since we're out of the select */
-       for (i = 0; i < BGP_NUM_PEERS; i++)
-               if (bgp_peers[i].state == Established)
-                       bgp_stop(&bgp_peers[i]);
-#endif /* BGP */
-
        /* remove plugins (so cleanup code gets run) */
        plugins_done();
 
@@ -3983,7 +4085,7 @@ static void update_config()
                        // test twice, In case someone works with
                        // a secondary radius server without defining
                        // a primary one, this will work even then.
-                       if (i>0 && !config->radiusport[i])
+                       if (i > 0 && !config->radiusport[i])
                                config->radiusport[i] = config->radiusport[i-1];
                        if (!config->radiusport[i])
                                config->radiusport[i] = RADPORT;
@@ -3992,12 +4094,12 @@ static void update_config()
        if (!config->numradiusservers)
                LOG(0, 0, 0, "No RADIUS servers defined!\n");
 
-       config->num_radfds = 2 << RADIUS_SHIFT;
+       config->num_radfds = 1 << RADIUS_SHIFT;
 
        // parse radius_authtypes_s
        config->radius_authtypes = config->radius_authprefer = 0;
        p = config->radius_authtypes_s;
-       while (*p)
+       while (p && *p)
        {
                char *s = strpbrk(p, " \t,");
                int type = 0;
@@ -4022,6 +4124,8 @@ static void update_config()
                config->radius_authtypes |= type;
                if (!config->radius_authprefer)
                        config->radius_authprefer = type;
+
+               p = s;
        }
 
        if (!config->radius_authtypes)
@@ -4066,7 +4170,6 @@ static void update_config()
        }
 
        memcpy(config->old_plugins, config->plugins, sizeof(config->plugins));
-       if (!config->cleanup_interval) config->cleanup_interval = 10;
        if (!config->multi_read_count) config->multi_read_count = 10;
        if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR);
        if (!*config->cluster_interface)
@@ -4173,8 +4276,16 @@ int sessionsetup(tunnelidt t, sessionidt s)
                for (i = 1; i <= config->cluster_highest_sessionid; i++)
                {
                        if (i == s) continue;
-                       if (ip == session[i].ip) sessionkill(i, "Duplicate IP address");
-                       if (!session[s].walled_garden && !session[i].walled_garden && strcasecmp(user, session[i].user) == 0)
+                       if (!session[s].opened) continue;
+                       if (ip == session[i].ip)
+                       {
+                               sessionkill(i, "Duplicate IP address");
+                               continue;
+                       }
+
+                       if (config->allow_duplicate_users) continue;
+                       if (session[s].walled_garden || session[i].walled_garden) continue;
+                       if (!strcasecmp(user, session[i].user))
                                sessionkill(i, "Duplicate session for users");
                }
        }
@@ -4421,6 +4532,7 @@ static int add_plugin(char *plugin_name)
                radiusnew,
                radiussend,
                getconfig,
+               sessionshutdown,
                sessionkill,
                throttle_session,
                cluster_send_session,