provide more comments/defaults in etc/startup-config.default
[l2tpns.git] / l2tpns.c
index 1489318..50ff43f 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
 
 // 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.107 2005/06/02 11:32:30 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.112 2005/06/24 07:05:04 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -52,7 +52,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.107 2005/06/02 11:32:30 bodea Exp
 
 #ifdef BGP
 #include "bgp.h"
 
 #ifdef BGP
 #include "bgp.h"
-#endif /* BGP */
+#endif
 
 // Globals
 configt *config = NULL;                // all configuration
 
 // Globals
 configt *config = NULL;                // all configuration
@@ -64,13 +64,14 @@ int snoopfd = -1;           // UDP file handle for sending out intercept data
 int *radfds = NULL;            // RADIUS requests file handles
 int ifrfd = -1;                        // File descriptor for routing, etc
 int ifr6fd = -1;               // File descriptor for IPv6 routing, etc
 int *radfds = NULL;            // RADIUS requests file handles
 int ifrfd = -1;                        // File descriptor for routing, etc
 int ifr6fd = -1;               // File descriptor for IPv6 routing, etc
-static int rand_fd = -1;       // Random data source
+int rand_fd = -1;              // Random data source
+int cluster_sockfd = -1;       // Intra-cluster communications socket.
+int epollfd = -1;              // event polling
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static int tunidx;             // ifr_ifindex of tun device
 static int syslog_log = 0;     // are we logging to syslog
 static FILE *log_stream = 0;   // file handle for direct logging (i.e. direct into file, not via syslog).
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static int tunidx;             // ifr_ifindex of tun device
 static int syslog_log = 0;     // are we logging to syslog
 static FILE *log_stream = 0;   // file handle for direct logging (i.e. direct into file, not via syslog).
-extern int cluster_sockfd;     // Intra-cluster communications socket.
 uint32_t last_id = 0;          // Unique ID for radius accounting
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
 uint32_t last_id = 0;          // Unique ID for radius accounting
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
@@ -176,8 +177,7 @@ static void free_ip_address(sessionidt s);
 static void dump_acct_info(int all);
 static void sighup_handler(int sig);
 static void sigalrm_handler(int sig);
 static void dump_acct_info(int all);
 static void sighup_handler(int sig);
 static void sigalrm_handler(int sig);
-static void sigterm_handler(int sig);
-static void sigquit_handler(int sig);
+static void shutdown_handler(int sig);
 static void sigchild_handler(int sig);
 static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_length, char **challenge_response);
 static void update_config(void);
 static void sigchild_handler(int sig);
 static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_length, char **challenge_response);
 static void update_config(void);
@@ -193,6 +193,10 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
 // on slaves, alow BGP to withdraw cleanly before exiting
 #define QUIT_DELAY     5
 
 // on slaves, alow BGP to withdraw cleanly before exiting
 #define QUIT_DELAY     5
 
+// quit actions (master)
+#define QUIT_FAILOVER  1 // SIGTERM: exit when all control messages have been acked (for cluster failover)
+#define QUIT_SHUTDOWN  2 // SIGQUIT: shutdown sessions/tunnels, reject new connections
+
 // return internal time (10ths since process startup), set f if given
 static clockt now(double *f)
 {
 // return internal time (10ths since process startup), set f if given
 static clockt now(double *f)
 {
@@ -322,7 +326,8 @@ static void initrandom(char *source)
                return;
 
        // close previous source, if any
                return;
 
        // close previous source, if any
-       if (rand_fd >= 0) close(rand_fd);
+       if (rand_fd >= 0)
+               close(rand_fd);
 
        rand_fd = -1;
 
 
        rand_fd = -1;
 
@@ -339,13 +344,6 @@ static void initrandom(char *source)
                                        path, strerror(errno));
                }
        }
                                        path, strerror(errno));
                }
        }
-
-       // no source: seed prng
-       {
-               unsigned seed = time_now ^ getpid();
-               LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
-               srand(seed);
-       }
 }
 
 // fill buffer with random data
 }
 
 // fill buffer with random data
@@ -366,7 +364,7 @@ void random_data(uint8_t *buf, int len)
                                        strerror(errno));
 
                                // fall back to rand()
                                        strerror(errno));
 
                                // fall back to rand()
-                               initrandom(0);
+                               initrandom(NULL);
                        }
 
                        n = 0;
                        }
 
                        n = 0;
