use ctime rather than duration
[l2tpns.git] / l2tpns.c
index 341cf74..127d515 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -1,10 +1,10 @@
 // L2TP Network Server
 // Adrian Kennard 2002
-// Copyright (c) 2003, 2004 Optus Internet Engineering
+// Copyright (c) 2003, 2004, 2005 Optus Internet Engineering
 // 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.72 2004/12/16 23:40:31 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.78 2005/01/13 07:57:36 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -62,6 +62,7 @@ int clifd = -1;                       // Socket listening for CLI connections.
 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
+static int rand_fd = -1;       // Random data source
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static uint32_t sessionid = 0; // session id for radius accounting
@@ -94,18 +95,20 @@ config_descriptt config_values[] = {
        CONFIG("debug", debug, INT),
        CONFIG("log_file", log_filename, STRING),
        CONFIG("pid_file", pid_file, STRING),
+       CONFIG("random_device", random_device, STRING),
        CONFIG("l2tp_secret", l2tpsecret, STRING),
-       CONFIG("primary_dns", default_dns1, IP),
-       CONFIG("secondary_dns", default_dns2, IP),
+       CONFIG("primary_dns", default_dns1, IPv4),
+       CONFIG("secondary_dns", default_dns2, IPv4),
        CONFIG("save_state", save_state, BOOL),
-       CONFIG("primary_radius", radiusserver[0], IP),
-       CONFIG("secondary_radius", radiusserver[1], IP),
+       CONFIG("primary_radius", radiusserver[0], IPv4),
+       CONFIG("secondary_radius", radiusserver[1], IPv4),
        CONFIG("primary_radius_port", radiusport[0], SHORT),
        CONFIG("secondary_radius_port", radiusport[1], SHORT),
        CONFIG("radius_accounting", radius_accounting, BOOL),
        CONFIG("radius_secret", radiussecret, STRING),
-       CONFIG("bind_address", bind_address, IP),
-       CONFIG("peer_address", peer_address, IP),
+       CONFIG("radius_authtypes", radius_authtypes_s, STRING),
+       CONFIG("bind_address", bind_address, IPv4),
+       CONFIG("peer_address", peer_address, IPv4),
        CONFIG("send_garp", send_garp, BOOL),
        CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG),
        CONFIG("throttle_buckets", num_tbfs, INT),
@@ -117,7 +120,8 @@ config_descriptt config_values[] = {
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
        CONFIG("lock_pages", lock_pages, BOOL),
        CONFIG("icmp_rate", icmp_rate, INT),
-       CONFIG("cluster_address", cluster_address, IP),
+       CONFIG("packet_limit", max_packets, INT),
+       CONFIG("cluster_address", cluster_address, IPv4),
        CONFIG("cluster_interface", cluster_interface, STRING),
        CONFIG("cluster_hb_interval", cluster_hb_interval, INT),
        CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT),
@@ -147,7 +151,7 @@ static sessionidt shut_acct_n = 0;
 
 tunnelt *tunnel = NULL;                        // Array of tunnel structures.
 sessiont *session = NULL;              // Array of session structures.
-sessioncountt *sess_count = NULL;      // Array of partial per-session traffic counters.
+sessionlocalt *sess_local = NULL;      // Array of local per-session counters.
 radiust *radius = NULL;                        // Array of radius structures.
 ippoolt *ip_address_pool = NULL;       // Array of dynamic IP addresses.
 ip_filtert *ip_filters = NULL; // Array of named filters.
@@ -203,7 +207,6 @@ clockt backoff(uint8_t try)
 void _log(int level, sessionidt s, tunnelidt t, const char *format, ...)
 {
        static char message[65536] = {0};
-       static char message2[65536] = {0};
        va_list ap;
 
 #ifdef RINGBUFFER
@@ -227,18 +230,13 @@ void _log(int level, sessionidt s, tunnelidt t, const char *format, ...)
        if (config->debug < level) return;
 
        va_start(ap, format);
+       vsnprintf(message, sizeof(message), format, ap);
+
        if (log_stream)
-       {
-               vsnprintf(message2, 65535, format, ap);
-               snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2);
-               fprintf(log_stream, "%s", message);
-       }
+               fprintf(log_stream, "%s %02d/%02d %s", time_now_string, t, s, message);
        else if (syslog_log)
