// 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.61 2004-11-29 03:55:21 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.68 2004-12-13 05:27:19 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
#define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *))
+// Counters for shutdown sessions
+static sessiont shut_acct[8192];
+static sessionidt shut_acct_n = 0;
+
tunnelt *tunnel = NULL; // Array of tunnel structures.
sessiont *session = NULL; // Array of session structures.
sessioncountt *sess_count = NULL; // Array of partial per-session traffic counters.
static void cache_ipmap(ipt ip, int s);
static void uncache_ipmap(ipt ip);
static void free_ip_address(sessionidt s);
-static void dump_acct_info(void);
+static void dump_acct_info(int all);
static void sighup_handler(int sig);
static void sigalrm_handler(int sig);
static void sigterm_handler(int sig);
run_plugins(PLUGIN_KILL_SESSION, &data);
}
- // RADIUS Stop message
if (session[s].opened && !walled_garden && !session[s].die)
{
+ // RADIUS Stop message
u16 r = session[s].radius;
if (!r)
{
radius[r].auth[n] = rand();
}
}
+
if (r && radius[r].state != RADIUSSTOP)
radiussend(r, RADIUSSTOP); // stop, if not already trying
+
+ // Save counters to dump to accounting file
+ if (*config->accounting_dir && shut_acct_n < sizeof(shut_acct) / sizeof(*shut_acct))
+ memcpy(&shut_acct[shut_acct_n++], &session[s], sizeof(session[s]));
}
if (session[s].ip)
// 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, 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
}
case 39: // seq required - we control it as an LNS anyway...
break;
case 36: // Random Vector
- LOG(4, s, t, " Random Vector received. Enabled AVP Hiding.\n");
+ LOG(4, s, t, " Random Vector received. Enabled AVP Hiding.\n");
memset(session[s].random_vector, 0, sizeof(session[s].random_vector));
memcpy(session[s].random_vector, b, n);
session[s].random_vector_length = n;
return;
}
- if (session[s].die)
- {
- LOG(3, s, t, "Session %d is closing. Don't process PPP packets\n", s);
-// I'm pretty sure this isn't right -- mo.
-// return; // closing session, PPP not processed
- }
if (prot == PPPPAP)
{
session[s].last_packet = time_now;
}
else if (prot == PPPIP)
{
+ if (session[s].die)
+ {
+ LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s);
+ return; // closing session, PPP not processed
+ }
+
session[s].last_packet = time_now;
if (session[s].walled_garden && !config->cluster_iam_master)
{
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
return;
}
+
processipin(t, s, p, l);
}
else
int count=0,i;
u16 r;
static clockt next_acct = 0;
+ static clockt next_shut_acct = 0;
int a;
LOG(3, 0, 0, "Begin regular cleanup\n");
// Drop sessions who have not responded within IDLE_TIMEOUT seconds
if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
{
- sessionkill(s, "No response to LCP ECHO requests");
+ sessionshutdown(s, "No response to LCP ECHO requests");
STAT(session_timeout);
if (++count >= MAX_ACTIONS) break;
continue;
}
}
- if (*config->accounting_dir && next_acct <= TIME)
+ if (*config->accounting_dir)
{
- // Dump accounting data
- next_acct = TIME + ACCT_TIME;
- dump_acct_info();
+ if (next_acct <= TIME)
+ {
+ // Dump accounting data
+ next_acct = TIME + ACCT_TIME;
+ next_shut_acct = TIME + ACCT_SHUT_TIME;
+ dump_acct_info(1);
+ }
+ else if (next_shut_acct <= TIME)
+ {
+ // Dump accounting data for shutdown sessions
+ next_shut_acct = TIME + ACCT_SHUT_TIME;
+ if (shut_acct_n)
+ dump_acct_info(0);
+ }
}
if (count >= MAX_ACTIONS)
// We stop waiting for radius after BUSY_WAIT_TIME 1/10th seconds
if (abs(TIME - start_busy_wait) > BUSY_WAIT_TIME)
{
- LOG(1, 0, 0, "Giving up waiting for RADIUS to be empty. Shutting down anyway.\n");
+ LOG(1, 0, 0, "Giving up waiting for RADIUS to be empty. Shutting down anyway.\n");
return 0;
}
clockt next_cluster_ping = 0; // send initial ping immediately
time_t next_clean = time_now + config->cleanup_interval;
- LOG(4, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
+ LOG(4, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
udpfd, tunfd, cluster_sockfd, controlfd);
FD_ZERO(&readset);
else if (n)
{
struct sockaddr_in addr;
- int alen = sizeof(addr);
- if (FD_ISSET(udpfd, &r))
- {
- int c, n;
- for (c = 0; c < config->multi_read_count; c++)
- {
- if ((n = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
- processudp(buf, n, &addr);
- else
- break;
- }
- }
- if (FD_ISSET(tunfd, &r))
+ int alen, c, s;
+
+ // nsctl commands
+ if (FD_ISSET(controlfd, &r))
{
- int c, n;
- for (c = 0; c < config->multi_read_count; c++)
- {
- if ((n = read(tunfd, buf, sizeof(buf))) > 0)
- processtun(buf, n);
- else
- break;
- }
+ alen = sizeof(addr);
+ processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+ n--;
}
+ // RADIUS responses
if (config->cluster_iam_master)
+ {
for (i = 0; i < config->num_radfds; i++)
+ {
if (FD_ISSET(radfds[i], &r))
+ {
processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
+ n--;
+ }
+ }
+ }
- if (FD_ISSET(cluster_sockfd, &r))
+ // CLI connections
+ if (FD_ISSET(clifd, &r))
{
- int size;
- size = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen);
- processcluster(buf, size, addr.sin_addr.s_addr);
+ int cli;
+
+ alen = sizeof(addr);
+ if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0)
+ {
+ cli_do(cli);
+ close(cli);
+ }
+ else
+ LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
+
+ n--;
}
- if (FD_ISSET(controlfd, &r))
- processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+#ifdef BGP
+ for (i = 0; i < BGP_NUM_PEERS; i++)
+ {
+ int isr = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0;
+ int isw = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0;
+ bgp_process(&bgp_peers[i], isr, isw);
+ if (isr) n--;
+ if (isw) n--;
+ }
+#endif /* BGP */
- if (FD_ISSET(clifd, &r))
+ for (c = 0; n && c < config->multi_read_count; c++)
{
- struct sockaddr_in addr;
- int sockfd;
- int len = sizeof(addr);
+ // L2TP
+ if (FD_ISSET(udpfd, &r))
+ {
+ alen = sizeof(addr);
+ if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
+ {
+ processudp(buf, s, &addr);
+ }
+ else
+ {
+ FD_CLR(udpfd, &r);
+ n--;
+ }
+ }
- if ((sockfd = accept(clifd, (struct sockaddr *)&addr, &len)) <= 0)
+ // incoming IP
+ if (FD_ISSET(tunfd, &r))
{
- LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
- continue;
+ if ((s = read(tunfd, buf, sizeof(buf))) > 0)
+ {
+ processtun(buf, s);
+ }
+ else
+ {
+ FD_CLR(tunfd, &r);
+ n--;
+ }
}
- else
+
+ // cluster
+ if (FD_ISSET(cluster_sockfd, &r))
{
- cli_do(sockfd);
- close(sockfd);
+ alen = sizeof(addr);
+ if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
+ {
+ processcluster(buf, s, addr.sin_addr.s_addr);
+ }
+ else
+ {
+ FD_CLR(cluster_sockfd, &r);
+ n--;
+ }
}
}
}
next_clean = time_now + config->cleanup_interval; // Did. Move to next interval.
}
}
-
-#ifdef BGP
- for (i = 0; i < BGP_NUM_PEERS; i++)
- {
- bgp_process(&bgp_peers[i],
- bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0,
- bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0);
- }
-#endif /* BGP */
}
// Are we the master and shutting down??
STAT(packets_snooped);
}
-static void dump_acct_info()
+static int dump_session(FILE **f, sessiont *s)
+{
+ if (!s->opened || !s->ip || !(s->cin || s->cout) || !*s->user || s->walled_garden)
+ return 1;
+
+ if (!*f)
+ {
+ char filename[1024];
+ char timestr[64];
+ time_t now = time(NULL);
+
+ strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(&now));
+ snprintf(filename, sizeof(filename), "%s/%s", config->accounting_dir, timestr);
+
+ if (!(*f = fopen(filename, "w")))
+ {
+ LOG(0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno));
+ return 0;
+ }
+
+ LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
+ fprintf(*f, "# dslwatch.pl dump file V1.01\n"
+ "# host: %s\n"
+ "# time: %ld\n"
+ "# uptime: %ld\n"
+ "# format: username ip qos uptxoctets downrxoctets\n",
+ hostname,
+ now,
+ now - basetime);
+ }
+
+ LOG(4, 0, 0, "Dumping accounting information for %s\n", s->user);
+ fprintf(*f, "%s %s %d %u %u\n",
+ s->user, // username
+ fmtaddr(htonl(s->ip), 0), // ip
+ (s->throttle_in || s->throttle_out) ? 2 : 1, // qos
+ (u32) s->cin, // uptxoctets
+ (u32) s->cout); // downrxoctets
+
+ s->pin = s->cin = 0;
+ s->pout = s->cout = 0;
+
+ return 1;
+}
+
+static void dump_acct_info(int all)
{
- char filename[1024];
- char timestr[64];
- time_t t = time(NULL);
int i;
FILE *f = NULL;
CSTAT(call_dump_acct_info);
- strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t));
- snprintf(filename, 1024, "%s/%s", config->accounting_dir, timestr);
-
- for (i = 0; i < MAXSESSION; i++)
+ if (shut_acct_n)
{
- if (!session[i].opened || !session[i].ip || !(session[i].cin || session[i].cout) || !*session[i].user || session[i].walled_garden)
- continue;
- if (!f)
- {
- time_t now = time(NULL);
- if (!(f = fopen(filename, "w")))
- {
- LOG(0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno));
- return ;
- }
- LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
- fprintf(f, "# dslwatch.pl dump file V1.01\n"
- "# host: %s\n"
- "# time: %ld\n"
- "# uptime: %ld\n"
- "# format: username ip qos uptxoctets downrxoctets\n",
- hostname,
- now,
- now - basetime);
- }
+ for (i = 0; i < shut_acct_n; i++)
+ dump_session(&f, &shut_acct[i]);
- LOG(4, 0, 0, "Dumping accounting information for %s\n", session[i].user);
- fprintf(f, "%s %s %d %u %u\n",
- session[i].user, // username
- fmtaddr(htonl(session[i].ip), 0), // ip
- (session[i].throttle_in || session[i].throttle_out) ? 2 : 1, // qos
- (u32)session[i].cin, // uptxoctets
- (u32)session[i].cout); // downrxoctets
-
- session[i].pin = session[i].cin = 0;
- session[i].pout = session[i].cout = 0;
+ shut_acct_n = 0;
}
+ if (all)
+ for (i = 1; i <= config->cluster_highest_sessionid; i++)
+ dump_session(&f, &session[i]);
+
if (f)
fclose(f);
}
{
if ((ret = sched_setscheduler(0, SCHED_FIFO, ¶ms)) == 0)
{
- LOG(1, 0, 0, "Using FIFO scheduler. Say goodbye to any other processes running\n");
+ LOG(1, 0, 0, "Using FIFO scheduler. Say goodbye to any other processes running\n");
}
else
{
int i;
LOG(1, 0, 0, "Shutting down without saving sessions\n");
- for (i = 1; i < MAXSESSION; i++)
- {
- if (session[i].opened)
- sessionkill(i, "L2TPNS Closing");
- }
- for (i = 1; i < MAXTUNNEL; i++)
+
+ if (config->cluster_iam_master)
{
- if (tunnel[i].ip || tunnel[i].state)
- tunnelshutdown(i, "L2TPNS Closing");
+ for (i = 1; i < MAXSESSION; i++)
+ {
+ if (session[i].opened)
+ sessionkill(i, "L2TPNS Closing");
+ }
+ for (i = 1; i < MAXTUNNEL; i++)
+ {
+ if (tunnel[i].ip || tunnel[i].state)
+ tunnelshutdown(i, "L2TPNS Closing");
+ }
}
main_quit++;
assign_ip_address(s);
if (!session[s].ip)
{
- LOG(0, s, t, " No IP allocated. The IP address pool is FULL!\n");
+ LOG(0, s, t, " No IP allocated. The IP address pool is FULL!\n");
sessionshutdown(s, "No IP addresses available");
return 0;
}
- LOG(3, s, t, " No IP allocated. Assigned %s from pool\n",
+ LOG(3, s, t, " No IP allocated. Assigned %s from pool\n",
fmtaddr(htonl(session[s].ip), 0));
}