@@ -1012,7 +1010,8 @@ static void processipout(uint8_t * buf, int len)
                if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second.
                {
                        LOG(4, 0, 0, "IP: Sending ICMP host unreachable to %s\n", fmtaddr(*(in_addr_t *)(buf + 12), 0));
                if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second.
                {
                        LOG(4, 0, 0, "IP: Sending ICMP host unreachable to %s\n", fmtaddr(*(in_addr_t *)(buf + 12), 0));
-                       host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4), ip, buf, (len < 64) ? 64 : len);
+                       host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4),
+                               config->bind_address ? config->bind_address : my_address, buf, len);
                }
                return;
        }
                }
                return;
        }
@@ -1715,7 +1714,7 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha
        // close session
        for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
                if (session[s].tunnel == t)
        // close session
        for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
                if (session[s].tunnel == t)
-                       sessionshutdown(s, reason, 3, 0);
+                       sessionshutdown(s, reason, 0, 0);
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = TIME + 700; // Clean up in 70 seconds
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = TIME + 700; // Clean up in 70 seconds
@@ -2025,8 +2024,6 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                continue;
                                        }
 
                                                continue;
                                        }
 
-                                       LOG(4, s, t, "Hidden AVP\n");
-
                                        // Unhide the AVP
                                        unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length);
 
                                        // Unhide the AVP
                                        unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length);
 
@@ -2047,7 +2044,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        n = orig_len;
                                }
 
                                        n = orig_len;
                                }
 
-                               LOG(4, s, t, "   AVP %d (%s) len %d\n", mtype, avp_name(mtype), n);
+                               LOG(4, s, t, "   AVP %d (%s) len %d%s%s\n", mtype, avp_name(mtype), n,
+                                       flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : "");
+
                                switch (mtype)
                                {
                                case 0:     // message type
                                switch (mtype)
                                {
                                case 0:     // message type
@@ -2269,6 +2268,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                case 36:    // Random Vector
                                        LOG(4, s, t, "   Random Vector received.  Enabled AVP Hiding.\n");
                                        memset(session[s].random_vector, 0, sizeof(session[s].random_vector));
                                case 36:    // Random Vector
                                        LOG(4, s, t, "   Random Vector received.  Enabled AVP Hiding.\n");
                                        memset(session[s].random_vector, 0, sizeof(session[s].random_vector));
+                                       if (n > sizeof(session[s].random_vector))
+                                               n = sizeof(session[s].random_vector);
                                        memcpy(session[s].random_vector, b, n);
                                        session[s].random_vector_length = n;
                                        break;
                                        memcpy(session[s].random_vector, b, n);
                                        session[s].random_vector_length = n;
                                        break;
@@ -2291,6 +2292,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                switch (message)
                                {
                                case 1:       // SCCRQ - Start Control Connection Request
                                switch (message)
                                {
                                case 1:       // SCCRQ - Start Control Connection Request
+                                       tunnel[t].state = TUNNELOPENING;
+                                       if (main_quit != QUIT_SHUTDOWN)
                                        {
                                                controlt *c = controlnew(2); // sending SCCRP
                                                control16(c, 2, version, 1); // protocol version
                                        {
                                                controlt *c = controlnew(2); // sending SCCRP
                                                control16(c, 2, version, 1); // protocol version
@@ -2300,7 +2303,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, t, 0); // send the resply
                                        }
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, t, 0); // send the resply
                                        }
-                                       tunnel[t].state = TUNNELOPENING;
+                                       else
+                                       {
+                                               tunnelshutdown(t, "Shutting down", 6, 0, 0);
+                                       }
                                        break;
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
                                        break;
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
@@ -2326,7 +2332,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        // TBA
                                        break;
                                case 10:      // ICRQ
                                        // TBA
                                        break;
                                case 10:      // ICRQ
-                                       if (sessionfree)
+                                       if (sessionfree && main_quit != QUIT_SHUTDOWN)
                                        {
                                                uint16_t r;
 
                                        {
                                                uint16_t r;
 
@@ -2368,8 +2374,12 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
 
                                        {
                                                controlt *c = controlnew(14); // CDN
 
                                        {
                                                controlt *c = controlnew(14); // CDN
-                                               control16(c, 1, 4, 1); // temporary lack of resources
-                                               controladd(c, session[s].tunnel, asession); // send the message
+                                               if (main_quit == QUIT_SHUTDOWN)
+                                                       control16(c, 1, 2, 7); // try another
+                                               else
+                                                       control16(c, 1, 4, 0); // temporary lack of resources
+
+                                               controladd(c, t, asession); // send the message
                                        }
                                        return;
                                case 11:      // ICRP
                                        }
                                        return;
                                case 11:      // ICRP
@@ -2707,7 +2717,8 @@ static void regular_cleanups(double period)
                        continue;
                }
 
                        continue;
                }
 
-               if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
+               if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED)
+                   && !(sess_local[s].radius && radius[sess_local[s].radius].state == RADIUSIPCP))
                {
                        // IPCP has not completed yet. Resend
                        LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
                {
                        // IPCP has not completed yet. Resend
                        LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
@@ -2823,7 +2834,8 @@ static void regular_cleanups(double period)
                    && !sess_local[s].radius // RADIUS already in progress
                    && time_now - sess_local[s].last_interim >= config->radius_interim)
                {
                    && !sess_local[s].radius // RADIUS already in progress
                    && time_now - sess_local[s].last_interim >= config->radius_interim)
                {
-                       if (!(r = radiusnew(s)))
+                       int rad = radiusnew(s);
+                       if (!rad)
                        {
                                LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n");
                                STAT(radius_overflow);
                        {
                                LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n");
                                STAT(radius_overflow);
@@ -2833,7 +2845,7 @@ static void regular_cleanups(double period)
                        LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n",
                                session[s].user, session[s].unique_id);
 
                        LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n",
                                session[s].user, session[s].unique_id);
 
-                       radiussend(r, RADIUSINTERIM);
+                       radiussend(rad, RADIUSINTERIM);
                        sess_local[s].last_interim = time_now;
                        s_actions++;
                }
                        sess_local[s].last_interim = time_now;
                        s_actions++;
                }