-       {
-               vsnprintf(message2, 65535, format, ap);
-               snprintf(message, 65535, "%02d/%02d %s", t, s, message2);
-               syslog(level + 2, message); // We don't need LOG_EMERG or LOG_ALERT
-       }
+               syslog(level + 2, "%02d/%02d %s", t, s, message); // We don't need LOG_EMERG or LOG_ALERT
+
        va_end(ap);
 }
 
@@ -293,6 +291,72 @@ void _log_hex(int level, const char *title, const char *data, int maxsize)
        }
 }
 
+// initialise the random generator
+static void initrandom(char *source)
+{
+       static char path[sizeof(config->random_device)] = "*undefined*";
+
+       // reinitialise only if we are forced to do so or if the config has changed
+       if (source && !strncmp(path, source, sizeof(path)))
+               return;
+
+       // close previous source, if any
+       if (rand_fd >= 0) close(rand_fd);
+
+       rand_fd = -1;
+
+       if (source)
+       {
+               // register changes
+               snprintf(path, sizeof(path), "%s", source);
+
+               if (*path == '/')
+               {
+                       rand_fd = open(path, O_RDONLY|O_NONBLOCK);
+                       if (rand_fd < 0)
+                               LOG(0, 0, 0, "Error opening the random device %s: %s\n",
+                                       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
+void random_data(uint8_t *buf, int len)
+{
+       int n = 0;
+
+       CSTAT(random_data);
+       if (rand_fd >= 0)
+       {
+               n = read(rand_fd, buf, len);
+               if (n >= len) return;
+               if (n < 0)
+               {
+                       if (errno != EAGAIN)
+                       {
+                               LOG(0, 0, 0, "Error reading from random source: %s\n",
+                                       strerror(errno));
+
+                               // fall back to rand()
+                               initrandom(0);
+                       }
+
+                       n = 0;
+               }
+       }
+
+       // append missing data
+       while (n < len)
+               // not using the low order bits from the prng stream
+               buf[n++] = (rand() >> 4) & 0xff;
+}
 
 // Add a route
 //
@@ -476,7 +540,7 @@ static int lookup_ipmap(in_addr_t ip)
 sessionidt sessionbyip(in_addr_t ip)
 {
        int s = lookup_ipmap(ip);
-       CSTAT(call_sessionbyip);
+       CSTAT(sessionbyip);
 
        if (s > 0 && s < MAXSESSION && session[s].tunnel)
                return (sessionidt) s;
@@ -576,7 +640,7 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc)
 sessionidt sessionbyuser(char *username)
 {
        int s;
-       CSTAT(call_sessionbyuser);
+       CSTAT(sessionbyuser);
 
        for (s = 1; s < MAXSESSION ; ++s)
        {
@@ -640,7 +704,7 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t)
 {
        struct sockaddr_in addr;
 
-       CSTAT(call_tunnelsend);
+       CSTAT(tunnelsend);
 
        if (!t)
        {
@@ -711,23 +775,23 @@ static void processipout(uint8_t * buf, int len)
        tunnelidt t;
        in_addr_t ip;
 
-       char * data = buf;      // Keep a copy of the originals.
+       char *data = buf;       // Keep a copy of the originals.
        int size = len;
 
        uint8_t b[MAXETHER + 20];
 
-       CSTAT(call_processipout);
+       CSTAT(processipout);
 
        if (len < MIN_IP_SIZE)
        {
                LOG(1, 0, 0, "Short IP, %d bytes\n", len);
-               STAT(tunnel_tx_errors);
+               STAT(tun_rx_errors);
                return;
        }
        if (len >= MAXETHER)
        {
                LOG(1, 0, 0, "Oversize IP packet %d bytes\n", len);
-               STAT(tunnel_tx_errors);
+               STAT(tun_rx_errors);
                return;
        }
 
@@ -765,6 +829,45 @@ static void processipout(uint8_t * buf, int len)
        t = session[s].tunnel;
        sp = &session[s];
 
+       // DoS prevention: enforce a maximum number of packets per 0.1s for a session
+       if (config->max_packets > 0)
+       {
+               if (sess_local[s].last_packet_out == TIME)
+               {
+                       int max = config->max_packets;
+
+                       // All packets for throttled sessions are handled by the
+                       // master, so further limit by using the throttle rate.
+                       // A bit of a kludge, since throttle rate is in kbps,
+                       // but should still be generous given our average DSL
+                       // packet size is 200 bytes: a limit of 28kbps equates
+                       // to around 180 packets per second.
+                       if (!config->cluster_iam_master && sp->throttle_out && sp->throttle_out < max)
+                               max = sp->throttle_out;
+
+                       if (++sess_local[s].packets_out > max)
+                       {
+                               sess_local[s].packets_dropped++;
+                               return;
+                       }
+               }
+               else
+               {
+                       if (sess_local[s].packets_dropped)
+                       {
+                               INC_STAT(tun_rx_dropped, sess_local[s].packets_dropped);
+                               LOG(3, s, t, "Dropped %u/%u packets to %s for %suser %s\n",
+                                       sess_local[s].packets_out, sess_local[s].packets_dropped,
+                                       fmtaddr(ip, 0), sp->throttle_out ? "throttled " : "",
+                                       sp->user);
+                       }
+
+                       sess_local[s].last_packet_out = TIME;
+                       sess_local[s].packets_out = 1;
+                       sess_local[s].packets_dropped = 0;
+               }
+       }
+
        // run access-list if any
        if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1))
                return;
@@ -802,7 +905,7 @@ static void processipout(uint8_t * buf, int len)
        sp->total_cout += len; // byte count
        sp->pout++;
        udp_tx += len;
-       sess_count[s].cout += len;      // To send to master..
+       sess_local[s].cout += len;      // To send to master..
 }
 
 //
@@ -852,7 +955,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
        sp->total_cout += len; // byte count
        sp->pout++;
        udp_tx += len;
-       sess_count[s].cout += len;      // To send to master..
+       sess_local[s].cout += len;      // To send to master..
 }
 
 // add an AVP (16 bit)
@@ -1051,7 +1154,7 @@ void sessionshutdown(sessionidt s, char *reason)
        int walled_garden = session[s].walled_garden;
 
 
-       CSTAT(call_sessionshutdown);
+       CSTAT(sessionshutdown);
 
        if (!session[s].tunnel)
        {
@@ -1079,9 +1182,7 @@ void sessionshutdown(sessionidt s, char *reason)
                        }
                        else
                        {
-                               int n;
-                               for (n = 0; n < 15; n++)
-                                       radius[r].auth[n] = rand();
+                               random_data(radius[r].auth, sizeof(radius[r].auth));
                        }
                }
 
@@ -1142,7 +1243,7 @@ void sendipcp(tunnelidt t, sessionidt s)
        uint16_t r = session[s].radius;
        uint8_t *q;
 
-       CSTAT(call_sendipcp);
+       CSTAT(sendipcp);
 
        if (!r)
                r = radiusnew(s);
@@ -1181,7 +1282,7 @@ void sendipcp(tunnelidt t, sessionidt s)
 static void sessionkill(sessionidt s, char *reason)
 {
 
-       CSTAT(call_sessionkill);
+       CSTAT(sessionkill);
 
        session[s].die = now();
        sessionshutdown(s, reason);  // close radius/routes, etc.
@@ -1211,7 +1312,7 @@ static void tunnelkill(tunnelidt t, char *reason)
        sessionidt s;
        controlt *c;
 
-       CSTAT(call_tunnelkill);
+       CSTAT(tunnelkill);
 
        tunnel[t].state = TUNNELDIE;
 
@@ -1241,7 +1342,7 @@ static void tunnelshutdown(tunnelidt t, char *reason)
 {
        sessionidt s;
 
-       CSTAT(call_tunnelshutdown);
+       CSTAT(tunnelshutdown);
 
        if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE)
        {
@@ -1276,7 +1377,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
        uint8_t *p = buf + 2;
 
 
-       CSTAT(call_processudp);
+       CSTAT(processudp);
 
        udp_rx += len;
        udp_rx_pkt++;
@@ -1523,12 +1624,12 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                b += 2;
                                n -= 6;
 
-                               LOG(4, s, t, "   AVP %d (%s) len %d\n", mtype, avpnames[mtype], n);
+                               LOG(4, s, t, "   AVP %d (%s) len %d\n", mtype, avp_name(mtype), n);
                                switch (mtype)
                                {
                                case 0:     // message type
                                        message = ntohs(*(uint16_t *) b);
-                                       LOG(4, s, t, "   Message type = %d (%s)\n", *b, l2tp_message_types[message]);
+                                       LOG(4, s, t, "   Message type = %d (%s)\n", *b, l2tp_message_type(message));
                                        mandatorymessage = flags;
                                        break;
                                case 1:     // result code
@@ -1537,23 +1638,18 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                const char* resdesc = "(unknown)";
                                                if (message == 4)
                                                { /* StopCCN */
-                                                       if (rescode <= MAX_STOPCCN_RESULT_CODE)
-                                                               resdesc = stopccn_result_codes[rescode];
+                                                       resdesc = stopccn_result_code(rescode);
                                                }
                                                else if (message == 14)
                                                { /* CDN */
-                                                       if (rescode <= MAX_CDN_RESULT_CODE)
-                                                               resdesc = cdn_result_codes[rescode];
+                                                       resdesc = cdn_result_code(rescode);
                                                }
 
                                                LOG(4, s, t, "   Result Code %d: %s\n", rescode, resdesc);
                                                if (n >= 4)
                                                {
                                                        uint16_t errcode = ntohs(*(uint16_t *)(b + 2));
-                                                       const char* errdesc = "(unknown)";
-                                                       if (errcode <= MAX_ERROR_CODE)
-                                                               errdesc = error_codes[errcode];
-                                                       LOG(4, s, t, "   Error Code %d: %s\n", errcode, errdesc);
+                                                       LOG(4, s, t, "   Error Code %d: %s\n", errcode, error_code(errcode));
                                                }
                                                if (n > 4)
                                                        LOG(4, s, t, "   Error String: %.*s\n", n-4, b+4);
@@ -1681,9 +1777,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        }
                                case 29:    // Proxy Authentication Type
                                        {
-                                               uint16_t authtype = ntohs(*(uint16_t *)b);
-                                               LOG(4, s, t, "   Proxy Auth Type %d (%s)\n", authtype, authtypes[authtype]);
-                                               requestchap = (authtype == 2);
+                                               uint16_t atype = ntohs(*(uint16_t *)b);
+                                               LOG(4, s, t, "   Proxy Auth Type %d (%s)\n", atype, auth_type(atype));
+                                               requestchap = (atype == 2);
                                                break;
                                        }
                                case 30:    // Proxy Authentication Name
@@ -1827,12 +1923,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                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
-                                               {
-                                                       // Generate a random challenge
-                                                       int n;
-                                                       for (n = 0; n < 15; n++)
-                                                               radius[r].auth[n] = rand();
-                                               }
+
+                                               // Generate a random challenge
+                                               random_data(radius[r].auth, sizeof(radius[r].auth));
                                                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);
@@ -1982,7 +2075,7 @@ static void processtun(uint8_t * buf, int len)
        STAT(tun_rx_packets);
        INC_STAT(tun_rx_bytes, len);
 
-       CSTAT(call_processtun);
+       CSTAT(processtun);
 
        eth_rx_pkt++;
        eth_rx += len;
@@ -1993,7 +2086,7 @@ static void processtun(uint8_t * buf, int len)
                return;
        }
 
-       if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IP
+       if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IPv4
                processipout(buf, len);
        // Else discard.
 }
@@ -2367,14 +2460,11 @@ static void mainloop(void)
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
 
-                       INC_STAT(select_ready, n);
-
                        // nsctl commands
                        if (FD_ISSET(controlfd, &r))
                        {
                                alen = sizeof(addr);
                                processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
-                               STAT(select_processed);
                                n--;
                        }
 
@@ -2386,7 +2476,6 @@ static void mainloop(void)
                                        if (FD_ISSET(radfds[i], &r))
                                        {
                                                processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
-                                               STAT(select_processed);
                                                n--;
                                        }
                                }
