more CHAP changes
[l2tpns.git] / l2tpns.c
index e6cdff7..28eca3b 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -4,7 +4,7 @@
 // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
 // vim: sw=8 ts=8
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.84 2005/02/14 06:58:39 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.96 2005/05/07 08:17:25 bodea Exp $";
 
 #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
-static uint32_t sessionid = 0; // session id for radius accounting
 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.
-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
@@ -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("radius_interim", radius_interim, INT),
        CONFIG("radius_secret", radiussecret, STRING),
        CONFIG("radius_authtypes", radius_authtypes_s, STRING),
        CONFIG("bind_address", bind_address, IPv4),
@@ -187,7 +187,7 @@ static int remove_plugin(char *plugin_name);
 static void plugins_done(void);
 static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen);
 static tunnelidt new_tunnel(void);
-static int unhide_avp(uint8_t *avp, tunnelidt t, sessionidt s, uint16_t length);
+static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len);
 
 // return internal time (10ths since process startup)
 static clockt now(void)
@@ -1264,7 +1264,7 @@ static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
        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);
@@ -1433,7 +1433,7 @@ static void filter_session(sessionidt s, int filter_in, int filter_out)
 }
 
 // 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;
 
@@ -1453,25 +1453,21 @@ void sessionshutdown(sessionidt s, char *reason)
                run_plugins(PLUGIN_KILL_SESSION, &data);
        }
 
-       if (!walled_garden && !session[s].die)
+       if (session[s].ip && !walled_garden && !session[s].die)
        {
                // RADIUS Stop message
-               uint16_t r = session[s].radius;
+               uint16_t r = sess_local[s].radius;
                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))
@@ -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 (result)
        {                            // 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
        }
@@ -1528,7 +1534,7 @@ void sessionshutdown(sessionidt s, char *reason)
 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);
@@ -1536,6 +1542,12 @@ void sendipcp(tunnelidt t, sessionidt s)
        if (!r)
                r = radiusnew(s);
 
+       if (!r)
+       {
+               sessionshutdown(s, "No free RADIUS sessions for IPCP");
+               return;
+       }
+
        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.
-               sessionshutdown(s, "No reply on IPCP");
+               sessionshutdown(s, "No reply to IPCP.", 3, 0);
                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)
 {
@@ -1606,17 +1629,12 @@ void sessionkill(sessionidt s, char *reason)
        }
 
        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);
-
-       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);
 }
 
@@ -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);
-       cli_tunnel_actions[s].action = 0;
+       cli_tunnel_actions[t].action = 0;
        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;
 
@@ -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)
-                       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?
-       {                            // 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
        }
 }
 
@@ -1761,7 +1800,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
        {                          // control
                uint16_t message = 0xFFFF;      // message type
                uint8_t fatal = 0;
-               uint8_t mandatorymessage = 0;
+               uint8_t mandatory = 0;
                uint8_t chap = 0;               // if CHAP being used
                uint16_t asession = 0;          // assigned session
                uint32_t amagic = 0;            // magic number
@@ -1777,7 +1816,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                        return;
                }
 
