Add interim accounting support from Vladislav Bjelic
authorBrendan O'Dea <bod@optus.net>
Thu, 5 May 2005 10:02:06 +0000 (10:02 +0000)
committerBrendan O'Dea <bod@optus.net>
Thu, 5 May 2005 10:02:06 +0000 (10:02 +0000)
Changes
THANKS
cli.c
cluster.c
constants.c
l2tpns.c
l2tpns.h
ppp.c
radius.c

diff --git a/Changes b/Changes
index e4ba742..c61169b 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,7 @@
 * Thu May 5 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.0
 - Add IPv6 support from Jonathan McDowell.
 - Add CHAP support from Jordan Hrycaj (work in progress).
+- Add interim accounting support from Vladislav Bjelic.
 - Sanity check that cluster_send_session is not called from a child
   process.
 - Throttle outgoing LASTSEEN packets to at most one per second for a
diff --git a/THANKS b/THANKS
index 571276b..d2887e9 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -15,3 +15,4 @@ Jonathan McDowell          <noodles@earth.li>
 Bjørn Augestad             <augestad@users.sourceforge.net>
 Roberto Chostakovis        <rchostakovis@users.sourceforge.net>
 Jordan Hrycaj              <jordan@mjh.teddy-net.com>
+Vladislav Bjelic           <vladislav@gmail.com>
diff --git a/cli.c b/cli.c
index 5abd426..495222d 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -2,7 +2,7 @@
 // vim: sw=8 ts=8
 
 char const *cvs_name = "$Name:  $";
-char const *cvs_id_cli = "$Id: cli.c,v 1.55 2005-05-02 09:55:04 bodea Exp $";
+char const *cvs_id_cli = "$Id: cli.c,v 1.56 2005-05-05 10:02:07 bodea Exp $";
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -413,7 +413,6 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].total_cout, session[s].total_cin);
                        cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin);
                        cli_print(cli, "\tMRU:\t\t%d", session[s].mru);
-                       cli_print(cli, "\tRadius Session:\t%u", session[s].radius);
                        cli_print(cli, "\tRx Speed:\t%u", session[s].rx_connect_speed);
                        cli_print(cli, "\tTx Speed:\t%u", session[s].tx_connect_speed);
                        if (session[s].filter_in && session[s].filter_in <= MAXFILTER)
index 2e01a98..cf85dbd 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -1,6 +1,6 @@
 // L2TPNS Clustering Stuff
 
-char const *cvs_id_cluster = "$Id: cluster.c,v 1.34 2005-05-02 09:06:05 bodea Exp $";
+char const *cvs_id_cluster = "$Id: cluster.c,v 1.35 2005-05-05 10:02:07 bodea Exp $";
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -594,7 +594,7 @@ void cluster_check_master(void)
 
                sess_local[i].cin = sess_local[i].cout = 0;
 
-               session[i].radius = 0;  // Reset authentication as the radius blocks aren't up to date.
+               sess_local[i].radius = 0;       // Reset authentication as the radius blocks aren't up to date.
 
                if (session[i].unique_id >= high_unique_id)     // This is different to the index into the session table!!!
                        high_unique_id = session[i].unique_id+1;
index 765cf5c..0b9ef42 100644 (file)
@@ -1,6 +1,6 @@
 // L2TPNS: constants
 
-char const *cvs_id_constants = "$Id: constants.c,v 1.4 2005-01-05 13:37:56 bodea Exp $";
+char const *cvs_id_constants = "$Id: constants.c,v 1.5 2005-05-05 10:02:07 bodea Exp $";
 
 #include <stdio.h>
 #include "constants.h"
