"show running-config" output contains clear-text shared secrets; require enable
[l2tpns.git] / l2tpns.c
index a28cd5f..000e594 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.85 2005/03/10 03:08:08 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.99 2005/05/07 13:12:25 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -68,11 +68,10 @@ static int rand_fd = -1;    // Random data source
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static int tunidx;             // ifr_ifindex of tun device
 time_t basetime = 0;           // base clock
 char hostname[1000] = "";      // us.
 static int tunidx;             // ifr_ifindex of tun device
-static uint32_t sessionid = 0; // session id for radius accounting
 static int syslog_log = 0;     // are we logging to syslog
 static int syslog_log = 0;     // are we logging to syslog
-static FILE *log_stream = NULL;        // file handle for direct logging (i.e. direct into file, not via 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.
 extern int cluster_sockfd;     // Intra-cluster communications socket.
-uint32_t last_id = 0;          // Last used PPP SID. Can I kill this?? -- mo
+uint32_t last_id = 0;          // Unique ID for radius accounting
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
 struct cli_tunnel_actions *cli_tunnel_actions = NULL;  // Pending tunnel changes required by CLI
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
 struct cli_tunnel_actions *cli_tunnel_actions = NULL;  // Pending tunnel changes required by CLI
@@ -111,6 +110,7 @@ config_descriptt config_values[] = {
        CONFIG("primary_radius_port", radiusport[0], SHORT),
        CONFIG("secondary_radius_port", radiusport[1], SHORT),
        CONFIG("radius_accounting", radius_accounting, BOOL),
        CONFIG("primary_radius_port", radiusport[0], SHORT),
        CONFIG("secondary_radius_port", radiusport[1], SHORT),
        CONFIG("radius_accounting", radius_accounting, BOOL),
+       CONFIG("radius_interim", radius_interim, INT),
        CONFIG("radius_secret", radiussecret, STRING),
        CONFIG("radius_authtypes", radius_authtypes_s, STRING),
        CONFIG("bind_address", bind_address, IPv4),
        CONFIG("radius_secret", radiussecret, STRING),
        CONFIG("radius_authtypes", radius_authtypes_s, STRING),
        CONFIG("bind_address", bind_address, IPv4),
@@ -1264,7 +1264,7 @@ static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
        c->length += 10;
 }
 
        c->length += 10;
 }
 
-// add an AVP (32 bit)
+// add an AVP (string)
 static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
 {
        uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
 static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
 {
        uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
@@ -1433,7 +1433,7 @@ static void filter_session(sessionidt s, int filter_in, int filter_out)
 }
 
 // start tidy shutdown of session
 }
 
 // start tidy shutdown of session
-void sessionshutdown(sessionidt s, char *reason)
+void sessionshutdown(sessionidt s, char *reason, int result, int error)
 {
        int walled_garden = session[s].walled_garden;
 
 {
        int walled_garden = session[s].walled_garden;
 
@@ -1453,25 +1453,21 @@ void sessionshutdown(sessionidt s, char *reason)
                run_plugins(PLUGIN_KILL_SESSION, &data);
        }
 
                run_plugins(PLUGIN_KILL_SESSION, &data);
        }
 