@@ -2850,13 +2862,13 @@ static void regular_cleanups(double period)
 static int still_busy(void)
 {
        int i;
 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
        static clockt last_talked = 0;
        static clockt start_busy_wait = 0;
 
        if (!config->cluster_iam_master)
        {
 #ifdef BGP
+               static time_t stopped_bgp = 0;
                if (bgp_configured)
                {
                        if (!stopped_bgp)
                if (bgp_configured)
                {
                        if (!stopped_bgp)
@@ -2883,6 +2895,22 @@ static int still_busy(void)
                return 0;
        }
 
                return 0;
        }
 
+       if (main_quit == QUIT_SHUTDOWN)
+       {
+               static int dropped = 0;
+               if (!dropped)
+               {
+                       int i;
+
+                       LOG(1, 0, 0, "Dropping sessions and tunnels\n");
+                       for (i = 1; i < MAXTUNNEL; i++)
+                               if (tunnel[i].ip || tunnel[i].state)
+                                       tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
+
+                       dropped = 1;
+               }
+       }
+
        if (start_busy_wait == 0)
                start_busy_wait = TIME;
 
        if (start_busy_wait == 0)
                start_busy_wait = TIME;
 
@@ -2924,41 +2952,89 @@ static int still_busy(void)
        return 0;
 }
 
        return 0;
 }
 