-               if ((*buf & 0xCA) != 0xC8)
+               // control messages must have bits 0x80|0x40|0x08
+               // (type, length and sequence) set, and bits 0x02|0x01
+               // (offset and priority) clear
+               if ((*buf & 0xCB) != 0xC8)
                {
                        LOG(1, s, t, "Bad control header %02X\n", *buf);
                        STAT(tunnel_rx_errors);
@@ -1887,71 +1929,111 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                }
                if (l)
                {                     // if not a null message
+                       int result = 0;
+                       int error = 0;
+                       char *msg = 0;
+
                        // process AVPs
-                       while (l && !(fatal & 0x80))
+                       while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP
                        {
                                uint16_t n = (ntohs(*(uint16_t *) p) & 0x3FF);
                                uint8_t *b = p;
                                uint8_t flags = *p;
                                uint16_t mtype;
-                               p += n;       // next
-                               if (l < n)
+                               if (n > l)
                                {
                                        LOG(1, s, t, "Invalid length in AVP\n");
                                        STAT(tunnel_rx_errors);
-                                       fatal = flags;
                                        return;
                                }
+                               p += n;       // next
                                l -= n;
+                               if (flags & 0x3C) // reserved bits, should be clear
+                               {
+                                       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;
+                               if (*(uint16_t *) (b))
+                               {
+                                       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;
+                               mtype = ntohs(*(uint16_t *) (b));
+                               b += 2;
+                               n -= 6;
+
                                if (flags & 0x40)
                                {
+                                       uint16_t orig_len;
+
                                        // handle hidden AVPs
                                        if (!*config->l2tpsecret)
                                        {
                                                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;
+                                               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;
+                                               result = 2; // general error
+                                               error = 2; // length is wrong
+                                               msg = 0;
                                                continue;
                                        }
+
                                        LOG(4, s, t, "Hidden AVP\n");
+
                                        // Unhide the AVP
-                                       n = unhide_avp(b, t, s, n);
-                                       if (n == 0)
+                                       unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length);
+
+                                       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;
+                                               result = 2; // general error
+                                               error = 2; // length is wrong
+                                               msg = 0;
                                                continue;
                                        }
+
+                                       b += 2;
+                                       n = orig_len;
                                }
-                               if (*b & 0x3C)
-                               {
-                                       LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b);
-                                       fatal = flags;
-                                       continue; // next
-                               }
-                               b += 2;
-                               if (*(uint16_t *) (b))
-                               {
-                                       LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b)));
-                                       fatal = flags;
-                                       continue; // next
-                               }
-                               b += 2;
-                               mtype = ntohs(*(uint16_t *) (b));
-                               b += 2;
-                               n -= 6;
 
                                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);
+                                       mandatory = flags & 0x80;
                                        LOG(4, s, t, "   Message type = %d (%s)\n", *b, l2tp_message_type(message));
-                                       mandatorymessage = flags;
                                        break;
                                case 1:     // result code
                                        {
@@ -1986,6 +2068,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;
+                                                       result = 5; // unspported protocol version
+                                                       error = 0x0100; // supported version
+                                                       msg = 0;
                                                        continue; // next
                                                }
                                        }
@@ -2004,14 +2089,14 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
 //                                     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));
-                                       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
@@ -2051,13 +2136,13 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        // 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
-                                       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
@@ -2070,8 +2155,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        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);
@@ -2084,8 +2170,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        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);
@@ -2105,34 +2192,32 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        }
                                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
                                        {
-                                               memcpy(radius[session[s].radius].auth, b, 16);
                                                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);
-                                               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
-                                       {
-                                               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)
@@ -2162,14 +2247,20 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        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)
-                               tunnelshutdown(t, "Unknown Mandatory AVP");
+                               tunnelshutdown(t, "Invalid mandatory AVP", result, error, msg);
                        else
                                switch (message)
                                {
@@ -2194,8 +2285,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        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
@@ -2213,7 +2303,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        if (!sessionfree)
                                        {
                                                STAT(session_overflow);
-                                               LOG(1, 0, t, "No free sessions");
+                                               LOG(1, 0, t, "No free sessions\n");
                                                return;
                                        }
                                        else
@@ -2232,12 +2322,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");
-                                                       sessionkill(s, "no free RADIUS sesions");
+                                                       sessionclear(s);
                                                        return;
                                                }
 
                                                c = controlnew(11); // sending ICRP
-                                               session[s].id = sessionid++;
                                                session[s].opened = time_now;
                                                session[s].tunnel = t;
                                                session[s].far = asession;
@@ -2246,8 +2335,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
 
-                                               // 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);
@@ -2269,15 +2356,15 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        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;
                                default:
                                        STAT(tunnel_rx_errors);
-                                       if (mandatorymessage & 0x80)
-                                               tunnelshutdown(t, "Unknown message");
+                                       if (mandatory)
+                                               tunnelshutdown(t, "Unknown message type", 2, 6, "unknown message type");
                                        else
                                                LOG(1, s, t, "Unknown message type %d\n", message);
                                        break;
@@ -2522,7 +2609,7 @@ static int regular_cleanups(void)
                        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);
                        }
                }
 
@@ -2538,31 +2625,34 @@ static int regular_cleanups(void)
                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))
                {
-                       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;
                }
 
-               // 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};
@@ -2590,7 +2680,7 @@ static int regular_cleanups(void)
                        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
                        }
 
@@ -2649,6 +2739,29 @@ static int regular_cleanups(void)
 
                        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)
@@ -3061,16 +3174,12 @@ static void initdata(int optdebug, char *optconfig)
 {
        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))))
        {
-               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);
        }