-       if (!walled_garden && !session[s].die)
+       if (session[s].ip && !walled_garden && !session[s].die)
        {
                // RADIUS Stop message
        {
                // RADIUS Stop message
-               uint16_t r = session[s].radius;
+               uint16_t r = sess_local[s].radius;
                if (!r)
                if (!r)
+                       r = radiusnew(s);
+
+               if (r)
                {
                {
-                       if (!(r = radiusnew(s)))
-                       {
-                               LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n");
-                               STAT(radius_overflow);
-                       }
-                       else
-                       {
-                               random_data(radius[r].auth, sizeof(radius[r].auth));
-                       }
+                       // stop, if not already trying
+                       if (radius[r].state != RADIUSSTOP)
+                               radiussend(r, RADIUSSTOP);
                }
                }
-
-               if (r && radius[r].state != RADIUSSTOP)
-                       radiussend(r, RADIUSSTOP); // stop, if not already trying
+               else
+                       LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n");
 
                // Save counters to dump to accounting file
                if (*config->accounting_dir && shut_acct_n < sizeof(shut_acct) / sizeof(*shut_acct))
 
                // Save counters to dump to accounting file
                if (*config->accounting_dir && shut_acct_n < sizeof(shut_acct) / sizeof(*shut_acct))
@@ -1508,9 +1504,19 @@ void sessionshutdown(sessionidt s, char *reason)
        if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled.
                throttle_session(s, 0, 0);
 
        if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled.
                throttle_session(s, 0, 0);
 
+       if (result)
        {                            // Send CDN
                controlt *c = controlnew(14); // sending CDN
        {                            // Send CDN
                controlt *c = controlnew(14); // sending CDN
-               control16(c, 1, 3, 1);    // result code (admin reasons - TBA make error, general error, add message
+               if (error)
+               {
+                       char buf[4];
+                       *(uint16_t *) buf     = htons(result);
+                       *(uint16_t *) (buf+2) = htons(error);
+                       controlb(c, 1, buf, 4, 1);
+               }
+               else
+                       control16(c, 1, result, 1);
+
                control16(c, 14, s, 1);   // assigned session (our end)
                controladd(c, session[s].tunnel, s); // send the message
        }
                control16(c, 14, s, 1);   // assigned session (our end)
                controladd(c, session[s].tunnel, s); // send the message
        }
@@ -1528,7 +1534,7 @@ void sessionshutdown(sessionidt s, char *reason)
 void sendipcp(tunnelidt t, sessionidt s)
 {
        uint8_t buf[MAXCONTROL];
 void sendipcp(tunnelidt t, sessionidt s)
 {
        uint8_t buf[MAXCONTROL];
-       uint16_t r = session[s].radius;
+       uint16_t r = sess_local[s].radius;
        uint8_t *q;
 
        CSTAT(sendipcp);
        uint8_t *q;
 
        CSTAT(sendipcp);
@@ -1536,6 +1542,12 @@ void sendipcp(tunnelidt t, sessionidt s)
        if (!r)
                r = radiusnew(s);
 
        if (!r)
                r = radiusnew(s);
 
+       if (!r)
+       {
+               sessionshutdown(s, "No free RADIUS sessions for IPCP", 3, 0);
+               return;
+       }
+
        if (radius[r].state != RADIUSIPCP)
        {
                radius[r].state = RADIUSIPCP;
        if (radius[r].state != RADIUSIPCP)
        {
                radius[r].state = RADIUSIPCP;
@@ -1546,7 +1558,7 @@ void sendipcp(tunnelidt t, sessionidt s)
        if (radius[r].try > 10)
        {
                radiusclear(r, s);      // Clear radius session.
        if (radius[r].try > 10)
        {
                radiusclear(r, s);      // Clear radius session.
-               sessionshutdown(s, "No reply on IPCP");
+               sessionshutdown(s, "No reply to IPCP.", 3, 0);
                return;
        }
 
                return;
        }
 
@@ -1590,6 +1602,17 @@ void sendipcp(tunnelidt t, sessionidt s)
        }
 }
 
        }
 }
 
+static void sessionclear(sessionidt s)
+{
+       memset(&session[s], 0, sizeof(session[s]));
+       memset(&sess_local[s], 0, sizeof(sess_local[s]));
+       memset(&cli_session_actions[s], 0, sizeof(cli_session_actions[s]));
+
+       session[s].tunnel = T_FREE;     // Mark it as free.
+       session[s].next = sessionfree;
+       sessionfree = s;
+}
+
 // kill a session now
 void sessionkill(sessionidt s, char *reason)
 {
 // kill a session now
 void sessionkill(sessionidt s, char *reason)
 {
@@ -1606,17 +1629,12 @@ void sessionkill(sessionidt s, char *reason)
        }
 
        session[s].die = TIME;
        }
 
        session[s].die = TIME;
-       sessionshutdown(s, reason);  // close radius/routes, etc.
-       if (session[s].radius)
-               radiusclear(session[s].radius, s); // cant send clean accounting data, session is killed
+       sessionshutdown(s, reason, 3, 0);  // close radius/routes, etc.
+       if (sess_local[s].radius)
+               radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
 
        LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
 
        LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
-
-       memset(&session[s], 0, sizeof(session[s]));
-       session[s].tunnel = T_FREE;     // Mark it as free.
-       session[s].next = sessionfree;
-       sessionfree = s;
-       cli_session_actions[s].action = 0;
+       sessionclear(s);
        cluster_send_session(s);
 }
 
        cluster_send_session(s);
 }
 
@@ -1654,12 +1672,12 @@ static void tunnelkill(tunnelidt t, char *reason)
        // free tunnel
        tunnelclear(t);
        LOG(1, 0, t, "Kill tunnel %d: %s\n", t, reason);
        // free tunnel
        tunnelclear(t);
        LOG(1, 0, t, "Kill tunnel %d: %s\n", t, reason);