-static fd_set readset;
-static int readset_n = 0;
+#ifdef HAVE_EPOLL
+# include <sys/epoll.h>
+#else
+# define FAKE_EPOLL_IMPLEMENTATION /* include the functions */
+# include "fake_epoll.h"
+#endif
+
+// the base set of fds polled: control, cli, udp, tun, cluster
+#define BASE_FDS       5
+
+// additional polled fds
+#ifdef BGP
+# define EXTRA_FDS     BGP_NUM_PEERS
+#else
+# define EXTRA_FDS     0
+#endif
 
 // main loop - gets packets on tun or udp and processes them
 static void mainloop(void)
 {
        int i;
        uint8_t buf[65536];
 
 // main loop - gets packets on tun or udp and processes them
 static void mainloop(void)
 {
        int i;
        uint8_t buf[65536];
-       struct timeval to;
        clockt next_cluster_ping = 0;   // send initial ping immediately
        clockt next_cluster_ping = 0;   // send initial ping immediately
+       struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS];
+       int maxevent = sizeof(events)/sizeof(*events);
+
+       if ((epollfd = epoll_create(maxevent)) < 0)
+       {
+               LOG(0, 0, 0, "epoll_create failed: %s\n", strerror(errno));
+               exit(1);
+       }
 
        LOG(4, 0, 0, "Beginning of main loop.  udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
                udpfd, tunfd, cluster_sockfd, controlfd);
 
 
        LOG(4, 0, 0, "Beginning of main loop.  udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
                udpfd, tunfd, cluster_sockfd, controlfd);
 
-       FD_ZERO(&readset);
-       FD_SET(udpfd, &readset);
-       FD_SET(tunfd, &readset);
-       FD_SET(controlfd, &readset);
-       FD_SET(clifd, &readset);
-       if (cluster_sockfd) FD_SET(cluster_sockfd, &readset);
-       readset_n = udpfd;
-       if (tunfd > readset_n)          readset_n = tunfd;
-       if (controlfd > readset_n)      readset_n = controlfd;
-       if (clifd > readset_n)          readset_n = clifd;
-       if (cluster_sockfd > readset_n) readset_n = cluster_sockfd;
-
-       while (!main_quit || still_busy())
+       /* setup our fds to poll for input */
        {
        {
-               fd_set r;
-               int n = readset_n;
+               static struct event_data d[BASE_FDS];
+               struct epoll_event e;
+
+               e.events = EPOLLIN;
+               i = 0;
+
+               d[i].type = FD_TYPE_CONTROL;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
+
+               d[i].type = FD_TYPE_CLI;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e);
+
+               d[i].type = FD_TYPE_UDP;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e);
+
+               d[i].type = FD_TYPE_TUN;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e);
+
+               d[i].type = FD_TYPE_CLUSTER;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e);
+       }
+
 #ifdef BGP
 #ifdef BGP
-               fd_set w;
-               int bgp_set[BGP_NUM_PEERS];
+       signal(SIGPIPE, SIG_IGN);
+       bgp_setup(config->as_number);
+       if (config->bind_address)
+               bgp_add_route(config->bind_address, 0xffffffff);
+
+       for (i = 0; i < BGP_NUM_PEERS; i++)
+       {
+               if (config->neighbour[i].name[0])
+                       bgp_start(&bgp_peers[i], config->neighbour[i].name,
+                               config->neighbour[i].as, config->neighbour[i].keepalive,
+                               config->neighbour[i].hold, 0); /* 0 = routing disabled */
+       }
 #endif /* BGP */
 #endif /* BGP */
+
+       while (!main_quit || still_busy())
+       {
                int more = 0;
                int more = 0;
+               int n;
 
                if (config->reload_config)
                {
 
                if (config->reload_config)
                {
@@ -2966,35 +3042,11 @@ static void mainloop(void)
                        update_config();
                }
 
                        update_config();
                }
 
-               memcpy(&r, &readset, sizeof(fd_set));
-               to.tv_sec = 0;
-               to.tv_usec = 100000; // 1/10th of a second.
-
 #ifdef BGP
 #ifdef BGP
-               FD_ZERO(&w);
-               for (i = 0; i < BGP_NUM_PEERS; i++)
-               {
-                       bgp_set[i] = bgp_select_state(&bgp_peers[i]);
-                       if (bgp_set[i] & 1)
-                       {
-                               FD_SET(bgp_peers[i].sock, &r);
-                               if (bgp_peers[i].sock > n)
-                                       n = bgp_peers[i].sock;
-                       }
-
-                       if (bgp_set[i] & 2)
-                       {
-                               FD_SET(bgp_peers[i].sock, &w);
-                               if (bgp_peers[i].sock > n)
-                                       n = bgp_peers[i].sock;
-                       }
-               }
-
-               n = select(n + 1, &r, &w, 0, &to);
-#else /* BGP */
-               n = select(n + 1, &r, 0, 0, &to);
+               bgp_set_poll();
 #endif /* BGP */
 
 #endif /* BGP */
 
+               n = epoll_wait(epollfd, events, maxevent, 100); // timeout 100ms (1/10th sec)
                STAT(select_called);
 
                TIME = now(NULL);
                STAT(select_called);
 
                TIME = now(NULL);
@@ -3005,70 +3057,85 @@ static void mainloop(void)
                                continue;
 
                        LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno));
                                continue;
 
                        LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno));
-                       main_quit++;
-                       break;
+                       break; // exit
                }
                }