@@ -156,7 +156,8 @@ CONSTANT(radius_state,
     "RADIUSIPCP",                                      // 3
     "RADIUSSTART",                                     // 4
     "RADIUSSTOP",                                      // 5
-    "RADIUSWAIT"                                       // 6
+    "RADIUSINTERIM",                                   // 6
+    "RADIUSWAIT"                                       // 7
 )
 
 CONSTANT(radius_code,
index 1502f6b..f96ac79 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.92 2005-05-05 02:39:54 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.93 2005-05-05 10:02:07 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -55,23 +55,23 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.92 2005-05-05 02:39:54 bodea Exp
 #endif /* BGP */
 
 // Globals
-configt *config = NULL;                        // all configuration
-int tunfd = -1;                                // tun interface file handle. (network device)
-int udpfd = -1;                                // UDP file handle
-int controlfd = -1;                    // Control signal handle
-int clifd = -1;                                // Socket listening for CLI connections.
-int snoopfd = -1;                      // UDP file handle for sending out intercept data
-int *radfds = NULL;                    // RADIUS requests file handles
-int ifrfd = -1;                                // File descriptor for routing, etc
-int ifr6fd = -1;                       // File descriptor for IPv6 routing, etc
-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 int syslog_log = 0;             // are we logging to syslog
-static FILE *log_stream = stderr;      // file handle for direct logging (i.e. direct into file, not via syslog).
-extern int cluster_sockfd;             // Intra-cluster communications socket.
-uint32_t last_id = 0;                  // Unique ID for radius accounting
+configt *config = NULL;                // all configuration
+int tunfd = -1;                        // tun interface file handle. (network device)
+int udpfd = -1;                        // UDP file handle
+int controlfd = -1;            // Control signal handle
+int clifd = -1;                        // Socket listening for CLI connections.
+int snoopfd = -1;              // UDP file handle for sending out intercept data
+int *radfds = NULL;            // RADIUS requests file handles
+int ifrfd = -1;                        // File descriptor for routing, etc
+int ifr6fd = -1;               // File descriptor for IPv6 routing, etc
+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 int syslog_log = 0;     // are we logging to syslog
+static FILE *log_stream = 0;   // file handle for direct logging (i.e. direct into file, not via syslog).
+extern int cluster_sockfd;     // Intra-cluster communications socket.
+uint32_t last_id = 0;          // Unique ID for radius accounting
 
 struct cli_session_actions *cli_session_actions = NULL;        // Pending session changes requested by CLI
 struct cli_tunnel_actions *cli_tunnel_actions = NULL;  // Pending tunnel changes required by CLI
@@ -110,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),
@@ -1455,7 +1456,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error)
        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)
                {
                        if (!(r = radiusnew(s)))
@@ -1537,7 +1538,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error)
 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);
@@ -1616,8 +1617,8 @@ void sessionkill(sessionidt s, char *reason)
 
        session[s].die = TIME;
        sessionshutdown(s, reason, 3, 0);  // close radius/routes, etc.
-       if (session[s].radius)
-               radiusclear(session[s].radius, s); // cant send clean accounting data, session is killed
+       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);
 
@@ -2189,16 +2190,17 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr)
                                        }
                                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
@@ -2618,19 +2620,22 @@ 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
@@ -2729,6 +2734,31 @@ 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 (!radiusnew(s))
+                       {
+                               LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n");
+                               STAT(radius_overflow);
+                               continue;
+                       }
+
+                       random_data(radius[r].auth, sizeof(radius[r].auth));
+
+                       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)
@@ -3155,6 +3185,8 @@ 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))))
        {
index 8014c78..3d0be8f 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -1,5 +1,5 @@
 // L2TPNS Global Stuff
-// $Id: l2tpns.h,v 1.64 2005-04-18 05:32:16 bodea Exp $
+// $Id: l2tpns.h,v 1.65 2005-05-05 10:02:08 bodea Exp $
 
 #ifndef __L2TPNS_H__
 #define __L2TPNS_H__
@@ -181,7 +181,6 @@ typedef struct
        time_t last_packet;             // Last packet from the user (used for idle timeouts)
        in_addr_t dns1, dns2;           // DNS servers
        routet route[MAXROUTE];         // static routes
-       uint16_t radius;                // which radius session is being used (0 for not waiting on authentication)
        uint16_t mru;                   // maximum receive unit
        uint16_t tbf_in;                // filter bucket for throttling in from the user.
        uint16_t tbf_out;               // filter bucket for throttling out to the user.
@@ -225,6 +224,12 @@ typedef struct
        clockt last_packet_out;
        uint32_t packets_out;
        uint32_t packets_dropped;
+
+       // RADIUS session in use
+       uint16_t radius;
+
+       // interim RADIUS
+       time_t last_interim;
 } sessionlocalt;
 
 #define        SESSIONPFC      1       // PFC negotiated flags
@@ -313,8 +318,8 @@ enum
        RADIUSIPCP,             // sending IPCP to end user
        RADIUSSTART,            // sending start accounting to RADIUS server
        RADIUSSTOP,             // sending stop accounting to RADIUS server
+       RADIUSINTERIM,          // sending interim accounting to RADIUS server
        RADIUSWAIT,             // waiting timeout before available, in case delayed replies