-       cli_tunnel_actions[s].action = 0;
+       cli_tunnel_actions[t].action = 0;
        cluster_send_tunnel(t);
 }
 
 // shut down a tunnel cleanly
        cluster_send_tunnel(t);
 }
 
 // shut down a tunnel cleanly
-static void tunnelshutdown(tunnelidt t, char *reason)
+static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg)
 {
        sessionidt s;
 
 {
        sessionidt s;
 
@@ -1676,17 +1694,38 @@ static void tunnelshutdown(tunnelidt t, char *reason)
        // 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);
+                       sessionshutdown(s, reason, 3, 0);
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = TIME + 700; // Clean up in 70 seconds
        cluster_send_tunnel(t);
        // TBA - should we wait for sessions to stop?
 
        tunnel[t].state = TUNNELDIE;
        tunnel[t].die = TIME + 700; // Clean up in 70 seconds
        cluster_send_tunnel(t);
        // TBA - should we wait for sessions to stop?
-       {                            // Send StopCCN
-               controlt *c = controlnew(4); // sending StopCCN
-               control16(c, 1, 1, 1);    // result code (admin reasons - TBA make error, general error, add message)
-               control16(c, 9, t, 1);    // assigned tunnel (our end)
-               controladd(c, t, 0);      // send the message
+       if (result) 
+       {
+               controlt *c = controlnew(4);    // sending StopCCN
+               if (error)
+               {
+                       char buf[64];
+                       int l = 4;
+                       *(uint16_t *) buf     = htons(result);
+                       *(uint16_t *) (buf+2) = htons(error);
+                       if (msg)
+                       {
+                               int m = strlen(msg);
+                               if (m + 4 > sizeof(buf))
+                                   m = sizeof(buf) - 4;
+
+                               memcpy(buf+4, msg, m);
+                               l += m;
+                       }
+
+                       controlb(c, 1, buf, l, 1);
+               }
+               else
+                       control16(c, 1, result, 1);
+
+               control16(c, 9, t, 1);          // assigned tunnel (our end)
+               controladd(c, t, 0);            // send the message
        }
 }
 
        }
 }
 
@@ -1762,12 +1801,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;
                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)
                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
 
                char called[MAXTEL] = "";       // called number
                char calling[MAXTEL] = "";      // calling number
 
@@ -1890,6 +1928,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                }
                if (l)
                {                     // if not a null message
                }
                if (l)
                {                     // if not a null message
+                       int result = 0;
+                       int error = 0;
+                       char *msg = 0;
+
                        // process AVPs
                        while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP
                        {
                        // process AVPs
                        while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP
                        {
@@ -1909,6 +1951,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                {
                                        LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b);
                                        fatal = flags;
                                {
                                        LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b);
                                        fatal = flags;
+                                       result = 2; // general error
+                                       error = 3; // reserved field non-zero
+                                       msg = 0;
                                        continue; // next
                                }
                                b += 2;
                                        continue; // next
                                }
                                b += 2;
@@ -1916,6 +1961,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                {
                                        LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b)));
                                        fatal = flags;
                                {
                                        LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b)));
                                        fatal = flags;
+                                       result = 2; // general error
+                                       error = 6; // generic vendor-specific error
+                                       msg = "unsupported vendor-specific";
                                        continue; // next
                                }
                                b += 2;
                                        continue; // next
                                }
                                b += 2;
@@ -1932,18 +1980,27 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        {
                                                LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n");
                                                fatal = flags;
                                        {
                                                LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n");
                                                fatal = flags;
+                                               result = 2; // general error
+                                               error = 6; // generic vendor-specific error
+                                               msg = "secret not specified";
                                                continue;
                                        }
                                        if (!session[s].random_vector_length)
                                        {
                                                LOG(1, s, t, "Hidden AVP requested, but no random vector.\n");
                                                fatal = flags;
                                                continue;
                                        }
                                        if (!session[s].random_vector_length)
                                        {
                                                LOG(1, s, t, "Hidden AVP requested, but no random vector.\n");
                                                fatal = flags;
+                                               result = 2; // general error
+                                               error = 6; // generic
+                                               msg = "no random vector";
                                                continue;
                                        }
                                        if (n < 8)
                                        {
                                                LOG(2, s, t, "Short hidden AVP.\n");
                                                fatal = flags;
                                                continue;
                                        }
                                        if (n < 8)
                                        {
                                                LOG(2, s, t, "Short hidden AVP.\n");
                                                fatal = flags;
+                                               result = 2; // general error
+                                               error = 2; // length is wrong
+                                               msg = 0;
                                                continue;
                                        }
 
                                                continue;
                                        }
 