-               else if (n)
+
+               if (n)
                {
                        struct sockaddr_in addr;
                        int alen, c, s;
                {
                        struct sockaddr_in addr;
                        int alen, c, s;
+                       int udp_ready = 0;
+                       int tun_ready = 0;
+                       int cluster_ready = 0;
                        int udp_pkts = 0;
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
                        int udp_pkts = 0;
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
+#ifdef BGP
+                       uint32_t bgp_events[BGP_NUM_PEERS];
+                       memset(bgp_events, 0, sizeof(bgp_events));
+#endif /* BGP */
 
 
-                       // nsctl commands
-                       if (FD_ISSET(controlfd, &r))
+                       for (c = n, i = 0; i < c; i++)
                        {
                        {
-                               alen = sizeof(addr);
-                               processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
-                               n--;
-                       }
+                               struct event_data *d = events[i].data.ptr;
+                               switch (d->type)
+                               {
+                               case FD_TYPE_CONTROL: // nsctl commands
+                                       alen = sizeof(addr);
+                                       processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+                                       n--;
+                                       break;
 
 
-                       // RADIUS responses
-                       if (config->cluster_iam_master)
-                       {
-                               for (i = 0; i < config->num_radfds; i++)
+                               case FD_TYPE_CLI: // CLI connections
                                {
                                {
-                                       if (FD_ISSET(radfds[i], &r))
+                                       int cli;
+                                       
+                                       alen = sizeof(addr);
+                                       if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0)
                                        {
                                        {
-                                               processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
-                                               n--;
+                                               cli_do(cli);
+                                               close(cli);
                                        }
                                        }
-                               }
-                       }
+                                       else
+                                               LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
 
 
-                       // CLI connections
-                       if (FD_ISSET(clifd, &r))
-                       {
-                               int cli;
-                               
-                               alen = sizeof(addr);
-                               if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0)
-                               {
-                                       cli_do(cli);
-                                       close(cli);
+                                       n--;
+                                       break;
                                }
                                }
-                               else
-                                       LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
 
 
-                               n--;
-                       }
+                               // these are handled below, with multiple interleaved reads
+                               case FD_TYPE_UDP:       udp_ready++; break;
+                               case FD_TYPE_TUN:       tun_ready++; break;
+                               case FD_TYPE_CLUSTER:   cluster_ready++; break;
+
+                               case FD_TYPE_RADIUS: // RADIUS response
+                                       s = recv(radfds[d->index], buf, sizeof(buf), 0);
+                                       if (s >= 0 && config->cluster_iam_master)
+                                               processrad(buf, s, d->index);
+
+                                       n--;
+                                       break;
 
 #ifdef BGP
 
 #ifdef BGP
-                       for (i = 0; i < BGP_NUM_PEERS; i++)
-                       {
-                               int isr = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0;
-                               int isw = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0;
-                               bgp_process(&bgp_peers[i], isr, isw);
-                               if (isr) n--;
-                               if (isw) n--;
+                               case FD_TYPE_BGP:
+                                       bgp_events[d->index] = events[i].events;
+                                       n--;
+                                       break;
+#endif /* BGP */
+
+                               default:
+                                       LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type);
+                               }
                        }
                        }
+
+#ifdef BGP
+                       bgp_process(bgp_events);
 #endif /* BGP */
 
                        for (c = 0; n && c < config->multi_read_count; c++)
                        {
                                // L2TP
 #endif /* BGP */
 
                        for (c = 0; n && c < config->multi_read_count; c++)
                        {
                                // L2TP
-                               if (FD_ISSET(udpfd, &r))
+                               if (udp_ready)
                                {
                                        alen = sizeof(addr);
                                        if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
                                {
                                        alen = sizeof(addr);
                                        if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
@@ -3078,13 +3145,13 @@ static void mainloop(void)
                                        }
                                        else
                                        {
                                        }
                                        else
                                        {
-                                               FD_CLR(udpfd, &r);
+                                               udp_ready = 0;
                                                n--;
                                        }
                                }
 
                                // incoming IP
                                                n--;
                                        }
                                }
 
                                // incoming IP
-                               if (FD_ISSET(tunfd, &r))
+                               if (tun_ready)
                                {
                                        if ((s = read(tunfd, buf, sizeof(buf))) > 0)
                                        {
                                {
                                        if ((s = read(tunfd, buf, sizeof(buf))) > 0)
                                        {
@@ -3093,13 +3160,13 @@ static void mainloop(void)
                                        }
                                        else
                                        {
                                        }
                                        else
                                        {
-                                               FD_CLR(tunfd, &r);
+                                               tun_ready = 0;
                                                n--;
                                        }
                                }
 
                                // cluster
                                                n--;
                                        }
                                }
 
                                // cluster
