X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/466ffcdaac51211102276e824d02ed9bc77dd82d..1642e0dbaae2eaaed894d01ef94d0fe2520c6de2:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index a28cd5f..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.85 2005/03/10 03:08:08 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.93 2005/05/05 10:02:07 bodea Exp $"; #include #include @@ -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), @@ -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,10 +1453,10 @@ 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) { if (!(r = radiusnew(s))) @@ -1508,9 +1508,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 +1538,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); @@ -1546,7 +1556,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; } @@ -1606,9 +1616,9 @@ 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); @@ -1654,12 +1664,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 +1686,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 } } @@ -1890,6 +1921,10 @@ 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)) // 0x80 = mandatory AVP { @@ -1909,6 +1944,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) { 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; @@ -1916,6 +1954,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; + result = 2; // general error + error = 6; // generic vendor-specific error + msg = "unsupported vendor-specific"; continue; // next } b += 2; @@ -1932,18 +1973,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; + 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; } @@ -1955,7 +2005,13 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) 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; } @@ -2004,6 +2060,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 } } @@ -2131,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 @@ -2180,14 +2240,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) { @@ -2212,8 +2278,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 @@ -2255,7 +2320,6 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) } c = controlnew(11); // sending ICRP - session[s].id = sessionid++; session[s].opened = time_now; session[s].tunnel = t; session[s].far = asession; @@ -2287,7 +2351,7 @@ 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"); @@ -2295,7 +2359,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) 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; @@ -2540,7 +2604,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); } } @@ -2556,31 +2620,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}; @@ -2608,7 +2675,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 } @@ -2667,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) @@ -3079,16 +3171,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)); @@ -3097,6 +3185,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)); @@ -3133,15 +3237,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)); @@ -3709,9 +3804,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; } @@ -3773,7 +3870,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); } } @@ -3833,7 +3930,9 @@ static void update_config() syslog_log = 0; if (log_stream) { - fclose(log_stream); + if (log_stream != stderr) + fclose(log_stream); + log_stream = NULL; } @@ -4051,7 +4150,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",