@@ -1955,7 +2012,13 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        orig_len = ntohs(*(uint16_t *) b);
                                        if (orig_len > n + 2)
                                        {
                                        orig_len = ntohs(*(uint16_t *) b);
                                        if (orig_len > n + 2)
                                        {
+                                               LOG(1, s, t, "Original length %d too long in hidden AVP of length %d; wrong secret?\n",
+                                                   orig_len, n);
+
                                                fatal = flags;
                                                fatal = flags;
+                                               result = 2; // general error
+                                               error = 2; // length is wrong
+                                               msg = 0;
                                                continue;
                                        }
 
                                                continue;
                                        }
 
@@ -2004,6 +2067,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                {   // allow 0.0 and 1.0
                                                        LOG(1, s, t, "   Bad protocol version %04X\n", version);
                                                        fatal = flags;
                                                {   // allow 0.0 and 1.0
                                                        LOG(1, s, t, "   Bad protocol version %04X\n", version);
                                                        fatal = flags;
+                                                       result = 5; // unspported protocol version
+                                                       error = 0x0100; // supported version
+                                                       msg = 0;
                                                        continue; // next
                                                }
                                        }
                                                        continue; // next
                                                }
                                        }
@@ -2022,14 +2088,14 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
 //                                     LOG(4, s, t, "Firmware revision\n");
                                        break;
                                case 7:     // host name
 //                                     LOG(4, s, t, "Firmware revision\n");
                                        break;
                                case 7:     // host name
-                                       memset(tunnel[t].hostname, 0, 128);
-                                       memcpy(tunnel[t].hostname, b, (n >= 127) ? 127 : n);
+                                       memset(tunnel[t].hostname, 0, sizeof(tunnel[t].hostname));
+                                       memcpy(tunnel[t].hostname, b, (n < sizeof(tunnel[t].hostname)) ? n : sizeof(tunnel[t].hostname) - 1);
                                        LOG(4, s, t, "   Tunnel hostname = \"%s\"\n", tunnel[t].hostname);
                                        // TBA - to send to RADIUS
                                        break;
                                case 8:     // vendor name
                                        memset(tunnel[t].vendor, 0, sizeof(tunnel[t].vendor));
                                        LOG(4, s, t, "   Tunnel hostname = \"%s\"\n", tunnel[t].hostname);
                                        // TBA - to send to RADIUS
                                        break;
                                case 8:     // vendor name
                                        memset(tunnel[t].vendor, 0, sizeof(tunnel[t].vendor));
-                                       memcpy(tunnel[t].vendor, b, (n >= sizeof(tunnel[t].vendor) - 1) ? sizeof(tunnel[t].vendor) - 1 : n);
+                                       memcpy(tunnel[t].vendor, b, (n < sizeof(tunnel[t].vendor)) ? n : sizeof(tunnel[t].vendor) - 1);
                                        LOG(4, s, t, "   Vendor name = \"%s\"\n", tunnel[t].vendor);
                                        break;
                                case 9:     // assigned tunnel
                                        LOG(4, s, t, "   Vendor name = \"%s\"\n", tunnel[t].vendor);
                                        break;
                                case 9:     // assigned tunnel
@@ -2069,13 +2135,13 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        // TBA
                                        break;
                                case 21:    // called number
                                        // TBA
                                        break;
                                case 21:    // called number
-                                       memset(called, 0, MAXTEL);
-                                       memcpy(called, b, (n >= MAXTEL) ? (MAXTEL-1) : n);
+                                       memset(called, 0, sizeof(called));
+                                       memcpy(called, b, (n < sizeof(called)) ? n : sizeof(called) - 1);
                                        LOG(4, s, t, "   Called <%s>\n", called);
                                        break;
                                case 22:    // calling number
                                        LOG(4, s, t, "   Called <%s>\n", called);
                                        break;
                                case 22:    // calling number
-                                       memset(calling, 0, MAXTEL);
-                                       memcpy(calling, b, (n >= MAXTEL) ? (MAXTEL-1) : n);
+                                       memset(calling, 0, sizeof(calling));
+                                       memcpy(calling, b, (n < sizeof(calling)) ? n : sizeof(calling) - 1);
                                        LOG(4, s, t, "   Calling <%s>\n", calling);
                                        break;
                                case 23:    // subtype
                                        LOG(4, s, t, "   Calling <%s>\n", calling);
                                        break;
                                case 23:    // subtype