-                               if (FD_ISSET(cluster_sockfd, &r))
+                               if (cluster_ready)
                                {
                                        alen = sizeof(addr);
                                        if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
                                {
                                        alen = sizeof(addr);
                                        if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
@@ -3109,7 +3176,7 @@ static void mainloop(void)
                                        }
                                        else
                                        {
                                        }
                                        else
                                        {
-                                               FD_CLR(cluster_sockfd, &r);
+                                               cluster_ready = 0;
                                                n--;
                                        }
                                }
                                                n--;
                                        }
                                }
@@ -3129,7 +3196,7 @@ static void mainloop(void)
                }
 
                        // Runs on every machine (master and slaves).
                }
 
                        // Runs on every machine (master and slaves).
-               if (cluster_sockfd && next_cluster_ping <= TIME)
+               if (next_cluster_ping <= TIME)
                {
                        // Check to see which of the cluster is still alive..
 
                {
                        // Check to see which of the cluster is still alive..
 
@@ -3211,7 +3278,7 @@ static void mainloop(void)
 
        //
        // Important!!! We MUST not process any packets past this point!
 
        //
        // Important!!! We MUST not process any packets past this point!
-       LOG(1, 0, 0, "Clean shutdown complete\n");
+       LOG(1, 0, 0, "Shutdown complete\n");
 }
 
 static void stripdomain(char *host)
 }
 
 static void stripdomain(char *host)
@@ -3707,10 +3774,12 @@ static int dump_session(FILE **f, sessiont *s)
                LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
                fprintf(*f, "# dslwatch.pl dump file V1.01\n"
                        "# host: %s\n"
                LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
                fprintf(*f, "# dslwatch.pl dump file V1.01\n"
                        "# host: %s\n"
+                       "# endpoint: %s\n"
                        "# time: %ld\n"
                        "# uptime: %ld\n"
                        "# format: username ip qos uptxoctets downrxoctets\n",
                        hostname,
                        "# time: %ld\n"
                        "# uptime: %ld\n"
                        "# format: username ip qos uptxoctets downrxoctets\n",
                        hostname,
+                       fmtaddr(config->bind_address ? config->bind_address : my_address, 0),
                        now,
                        now - basetime);
        }
                        now,
                        now - basetime);
        }
@@ -3851,19 +3920,6 @@ int main(int argc, char *argv[])
        if (cluster_init() < 0)
                exit(1);
 
        if (cluster_init() < 0)
                exit(1);
 
-#ifdef BGP
-       signal(SIGPIPE, SIG_IGN);
-       bgp_setup(config->as_number);
-       bgp_add_route(config->bind_address, 0xffffffff);
-       for (i = 0; i < BGP_NUM_PEERS; i++)
-       {
-               if (config->neighbour[i].name[0])
-                       bgp_start(&bgp_peers[i], config->neighbour[i].name,
-                               config->neighbour[i].as, config->neighbour[i].keepalive,
-                               config->neighbour[i].hold, 0); /* 0 = routing disabled */
-       }
-#endif /* BGP */
-
        inittun();
        LOG(1, 0, 0, "Set up on interface %s\n", config->tundevice);
 
        inittun();
        LOG(1, 0, 0, "Set up on interface %s\n", config->tundevice);
 
@@ -3871,11 +3927,18 @@ int main(int argc, char *argv[])
        initrad();
        initippool();
 
        initrad();
        initippool();
 
-       signal(SIGHUP, sighup_handler);
-       signal(SIGTERM, sigterm_handler);
-       signal(SIGINT, sigterm_handler);
-       signal(SIGQUIT, sigquit_handler);
+       // seed prng
+       {
+               unsigned seed = time_now ^ getpid();
+               LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
+               srand(seed);
+       }
+
+       signal(SIGHUP,  sighup_handler);
        signal(SIGCHLD, sigchild_handler);
        signal(SIGCHLD, sigchild_handler);
+       signal(SIGTERM, shutdown_handler);
+       signal(SIGINT,  shutdown_handler);
+       signal(SIGQUIT, shutdown_handler);
 
        // Prevent us from getting paged out
        if (config->lock_pages)
 
        // Prevent us from getting paged out
        if (config->lock_pages)