@@ -2406,21 +2495,17 @@ static void mainloop(void)
                                else
                                        LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
 
-                               STAT(select_processed);
                                n--;
                        }
 
 #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;
+                               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 || isw)
-                               {
-                                       INC_STAT(select_processed, isr + isw);
-                                       n -= (isr + isw);
-                               }
+                               if (isr) n--;
+                               if (isw) n--;
                        }
 #endif /* BGP */
 
@@ -2433,7 +2518,6 @@ static void mainloop(void)
                                        if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
                                        {
                                                processudp(buf, s, &addr);
-                                               STAT(select_processed);
                                                udp_pkts++;
                                        }
                                        else
@@ -2449,7 +2533,6 @@ static void mainloop(void)
                                        if ((s = read(tunfd, buf, sizeof(buf))) > 0)
                                        {
                                                processtun(buf, s);
-                                               STAT(select_processed);
                                                tun_pkts++;
                                        }
                                        else
@@ -2466,7 +2549,6 @@ static void mainloop(void)
                                        if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
                                        {
                                                processcluster(buf, s, addr.sin_addr.s_addr);
-                                               STAT(select_processed);
                                                cluster_pkts++;
                                        }
                                        else
@@ -2477,9 +2559,16 @@ static void mainloop(void)
                                }
                        }
 
