From: bodea Date: Thu, 5 May 2005 10:02:06 +0000 (+0000) Subject: Add interim accounting support from Vladislav Bjelic X-Git-Tag: release_2_1_0~52 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/1642e0dbaae2eaaed894d01ef94d0fe2520c6de2 Add interim accounting support from Vladislav Bjelic --- diff --git a/Changes b/Changes index e4ba742..c61169b 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,7 @@ * Thu May 5 2005 Brendan O'Dea 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 --- a/THANKS +++ b/THANKS @@ -15,3 +15,4 @@ Jonathan McDowell Bjørn Augestad Roberto Chostakovis Jordan Hrycaj +Vladislav Bjelic diff --git a/cli.c b/cli.c index ae7b9ae..aa9d518 100644 --- 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 #include @@ -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) diff --git a/cluster.c b/cluster.c index 549edfd..e6c337c 100644 --- 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 #include @@ -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; diff --git a/constants.c b/constants.c index 850b074..1b6918f 100644 --- a/constants.c +++ b/constants.c @@ -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 #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, diff --git a/l2tpns.c b/l2tpns.c index 9999696..7eac740 100644 --- 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 #include @@ -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)))) { diff --git a/l2tpns.h b/l2tpns.h index 6ee0bb9..c42b6b7 100644 --- 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 4de5ba7..5ba0279 100644 --- 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 #include @@ -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); diff --git a/radius.c b/radius.c index f14f614..5d94720 100644 --- 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 #include @@ -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);