@@ -3953,33 +4016,10 @@ static void sigalrm_handler(int sig)
 
 }
 
 
 }
 
-static void sigterm_handler(int sig)
+static void shutdown_handler(int sig)
 {
 {
-       LOG(1, 0, 0, "Shutting down cleanly\n");
-       main_quit++;
-}
-
-static void sigquit_handler(int sig)
-{
-       int i;
-
-       LOG(1, 0, 0, "Shutting down without saving sessions\n");
-
-       if (config->cluster_iam_master)
-       {
-               for (i = 1; i < MAXSESSION; i++)
-               {
-                       if (session[i].opened)
-                               sessionkill(i, "L2TPNS Closing");
-               }
-               for (i = 1; i < MAXTUNNEL; i++)
-               {
-                       if (tunnel[i].ip || tunnel[i].state)
-                               tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
-               }
-       }
-
-       main_quit++;
+       LOG(1, 0, 0, "Shutting down\n");
+       main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER;
 }
 
 static void sigchild_handler(int sig)
 }
 
 static void sigchild_handler(int sig)
@@ -4094,8 +4134,6 @@ static void update_config()
        if (!config->numradiusservers)
                LOG(0, 0, 0, "No RADIUS servers defined!\n");
 
        if (!config->numradiusservers)
                LOG(0, 0, 0, "No RADIUS servers defined!\n");
 
-       config->num_radfds = 1 << RADIUS_SHIFT;
-
        // parse radius_authtypes_s
        config->radius_authtypes = config->radius_authprefer = 0;
        p = config->radius_authtypes_s;
        // parse radius_authtypes_s
        config->radius_authtypes = config->radius_authprefer = 0;
        p = config->radius_authtypes_s;
@@ -4256,7 +4294,7 @@ int sessionsetup(tunnelidt t, sessionidt s)
                if (!session[s].ip)
                {
                        LOG(0, s, t, "   No IP allocated.  The IP address pool is FULL!\n");
                if (!session[s].ip)
                {
                        LOG(0, s, t, "   No IP allocated.  The IP address pool is FULL!\n");
-                       sessionshutdown(s, "No IP addresses available.", 2, 7);
+                       sessionshutdown(s, "No IP addresses available.", 2, 7); // try another
                        return 0;
                }
                LOG(3, s, t, "   No IP allocated.  Assigned %s from pool\n",
                        return 0;
                }
                LOG(3, s, t, "   No IP allocated.  Assigned %s from pool\n",
@@ -4853,6 +4891,9 @@ static tunnelidt new_tunnel()
 void become_master(void)
 {
        int s, i;
 void become_master(void)
 {
        int s, i;
+       static struct event_data d[RADIUS_FDS];
+       struct epoll_event e;
+
        run_plugins(PLUGIN_BECOME_MASTER, NULL);
 
        // running a bunch of iptables commands is slow and can cause
        run_plugins(PLUGIN_BECOME_MASTER, NULL);
 
        // running a bunch of iptables commands is slow and can cause
@@ -4871,11 +4912,14 @@ void become_master(void)
        }
 
        // add radius fds
        }
 
        // add radius fds
-       for (i = 0; i < config->num_radfds; i++)
+       e.events = EPOLLIN;
+       for (i = 0; i < RADIUS_FDS; i++)
        {
        {
-               FD_SET(radfds[i], &readset);
-               if (radfds[i] > readset_n)
-                       readset_n = radfds[i];
+               d[i].type = FD_TYPE_RADIUS;
+               d[i].index = i;
+               e.data.ptr = &d[i];
+
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, radfds[i], &e);
        }
 }
 
        }
 }
 
@@ -4969,11 +5013,11 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec
        uint8_t digest[16];
        uint8_t *last;
        size_t d = 0;
        uint8_t digest[16];
        uint8_t *last;
        size_t d = 0;
+       uint16_t m = htons(type);
 
        // Compute initial pad
        MD5Init(&ctx);
 
        // Compute initial pad
        MD5Init(&ctx);
-       MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1);
-       MD5Update(&ctx, (uint8_t)  type       & 0xff, 1);
+       MD5Update(&ctx, (unsigned char *) &m, 2);
        MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
        MD5Update(&ctx, vector, vec_len);
        MD5Final(digest, &ctx);
        MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
        MD5Update(&ctx, vector, vec_len);
        MD5Final(digest, &ctx);