+                       if (udp_pkts > 1 || tun_pkts > 1 || cluster_pkts > 1)
+                               STAT(multi_read_used);
+
                        if (c >= config->multi_read_count)
+                       {
                                LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n",
                                        config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
+
+                               STAT(multi_read_exceeded);
+                       }
                }
 
                        // Runs on every machine (master and slaves).
@@ -2629,6 +2718,7 @@ static void initdata(int optdebug, char *optconfig)
        config->debug = optdebug;
        config->num_tbfs = MAXTBFS;
        config->rl_rate = 28; // 28kbps
+       strcpy(config->random_device, RANDOMDEVICE);
 
        if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL)))
        {
@@ -2641,9 +2731,9 @@ static void initdata(int optdebug, char *optconfig)
                exit(1);
        }
 
-       if (!(sess_count = shared_malloc(sizeof(sessioncountt) * MAXSESSION)))
+       if (!(sess_local = shared_malloc(sizeof(sessionlocalt) * MAXSESSION)))
        {
-               LOG(0, 0, 0, "Error doing malloc for sessions_count: %s\n", strerror(errno));
+               LOG(0, 0, 0, "Error doing malloc for sess_local: %s\n", strerror(errno));
                exit(1);
        }
 
@@ -2659,12 +2749,12 @@ static void initdata(int optdebug, char *optconfig)
                exit(1);
        }
 