+
        memset(config, 0, sizeof(configt));
        time(&config->start_time);
        strncpy(config->config_file, optconfig, strlen(optconfig));
@@ -3079,6 +3188,22 @@ static void initdata(int optdebug, char *optconfig)
        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));
@@ -3115,15 +3240,6 @@ static void initdata(int optdebug, char *optconfig)
        }
        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));
@@ -3691,9 +3807,11 @@ int main(int argc, char *argv[])
 
 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;
        }
 
@@ -3755,7 +3873,7 @@ static void sigquit_handler(int sig)
                for (i = 1; i < MAXTUNNEL; i++)
                {
                        if (tunnel[i].ip || tunnel[i].state)
-                               tunnelshutdown(i, "L2TPNS Closing");
+                               tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
                }
        }
 
@@ -3815,7 +3933,9 @@ static void update_config()
        syslog_log = 0;
        if (log_stream)
        {
-               fclose(log_stream);
+               if (log_stream != stderr)
+                       fclose(log_stream);
+
                log_stream = NULL;
        }
 
@@ -3897,7 +4017,7 @@ static void update_config()
                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)
@@ -4033,7 +4153,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");
-                       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",
@@ -4727,75 +4847,45 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc
 
 /* Unhide an avp.
  *
- * This unencodes the AVP using the L2TP CHAP secret and the
- * previously stored random vector. It replaces the hidden data with
- * the cleartext data and returns the length of the cleartext data
- * (including the AVP "header" of 6 bytes).
- *
- * Based on code from rp-l2tpd by Roaring Penguin Software Inc.
+ * This unencodes the AVP using the L2TP secret and the previously
+ * stored random vector.  It overwrites the hidden data with the
+ * unhidden AVP subformat.
  */
-static int unhide_avp(uint8_t *avp, tunnelidt t, sessionidt s, uint16_t length)
+static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len)
 {
        MD5_CTX ctx;
-       uint8_t *cursor;
        uint8_t digest[16];
-       uint8_t working_vector[16];
-       uint16_t hidden_length;
-       uint8_t type[2];
-       size_t done, todo;
-       uint8_t *output;
-
-       // Find the AVP type.
-       type[0] = *(avp + 4);
-       type[1] = *(avp + 5);
-
-       // Line up with the hidden data
-       cursor = output = avp + 6;
+       uint8_t *last;
+       size_t d = 0;
 
        // Compute initial pad
        MD5Init(&ctx);
-       MD5Update(&ctx, type, 2);
+       MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1);
+       MD5Update(&ctx, (uint8_t)  type       & 0xff, 1);
        MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
-       MD5Update(&ctx, session[s].random_vector, session[s].random_vector_length);
+       MD5Update(&ctx, vector, vec_len);
        MD5Final(digest, &ctx);
 
-       // Get hidden length
-       hidden_length = ((uint16_t) (digest[0] ^ cursor[0])) * 256 + (uint16_t) (digest[1] ^ cursor[1]);
-
-       // Keep these for later use
-       working_vector[0] = *cursor;
-       working_vector[1] = *(cursor + 1);
-       cursor += 2;
-
-       if (hidden_length > length - 8)
-       {
-               LOG(1, s, t, "Hidden length %d too long in AVP of length %d\n", (int) hidden_length, (int) length);
-               return 0;
-       }
+       // pointer to last decoded 16 octets
+       last = value;
 
-       /* Decrypt remainder */
-       done = 2;
-       todo = hidden_length;
-       while (todo)
+       while (len > 0)
        {
-               working_vector[done] = *cursor;
-               *output = digest[done] ^ *cursor;
-               ++output;
-               ++cursor;
-               --todo;
-               ++done;
-               if (done == 16 && todo)
+               // calculate a new pad based on the last decoded block
+               if (d >= sizeof(digest))
                {
-                       // Compute new digest
-                       done = 0;
                        MD5Init(&ctx);
                        MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
-                       MD5Update(&ctx, &working_vector, 16);
+                       MD5Update(&ctx, last, sizeof(digest));
                        MD5Final(digest, &ctx);
+
+                       d = 0;
+                       last = value;
                }
-       }
 
-       return hidden_length + 6;
+               *value++ ^= digest[d++];
+               len--;
+       }
 }
 
 static int ip_filter_port(ip_filter_portt *p, uint16_t port)