@@ -2088,8 +2154,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        else
                                        {
                                                // AS5300s send connect speed as a string
                                        else
                                        {
                                                // AS5300s send connect speed as a string
-                                               char tmp[30] = {0};
-                                               memcpy(tmp, b, (n >= 30) ? 30 : n);
+                                               char tmp[30];
+                                               memset(tmp, 0, sizeof(tmp));
+                                               memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1);
                                                session[s].tx_connect_speed = atol(tmp);
                                        }
                                        LOG(4, s, t, "   TX connect speed <%u>\n", session[s].tx_connect_speed);
                                                session[s].tx_connect_speed = atol(tmp);
                                        }
                                        LOG(4, s, t, "   TX connect speed <%u>\n", session[s].tx_connect_speed);
@@ -2102,8 +2169,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        else
                                        {
                                                // AS5300s send connect speed as a string
                                        else
                                        {
                                                // AS5300s send connect speed as a string
-                                               char tmp[30] = {0};
-                                               memcpy(tmp, b, (n >= 30) ? 30 : n);
+                                               char tmp[30];
+                                               memset(tmp, 0, sizeof(tmp));
+                                               memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1);
                                                session[s].rx_connect_speed = atol(tmp);
                                        }
                                        LOG(4, s, t, "   RX connect speed <%u>\n", session[s].rx_connect_speed);
                                                session[s].rx_connect_speed = atol(tmp);
                                        }
                                        LOG(4, s, t, "   RX connect speed <%u>\n", session[s].rx_connect_speed);
@@ -2118,47 +2186,51 @@ 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));
                                        {
                                                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
                                        {
                                                break;
                                        }
                                case 30:    // Proxy Authentication Name
                                        {
-                                               char authname[64] = {0};
-                                               memcpy(authname, b, (n > 63) ? 63 : n);
+                                               char authname[64];
+                                               memset(authname, 0, sizeof(authname));
+                                               memcpy(authname, b, (n < sizeof(authname)) ? n : sizeof(authname) - 1);
                                                LOG(4, s, t, "   Proxy Auth Name (%s)\n",
                                                        authname);
                                                break;
                                        }
                                case 31:    // Proxy Authentication Challenge
                                        {
                                                LOG(4, s, t, "   Proxy Auth Name (%s)\n",
                                                        authname);
                                                break;
                                        }
                                case 31:    // Proxy Authentication Challenge
                                        {
-                                               memcpy(radius[session[s].radius].auth, b, 16);
                                                LOG(4, s, t, "   Proxy Auth Challenge\n");
                                                LOG(4, s, t, "   Proxy Auth Challenge\n");
+                                               if (sess_local[s].radius)
+                                                       memcpy(radius[sess_local[s].radius].auth, b, 16);
                                                break;
                                        }
                                case 32:    // Proxy Authentication ID
                                        {
                                                uint16_t authid = ntohs(*(uint16_t *)(b));
                                                LOG(4, s, t, "   Proxy Auth ID (%d)\n", authid);
                                                break;
                                        }
                                case 32:    // Proxy Authentication ID
                                        {
                                                uint16_t authid = ntohs(*(uint16_t *)(b));
                                                LOG(4, s, t, "   Proxy Auth ID (%d)\n", authid);
-                                               if (session[s].radius)
-                                                       radius[session[s].radius].id = authid;
+                                               if (sess_local[s].radius)
+                                                       radius[sess_local[s].radius].id = authid;
                                                break;
                                        }
                                case 33:    // Proxy Authentication Response
                                                break;
                                        }
                                case 33:    // Proxy Authentication Response