-       RADIUSDEAD,             // errored while talking to radius server.
 };
 
 struct Tstats
@@ -442,6 +447,7 @@ typedef struct
 
        char            radiussecret[64];
        int             radius_accounting;
+       int             radius_interim;
        in_addr_t       radiusserver[MAXRADSERVER];     // radius servers
        uint16_t        radiusport[MAXRADSERVER];       // radius base ports
        uint8_t         numradiusservers;               // radius server count
diff --git a/ppp.c b/ppp.c
index fbc72d8..e3a2c74 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1,6 +1,6 @@
 // L2TPNS PPP Stuff
 
-char const *cvs_id_ppp = "$Id: ppp.c,v 1.47 2005-04-27 13:53:17 bodea Exp $";
+char const *cvs_id_ppp = "$Id: ppp.c,v 1.48 2005-05-05 10:02:08 bodea Exp $";
 
 #include <stdio.h>
 #include <string.h>
@@ -77,7 +77,7 @@ void processpap(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
                }
                LOG(3, s, t, "PAP login %s/%s\n", user, pass);
        }
-       if (session[s].ip || !session[s].radius)
+       if (session[s].ip || !sess_local[s].radius)
        {
                // respond now, either no RADIUS available or already authenticated
                uint8_t b[MAXCONTROL];
@@ -110,7 +110,7 @@ void processpap(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        else
        {
                // set up RADIUS request
-               uint16_t r = session[s].radius;
+               uint16_t r = sess_local[s].radius;
 
                // Run PRE_AUTH plugins
                struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(user), strdup(pass), PPPPAP, 1 };
@@ -144,7 +144,7 @@ void processchap(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        CSTAT(processchap);
 
        LOG_HEX(5, "CHAP", p, l);
-       r = session[s].radius;
+       r = sess_local[s].radius;
        if (!r)
        {
                LOG(1, s, t, "Unexpected CHAP message\n");
@@ -592,7 +592,7 @@ void processipcp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        if (*p == ConfigAck)
        {
                // happy with our IPCP
-               uint16_t r = session[s].radius;
+               uint16_t r = sess_local[s].radius;
                if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden)
                {
                        if (!r)
@@ -1084,9 +1084,8 @@ void processccp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
 void sendchap(tunnelidt t, sessionidt s)
 {
        uint8_t b[MAXCONTROL];
-       uint16_t r = session[s].radius;
+       uint16_t r = sess_local[s].radius;
        uint8_t *q;
-       uint8_t *l;
 
        CSTAT(sendchap);
 
index 90300dd..2913f06 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -1,6 +1,6 @@
 // L2TPNS Radius Stuff
 
-char const *cvs_id_radius = "$Id: radius.c,v 1.28 2005-05-03 05:11:34 bodea Exp $";
+char const *cvs_id_radius = "$Id: radius.c,v 1.29 2005-05-05 10:02:08 bodea Exp $";
 
 #include <time.h>
 #include <stdio.h>
@@ -42,7 +42,7 @@ void initrad(void)
 
 void radiusclear(uint16_t r, sessionidt s)
 {
-       if (s) session[s].radius = 0;
+       if (s) sess_local[s].radius = 0;
        memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL;
 }
 
@@ -69,7 +69,7 @@ static uint16_t get_free_radius()
 
 uint16_t radiusnew(sessionidt s)
 {
-       uint16_t r = session[s].radius;
+       uint16_t r = sess_local[s].radius;
 
        /* re-use */
        if (r)
@@ -86,7 +86,7 @@ uint16_t radiusnew(sessionidt s)
        };
 
        memset(&radius[r], 0, sizeof(radius[r]));
-       session[s].radius = r;
+       sess_local[s].radius = r;
        radius[r].session = s;
        radius[r].state = RADIUSWAIT;
        radius[r].retry = TIME + 1200; // Wait at least 120 seconds to re-claim this.
@@ -165,6 +165,7 @@ void radiussend(uint16_t r, uint8_t state)
                        break;
                case RADIUSSTART:
                case RADIUSSTOP:
+               case RADIUSINTERIM:
                        b[0] = 4;               // accounting request
                        break;
                default:
@@ -229,11 +230,11 @@ void radiussend(uint16_t r, uint8_t state)
                        p += p[1];
                }
        }
-       else if (state == RADIUSSTART || state == RADIUSSTOP)
+       else if (state == RADIUSSTART || state == RADIUSSTOP || state == RADIUSINTERIM)
        {                          // accounting
                *p = 40;                // accounting type
                p[1] = 6;
-               *(uint32_t *) (p + 2) = htonl((state == RADIUSSTART) ? 1 : 2);
+               *(uint32_t *) (p + 2) = htonl(state - RADIUSSTART + 1); // start=1, stop=2, interim=3
                p += p[1];
                if (s)
                {
@@ -241,8 +242,16 @@ void radiussend(uint16_t r, uint8_t state)
                        p[1] = 18;
                        sprintf(p + 2, "%08X%08X", session[s].unique_id, session[s].opened);
                        p += p[1];
-                       if (state == RADIUSSTOP)
-                       {                // stop
+                       if (state == RADIUSSTART)
+                       {                // start
+                               *p = 41;      // delay
+                               p[1] = 6;
+                               *(uint32_t *) (p + 2) = htonl(time(NULL) - session[s].opened);
+                               p += p[1];
+                               sess_local[s].last_interim = time_now; // Setup "first" Interim
+                       }
+                       else
+                       {                // stop, interim
                                *p = 42;      // input octets
                                p[1] = 6;
                                *(uint32_t *) (p + 2) = htonl(session[s].cin);
@@ -264,13 +273,6 @@ void radiussend(uint16_t r, uint8_t state)
                                *(uint32_t *) (p + 2) = htonl(session[s].pout);
                                p += p[1];
                        }
-                       else
-                       {                // start
-                               *p = 41;      // delay
-                               p[1] = 6;
-                               *(uint32_t *) (p + 2) = htonl(time(NULL) - session[s].opened);
-                               p += p[1];
-                       }
 
                        if (session[s].snoop_ip && session[s].snoop_port)
                        {
@@ -393,7 +395,8 @@ void processrad(uint8_t *buf, int len, char socket_index)
                LOG(1, s, session[s].tunnel, "   Unexpected RADIUS response\n");
                return;
        }
-       if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSSTART && radius[r].state != RADIUSSTOP)
+       if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSSTART
+           && radius[r].state != RADIUSSTOP && radius[r].state != RADIUSINTERIM)
        {
                LOG(1, s, session[s].tunnel, "   Unexpected RADIUS response\n");
                return;
@@ -413,7 +416,7 @@ void processrad(uint8_t *buf, int len, char socket_index)
                }
 
                if ((radius[r].state == RADIUSAUTH && r_code != AccessAccept && r_code != AccessReject) ||
-                       ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP) && r_code != AccountingResponse))
+                       ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP || radius[r].state == RADIUSINTERIM) && r_code != AccountingResponse))
                {
                        LOG(1, s, session[s].tunnel, "   Unexpected RADIUS response %s\n", radius_code(r_code));
                        return; // We got something we didn't expect. Let the timeouts take
@@ -709,24 +712,27 @@ void radiusretry(uint16_t r)
        radius[r].retry = backoff(radius[r].try + 1);
        switch (radius[r].state)
        {
-               case RADIUSCHAP:           // sending CHAP down PPP
+               case RADIUSCHAP:        // sending CHAP down PPP
                        sendchap(t, s);
                        break;
                case RADIUSIPCP:
-                       sendipcp(t, s);         // send IPCP
+                       sendipcp(t, s); // send IPCP
                        break;
-               case RADIUSAUTH:           // sending auth to RADIUS server
+               case RADIUSAUTH:        // sending auth to RADIUS server
                        radiussend(r, RADIUSAUTH);
                        break;
-               case RADIUSSTART:          // sending start accounting to RADIUS server
+               case RADIUSSTART:       // sending start accounting to RADIUS server
                        radiussend(r, RADIUSSTART);
                        break;
-               case RADIUSSTOP:           // sending stop accounting to RADIUS server
+               case RADIUSSTOP:        // sending stop accounting to RADIUS server
                        radiussend(r, RADIUSSTOP);
                        break;
+               case RADIUSINTERIM:     // sending interim accounting to RADIUS server
+                       radiussend(r, RADIUSINTERIM);
+                       break;
                default:
-               case RADIUSNULL:           // Not in use
-               case RADIUSWAIT:           // waiting timeout before available, in case delayed reply from RADIUS server
+               case RADIUSNULL:        // Not in use
+               case RADIUSWAIT:        // waiting timeout before available, in case delayed reply from RADIUS server
                        // free up RADIUS task
                        radiusclear(r, s);
                        LOG(3, s, session[s].tunnel, "Freeing up radius session %d\n", r);