-if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER)))
-{
-       LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno));
-       exit(1);
-}
-memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
+       if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER)))
+       {
+               LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno));
+               exit(1);
+       }
+       memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
 
 #ifdef RINGBUFFER
        if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
@@ -2734,7 +2824,7 @@ static int assign_ip_address(sessionidt s)
        char reuse = 0;
 
 
-       CSTAT(call_assign_ip_address);
+       CSTAT(assign_ip_address);
 
        for (i = 1; i < ip_pool_size; i++)
        {
@@ -2787,7 +2877,7 @@ static void free_ip_address(sessionidt s)
        int i = session[s].ip_pool_index;
 
 
-       CSTAT(call_free_ip_address);
+       CSTAT(free_ip_address);
 
        if (!session[s].ip)
                return; // what the?
@@ -3058,7 +3148,7 @@ static void dump_acct_info(int all)
        FILE *f = NULL;
 
 
-       CSTAT(call_dump_acct_info);
+       CSTAT(dump_acct_info);
 
        if (shut_acct_n)
        {
@@ -3132,7 +3222,7 @@ int main(int argc, char *argv[])
        init_tbf(config->num_tbfs);
 
        LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
-       LOG(0, 0, 0, "Copyright (c) 2003, 2004 Optus Internet Engineering\n");
+       LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005 Optus Internet Engineering\n");
        LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
        {
                struct rlimit rlim;
@@ -3537,6 +3627,7 @@ static int facility_value(char *name)
 static void update_config()
 {
        int i;
+       char *p;
        static int timeout = 0;
        static int interval = 0;
 
@@ -3548,6 +3639,7 @@ static void update_config()
                fclose(log_stream);
                log_stream = NULL;
        }
+
        if (*config->log_filename)
        {
                if (strstr(config->log_filename, "syslog:") == config->log_filename)
@@ -3579,7 +3671,6 @@ static void update_config()
                setbuf(log_stream, NULL);
        }
 
-
        // Update radius
        config->numradiusservers = 0;
        for (i = 0; i < MAXRADSERVER; i++)
@@ -3604,6 +3695,59 @@ static void update_config()
 
        config->num_radfds = 2 << RADIUS_SHIFT;
 
+       // parse radius_authtypes_s
+       config->radius_authtypes = config->radius_authprefer = 0;
+       p = config->radius_authtypes_s;
+       while (*p)
+       {
+               char *s = strpbrk(p, " \t,");
+               int type = 0;
+
+               if (s)
+               {
+                       *s++ = 0;
+                       while (*s == ' ' || *s == '\t')
+                               s++;
+
+                       if (!*s)
+                               s = 0;
+               }
+
+               if (!strncasecmp("chap", p, strlen(p)))
+                       type = AUTHCHAP;
+               else if (!strncasecmp("pap", p, strlen(p)))
+                       type = AUTHPAP;
+               else
+                       LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"", p);
+
+               config->radius_authtypes |= type;
+               if (!config->radius_authprefer)
+                       config->radius_authprefer = type;
+       }
+
+       if (!config->radius_authtypes)
+       {
+               LOG(0, 0, 0, "Defaulting to PAP authentication\n");
+               config->radius_authtypes = config->radius_authprefer = AUTHPAP;
+       }
+
+       // normalise radius_authtypes_s
+       if (config->radius_authprefer == AUTHPAP)
+       {
+               strcpy(config->radius_authtypes_s, "pap");
+               if (config->radius_authtypes & AUTHCHAP)
+                       strcat(config->radius_authtypes_s, ", chap");
+       }
+       else
+       {
+               strcpy(config->radius_authtypes_s, "chap");
+               if (config->radius_authtypes & AUTHPAP)
+                       strcat(config->radius_authtypes_s, ", pap");
+       }
+
+       // re-initialise the random number source
+       initrandom(config->random_device);
+
        // Update plugins
        for (i = 0; i < MAXPLUGINS; i++)
        {
@@ -3621,6 +3765,7 @@ static void update_config()
                        remove_plugin(config->old_plugins[i]);
                }
        }
+
        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;
@@ -3699,7 +3844,7 @@ int sessionsetup(tunnelidt t, sessionidt s)
        sessionidt i;
        int r;
 
-       CSTAT(call_sessionsetup);
+       CSTAT(sessionsetup);
 
        LOG(3, s, t, "Doing session setup for session\n");