-                                       {
-                                               char authresp[64] = {0};
-                                               memcpy(authresp, b, (n > 63) ? 63 : n);
-                                               LOG(4, s, t, "   Proxy Auth Response\n");
-                                               break;
-                                       }
-                               case 27:    // last send lcp
+                                       LOG(4, s, t, "   Proxy Auth Response\n");
+                                       break;
+                               case 27:    // last sent lcp
                                        {        // find magic number
                                                uint8_t *p = b, *e = p + n;
                                                while (p + 1 < e && p[1] && p + p[1] <= e)
                                                {
                                                        if (*p == 5 && p[1] == 6) // Magic-Number
                                                                amagic = ntohl(*(uint32_t *) (p + 2));
                                        {        // find magic number
                                                uint8_t *p = b, *e = p + n;
                                                while (p + 1 < e && p[1] && p + p[1] <= e)
                                                {
                                                        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
                                                        else if (*p == 7) // Protocol-Field-Compression
                                                                aflags |= SESSIONPFC;
                                                        else if (*p == 8) // Address-and-Control-Field-Compression
@@ -2180,14 +2252,20 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        session[s].random_vector_length = n;
                                        break;
                                default:
                                        session[s].random_vector_length = n;
                                        break;
                                default:
-                                       LOG(2, s, t, "   Unknown AVP type %d\n", mtype);
-                                       fatal = flags;
-                                       continue; // next
+                                       {
+                                               static char e[] = "unknown AVP 0xXXXX";
+                                               LOG(2, s, t, "   Unknown AVP type %d\n", mtype);
+                                               fatal = flags;
+                                               result = 2; // general error
+                                               error = 8; // unknown mandatory AVP
+                                               sprintf((msg = e) + 14, "%04x", mtype);
+                                               continue; // next
+                                       }
                                }
                        }
                        // process message
                        if (fatal & 0x80)
                                }
                        }
                        // process message
                        if (fatal & 0x80)
-                               tunnelshutdown(t, "Unknown Mandatory AVP");
+                               tunnelshutdown(t, "Invalid mandatory AVP", result, error, msg);
                        else
                                switch (message)
                                {
                        else
                                switch (message)
                                {
@@ -2212,8 +2290,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        break;
                                case 4:       // StopCCN
                                        controlnull(t); // ack
                                        break;
                                case 4:       // StopCCN
                                        controlnull(t); // ack
-                                       tunnelshutdown(t, "Stopped"); // Shut down cleanly
-                                       tunnelkill(t, "Stopped"); // Immediately force everything dead
+                                       tunnelshutdown(t, "Stopped", 0, 0, 0); // Shut down cleanly
                                        break;
                                case 6:       // HELLO
                                        controlnull(t); // simply ACK
                                        break;
                                case 6:       // HELLO
                                        controlnull(t); // simply ACK
@@ -2231,7 +2308,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        if (!sessionfree)
                                        {
                                                STAT(session_overflow);
                                        if (!sessionfree)
                                        {
                                                STAT(session_overflow);
-                                               LOG(1, 0, t, "No free sessions");
+                                               LOG(1, 0, t, "No free sessions\n");
                                                return;
                                        }
                                        else
                                                return;
                                        }
                                        else
@@ -2250,12 +2327,11 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                if (!(r = radiusnew(s)))
                                                {
                                                        LOG(1, s, t, "No free RADIUS sessions for ICRQ\n");
                                                if (!(r = radiusnew(s)))
                                                {
                                                        LOG(1, s, t, "No free RADIUS sessions for ICRQ\n");
-                                                       sessionkill(s, "no free RADIUS sesions");
+                                                       sessionclear(s);
                                                        return;
                                                }
 
                                                c = controlnew(11); // sending ICRP
                                                        return;
                                                }
 
                                                c = controlnew(11); // sending ICRP
-                                               session[s].id = sessionid++;
                                                session[s].opened = time_now;
                                                session[s].tunnel = t;
                                                session[s].far = asession;
                                                session[s].opened = time_now;
                                                session[s].tunnel = t;
                                                session[s].far = asession;
@@ -2264,8 +2340,6 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                                control16(c, 14, s, 1); // assigned session
                                                controladd(c, t, s); // send the reply
 
                                                control16(c, 14, s, 1); // assigned session
                                                controladd(c, t, s); // send the reply
 
-                                               // 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);
                                                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);
@@ -2281,13 +2355,13 @@ 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
                                        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 (authtype && !(config->radius_authtypes & authtype))
+                                               sendlcp(t, s, config->radius_authprefer);
                                        break;
                                case 14:      // CDN
                                        controlnull(t); // ack
                                        break;
                                case 14:      // CDN
                                        controlnull(t); // ack
-                                       sessionshutdown(s, "Closed (Received CDN)");
+                                       sessionshutdown(s, "Closed (Received CDN).", 0, 0);
                                        break;
                                case 0xFFFF:
                                        LOG(1, s, t, "Missing message type\n");
                                        break;
                                case 0xFFFF:
                                        LOG(1, s, t, "Missing message type\n");
@@ -2295,7 +2369,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                default:
                                        STAT(tunnel_rx_errors);
                                        if (mandatory)
                                default:
                                        STAT(tunnel_rx_errors);
                                        if (mandatory)
-                                               tunnelshutdown(t, "Unknown message type");
+                                               tunnelshutdown(t, "Unknown message type", 2, 6, "unknown message type");
                                        else
                                                LOG(1, s, t, "Unknown message type %d\n", message);
                                        break;
                                        else
                                                LOG(1, s, t, "Unknown message type %d\n", message);
                                        break;
@@ -2540,7 +2614,7 @@ static int regular_cleanups(void)
                        if (a & CLI_TUN_KILL)
                        {
                                LOG(2, 0, t, "Dropping tunnel by CLI\n");
                        if (a & CLI_TUN_KILL)
                        {
                                LOG(2, 0, t, "Dropping tunnel by CLI\n");
-                               tunnelshutdown(t, "Requested by administrator");
+                               tunnelshutdown(t, "Requested by administrator", 1, 0, 0);
                        }
                }
 
                        }
                }
 
@@ -2556,31 +2630,34 @@ static int regular_cleanups(void)
                if (!session[s].opened) // Session isn't in use
                        continue;
 
                if (!session[s].opened) // Session isn't in use
                        continue;
 
-               if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
+               // check for expired sessions
+               if (session[s].die)
                {
                {
-                       // 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);
+                       if (session[s].die <= TIME)
+                       {
+                               sessionkill(s, "Expired");
+                               if (++count >= MAX_ACTIONS) break;
+                       }
+                       continue;
                }
 
                }
 
-               // check for expired sessions
-               if (session[s].die && session[s].die <= TIME)
+               if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
                {
                {
-                       sessionkill(s, "Expired");
-                       if (++count >= MAX_ACTIONS) break;
-                       continue;
+                       // 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);
                }
 
                // Drop sessions who have not responded within IDLE_TIMEOUT seconds
                if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
                {
                }
 
                // Drop sessions who have not responded within IDLE_TIMEOUT seconds
                if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
                {
-                       sessionshutdown(s, "No response to LCP ECHO requests");
+                       sessionshutdown(s, "No response to LCP ECHO requests.", 3, 0);
                        STAT(session_timeout);
                        if (++count >= MAX_ACTIONS) break;
                        continue;
                }
 
                        STAT(session_timeout);
                        if (++count >= MAX_ACTIONS) break;
                        continue;
                }
 
-               // No data in IDLE_TIMEOUT seconds, send LCP ECHO
+               // No data in ECHO_TIMEOUT seconds, send LCP ECHO
                if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT))
                {
                        uint8_t b[MAXCONTROL] = {0};
                if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT))
                {
                        uint8_t b[MAXCONTROL] = {0};
@@ -2608,7 +2685,7 @@ static int regular_cleanups(void)
                        if (a & CLI_SESS_KILL)
                        {
                                LOG(2, s, session[s].tunnel, "Dropping session by CLI\n");
                        if (a & CLI_SESS_KILL)
                        {
                                LOG(2, s, session[s].tunnel, "Dropping session by CLI\n");
-                               sessionshutdown(s, "Requested by administrator");
+                               sessionshutdown(s, "Requested by administrator.", 3, 0);
                                a = 0; // dead, no need to check for other actions
                        }
 
                                a = 0; // dead, no need to check for other actions
                        }
 
@@ -2667,6 +2744,29 @@ static int regular_cleanups(void)
 
                        if (++count >= MAX_ACTIONS) break;
                }
 
                        if (++count >= MAX_ACTIONS) break;
                }
+
+               // RADIUS interim accounting
+               if (config->radius_accounting && config->radius_interim > 0
+                   && session[s].ip && !session[s].walled_garden
+                   && !sess_local[s].radius // RADIUS already in progress
+                   && time_now - sess_local[s].last_interim >= config->radius_interim)
+               {
+                       if (!(r = radiusnew(s)))
+                       {
+                               LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n");
+                               STAT(radius_overflow);
+                               continue;
+                       }
+
+                       LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n",
+                               session[s].user, session[s].unique_id);
+
+                       radiussend(r, RADIUSINTERIM);
+                       sess_local[s].last_interim = time_now;
+
+                       if (++count >= MAX_ACTIONS)
+                               break;
+               }
        }
 
        if (*config->accounting_dir)
        }
 
        if (*config->accounting_dir)
@@ -3079,16 +3179,12 @@ static void initdata(int optdebug, char *optconfig)
 {
        int i;
 
 {
        int i;
 
-       if (!(_statistics = shared_malloc(sizeof(struct Tstats))))
-       {
-               LOG(0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno));
-               exit(1);
-       }
        if (!(config = shared_malloc(sizeof(configt))))
        {
        if (!(config = shared_malloc(sizeof(configt))))
        {
-               LOG(0, 0, 0, "Error doing malloc for configuration: %s\n", strerror(errno));
+               fprintf(stderr, "Error doing malloc for configuration: %s\n", strerror(errno));
                exit(1);
        }
                exit(1);
        }
+
        memset(config, 0, sizeof(configt));
        time(&config->start_time);
        strncpy(config->config_file, optconfig, strlen(optconfig));
        memset(config, 0, sizeof(configt));
        time(&config->start_time);
        strncpy(config->config_file, optconfig, strlen(optconfig));
@@ -3097,6 +3193,22 @@ static void initdata(int optdebug, char *optconfig)
        config->rl_rate = 28; // 28kbps
        strcpy(config->random_device, RANDOMDEVICE);
 
        config->rl_rate = 28; // 28kbps
        strcpy(config->random_device, RANDOMDEVICE);
 
+       log_stream = stderr;
+
+#ifdef RINGBUFFER
+       if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
+       {
+               LOG(0, 0, 0, "Error doing malloc for ringbuffer: %s\n", strerror(errno));
+               exit(1);
+       }
+       memset(ringbuffer, 0, sizeof(struct Tringbuffer));
+#endif
+
+       if (!(_statistics = shared_malloc(sizeof(struct Tstats))))
+       {
+               LOG(0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno));
+               exit(1);
+       }
        if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL)))
        {
                LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno));
        if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL)))
        {
                LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno));
@@ -3133,15 +3245,6 @@ static void initdata(int optdebug, char *optconfig)
        }
        memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
 
        }
        memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
 
-#ifdef RINGBUFFER
-       if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
-       {
-               LOG(0, 0, 0, "Error doing malloc for ringbuffer: %s\n", strerror(errno));
-               exit(1);
-       }
-       memset(ringbuffer, 0, sizeof(struct Tringbuffer));
-#endif
-
        if (!(cli_session_actions = shared_malloc(sizeof(struct cli_session_actions) * MAXSESSION)))
        {
                LOG(0, 0, 0, "Error doing malloc for cli session actions: %s\n", strerror(errno));
        if (!(cli_session_actions = shared_malloc(sizeof(struct cli_session_actions) * MAXSESSION)))
        {
                LOG(0, 0, 0, "Error doing malloc for cli session actions: %s\n", strerror(errno));
@@ -3709,9 +3812,11 @@ int main(int argc, char *argv[])
 
 static void sighup_handler(int sig)
 {
 
 static void sighup_handler(int sig)
 {
-       if (log_stream && log_stream != stderr)
+       if (log_stream)
        {
        {
-               fclose(log_stream);
+               if (log_stream != stderr)
+                       fclose(log_stream);
+
                log_stream = NULL;
        }
 
                log_stream = NULL;
        }
 
@@ -3773,7 +3878,7 @@ static void sigquit_handler(int sig)
                for (i = 1; i < MAXTUNNEL; i++)
                {
                        if (tunnel[i].ip || tunnel[i].state)
                for (i = 1; i < MAXTUNNEL; i++)
                {
                        if (tunnel[i].ip || tunnel[i].state)
-                               tunnelshutdown(i, "L2TPNS Closing");
+                               tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
                }
        }
 
                }
        }
 
@@ -3833,7 +3938,9 @@ static void update_config()
        syslog_log = 0;
        if (log_stream)
        {
        syslog_log = 0;
        if (log_stream)
        {
-               fclose(log_stream);
+               if (log_stream != stderr)
+                       fclose(log_stream);
+
                log_stream = NULL;
        }
 
                log_stream = NULL;
        }
 
@@ -3915,7 +4022,7 @@ static void update_config()
                else if (!strncasecmp("pap", p, strlen(p)))
                        type = AUTHPAP;
                else
                else if (!strncasecmp("pap", p, strlen(p)))
                        type = AUTHPAP;
                else
-                       LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"", p);
+                       LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"\n", p);
 
                config->radius_authtypes |= type;
                if (!config->radius_authprefer)
 
                config->radius_authtypes |= type;
                if (!config->radius_authprefer)
@@ -4051,7 +4158,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");
+                       sessionshutdown(s, "No IP addresses available.", 2, 7);
                        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",