From: David Parrish Date: Fri, 5 Mar 2004 00:09:03 +0000 (+0000) Subject: * Fri Mar 5 2004 David Parrish 1.1.0 X-Git-Tag: 2.2.1-2fdn3.1~19^2^2~1^2~479 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/fc0a36320874bea43b9fd73df0e0990bfd3b59cd?hp=7c1104efffac3af9f068ace3834f2720518ff54a * Fri Mar 5 2004 David Parrish 1.1.0 - Change all strcpy() calls to strncpy() to avoid buffer overflow potential - Add ICMP host unreachable support - Logging to syslog if log_file = "syslog:facility" - Now requires libcli 1.5 - All configuration moves to a config structure - Ability to modify and write config on the fly through command-line interface - Config file support is removed, and now handled by the cli - Show hostname in cli prompt - Keep current state type for tunnels - Add uptime command do CLI, which also shows real-time bandwidth utilisation - Add goodbye command to cluster master, which forces droppping a slave - Cache IP address allocation, so that reconnecting users get the same address - Fix tunnel resend timeouts, so that dead tunnels will be cleaned up - Allocate tunnels and radius without using a linked list which had issues - Fix some off-by-one errors in tunnel and session and radius arrays - Save and reload ip address pool when dieing - Check version and size of reloaded data when restarting - Remove plugin_config support - Remove old support for TBF which didn't work anyway. HTB is required to do throttling now. - Add COPYING and Changes files --- diff --git a/Changes b/Changes new file mode 100644 index 0000000..e2731d8 --- /dev/null +++ b/Changes @@ -0,0 +1,22 @@ +* Fri Mar 5 2004 David Parrish 1.1.0 +- Change all strcpy() calls to strncpy() to avoid buffer overflow potential +- Add ICMP host unreachable support +- Logging to syslog if log_file = "syslog:facility" +- Now requires libcli 1.5 +- All configuration moves to a config structure +- Ability to modify and write config on the fly through command-line interface +- Config file support is removed, and now handled by the cli +- Show hostname in cli prompt +- Keep current state type for tunnels +- Add uptime command do CLI, which also shows real-time bandwidth utilisation +- Add goodbye command to cluster master, which forces droppping a slave +- Cache IP address allocation, so that reconnecting users get the same address +- Fix tunnel resend timeouts, so that dead tunnels will be cleaned up +- Allocate tunnels and radius without using a linked list which had issues +- Fix some off-by-one errors in tunnel and session and radius arrays +- Save and reload ip address pool when dieing +- Check version and size of reloaded data when restarting +- Remove plugin_config support +- Remove old support for TBF which didn't work anyway. HTB is required to do throttling now. +- Add COPYING and Changes files + diff --git a/INSTALL b/INSTALL index 4ccf61f..22f03a9 100644 --- a/INSTALL +++ b/INSTALL @@ -2,9 +2,8 @@ Brief Installation guide for L2TPNS 1. Requirements - * You must have libcli installed to enable the command-line - interface. You can get it from http://sourceforge.net/projects/libcli. - If you don't have it, command-line support will not be compiled in. + * libcli 1.5.0 or greater + You can get it from http://sourceforge.net/projects/libcli. * A kernel with iptables support diff --git a/Makefile.in b/Makefile.in index 404a715..7055f58 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,6 +16,7 @@ INSTALL = @INSTALL@ DEFS = @DEFS@ OBJS= md5.o \ + icmp.o \ cli.o \ l2tpns.o \ ppp.o \ diff --git a/arp.c b/arp.c index 122880a..05929cd 100644 --- a/arp.c +++ b/arp.c @@ -42,15 +42,16 @@ void sendarp(int ifr_idx, const unsigned char* mac, ipt ip) memcpy(&buf.ar_sip, &ip, sizeof(ip)); memcpy(buf.ar_tha, mac, ETH_ALEN); memcpy(&buf.ar_tip, &ip, sizeof(ip)); - + /* Now actually send the thing */ fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP)); memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; - strncpy(sll.sll_addr, mac, sizeof(sll.sll_addr)); /* Null-pad */ + strncpy(sll.sll_addr, mac, sizeof(sll.sll_addr) - 1); sll.sll_halen = ETH_ALEN; sll.sll_ifindex = ifr_idx; sendto(fd, &buf, sizeof(buf), 0, (struct sockaddr*)&sll, sizeof(sll)); + close(fd); } diff --git a/bounce.c b/bounce.c deleted file mode 100644 index 9d3f334..0000000 --- a/bounce.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PORT 39000 - -void sigalarm(int junk); -unsigned long long recv_count = 0; -unsigned long pps = 0; -unsigned long bytes = 0; -unsigned port = PORT; - -int main(int argc, char *argv[]) -{ - int on = 1; - struct sockaddr_in addr; - int s; - char *packet; - - while ((s = getopt(argc, argv, "?p:")) > 0) - { - switch (s) - { - case 'p' : - port = atoi(optarg); - break; - case '?' : - printf("Options:\n"); - printf("\t-p port to listen on\n"); - return(0); - break; - } - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (bind(s, (void *) &addr, sizeof(addr)) < 0) - { - perror("bind"); - return -1; - } - - signal(SIGALRM, sigalarm); - alarm(1); - - printf("Waiting on port %d\n", port); - packet = (char *)malloc(65535); - while (1) - { - struct sockaddr_in addr; - int alen = sizeof(addr), l; - - l = recvfrom(s, packet, 65535, 0, (void *) &addr, &alen); - if (l < 0) continue; - recv_count++; - pps++; - bytes += l; - - sendto(s, packet, l, 0, (struct sockaddr *)&addr, alen); - } - - free(packet); -} - -void sigalarm(int junk) -{ - printf("Recv: %10llu %0.1fMbits/s (%lu pps)\n", recv_count, (bytes / 1024.0 / 1024.0 * 8), pps); - pps = bytes = 0; - alarm(1); -} - diff --git a/cli.c b/cli.c index 3aafb10..af73a6c 100644 --- a/cli.c +++ b/cli.c @@ -1,10 +1,11 @@ // L2TPNS Command Line Interface -// $Id: cli.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: cli.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ // vim: sw=4 ts=8 #include #include #include +#include #include #include #include @@ -15,12 +16,9 @@ #include #include #include -#include #include "l2tpns.h" +#include "libcli.h" #include "util.h" -#include "config.h" - -#ifdef HAVE_LIBCLI extern tunnelt *tunnel; extern sessiont *session; @@ -34,11 +32,14 @@ extern int clifd, udpfd, tapfd, snoopfd, radfd, ifrfd, cluster_sockfd; extern sessionidt *cli_session_kill; extern tunnelidt *cli_tunnel_kill; extern tbft *filter_buckets; +extern struct configt *config; +extern struct config_descriptt config_values[]; +extern char hostname[]; #ifdef RINGBUFFER extern struct Tringbuffer *ringbuffer; #endif -char *rcs_id = "$Id: cli.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $"; +char *rcs_id = "$Id: cli.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $"; char *debug_levels[] = { "CRIT", @@ -62,27 +63,36 @@ struct int debug_session; int debug_tunnel; int debug_rb_tail; - -int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_show_users(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_show_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_show_banana(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc); -int regular_stuff(struct cli_def *cli, FILE *w); +FILE *save_config_fh; + +int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_watch_session(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_watch_tunnel(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_set(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc); +int regular_stuff(struct cli_def *cli); void init_cli() { @@ -95,38 +105,57 @@ void init_cli() cli = cli_init(); c = cli_register_command(cli, NULL, "show", NULL, NULL); - cli_register_command(cli, c, "session", cmd_show_session, "Show a list of sessions"); - cli_register_command(cli, c, "tunnels", cmd_show_tunnels, NULL); - cli_register_command(cli, c, "users", cmd_show_users, NULL); - cli_register_command(cli, c, "version", cmd_show_version, NULL); + cli_register_command(cli, c, "session", cmd_show_session, "Show a list of sessions or details for a single session"); + cli_register_command(cli, c, "tunnels", cmd_show_tunnels, "Show a list of tunnels or details for a single tunnel"); + cli_register_command(cli, c, "users", cmd_show_users, "Show a list of all connected users"); + cli_register_command(cli, c, "version", cmd_show_version, "Show currently running software version"); cli_register_command(cli, c, "banana", cmd_show_banana, "Show a banana"); - cli_register_command(cli, c, "pool", cmd_show_pool, NULL); + cli_register_command(cli, c, "pool", cmd_show_pool, "Show the IP address allocation pool"); + cli_register_command(cli, c, "running-config", cmd_show_run, "Show the currently running configuration"); + cli_register_command(cli, c, "radius", cmd_show_radius, "Show active radius queries"); + cli_register_command(cli, c, "plugins", cmd_show_plugins, "List all installed plugins"); #ifdef STATISTICS - cli_register_command(cli, c, "counters", cmd_show_counters, NULL); + cli_register_command(cli, c, "counters", cmd_show_counters, "Display all the internal counters and running totals"); c = cli_register_command(cli, NULL, "clear", NULL, NULL); - cli_register_command(cli, c, "counters", cmd_clear_counters, NULL); + cli_register_command(cli, c, "counters", cmd_clear_counters, "Clear internal counters"); #endif - cli_register_command(cli, NULL, "snoop", cmd_snoop, NULL); - cli_register_command(cli, NULL, "throttle", cmd_throttle, NULL); + cli_register_command(cli, NULL, "uptime", cmd_uptime, "Show uptime and bandwidth utilisation"); + + c = cli_register_command(cli, NULL, "write", NULL, NULL); + cli_register_command(cli, c, "memory", cmd_write_memory, "Save the running config to flash"); + cli_register_command(cli, c, "terminal", cmd_show_run, "Show the running config"); + + cli_register_command(cli, NULL, "snoop", cmd_snoop, "Temporarily enable interception for a user"); + cli_register_command(cli, NULL, "throttle", cmd_throttle, "Temporarily enable throttling for a user"); c = cli_register_command(cli, NULL, "no", NULL, NULL); - cli_register_command(cli, c, "snoop", cmd_no_snoop, NULL); - cli_register_command(cli, c, "throttle", cmd_no_throttle, NULL); - cli_register_command(cli, c, "debug", cmd_no_debug, NULL); + cli_register_command(cli, c, "snoop", cmd_no_snoop, "Temporarily disable interception for a user"); + cli_register_command(cli, c, "throttle", cmd_no_throttle, "Temporarily disable throttling for a user"); + cli_register_command(cli, c, "debug", cmd_no_debug, "Turn off logging of a certain level of debugging"); c = cli_register_command(cli, NULL, "drop", NULL, NULL); - cli_register_command(cli, c, "user", cmd_drop_user, NULL); - cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, NULL); - cli_register_command(cli, c, "session", cmd_drop_session, NULL); + cli_register_command(cli, c, "user", cmd_drop_user, "Disconnect a user"); + cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, "Disconnect a tunnel and all sessions on that tunnel"); + cli_register_command(cli, c, "session", cmd_drop_session, "Disconnect a session"); - cli_register_command(cli, NULL, "debug", cmd_debug, "Specify a debugging level"); + cli_register_command(cli, NULL, "debug", cmd_debug, "Set the level of logging that is shown on the console"); + /* c = cli_register_command(cli, NULL, "watch", NULL, NULL); - cli_register_command(cli, c, "session", cmd_watch_session, "Dump logs for a tunnel"); + cli_register_command(cli, c, "session", cmd_watch_session, "Dump logs for a session"); cli_register_command(cli, c, "tunnel", cmd_watch_tunnel, "Dump logs for a tunnel"); + */ + + c = cli_register_command(cli, NULL, "load", NULL, NULL); + cli_register_command(cli, c, "plugin", cmd_load_plugin, "Load a plugin"); + + c = cli_register_command(cli, NULL, "remove", NULL, NULL); + cli_register_command(cli, c, "plugin", cmd_remove_plugin, "Remove a plugin"); + + cli_register_command(cli, NULL, "set", cmd_set, "Set a configuration variable"); // Enable regular processing cli_regular(cli, regular_stuff); @@ -173,7 +202,7 @@ void init_cli() void cli_do(int sockfd) { - if ((cli_pid = fork())) return; + if (fork()) return; // Close sockets if (udpfd) close(udpfd); udpfd = 0; @@ -202,14 +231,31 @@ void cli_do(int sockfd) memset(&debug_flags, 0, sizeof(debug_flags)); debug_flags.critical = 1; - cli_loop(cli, sockfd, "l2tpns> "); + { + char prompt[1005]; + snprintf(prompt, 1005, "%s> ", hostname); + cli_loop(cli, sockfd, prompt); + } close(sockfd); log(3, 0, 0, 0, "Closed CLI connection\n"); exit(0); } -int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +void cli_print_log(struct cli_def *cli, char *string) +{ + log(3, 0, 0, 0, "%s\n", string); +} + +void cli_do_file(FILE *fh) +{ + log(3, 0, 0, 0, "Reading configuration file\n"); + cli_print_callback(cli, cli_print_log); + cli_file(cli, fh); + cli_print_callback(cli, NULL); +} + +int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) { int i; time_t time_now; @@ -224,34 +270,34 @@ int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, i s = atoi(argv[i]); if (!s || s > MAXSESSION) { - fprintf(w, "Invalid session id \"%s\"\r\n", argv[i]); + cli_print(cli, "Invalid session id \"%s\"", argv[i]); continue; } - fprintf(w, "\r\nSession %d:\r\n", s); - fprintf(w, " User: %s\r\n", session[s].user[0] ? session[s].user : "none"); - fprintf(w, " Calling Num: %s\r\n", session[s].calling); - fprintf(w, " Called Num: %s\r\n", session[s].called); - fprintf(w, " Tunnel ID: %d\r\n", session[s].tunnel); - fprintf(w, " IP address: %s\r\n", inet_toa(htonl(session[s].ip))); - fprintf(w, " HSD sid: %lu\r\n", session[s].sid); - fprintf(w, " Idle time: %u seconds\r\n", abs(time_now - session[s].last_packet)); - fprintf(w, " Next Recv: %u\r\n", session[s].nr); - fprintf(w, " Next Send: %u\r\n", session[s].ns); - fprintf(w, " Bytes In/Out: %lu/%lu\r\n", (unsigned long)session[s].cin, (unsigned long)session[s].cout); - fprintf(w, " Pkts In/Out: %lu/%lu\r\n", (unsigned long)session[s].pin, (unsigned long)session[s].pout); - fprintf(w, " Radius Session: %u\r\n", session[s].radius); - fprintf(w, " Rx Speed: %lu\r\n", session[s].rx_connect_speed); - fprintf(w, " Tx Speed: %lu\r\n", session[s].tx_connect_speed); - fprintf(w, " Intercepted: %s\r\n", session[s].snoop ? "YES" : "no"); - fprintf(w, " Throttled: %s\r\n", session[s].throttle ? "YES" : "no"); - fprintf(w, " Walled Garden: %s\r\n", session[s].walled_garden ? "YES" : "no"); - fprintf(w, " Filter Bucket: %s\r\n", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none"); + cli_print(cli, "\r\nSession %d:", s); + cli_print(cli, " User: %s", session[s].user[0] ? session[s].user : "none"); + cli_print(cli, " Calling Num: %s", session[s].calling); + cli_print(cli, " Called Num: %s", session[s].called); + cli_print(cli, " Tunnel ID: %d", session[s].tunnel); + cli_print(cli, " IP address: %s", inet_toa(htonl(session[s].ip))); + cli_print(cli, " HSD sid: %lu", session[s].sid); + cli_print(cli, " Idle time: %u seconds", abs(time_now - session[s].last_packet)); + cli_print(cli, " Next Recv: %u", session[s].nr); + cli_print(cli, " Next Send: %u", session[s].ns); + cli_print(cli, " Bytes In/Out: %lu/%lu", (unsigned long)session[s].cin, (unsigned long)session[s].total_cout); + cli_print(cli, " Pkts In/Out: %lu/%lu", (unsigned long)session[s].pin, (unsigned long)session[s].pout); + cli_print(cli, " Radius Session: %u", session[s].radius); + cli_print(cli, " Rx Speed: %lu", session[s].rx_connect_speed); + cli_print(cli, " Tx Speed: %lu", session[s].tx_connect_speed); + cli_print(cli, " Intercepted: %s", session[s].snoop ? "YES" : "no"); + cli_print(cli, " Throttled: %s", session[s].throttle ? "YES" : "no"); + cli_print(cli, " Servicenet: %s", session[s].servicenet ? "YES" : "no"); + cli_print(cli, " Filter Bucket: %s", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none"); } return CLI_OK; } // Show Summary - fprintf(w, " %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s\r\n", + cli_print(cli, " %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s", "SID", "TID", "Username", @@ -265,23 +311,23 @@ int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, i "idle", "LAC", "CLI"); - for (i = 0; i < MAXSESSION; i++) + for (i = 1; i < MAXSESSION; i++) { char *userip, *tunnelip; if (!session[i].opened) continue; userip = strdup(inet_toa(htonl(session[i].ip))); tunnelip = strdup(inet_toa(htonl(tunnel[ session[i].tunnel ].ip))); - fprintf(w, "%5d %4d %-32s %-15s %s %s %s %10lu %10lu %10lu %4u %-15s %s\r\n", + cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %10u %10lu %10lu %4u %-15s %s", i, session[i].tunnel, session[i].user[0] ? session[i].user : "*", userip, (session[i].snoop) ? "Y" : "N", (session[i].throttle) ? "Y" : "N", - (session[i].walled_garden) ? "Y" : "N", - (unsigned long)session[i].opened, - (unsigned long)session[i].cout, - (unsigned long)session[i].cin, + (session[i].servicenet) ? "Y" : "N", + abs(time_now - (unsigned long)session[i].opened), + (unsigned long)session[i].total_cout, + (unsigned long)session[i].total_cin, abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)), tunnelip, session[i].calling[0] ? session[i].calling : "*"); @@ -291,235 +337,381 @@ int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, i return CLI_OK; } -int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc) { - int i, x; + int i, x, show_all = 0; time_t time_now; + char *states[] = { + "Free", + "Open", + "Closing", + "Opening", + }; time(&time_now); if (argc > 0) { - // Show individual tunnel - for (i = 0; i < argc; i++) + if (strcmp(argv[0], "all") == 0) { - char s[65535] = {0}; - unsigned int t; - t = atoi(argv[i]); - if (!t || t > MAXTUNNEL) + show_all = 1; + } + else + { + // Show individual tunnel + for (i = 0; i < argc; i++) { - fprintf(w, "Invalid tunnel id \"%s\"\r\n", argv[i]); - continue; + char s[65535] = {0}; + unsigned int t; + t = atoi(argv[i]); + if (!t || t > MAXTUNNEL) + { + cli_print(cli, "Invalid tunnel id \"%s\"", argv[i]); + continue; + } + cli_print(cli, "\r\nTunnel %d:", t); + cli_print(cli, " State: %s", states[tunnel[t].state]); + cli_print(cli, " Hostname: %s", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)"); + cli_print(cli, " Remote IP: %s", inet_toa(htonl(tunnel[t].ip))); + cli_print(cli, " Remote Port: %d", tunnel[t].port); + cli_print(cli, " Rx Window: %u", tunnel[t].window); + cli_print(cli, " Next Recv: %u", tunnel[t].nr); + cli_print(cli, " Next Send: %u", tunnel[t].ns); + cli_print(cli, " Queue Len: %u", tunnel[t].controlc); + cli_print(cli, " Last Packet Age:%u", (unsigned)(time_now - tunnel[t].last)); + + for (x = 0; x < MAXSESSION; x++) + if (session[x].tunnel == t && session[x].opened && !session[x].die) + sprintf(s, "%s%u ", s, x); + cli_print(cli, " Sessions: %s", s); } - fprintf(w, "\r\nTunnel %d:\r\n", t); - fprintf(w, " Hostname: %s\r\n", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)"); - fprintf(w, " Remote IP: %s\r\n", inet_toa(htonl(tunnel[t].ip))); - fprintf(w, " Remote Port: %d\r\n", tunnel[t].port); - fprintf(w, " Rx Window: %u\r\n", tunnel[t].window); - fprintf(w, " Next Recv: %u\r\n", tunnel[t].nr); - fprintf(w, " Next Send: %u\r\n", tunnel[t].ns); - fprintf(w, " Queue Len: %u\r\n", tunnel[t].controlc); - fprintf(w, " Last Packet Age:%u\r\n", (unsigned)(time_now - tunnel[t].last)); - - for (x = 0; x < MAXSESSION; x++) - if (session[x].tunnel == t && session[x].opened && !session[x].die) - sprintf(s, "%s%u ", s, x); - fprintf(w, " Sessions: %s\r\n", s); + return CLI_OK; } - return CLI_OK; } // Show tunnel summary - fprintf(w, "%s %s %s %s\r\n", + cli_print(cli, "%s %s %s %s %s", "TID", "Hostname", "IP", + "State", "Sessions"); - for (i = 0; i < MAXTUNNEL; i++) + for (i = 1; i < MAXTUNNEL; i++) { int sessions = 0; - if (!tunnel[i].ip || tunnel[i].die || !tunnel[i].hostname[0]) continue; + if (!show_all && (!tunnel[i].ip || tunnel[i].die || !tunnel[i].hostname[0])) continue; for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++; - fprintf(w, "%d %s %s %d\r\n", + cli_print(cli, "%d %s %s %s %d", i, - tunnel[i].hostname, + *tunnel[i].hostname ? tunnel[i].hostname : "(null)", inet_toa(htonl(tunnel[i].ip)), + states[tunnel[i].state], sessions); } return CLI_OK; } -int cmd_show_users(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc) { int i; for (i = 0; i < MAXSESSION; i++) { if (!session[i].opened) continue; if (!session[i].user[0]) continue; - fprintf(w, "%s\r\n", + cli_print(cli, "%s", session[i].user); } return CLI_OK; } -int cmd_show_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) { - fprintf(w, "%-10s %-8s %-10s %-8s\r\n", "Ethernet", "Bytes", "Packets", "Errors"); - fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "RX", + cli_print(cli, "%-10s %-8s %-10s %-8s", "Ethernet", "Bytes", "Packets", "Errors"); + cli_print(cli, "%-10s %8lu %8lu %8lu", "RX", GET_STAT(tap_rx_bytes), GET_STAT(tap_rx_packets), GET_STAT(tap_rx_errors)); - fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "TX", + cli_print(cli, "%-10s %8lu %8lu %8lu", "TX", GET_STAT(tap_tx_bytes), GET_STAT(tap_tx_packets), GET_STAT(tap_tx_errors)); - fprintf(w, "\r\n"); + cli_print(cli, ""); - fprintf(w, "%-10s %-8s %-10s %-8s %-8s\r\n", "Tunnel", "Bytes", "Packets", "Errors", "Retries"); - fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "RX", + cli_print(cli, "%-10s %-8s %-10s %-8s %-8s", "Tunnel", "Bytes", "Packets", "Errors", "Retries"); + cli_print(cli, "%-10s %8lu %8lu %8lu %8lu", "RX", GET_STAT(tunnel_rx_bytes), GET_STAT(tunnel_rx_packets), GET_STAT(tunnel_rx_errors), 0L); - fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "TX", + cli_print(cli, "%-10s %8lu %8lu %8lu %8lu", "TX", GET_STAT(tunnel_tx_bytes), GET_STAT(tunnel_tx_packets), GET_STAT(tunnel_rx_errors), GET_STAT(tunnel_retries)); - fprintf(w, "\r\n"); - - fprintf(w, "%-30s%-10s\r\n", "Counter", "Value"); - fprintf(w, "-----------------------------------------\r\n"); - fprintf(w, "%-30s%lu\r\n", "radius_retries", GET_STAT(radius_retries)); - fprintf(w, "%-30s%lu\r\n", "arp_errors", GET_STAT(arp_errors)); - fprintf(w, "%-30s%lu\r\n", "arp_replies", GET_STAT(arp_replies)); - fprintf(w, "%-30s%lu\r\n", "arp_discarded", GET_STAT(arp_discarded)); - fprintf(w, "%-30s%lu\r\n", "arp_sent", GET_STAT(arp_sent)); - fprintf(w, "%-30s%lu\r\n", "arp_recv", GET_STAT(arp_recv)); - fprintf(w, "%-30s%lu\r\n", "packets_snooped", GET_STAT(packets_snooped)); - fprintf(w, "%-30s%lu\r\n", "tunnel_created", GET_STAT(tunnel_created)); - fprintf(w, "%-30s%lu\r\n", "session_created", GET_STAT(session_created)); - fprintf(w, "%-30s%lu\r\n", "tunnel_timeout", GET_STAT(tunnel_timeout)); - fprintf(w, "%-30s%lu\r\n", "session_timeout", GET_STAT(session_timeout)); - fprintf(w, "%-30s%lu\r\n", "radius_timeout", GET_STAT(radius_timeout)); - fprintf(w, "%-30s%lu\r\n", "radius_overflow", GET_STAT(radius_overflow)); - fprintf(w, "%-30s%lu\r\n", "tunnel_overflow", GET_STAT(tunnel_overflow)); - fprintf(w, "%-30s%lu\r\n", "session_overflow", GET_STAT(session_overflow)); - fprintf(w, "%-30s%lu\r\n", "ip_allocated", GET_STAT(ip_allocated)); - fprintf(w, "%-30s%lu\r\n", "ip_freed", GET_STAT(ip_freed)); + cli_print(cli, ""); + + cli_print(cli, "%-30s%-10s", "Counter", "Value"); + cli_print(cli, "-----------------------------------------"); + cli_print(cli, "%-30s%lu", "radius_retries", GET_STAT(radius_retries)); + cli_print(cli, "%-30s%lu", "arp_errors", GET_STAT(arp_errors)); + cli_print(cli, "%-30s%lu", "arp_replies", GET_STAT(arp_replies)); + cli_print(cli, "%-30s%lu", "arp_discarded", GET_STAT(arp_discarded)); + cli_print(cli, "%-30s%lu", "arp_sent", GET_STAT(arp_sent)); + cli_print(cli, "%-30s%lu", "arp_recv", GET_STAT(arp_recv)); + cli_print(cli, "%-30s%lu", "packets_snooped", GET_STAT(packets_snooped)); + cli_print(cli, "%-30s%lu", "tunnel_created", GET_STAT(tunnel_created)); + cli_print(cli, "%-30s%lu", "session_created", GET_STAT(session_created)); + cli_print(cli, "%-30s%lu", "tunnel_timeout", GET_STAT(tunnel_timeout)); + cli_print(cli, "%-30s%lu", "session_timeout", GET_STAT(session_timeout)); + cli_print(cli, "%-30s%lu", "radius_timeout", GET_STAT(radius_timeout)); + cli_print(cli, "%-30s%lu", "radius_overflow", GET_STAT(radius_overflow)); + cli_print(cli, "%-30s%lu", "tunnel_overflow", GET_STAT(tunnel_overflow)); + cli_print(cli, "%-30s%lu", "session_overflow", GET_STAT(session_overflow)); + cli_print(cli, "%-30s%lu", "ip_allocated", GET_STAT(ip_allocated)); + cli_print(cli, "%-30s%lu", "ip_freed", GET_STAT(ip_freed)); #ifdef STAT_CALLS - fprintf(w, "\n%-30s%-10s\r\n", "Counter", "Value"); - fprintf(w, "-----------------------------------------\r\n"); - fprintf(w, "%-30s%lu\r\n", "call_processtap", GET_STAT(call_processtap)); - fprintf(w, "%-30s%lu\r\n", "call_processarp", GET_STAT(call_processarp)); - fprintf(w, "%-30s%lu\r\n", "call_processipout", GET_STAT(call_processipout)); - fprintf(w, "%-30s%lu\r\n", "call_processudp", GET_STAT(call_processudp)); - fprintf(w, "%-30s%lu\r\n", "call_processpap", GET_STAT(call_processpap)); - fprintf(w, "%-30s%lu\r\n", "call_processchap", GET_STAT(call_processchap)); - fprintf(w, "%-30s%lu\r\n", "call_processlcp", GET_STAT(call_processlcp)); - fprintf(w, "%-30s%lu\r\n", "call_processipcp", GET_STAT(call_processipcp)); - fprintf(w, "%-30s%lu\r\n", "call_processipin", GET_STAT(call_processipin)); - fprintf(w, "%-30s%lu\r\n", "call_processccp", GET_STAT(call_processccp)); - fprintf(w, "%-30s%lu\r\n", "call_processrad", GET_STAT(call_processrad)); - fprintf(w, "%-30s%lu\r\n", "call_sendarp", GET_STAT(call_sendarp)); - fprintf(w, "%-30s%lu\r\n", "call_sendipcp", GET_STAT(call_sendipcp)); - fprintf(w, "%-30s%lu\r\n", "call_sendchap", GET_STAT(call_sendchap)); - fprintf(w, "%-30s%lu\r\n", "call_sessionbyip", GET_STAT(call_sessionbyip)); - fprintf(w, "%-30s%lu\r\n", "call_sessionbyuser", GET_STAT(call_sessionbyuser)); - fprintf(w, "%-30s%lu\r\n", "call_tunnelsend", GET_STAT(call_tunnelsend)); - fprintf(w, "%-30s%lu\r\n", "call_tunnelkill", GET_STAT(call_tunnelkill)); - fprintf(w, "%-30s%lu\r\n", "call_tunnelshutdown", GET_STAT(call_tunnelshutdown)); - fprintf(w, "%-30s%lu\r\n", "call_sessionkill", GET_STAT(call_sessionkill)); - fprintf(w, "%-30s%lu\r\n", "call_sessionshutdown", GET_STAT(call_sessionshutdown)); - fprintf(w, "%-30s%lu\r\n", "call_sessionsetup", GET_STAT(call_sessionsetup)); - fprintf(w, "%-30s%lu\r\n", "call_assign_ip_address",GET_STAT(call_assign_ip_address)); - fprintf(w, "%-30s%lu\r\n", "call_free_ip_address", GET_STAT(call_free_ip_address)); - fprintf(w, "%-30s%lu\r\n", "call_dump_acct_info", GET_STAT(call_dump_acct_info)); - fprintf(w, "%-30s%lu\r\n", "call_radiussend", GET_STAT(call_radiussend)); - fprintf(w, "%-30s%lu\r\n", "call_radiusretry", GET_STAT(call_radiusretry)); + cli_print(cli, "\n%-30s%-10s", "Counter", "Value"); + cli_print(cli, "-----------------------------------------"); + cli_print(cli, "%-30s%lu", "call_processtap", GET_STAT(call_processtap)); + cli_print(cli, "%-30s%lu", "call_processarp", GET_STAT(call_processarp)); + cli_print(cli, "%-30s%lu", "call_processipout", GET_STAT(call_processipout)); + cli_print(cli, "%-30s%lu", "call_processudp", GET_STAT(call_processudp)); + cli_print(cli, "%-30s%lu", "call_processpap", GET_STAT(call_processpap)); + cli_print(cli, "%-30s%lu", "call_processchap", GET_STAT(call_processchap)); + cli_print(cli, "%-30s%lu", "call_processlcp", GET_STAT(call_processlcp)); + cli_print(cli, "%-30s%lu", "call_processipcp", GET_STAT(call_processipcp)); + cli_print(cli, "%-30s%lu", "call_processipin", GET_STAT(call_processipin)); + cli_print(cli, "%-30s%lu", "call_processccp", GET_STAT(call_processccp)); + cli_print(cli, "%-30s%lu", "call_processrad", GET_STAT(call_processrad)); + cli_print(cli, "%-30s%lu", "call_sendarp", GET_STAT(call_sendarp)); + cli_print(cli, "%-30s%lu", "call_sendipcp", GET_STAT(call_sendipcp)); + cli_print(cli, "%-30s%lu", "call_sendchap", GET_STAT(call_sendchap)); + cli_print(cli, "%-30s%lu", "call_sessionbyip", GET_STAT(call_sessionbyip)); + cli_print(cli, "%-30s%lu", "call_sessionbyuser", GET_STAT(call_sessionbyuser)); + cli_print(cli, "%-30s%lu", "call_tunnelsend", GET_STAT(call_tunnelsend)); + cli_print(cli, "%-30s%lu", "call_tunnelkill", GET_STAT(call_tunnelkill)); + cli_print(cli, "%-30s%lu", "call_tunnelshutdown", GET_STAT(call_tunnelshutdown)); + cli_print(cli, "%-30s%lu", "call_sessionkill", GET_STAT(call_sessionkill)); + cli_print(cli, "%-30s%lu", "call_sessionshutdown", GET_STAT(call_sessionshutdown)); + cli_print(cli, "%-30s%lu", "call_sessionsetup", GET_STAT(call_sessionsetup)); + cli_print(cli, "%-30s%lu", "call_assign_ip_address",GET_STAT(call_assign_ip_address)); + cli_print(cli, "%-30s%lu", "call_free_ip_address", GET_STAT(call_free_ip_address)); + cli_print(cli, "%-30s%lu", "call_dump_acct_info", GET_STAT(call_dump_acct_info)); + cli_print(cli, "%-30s%lu", "call_radiussend", GET_STAT(call_radiussend)); + cli_print(cli, "%-30s%lu", "call_radiusretry", GET_STAT(call_radiusretry)); #endif return CLI_OK; } -int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc) { - fprintf(w, "L2TPNS %s\r\n", VERSION); - fprintf(w, "ID: %s\r\n", rcs_id); + cli_print(cli, "L2TPNS %s", VERSION); + cli_print(cli, "ID: %s", rcs_id); return CLI_OK; } -int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) + +int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc) { int i; - int used = 0, free = 0; + int used = 0, free = 0, show_all = 0; + time_t time_now; + + if (argc > 0 && strcmp(argv[0], "all") == 0) + show_all = 1; - fprintf(w, "%-15s %4s %8s %s\r\n", "IP Address", "Used", "Session", "User"); + time(&time_now); + cli_print(cli, "%-15s %4s %8s %s", "IP Address", "Used", "Session", "User"); for (i = 0; i < MAXIPPOOL; i++) { - sessionidt s = 0; - if (!ip_address_pool[i].address) continue; if (ip_address_pool[i].assigned) { + sessionidt s = sessionbyip(ip_address_pool[i].address); + cli_print(cli, "%-15s Y %8d %s", + inet_toa(ip_address_pool[i].address), s, session[s].user); + used++; - s = sessionbyip(ip_address_pool[i].address); } else { + if (ip_address_pool[i].last) + cli_print(cli, "%-15s N %8s [%s] %ds", + inet_toa(ip_address_pool[i].address), "", + ip_address_pool[i].user, time_now - ip_address_pool[i].last); + else if (show_all) + cli_print(cli, "%-15s N", inet_toa(ip_address_pool[i].address)); + free++; } + } + + if (!show_all) + cli_print(cli, "(Not displaying unused addresses)"); + + cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used); + return CLI_OK; +} + +void print_save_config(struct cli_def *cli, char *string) +{ + if (save_config_fh) + fprintf(save_config_fh, "%s\n", string); +} + +int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc) +{ + if ((save_config_fh = fopen(config->config_file, "w"))) + { + cli_print(cli, "Writing configuration"); + cli_print_callback(cli, print_save_config); + cmd_show_run(cli, command, argv, argc); + cli_print_callback(cli, NULL); + fclose(save_config_fh); + sleep(1); + } + else + { + cli_print(cli, "Error writing configuration: %s", strerror(errno)); + } + return CLI_OK; +} + +int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + + cli_print(cli, "# Current configuration:"); + + for (i = 0; config_values[i].key; i++) + { + void *value = ((void *)config) + config_values[i].offset; + if (config_values[i].type == STRING) + cli_print(cli, "set %s \"%.*s\"", config_values[i].key, config_values[i].size, (char *)value); + else if (config_values[i].type == IP) + cli_print(cli, "set %s %s", config_values[i].key, inet_toa(*(unsigned *)value)); + else if (config_values[i].type == SHORT) + cli_print(cli, "set %s %hu", config_values[i].key, *(short *)value); + else if (config_values[i].type == BOOL) + cli_print(cli, "set %s %s", config_values[i].key, (*(int *)value) ? "yes" : "no"); + else if (config_values[i].type == INT) + cli_print(cli, "set %s %d", config_values[i].key, *(int *)value); + else if (config_values[i].type == UNSIGNED_LONG) + cli_print(cli, "set %s %lu", config_values[i].key, *(unsigned long *)value); + } + + cli_print(cli, "# Plugins"); + for (i = 0; i < MAXPLUGINS; i++) + { + if (*config->plugins[i]) + { + cli_print(cli, "load plugin \"%s\"", config->plugins[i]); + } + } + + cli_print(cli, "# end"); + return CLI_OK; +} - fprintf(w, "%-15s %4s %8d %s\r\n", - inet_toa(ip_address_pool[i].address), - (s) ? "Y" : "N", - s, - session[s].user); +int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc) +{ + char *states[] = { + "NULL", + "CHAP", + "AUTH", + "IPCP", + "START", + "STOP", + "WAIT", + }; + int i, free = 0, used = 0, show_all = 0; + time_t time_now; + + cli_print(cli, "%6s%6s%9s%9s%4s", "Radius", "State", "Session", "Retry", "Try"); + + time(&time_now); + + if (argc > 0 && strcmp(argv[0], "all") == 0) + show_all = 1; + + for (i = 1; i < MAXRADIUS; i++) + { + if (radius[i].state == RADIUSNULL) + free++; + else + used++; + + if (!show_all && radius[i].state == RADIUSNULL) continue; + + cli_print(cli, "%6d%6s%9d%9u%4d", + i, + states[radius[i].state], + radius[i].session, + radius[i].retry, + radius[i].try); } - fprintf(w, "\r\nFree: %d\r\nUsed: %d\r\n", free, used); + + cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used); + return CLI_OK; } -int cmd_show_banana(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) -{ - fputs(" _\r\n" - "//\\\r\n" - "V \\\r\n" - " \\ \\_\r\n" - " \\,'.`-.\r\n" - " |\\ `. `.\r\n" - " ( \\ `. `-. _,.-:\\\r\n" - " \\ \\ `. `-._ __..--' ,-';/\r\n" - " \\ `. `-. `-..___..---' _.--' ,'/\r\n" - " `. `. `-._ __..--' ,' /\r\n" - " `. `-_ ``--..'' _.-' ,'\r\n" - " `-_ `-.___ __,--' ,'\r\n" - " `-.__ `----\"\"\" __.-'\r\n" - "hh `--..____..--'\r\n", w); +int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + cli_print(cli, "Plugins currently loaded:"); + for (i = 0; i < MAXPLUGINS; i++) + { + if (*config->plugins[i]) + { + cli_print(cli, " %s", config->plugins[i]); + } + } return CLI_OK; } -int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc) { - fprintf(w, "Counters cleared\r\n"); + cli_print(cli, " _\n" + "//\\\n" + "V \\\n" + " \\ \\_\n" + " \\,'.`-.\n" + " |\\ `. `.\n" + " ( \\ `. `-. _,.-:\\\n" + " \\ \\ `. `-._ __..--' ,-';/\n" + " \\ `. `-. `-..___..---' _.--' ,'/\n" + " `. `. `-._ __..--' ,' /\n" + " `. `-_ ``--..'' _.-' ,'\n" + " `-_ `-.___ __,--' ,'\n" + " `-.__ `----\"\"\" __.-'\n" + "hh `--..____..--'"); + + return CLI_OK; +} + +int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc) +{ + cli_print(cli, "Counters cleared"); SET_STAT(last_reset, time(NULL)); return CLI_OK; } -int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc) { int i; sessionidt s; if (!argc) { - fprintf(w, "Specify a user to drop\r\n"); + cli_print(cli, "Specify a user to drop"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "username ..."); + cli_print(cli, "username ..."); return CLI_OK; } } @@ -528,7 +720,7 @@ int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int { if (!(s = sessionbyuser(argv[i]))) { - fprintf(w, "User %s is not connected\r\n", argv[i]); + cli_print(cli, "User %s is not connected", argv[i]); continue; } @@ -536,7 +728,7 @@ int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int { int x; - fprintf(w, "Dropping user %s\r\n", session[s].user); + cli_print(cli, "Dropping user %s", session[s].user); for (x = 0; x < MAXSESSION; x++) { if (!cli_session_kill[x]) @@ -551,21 +743,21 @@ int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int return CLI_OK; } -int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc) { int i; tunnelidt tid; if (!argc) { - fprintf(w, "Specify a tunnel to drop\r\n"); + cli_print(cli, "Specify a tunnel to drop"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "tunnel_id ..."); + cli_print(cli, "tunnel_id ..."); return CLI_OK; } } @@ -576,19 +768,19 @@ int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, in if ((tid = atol(argv[i])) <= 0 || (tid > MAXTUNNEL)) { - fprintf(w, "Invalid tunnel ID (%d - %d)\r\n", 0, MAXTUNNEL); + cli_print(cli, "Invalid tunnel ID (%d - %d)", 0, MAXTUNNEL); continue; } if (!tunnel[tid].ip) { - fprintf(w, "Tunnel %d is not connected\r\n", tid); + cli_print(cli, "Tunnel %d is not connected", tid); continue; } if (tunnel[tid].die) { - fprintf(w, "Tunnel %d is already being shut down\r\n", tid); + cli_print(cli, "Tunnel %d is already being shut down", tid); continue; } @@ -597,7 +789,7 @@ int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, in if (!cli_tunnel_kill[x]) { cli_tunnel_kill[x] = tid; - fprintf(w, "Tunnel %d shut down (%s)\r\n", tid, tunnel[tid].hostname); + cli_print(cli, "Tunnel %d shut down (%s)", tid, tunnel[tid].hostname); break; } } @@ -606,21 +798,21 @@ int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, in return CLI_OK; } -int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc) { int i; sessionidt s; if (!argc) { - fprintf(w, "Specify a session id to drop\r\n"); + cli_print(cli, "Specify a session id to drop"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "session_id ..."); + cli_print(cli, "session_id ..."); return CLI_OK; } } @@ -629,11 +821,11 @@ int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, i { if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION)) { - fprintf(w, "Invalid session ID (%d - %d)\r\n", 0, MAXSESSION); + cli_print(cli, "Invalid session ID (%d - %d)", 0, MAXSESSION); continue; } - if (session[s].ip && session[s].opened && !session[s].die) + if (session[s].opened && !session[s].die) { int x; for (x = 0; x < MAXSESSION; x++) @@ -644,32 +836,32 @@ int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, i break; } } - fprintf(w, "Dropping session %d\r\n", s); + cli_print(cli, "Dropping session %d", s); } else { - fprintf(w, "Session %d is not active.\r\n", s); + cli_print(cli, "Session %d is not active.", s); } } return CLI_OK; } -int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc) { int i; sessionidt s; if (!argc) { - fprintf(w, "Specify a user\r\n"); + cli_print(cli, "Specify a user"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "username ..."); + cli_print(cli, "username ..."); return CLI_OK; } } @@ -678,31 +870,31 @@ int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc { if (!(s = sessionbyuser(argv[i]))) { - fprintf(w, "User %s is not connected\r\n", argv[i]); + cli_print(cli, "User %s is not connected", argv[i]); continue; } session[s].snoop = 1; - fprintf(w, "Snooping user %s\r\n", argv[i]); + cli_print(cli, "Snooping user %s", argv[i]); } return CLI_OK; } -int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) { int i; sessionidt s; if (!argc) { - fprintf(w, "Specify a user\r\n"); + cli_print(cli, "Specify a user"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "username ..."); + cli_print(cli, "username ..."); return CLI_OK; } } @@ -711,31 +903,31 @@ int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int a { if (!(s = sessionbyuser(argv[i]))) { - fprintf(w, "User %s is not connected\r\n", argv[i]); + cli_print(cli, "User %s is not connected", argv[i]); continue; } session[s].snoop = 0; - fprintf(w, "Not snooping user %s\r\n", argv[i]); + cli_print(cli, "Not snooping user %s", argv[i]); } return CLI_OK; } -int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) { int i; sessionidt s; if (!argc) { - fprintf(w, "Specify a user\r\n"); + cli_print(cli, "Specify a user"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "username ..."); + cli_print(cli, "username ..."); return CLI_OK; } } @@ -744,31 +936,31 @@ int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int a { if (!(s = sessionbyuser(argv[i]))) { - fprintf(w, "User %s is not connected\r\n", argv[i]); + cli_print(cli, "User %s is not connected", argv[i]); continue; } throttle_session(s, 1); - fprintf(w, "throttling user %s\r\n", argv[i]); + cli_print(cli, "throttling user %s", argv[i]); } return CLI_OK; } -int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) { int i; sessionidt s; if (!argc) { - fprintf(w, "Specify a user\r\n"); + cli_print(cli, "Specify a user"); return CLI_OK; } for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - fprintf(w, "username ..."); + cli_print(cli, "username ..."); return CLI_OK; } } @@ -777,30 +969,30 @@ int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, in { if (!(s = sessionbyuser(argv[i]))) { - fprintf(w, "User %s is not connected\r\n", argv[i]); + cli_print(cli, "User %s is not connected", argv[i]); continue; } throttle_session(s, 0); - fprintf(w, "unthrottling user %s\r\n", argv[i]); + cli_print(cli, "unthrottling user %s", argv[i]); } return CLI_OK; } -int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc) { int i; if (!argc) { - fprintf(w, "Currently debugging: "); - if (debug_flags.critical) fprintf(w, "critical "); - if (debug_flags.error) fprintf(w, "error "); - if (debug_flags.warning) fprintf(w, "warning "); - if (debug_flags.info) fprintf(w, "info "); - if (debug_flags.calls) fprintf(w, "calls "); - if (debug_flags.data) fprintf(w, "data "); - fprintf(w, "\r\n"); + cli_print(cli, "Currently debugging: "); + if (debug_flags.critical) cli_print(cli, "critical "); + if (debug_flags.error) cli_print(cli, "error "); + if (debug_flags.warning) cli_print(cli, "warning "); + if (debug_flags.info) cli_print(cli, "info "); + if (debug_flags.calls) cli_print(cli, "calls "); + if (debug_flags.data) cli_print(cli, "data "); + cli_print(cli, ""); return CLI_OK; } @@ -808,13 +1000,13 @@ int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc { if (*argv[i] == '?') { - fprintf(w, "Possible debugging states are:\r\n"); - fprintf(w, " critical\r\n"); - fprintf(w, " error\r\n"); - fprintf(w, " warning\r\n"); - fprintf(w, " info\r\n"); - fprintf(w, " calls\r\n"); - fprintf(w, " data\r\n"); + cli_print(cli, "Possible debugging states are:"); + cli_print(cli, " critical"); + cli_print(cli, " error"); + cli_print(cli, " warning"); + cli_print(cli, " info"); + cli_print(cli, " calls"); + cli_print(cli, " data"); return CLI_OK; } } @@ -837,7 +1029,7 @@ int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc return CLI_OK; } -int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc) { int i; @@ -855,49 +1047,205 @@ int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int a return CLI_OK; } -int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_watch_session(struct cli_def *cli, char *command, char **argv, int argc) { sessionidt s; if (argc != 1) { - fprintf(w, "Specify a single session to debug (0 to disable)\r\n"); + cli_print(cli, "Specify a single session to debug (0 to disable)"); return CLI_OK; } s = atoi(argv[0]); if (debug_session) - fprintf(w, "No longer debugging session %d\r\n", debug_session); + cli_print(cli, "No longer debugging session %d", debug_session); - if (s) fprintf(w, "Debugging session %d.\r\n", s); + if (s) cli_print(cli, "Debugging session %d.", s); debug_session = s; return CLI_OK; } -int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_watch_tunnel(struct cli_def *cli, char *command, char **argv, int argc) { tunnelidt s; if (argc != 1) { - fprintf(w, "Specify a single tunnel to debug (0 to disable)\r\n"); + cli_print(cli, "Specify a single tunnel to debug (0 to disable)"); return CLI_OK; } s = atoi(argv[0]); if (debug_tunnel) - fprintf(w, "No longer debugging tunnel %d\r\n", debug_tunnel); + cli_print(cli, "No longer debugging tunnel %d", debug_tunnel); - if (s) fprintf(w, "Debugging tunnel %d.\r\n", s); + if (s) cli_print(cli, "Debugging tunnel %d.", s); debug_tunnel = s; return CLI_OK; } -int regular_stuff(struct cli_def *cli, FILE *w) +int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i, firstfree = 0; + if (argc != 1) + { + cli_print(cli, "Specify a plugin to load"); + return CLI_OK; + } + + for (i = 0; i < MAXPLUGINS; i++) + { + if (!*config->plugins[i] && !firstfree) + firstfree = i; + if (strcmp(config->plugins[i], argv[0]) == 0) + { + cli_print(cli, "Plugin is already loaded"); + return CLI_OK; + } + } + + if (firstfree) + { + strncpy(config->plugins[firstfree], argv[0], sizeof(config->plugins[firstfree]) - 1); + config->reload_config = 1; + cli_print(cli, "Loading plugin %s", argv[0]); + } + + return CLI_OK; +} + +int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + + if (argc != 1) + { + cli_print(cli, "Specify a plugin to remove"); + return CLI_OK; + } + + for (i = 0; i < MAXPLUGINS; i++) + { + if (strcmp(config->plugins[i], argv[0]) == 0) + { + config->reload_config = 1; + memset(config->plugins[i], 0, sizeof(config->plugins[i])); + return CLI_OK; + } + } + + cli_print(cli, "Plugin is not loaded"); + return CLI_OK; +} + +char *duration(time_t seconds) +{ + static char *buf = NULL; + if (!buf) buf = calloc(64, 1); + + if (seconds > 86400) + sprintf(buf, "%d days", (int)(seconds / 86400.0)); + else if (seconds > 60) + sprintf(buf, "%02d:%02lu", (int)(seconds / 3600.0), seconds % 60); + else + sprintf(buf, "%lu sec", seconds); + return buf; +} + +int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc) +{ + FILE *fh; + char buf[100], *p = buf, *loads[3]; + int i, num_sessions = 0; + time_t time_now; + + fh = fopen("/proc/loadavg", "r"); + fgets(buf, 100, fh); + fclose(fh); + + for (i = 0; i < 3; i++) + loads[i] = strdup(strsep(&p, " ")); + + time(&time_now); + strftime(buf, 99, "%H:%M:%S", localtime(&time_now)); + + for (i = 1; i < MAXSESSION; i++) + if (session[i].opened) num_sessions++; + + cli_print(cli, "%s up %s, %d users, load average: %s, %s, %s", + buf, + duration(abs(time_now - config->start_time)), + num_sessions, + loads[0], loads[1], loads[2] + ); + for (i = 0; i < 3; i++) + if (loads[i]) free(loads[i]); + + cli_print(cli, "Bandwidth: %s", config->bandwidth); + + return CLI_OK; +} + +int cmd_set(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + + if (argc != 2) + { + cli_print(cli, "Usage: set "); + return CLI_OK; + } + + for (i = 0; config_values[i].key; i++) + { + void *value = ((void *)config) + config_values[i].offset; + if (strcmp(config_values[i].key, argv[0]) == 0) + { + // Found a value to set + cli_print(cli, "Setting \"%s\" to \"%s\"", argv[0], argv[1]); + switch (config_values[i].type) + { + case STRING: + strncpy((char *)value, argv[1], config_values[i].size - 1); + break; + case INT: + *(int *)value = atoi(argv[1]); + break; + case UNSIGNED_LONG: + *(unsigned long *)value = atol(argv[1]); + break; + case SHORT: + *(short *)value = atoi(argv[1]); + break; + case IP: + *(unsigned *)value = inet_addr(argv[1]); + break; + case BOOL: + if (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "true") == 0 || strcasecmp(argv[1], "1") == 0) + *(int *)value = 1; + else + *(int *)value = 0; + break; + default: + cli_print(cli, "Unknown variable type"); + break; + } + config->reload_config = 1; + return CLI_OK; + } + } + + cli_print(cli, "Unknown variable \"%s\"", argv[0]); + return CLI_OK; +} + +int regular_stuff(struct cli_def *cli) { int i = debug_rb_tail; + int reprompt = 0; #ifdef RINGBUFFER while (i != ringbuffer->tail) @@ -924,12 +1272,14 @@ int regular_stuff(struct cli_def *cli, FILE *w) memcpy(&addr, &address, sizeof(ringbuffer->buffer[i].address)); ipaddr = inet_ntoa(addr); - fprintf(w, "%s-%s-%u-%u %s\r", + cli_print(cli, "\r%s-%s-%u-%u %s", debug_levels[(int)ringbuffer->buffer[i].level], ipaddr, ringbuffer->buffer[i].tunnel, ringbuffer->buffer[i].session, ringbuffer->buffer[i].message); + + reprompt = 1; } if (++i == ringbuffer->tail) break; @@ -937,8 +1287,8 @@ int regular_stuff(struct cli_def *cli, FILE *w) } debug_rb_tail = ringbuffer->tail; + if (reprompt) + cli_reprompt(cli); #endif return CLI_OK; } - -#endif diff --git a/cluster.c b/cluster.c index 5ad3ff3..4803d13 100644 --- a/cluster.c +++ b/cluster.c @@ -1,5 +1,5 @@ // L2TPNS Clustering Stuff -// $Id: cluster.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ #include #include +// $Id: cluster.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include #include #include diff --git a/cluster.h b/cluster.h index 57759fc..69969eb 100644 --- a/cluster.h +++ b/cluster.h @@ -1,11 +1,12 @@ // L2TPNS Clustering Stuff -// $Id: cluster.h,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: cluster.h,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #define C_HELLO 1 #define C_HELLO_RESPONSE 2 #define C_PING 3 #define C_TUNNEL 4 #define C_SESSION 5 +#define C_GOODBYE 6 #define CLUSTERPORT 32792 #define CLUSTERCLIENTPORT 32793 diff --git a/cluster_master.c b/cluster_master.c index 0cc43ee..4516f4e 100644 --- a/cluster_master.c +++ b/cluster_master.c @@ -1,5 +1,5 @@ // L2TPNS Cluster Master -// $Id: cluster_master.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: cluster_master.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include @@ -51,6 +51,7 @@ int handle_hello(char *buf, int l, struct sockaddr_in *src_addr, uint32_t addr); int handle_tunnel(char *buf, int l, uint32_t addr); int handle_session(char *buf, int l, uint32_t addr); int handle_ping(char *buf, int l, uint32_t addr); +int handle_goodbye(char *buf, int l, uint32_t addr); int backup_up(slave *s); int backup_down(slave *s); int return_state(slave *s); @@ -91,7 +92,7 @@ int main(int argc, char *argv[]) signal(SIGCHLD, sigchild_handler); - log(0, "Cluster Manager $Id: cluster_master.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ starting\n"); + log(0, "Cluster Manager $Id: cluster_master.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ starting\n"); to.tv_sec = 1; to.tv_usec = 0; @@ -156,6 +157,7 @@ int main(int argc, char *argv[]) int processmsg(char *buf, int l, struct sockaddr_in *src_addr) { + slave *s; char mtype; uint32_t addr; @@ -168,6 +170,16 @@ int processmsg(char *buf, int l, struct sockaddr_in *src_addr) mtype = *buf; buf++; l--; + if (mtype != C_GOODBYE && (s = find_slave(addr)) && s->down) + { + char *hostname; + hostname = calloc(l + 1, 1); + memcpy(hostname, buf, l); + log(1, "Slave \"%s\" (for %s) has come back.\n", hostname, inet_toa(s->ip_address)); + backup_down(s); + free(hostname); + } + switch (mtype) { case C_HELLO: @@ -187,6 +199,10 @@ int processmsg(char *buf, int l, struct sockaddr_in *src_addr) if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr); handle_session(buf, l, addr); break; + case C_GOODBYE: + if (!find_slave(addr)) break; + handle_goodbye(buf, l, addr); + break; } return mtype; } @@ -478,3 +494,24 @@ int backup_down(slave *s) return 0; } +int handle_goodbye(char *buf, int l, uint32_t addr) +{ + int i; + slave *s; + + // Is this a slave we have state information for? + if ((s = find_slave(addr))) + { + log(0, "Received goodbye for slave %s\n", s->hostname); + ll_delete(slaves, s); + for (i = 0; i < s->num_tunnels; i++) + if (s->tunnels[i]) free(s->tunnels[i]); + for (i = 0; i < s->num_sessions; i++) + if (s->sessions[i]) free(s->sessions[i]); + if (s->hostname) free(s->hostname); + free(s); + } + + return 0; +} + diff --git a/cluster_slave.c b/cluster_slave.c index 71e2eac..289d96d 100644 --- a/cluster_slave.c +++ b/cluster_slave.c @@ -1,5 +1,5 @@ // L2TPNS Cluster Master -// $Id: cluster_slave.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: cluster_slave.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include @@ -19,16 +19,13 @@ #include "ll.h" #include "util.h" +// vim: sw=4 ts=8 + extern int cluster_sockfd; -extern tunnelt *tunnel; -extern sessiont *session; -extern uint32_t cluster_address; extern char hostname[1000]; -extern int debug; extern ippoolt *ip_address_pool; extern uint32_t vip_address; -extern tunnelidt tunnelfree; -extern sessionidt sessionfree; +extern struct configt *config; int handle_tunnel(char *buf, int l); int handle_session(char *buf, int l); @@ -78,6 +75,13 @@ int handle_tunnel(char *buf, int l) { int t; + // Ignore tunnel message if NOSTATEFILE exists + if (config->ignore_cluster_updates) + { + log(1, 0, 0, 0, "Discarding tunnel message from cluster master.\n", l, sizeof(tunnelt)); + return 0; + } + t = *(int *)buf; log(1, 0, 0, t, "Receiving tunnel %d from cluster master (%d bytes)\n", t, l); buf += sizeof(int); l -= sizeof(int); @@ -94,16 +98,6 @@ int handle_tunnel(char *buf, int l) return 0; } - if (t > 1) - { - tunnel[t-1].next = tunnel[t].next; - } - - if (tunnelfree == t) - { - tunnelfree = tunnel[t].next; - } - memcpy(&tunnel[t], buf, l); log(3, 0, 0, t, "Cluster master sent tunnel for %s\n", tunnel[t].hostname); @@ -117,6 +111,13 @@ int handle_session(char *buf, int l) { int s; + // Ignore tunnel message if NOSTATEFILE exists + if (config->ignore_cluster_updates) + { + log(1, 0, 0, 0, "Discarding session message from cluster master.\n", l, sizeof(tunnelt)); + return 0; + } + s = *(int *)buf; log(1, 0, s, 0, "Receiving session %d from cluster master (%d bytes)\n", s, l); buf += sizeof(int); l -= sizeof(int); @@ -163,6 +164,10 @@ int handle_session(char *buf, int l) } } } + /* + if (session[s].servicenet) + servicenet_session(s, 1); + */ return 0; } @@ -214,7 +219,7 @@ int cluster_send_session(int s) memcpy((char *)(packet + len), &session[s], sizeof(sessiont)); len += sizeof(sessiont); - cluster_send_message(cluster_address, vip_address, C_SESSION, packet, len); + cluster_send_message(config->cluster_address, vip_address, C_SESSION, packet, len); free(packet); return 1; @@ -241,7 +246,27 @@ int cluster_send_tunnel(int t) memcpy((char *)(packet + len), &tunnel[t], sizeof(tunnelt)); len += sizeof(tunnelt); - cluster_send_message(cluster_address, vip_address, C_TUNNEL, packet, len); + cluster_send_message(config->cluster_address, vip_address, C_TUNNEL, packet, len); + free(packet); + + return 1; +} + +int cluster_send_goodbye() +{ + char *packet; + int len = 0; + + packet = malloc(4096); + + log(2, 0, 0, 0, "Sending goodbye to cluster master\n"); + // Hostname + len = strlen(hostname); + *(char *)packet = len; + memcpy((char *)(packet + 1), hostname, len); + len++; + + cluster_send_message(config->cluster_address, vip_address, C_GOODBYE, packet, len); free(packet); return 1; diff --git a/garden.c b/garden.c index 064bea2..1083b65 100644 --- a/garden.c +++ b/garden.c @@ -10,43 +10,43 @@ int __plugin_api_version = 1; struct pluginfuncs p; -int garden_session(sessiont *s, int flag); - char *init_commands[] = { // This is for incoming connections to a gardened user "iptables -t nat -N garden_users 2>&1 >/dev/null", - "iptables -t nat -F garden_users 2>&1 >/dev/null", - "iptables -t nat -N garden 2>&1 >/dev/null", + "iptables -t nat -F garden_users", + "iptables -t nat -N garden 2>&1", /* Don't flush - init script sets this up */ "iptables -t nat -A l2tpns -j garden_users", NULL }; char *done_commands[] = { "iptables -t nat -F garden_users 2>&1 >/dev/null", - "iptables -t nat -D l2tpns -j garden_users 2>&1 >/dev/null", + "iptables -t nat -D l2tpns -j garden_users", NULL }; +int garden_session(sessiont *s, int flag); + int plugin_post_auth(struct param_post_auth *data) { // Ignore if user authentication was successful if (data->auth_allowed) return PLUGIN_RET_OK; - p.log(3, 0, 0, 0, "User allowed into walled garden\n"); + p.log(3, 0, 0, 0, "Walled Garden allowing login\n"); data->auth_allowed = 1; - data->s->walled_garden = 1; + data->s->garden = 1; return PLUGIN_RET_OK; } int plugin_new_session(struct param_new_session *data) { - if (data->s->walled_garden) garden_session(data->s, 1); + if (data->s->garden) garden_session(data->s, 1); return PLUGIN_RET_OK; } int plugin_kill_session(struct param_new_session *data) { - if (data->s->walled_garden) garden_session(data->s, 0); + if (data->s->garden) garden_session(data->s, 0); return PLUGIN_RET_OK; } @@ -58,10 +58,7 @@ int plugin_control(struct param_control *data) if (data->type != PKT_GARDEN && data->type != PKT_UNGARDEN) return PLUGIN_RET_OK; if (!data->data && data->data_length) return PLUGIN_RET_OK; session = atoi((char*)(data->data)); - - if (!session) - return PLUGIN_RET_OK; - + if (!session) return PLUGIN_RET_OK; // Really? data->send_response = 1; s = p.get_session_by_id(session); if (!s || !s->ip) @@ -86,11 +83,6 @@ int plugin_control(struct param_control *data) return PLUGIN_RET_STOP; } -int plugin_config(struct param_config *data) -{ - return PLUGIN_RET_OK; -} - int garden_session(sessiont *s, int flag) { char cmd[2048]; @@ -98,25 +90,48 @@ int garden_session(sessiont *s, int flag) if (!s) return 0; if (!s->opened) return 0; + /* Note that we don't handle throttling/snooping/etc here + * To do that, we'd need to send an end accounting record + * then a radius auth, then start accouting again. + * That means that we need the password (which garden has) + * and a lot of code to check that the new set of params + * (routes, IP, ACLs, etc) 'matched' the old one in a + * 'compatable' way. (ie user's system doesn't need to be told + * of the change) + * + * Thats a lot of pain/code for very little gain. + * If we want them redone from scratch, just sessionkill them - + * a user on garden isn't going to have any open TCP + * connections which are worth caring about, anyway. + * + * Note that the user will be rethrottled shortly by the scan + * script thingy if appropriate. + * + * Currently, garden only directly ungardens someone if + * they haven't paid their bill, and then subsequently do so + * online. This isn't something which can be set up by a malicious + * customer at will. + */ if (flag == 1) { + // Gardened User p.log(2, 0, 0, s->tunnel, "Trap user %s (%s) in walled garden\n", s->user, p.inet_toa(ntohl(s->ip))); snprintf(cmd, 2048, "iptables -t nat -A garden_users -s %s -j garden", p.inet_toa(ntohl(s->ip))); p.log(3, 0, 0, s->tunnel, "%s\n", cmd); system(cmd); - s->walled_garden = 1; + s->garden = 1; } else { sessionidt other; - int count = 10; + int count = 40; // Normal User p.log(2, 0, 0, s->tunnel, "Release user %s (%s) from walled garden\n", s->user, p.inet_toa(ntohl(s->ip))); // Kick off any duplicate usernames // but make sure not to kick off ourself if (s->ip && !s->die && (other = p.get_session_by_username(s->user)) && s != p.get_session_by_id(other)) { - p.sessionkill(other, "Duplicate session when user ungardened"); + p.sessionkill(other, "Duplicate session when user un-gardened"); } /* Clean up counters */ s->cin = s->cout = 0; @@ -130,7 +145,7 @@ int garden_session(sessiont *s, int flag) if (WEXITSTATUS(status) != 0) break; } - s->walled_garden = 0; + s->garden = 0; if (!s->die) { /* OK, we're up! */ @@ -138,7 +153,7 @@ int garden_session(sessiont *s, int flag) p.radiussend(r, RADIUSSTART); } } - s->walled_garden = flag; + s->garden = flag; return 1; } @@ -149,11 +164,9 @@ int plugin_init(struct pluginfuncs *funcs) if (!funcs) return 0; memcpy(&p, funcs, sizeof(p)); - p.log(1, 0, 0, 0, "Enabling walled garden service\n"); - for (i = 0; init_commands[i] && *init_commands[i]; i++) { - p.log(4, 0, 0, 0, "Running %s\n", init_commands[i]); + p.log(3, 0, 0, 0, "Running %s\n", init_commands[i]); system(init_commands[i]); } @@ -165,7 +178,7 @@ void plugin_done() int i; for (i = 0; done_commands[i] && *done_commands[i]; i++) { - p.log(4, 0, 0, 0, "Running %s\n", done_commands[i]); + p.log(3, 0, 0, 0, "Running %s\n", done_commands[i]); system(done_commands[i]); } } diff --git a/icmp.c b/icmp.c new file mode 100644 index 0000000..0a56740 --- /dev/null +++ b/icmp.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" + +extern ipt myip; + +__u16 _checksum(unsigned char *addr, int count); + +void host_unreachable(ipt destination, u16 id, ipt source, char *packet, int packet_len) +{ + char buf[128] = {0}; + struct iphdr *iph; + struct icmphdr *icmp; + char *data; + int len = 0, on = 1, icmp_socket; + struct sockaddr_in whereto = {0}; + + if (!(icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))) + return; + setsockopt(icmp_socket, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)); + + whereto.sin_addr.s_addr = destination; + whereto.sin_family = AF_INET; + + iph = (struct iphdr *)(buf); + len = sizeof(struct iphdr); + icmp = (struct icmphdr *)(buf + len); + len += sizeof(struct icmphdr); + data = (char *)(buf + len); + len += (packet_len < 64) ? packet_len : 64; + memcpy(data, packet, (packet_len < 64) ? packet_len : 64); + + iph->tos = 0; + iph->id = id; + iph->frag_off = 0; + iph->ttl = 30; + iph->check = 0; + iph->version = 4; + iph->ihl = 5; + iph->protocol = 1; + iph->check = 0; + iph->daddr = destination; + iph->saddr = source; + + iph->tot_len = ntohs(len); + + icmp->type = ICMP_DEST_UNREACH; + icmp->code = ICMP_HOST_UNREACH; + icmp->checksum = _checksum((char *)icmp, sizeof(struct icmphdr) + ((packet_len < 64) ? packet_len : 64)); + + iph->check = _checksum((char *)iph, sizeof(struct iphdr)); + + sendto(icmp_socket, (char *)buf, len, 0, (struct sockaddr *)&whereto, sizeof(struct sockaddr)); + close(icmp_socket); +} + +__u16 _checksum(unsigned char *addr, int count) +{ + register long sum = 0; + + for (; count > 1; count -= 2) + { + sum += ntohs(*(u32 *)addr); + addr += 2; + } + + if (count > 1) sum += *(unsigned char *)addr; + + // take only 16 bits out of the 32 bit sum and add up the carries + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + // one's complement the result + sum = ~sum; + + return htons((u16) sum); +} diff --git a/l2tpns.c b/l2tpns.c index 40f4eed..7e591ce 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -8,6 +8,8 @@ #include #include #include +#define SYSLOG_NAMES +#include #include #include #include @@ -18,6 +20,7 @@ #include #include #include +#define __USE_GNU #include #include #include @@ -27,12 +30,10 @@ #include #include #include +#include #include #include #include -#ifdef HAVE_LIBCLI -#include -#endif #include "md5.h" #include "l2tpns.h" #include "cluster.h" @@ -42,62 +43,65 @@ #include "control.h" #include "util.h" -ipt radiusserver[MAXRADSERVER]; // radius servers -u8 numradiusservers = 0; // how many radius servers - // Globals -char tapdevice[10] = ""; // tap device name -int tapfd = -1; // tap interface file handle -int udpfd = -1; // UDP file handle -int controlfd = -1; // Control signal handle -int snoopfd = -1; // UDP file handle for sending out intercept data -int radfd = -1; // RADIUS requests file handle -int ifrfd = -1; // File descriptor for routing, etc -char debug = 0; // debug leveL -time_t basetime = 0; // base clock -char hostname[1000] = ""; // us. -ipt myip = 0; // MY IP -u16 tapmac[3]; // MAC of tap interface -int tapidx; // ifr_ifindex of tap device -char *radiussecret = 0; // RADIUS secret -char *l2tpsecret = 0; // L2TP secret -u32 sessionid = 0; // session id for radius accounting -char *snoop_destination_host = NULL; -u16 snoop_destination_port = 0; -char *log_filename = NULL; -char *config_file = CONFIGFILE; +struct configt *config = NULL; // all configuration +int tapfd = -1; // tap interface file handle +int udpfd = -1; // UDP file handle +int controlfd = -1; // Control signal handle +int snoopfd = -1; // UDP file handle for sending out intercept data +int radfd = -1; // RADIUS requests file handle +int ifrfd = -1; // File descriptor for routing, etc +time_t basetime = 0; // base clock +char hostname[1000] = ""; // us. +ipt myip = 0; // MY IP +u16 tapmac[3]; // MAC of tap interface +int tapidx; // ifr_ifindex of tap device +u32 sessionid = 0; // session id for radius accounting +int syslog_log = 0; // are we logging to syslog FILE *log_stream = NULL; -unsigned long default_dns1 = 0, default_dns2 = 0; struct sockaddr_in snoop_addr = {0}; -extern unsigned long rl_rate; extern int cluster_sockfd; unsigned long last_sid = 0; -int config_save_state = 0; -int radius_accounting = 0; -char *accounting_dir = NULL; -uint32_t cluster_address = 0; -uint32_t bind_address = INADDR_ANY; int handle_interface = 0; -#ifdef HAVE_LIBCLI -pid_t cli_pid = 0; int clifd = 0; sessionidt *cli_session_kill = NULL; tunnelidt *cli_tunnel_kill = NULL; -#endif static void *ip_hash[256]; unsigned long udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0; unsigned long eth_tx = 0, eth_rx = 0, eth_rx_pkt = 0; -unsigned int ip_pool_index = 0; unsigned int ip_pool_size = 0; time_t time_now; char time_now_string[64] = {0}; char main_quit = 0; -int dump_speed = 0; -int target_uid = 500; char *_program_name = NULL; linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; +#define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER) +#define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(struct configt, MEMBER), membersize(struct configt, MEMBER), TYPE } + +struct config_descriptt config_values[] = { + CONFIG("debug", debug, INT), + CONFIG("log_file", log_filename, STRING), + CONFIG("l2tp_secret", l2tpsecret, STRING), + CONFIG("primary_dns", default_dns1, IP), + CONFIG("secondary_dns", default_dns2, IP), + CONFIG("save_state", save_state, BOOL), + CONFIG("snoop_host", snoop_destination_host, IP), + CONFIG("snoop_port", snoop_destination_port, SHORT), + CONFIG("primary_radius", radiusserver[0], IP), + CONFIG("secondary_radius", radiusserver[1], IP), + CONFIG("radius_accounting", radius_accounting, BOOL), + CONFIG("radius_secret", radiussecret, STRING), + CONFIG("bind_address", bind_address, IP), + CONFIG("cluster_master", cluster_address, IP), + CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG), + CONFIG("accounting_dir", accounting_dir, STRING), + CONFIG("setuid", target_uid, INT), + CONFIG("dump_speed", dump_speed, BOOL), + { NULL, 0, 0, 0 }, +}; + char *plugin_functions[] = { NULL, "plugin_pre_auth", @@ -105,7 +109,6 @@ char *plugin_functions[] = { "plugin_packet_rx", "plugin_packet_tx", "plugin_timer", - "plugin_config", "plugin_new_session", "plugin_kill_session", "plugin_control", @@ -113,13 +116,10 @@ char *plugin_functions[] = { }; #define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) -tunnelt *tunnel = NULL; // 1000 * 45 = 45000 = 45k +tunnelt *tunnel = NULL; // 1000 * 45 = 45000 = 45k sessiont *session = NULL; // 5000 * 213 = 1065000 = 1 Mb radiust *radius = NULL; ippoolt *ip_address_pool = NULL; -tunnelidt tunnelfree; // free list link heads -sessionidt sessionfree = 0; -u8 radiusfree; controlt *controlfree = 0; struct Tstats *_statistics = NULL; #ifdef RINGBUFFER @@ -136,6 +136,9 @@ void sigsegv_handler(int); void read_config_file(); void read_state(); void dump_state(); +void tunnel_clean(); +tunnelidt new_tunnel(); +void update_config(); // return internal time (10ths since run) clockt now(void) @@ -154,6 +157,8 @@ clockt backoff(u8 try) void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...) { + static char message[65535] = {0}; + static char message2[65535] = {0}; va_list ap; #ifdef RINGBUFFER @@ -175,68 +180,73 @@ void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, } #endif - if (debug < level) return; + if (config->debug < level) return; - if (!log_stream && log_filename) + va_start(ap, format); + if (log_stream) { - if ((log_stream = fopen(log_filename, "a"))) - fseek(log_stream, 0, SEEK_END); - setbuf(log_stream, NULL); + vsnprintf(message2, 65535, format, ap); + snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2); + fprintf(log_stream, message); } - if (!log_stream) + else if (syslog_log) { - log_stream = stderr; - setbuf(log_stream, NULL); + vsnprintf(message2, 65535, format, ap); + snprintf(message, 65535, "%02d/%02d %s", t, s, message2); + syslog(level + 2, message); // We don't need LOG_EMERG or LOG_ALERT } - - va_start(ap, format); - fprintf(log_stream, "%s %02d/%02d ", time_now_string, t, s); - vfprintf(log_stream, format, ap); va_end(ap); } void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize) { - unsigned int i, j; + int i, j; unsigned const char *d = (unsigned const char *)data; - if (debug < level) return; - log(level, address, s, t, "%s (%d bytes):\n", title, maxsize); - setvbuf(log_stream, NULL, _IOFBF, 16384); - for (i = 0; i < maxsize; ) + if (config->debug < level) return; + + // No support for log_hex to syslog + if (log_stream) { - fprintf(log_stream, "%4X: ", i); - for (j = i; j < maxsize && j < (i + 16); j++) - { - fprintf(log_stream, "%02X ", d[j]); - if (j == i + 7) - fputs(": ", log_stream); - } + log(level, address, s, t, "%s (%d bytes):\n", title, maxsize); + setvbuf(log_stream, NULL, _IOFBF, 16384); - for (; j < i + 16; j++) + for (i = 0; i < maxsize; ) { - fputs(" ", log_stream); - if (j == i + 7) - fputs(": ", log_stream); - } + fprintf(log_stream, "%4X: ", i); + for (j = i; j < maxsize && j < (i + 16); j++) + { + fprintf(log_stream, "%02X ", d[j]); + if (j == i + 7) + fputs(": ", log_stream); + } - fputs(" ", log_stream); - for (j = i; j < maxsize && j < (i + 16); j++) - { - if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) - fputc(d[j], log_stream); - else - fputc('.', log_stream); + for (; j < i + 16; j++) + { + fputs(" ", log_stream); + if (j == i + 7) + fputs(": ", log_stream); + } - if (j == i + 7) - fputs(" ", log_stream); + fputs(" ", log_stream); + for (j = i; j < maxsize && j < (i + 16); j++) + { + if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) + fputc(d[j], log_stream); + else + fputc('.', log_stream); + + if (j == i + 7) + fputs(" ", log_stream); + } + + i = j; + fputs("\n", log_stream); } - i = j; - fputs("\n", log_stream); + fflush(log_stream); + setbuf(log_stream, NULL); } - fflush(log_stream); - setbuf(log_stream, NULL); } @@ -245,13 +255,13 @@ void routeset(ipt ip, ipt mask, ipt gw, u8 add) { struct rtentry r; memset(&r, 0, sizeof(r)); - r.rt_dev = tapdevice; + r.rt_dev = config->tapdevice; r.rt_dst.sa_family = AF_INET; *(u32 *) & (((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr) = htonl(ip); r.rt_gateway.sa_family = AF_INET; *(u32 *) & (((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr) = htonl(gw); r.rt_genmask.sa_family = AF_INET; - *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask ? : 0xFFFFFFF); + *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask ? mask : 0xFFFFFFF); r.rt_flags = (RTF_UP | RTF_STATIC); if (gw) r.rt_flags |= RTF_GATEWAY; @@ -280,12 +290,12 @@ void inittap(void) log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno)); exit(-1); } - assert(strlen(ifr.ifr_name) < sizeof(tapdevice)); - strcpy(tapdevice, ifr.ifr_name); + assert(strlen(ifr.ifr_name) < sizeof(config->tapdevice)); + strncpy(config->tapdevice, ifr.ifr_name, sizeof(config->tapdevice) - 1); ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); sin.sin_family = AF_INET; - sin.sin_addr.s_addr = handle_interface ? bind_address : 0x01010101; // 1.1.1.1 + sin.sin_addr.s_addr = handle_interface ? config->bind_address : 0x01010101; // 1.1.1.1 memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) @@ -323,12 +333,12 @@ void initudp(void) memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(L2TPPORT); - addr.sin_addr.s_addr = bind_address; + addr.sin_addr.s_addr = config->bind_address; udpfd = socket(AF_INET, SOCK_DGRAM, UDP); setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) { - perror("bind"); + perror("udp bind"); exit( -1); } snoopfd = socket(AF_INET, SOCK_DGRAM, UDP); @@ -349,7 +359,7 @@ void initudp(void) // Find session by IP, 0 for not found sessionidt sessionbyip(ipt ip) { - unsigned char *a = (char *)&ip; + unsigned char *a = (unsigned char *)&ip; char **d = (char **) ip_hash; #ifdef STAT_CALLS @@ -365,7 +375,7 @@ sessionidt sessionbyip(ipt ip) void cache_sessionid(ipt ip, sessionidt s) { - unsigned char *a = (char *) &ip; + unsigned char *a = (unsigned char *) &ip; char **d = (char **) ip_hash; int i; @@ -386,7 +396,7 @@ void cache_sessionid(ipt ip, sessionidt s) void uncache_sessionid(ipt ip) { - unsigned char *a = (char *) &ip; + unsigned char *a = (unsigned char *) &ip; char **d = (char **) ip_hash; int i; @@ -399,7 +409,7 @@ void uncache_sessionid(ipt ip) } // Find session by username, 0 for not found -// walled garden'd users aren't authenticated, so the username is +// walled garden users aren't authenticated, so the username is // reasonably useless. Ignore them to avoid incorrect actions sessionidt sessionbyuser(char *username) { @@ -407,7 +417,7 @@ sessionidt sessionbyuser(char *username) #ifdef STAT_CALLS STAT(call_sessionbyuser); #endif - for (s = 1; s < MAXSESSION && (session[s].walled_garden || strncmp(session[s].user, username, 128)); s++); + for (s = 1; s < MAXSESSION && (session[s].servicenet || strncmp(session[s].user, username, 128)); s++); if (s < MAXSESSION) return s; return 0; @@ -426,7 +436,7 @@ void send_garp(ipt ip) exit(-1); } memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, "eth0"); + strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) { perror("get eth0 hwaddr"); @@ -626,7 +636,8 @@ void processipout(u8 * buf, int len) ip = *(u32 *)(buf + 16); if (!(s = sessionbyip(ip))) { -// log(4, 0, 0, 0, "IP: Can't find session for IP %s\n", inet_toa(ip)); + log(4, 0, 0, 0, "IP: Sending ICMP host unreachable to %s\n", inet_toa(*(u32 *)(buf + 12))); + host_unreachable(*(u32 *)(buf + 12), *(u16 *)(buf + 4), ip, buf, (len < 64) ? 64 : len); return; } t = session[s].tunnel; @@ -648,6 +659,7 @@ void processipout(u8 * buf, int len) u8 *p = makeppp(b, buf, len, t, s, PPPIP); tunnelsend(b, len + (p-b), t); // send it... sp->cout += len; // byte count + sp->total_cout += len; // byte count sp->pout++; udp_tx += len; } @@ -758,7 +770,7 @@ void controladd(controlt * c, tunnelidt t, sessionidt s) void sessionshutdown(sessionidt s, char *reason) { int dead = session[s].die; - int walled_garden = session[s].walled_garden; + int servicenet = session[s].servicenet; #ifdef STAT_CALLS STAT(call_sessionshutdown); @@ -777,17 +789,18 @@ void sessionshutdown(sessionidt s, char *reason) } // RADIUS Stop message - if (session[s].opened && !walled_garden && !dead) { + if (session[s].opened && !servicenet && !dead) { u8 r = session[s].radius; if (!r) { - if (!radiusfree) + if (!(r = radiusnew(s))) { log(1, 0, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); STAT(radius_overflow); - } else { + } + else + { int n; - r = radiusnew(s); for (n = 0; n < 15; n++) radius[r].auth[n] = rand(); } @@ -812,8 +825,7 @@ void sessionshutdown(sessionidt s, char *reason) } } if (session[s].throttle) throttle_session(s, 0); session[s].throttle = 0; - free_ip_address(session[s].ip); - session[s].ip = 0; + free_ip_address(s); } { // Send CDN controlt *c = controlnew(14); // sending CDN @@ -851,7 +863,7 @@ void sendipcp(tunnelidt t, sessionidt s) *(u16 *) (q + 2) = htons(10); q[4] = 3; q[5] = 6; - *(u32 *) (q + 6) = htonl(myip ? : session[s].ip); // send my IP (use theirs if I dont have one) + *(u32 *) (q + 6) = htonl(myip ? myip : session[s].ip); // send my IP (use theirs if I dont have one) tunnelsend(buf, 10 + (q - buf), t); // send it } @@ -863,7 +875,7 @@ void sessionkill(sessionidt s, char *reason) #endif sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) - radius[session[s].radius].session = 0; // cant send clean accounting data, session is killed + radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed memset(&session[s], 0, sizeof(session[s])); session[s].next = sessionfree; sessionfree = s; @@ -879,6 +891,9 @@ void tunnelkill(tunnelidt t, char *reason) #ifdef STAT_CALLS STAT(call_tunnelkill); #endif + + tunnel[t].state = TUNNELDIE; + // free control messages while ((c = tunnel[t].controls)) { @@ -889,34 +904,39 @@ void tunnelkill(tunnelidt t, char *reason) controlfree = c; } // kill sessions - for (s = 0; s < MAXSESSION; s++) + for (s = 1; s < MAXSESSION; s++) if (session[s].tunnel == t) sessionkill(s, reason); + // free tunnel - memset(&tunnel[t], 0, sizeof(tunnel[t])); - tunnel[t].next = tunnelfree; + tunnelclear(t); cluster_send_tunnel(t); log(1, 0, 0, t, "Kill tunnel %d: %s\n", t, reason); - tunnelfree = t; + tunnel[t].die = 0; + tunnel[t].state = TUNNELFREE; } -// shut down a tunnel +// shut down a tunnel cleanly void tunnelshutdown(tunnelidt t, char *reason) { sessionidt s; #ifdef STAT_CALLS STAT(call_tunnelshutdown); #endif - if (!tunnel[t].last || !tunnel[t].far) - { // never set up, can immediately kill + if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE) + { + // never set up, can immediately kill tunnelkill(t, reason); return; } log(1, 0, 0, t, "Shutting down tunnel %d (%s)\n", t, reason); + // close session - for (s = 0; s < MAXSESSION; s++) + for (s = 1; s < MAXSESSION; s++) if (session[s].tunnel == t) sessionkill(s, reason); + + tunnel[t].state = TUNNELDIE; tunnel[t].die = now() + 700; // Clean up in 70 seconds cluster_send_tunnel(t); // TBA - should we wait for sessions to stop? @@ -1001,7 +1021,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) return; } l -= (p - buf); - if (t) tunnel[t].last = time_now; if (*buf & 0x80) { // control u16 message = 0xFFFF; // message type @@ -1026,41 +1045,25 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) // if no tunnel specified, assign one if (!t) { - /* - ipt ip = ntohl(*(ipt *) & addr->sin_addr); - portt port = ntohs(addr->sin_port); - - // find existing tunnel that was not fully set up - for (t = 0; t < MAXTUNNEL; t++) - { - if ((tunnel[t].ip == ip && tunnel[t].port == port) && - (!tunnel[t].die || !tunnel[t].hostname[0])) - { - char buf[600] = {0}; - snprintf(buf, 600, "Duplicate tunnel with %d. ip=%u port=%d die=%d hostname=%s", - t, tunnel[t].ip, tunnel[t].port, tunnel[t].die, tunnel[t].hostname); - tunnelshutdown(t, buf); - break; - } - } - */ - - t = tunnelfree; - if (!t) + if (!(t = new_tunnel())) { log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "No more tunnels\n"); STAT(tunnel_overflow); return; } - tunnelfree = tunnel[t].next; - memset(&tunnel[t], 0, sizeof(tunnelt)); + tunnelclear(t); tunnel[t].ip = ntohl(*(ipt *) & addr->sin_addr); tunnel[t].port = ntohs(addr->sin_port); tunnel[t].window = 4; // default window log(1, ntohl(addr->sin_addr.s_addr), 0, t, " New tunnel from %u.%u.%u.%u/%u ID %d\n", tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255, tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t); STAT(tunnel_created); } - { // check sequence of this message + + // This is used to time out old tunnels + tunnel[t].lastrec = time_now; + + // check sequence of this message + { int skip = tunnel[t].window; // track how many in-window packets are still in queue if (tunnel[t].controlc) { // some to clear maybe @@ -1083,12 +1086,12 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) // controlnull(t); return; } - if (l) - tunnel[t].nr++; // receiver advance (do here so quoted correctly in any sends below) - if (skip < 0) - skip = 0; + // receiver advance (do here so quoted correctly in any sends below) + if (l) tunnel[t].nr++; + if (skip < 0) skip = 0; if (skip < tunnel[t].controlc) - { // some control packets can now be sent that were previous stuck out of window + { + // some control packets can now be sent that were previous stuck out of window int tosend = tunnel[t].window - skip; controlt *c = tunnel[t].controls; while (c && skip) @@ -1128,7 +1131,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) if (flags & 0x40) { // handle hidden AVPs - if (!l2tpsecret) + if (!*config->l2tpsecret) { log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP requested, but no L2TP secret.\n"); fatal = flags; @@ -1193,11 +1196,10 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) } if (n > 4) { /* %*s doesn't work?? */ - char buf[n-4+2]; - memcpy(buf, b+4, n-4); - buf[n-4+1] = '\0'; + char *buf = (char *)strndup(b+4, n-4); log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error String: %s\n", buf); + free(buf); } break; } @@ -1222,8 +1224,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) // log(4, ntohl(addr->sin_addr.s_addr), s, t, "Bearer capabilities\n"); break; case 5: // tie breaker - // We never open tunnels, so we don't - // care about tie breakers + // We never open tunnels, so we don't care about tie breakers // log(4, ntohl(addr->sin_addr.s_addr), s, t, "Tie breaker\n"); continue; case 6: // firmware revision @@ -1422,11 +1423,13 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) control16(c, 9, t, 1); // assigned tunnel controladd(c, t, s); // send the resply } + tunnel[t].state = TUNNELOPENING; break; case 2: // SCCRP - // TBA + tunnel[t].state = TUNNELOPEN; break; case 3: // SCCN + tunnel[t].state = TUNNELOPEN; controlnull(t); // ack break; case 4: // StopCCN @@ -1457,39 +1460,36 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) u8 r; controlt *c; + s = sessionfree; + sessionfree = session[s].next; + memset(&session[s], 0, sizeof(session[s])); + // make a RADIUS session - if (!radiusfree) + if (!(r = radiusnew(s))) { - STAT(radius_overflow); log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); + sessionkill(s, "no free RADIUS sesions"); return; } c = controlnew(11); // sending ICRP - s = sessionfree; - sessionfree = session[s].next; - memset(&session[s], 0, sizeof(session[s])); session[s].id = sessionid++; session[s].opened = time(NULL); session[s].tunnel = t; session[s].far = asession; + session[s].last_packet = time_now; log(3, ntohl(addr->sin_addr.s_addr), s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far); control16(c, 14, s, 1); // assigned session controladd(c, t, s); // send the reply - r = radiusfree; - radiusfree = radius[r].next; - memset(&radius[r], 0, sizeof(radius[r])); - session[s].radius = r; - radius[r].session = s; { // Generate a random challenge int n; for (n = 0; n < 15; n++) radius[r].auth[n] = rand(); } - strcpy(radius[r].calling, calling); - strcpy(session[s].called, called); - strcpy(session[s].calling, calling); + 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); STAT(session_created); } break; @@ -1645,23 +1645,26 @@ void mainloop(void) FD_SET(tapfd, &cr); FD_SET(radfd, &cr); FD_SET(controlfd, &cr); -#ifdef HAVE_LIBCLI FD_SET(clifd, &cr); -#endif if (cluster_sockfd) FD_SET(cluster_sockfd, &cr); cn = udpfd; if (cn < radfd) cn = radfd; if (cn < tapfd) cn = tapfd; if (cn < controlfd) cn = controlfd; -#ifdef HAVE_LIBCLI if (cn < clifd) cn = clifd; -#endif if (cn < cluster_sockfd) cn = cluster_sockfd; while (!main_quit) { fd_set r; int n = cn; + + if (config->reload_config) + { + // Update the config state based on config settings + update_config(); + } + memcpy(&r, &cr, sizeof(fd_set)); n = select(n + 1, &r, 0, 0, &to); if (n < 0) @@ -1686,7 +1689,6 @@ void mainloop(void) processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)); else if (FD_ISSET(controlfd, &r)) processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); -#ifdef HAVE_LIBCLI else if (FD_ISSET(clifd, &r)) { struct sockaddr_in addr; @@ -1704,7 +1706,6 @@ void mainloop(void) close(sockfd); } } -#endif else { log(1, 0, 0, 0, "Main select() loop returned %d, but no fds have data waiting\n", n); @@ -1717,7 +1718,8 @@ void mainloop(void) sessionidt s; tunnelidt t; u8 r; - for (r = 0; r < MAXRADIUS; r++) + + for (r = 1; r < MAXRADIUS; r++) if (radius[r].state && radius[r].retry) { if (radius[r].retry <= when) @@ -1725,7 +1727,7 @@ void mainloop(void) if (radius[r].retry && radius[r].retry < best) best = radius[r].retry; } - for (t = 0; t < MAXTUNNEL; t++) + for (t = 1; t < MAXTUNNEL; t++) { // check for expired tunnels if (tunnel[t].die && tunnel[t].die <= when) @@ -1756,7 +1758,7 @@ void mainloop(void) best = tunnel[t].retry; } // Send hello - if (tunnel[t].ip && !tunnel[t].die && tunnel[t].last < when + 600 && !tunnel[t].controlc) + if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < when + 600) { controlt *c = controlnew(6); // sending HELLO controladd(c, t, 0); // send the message @@ -1764,15 +1766,14 @@ void mainloop(void) } } -#ifdef HAVE_LIBCLI // Check for sessions that have been killed from the CLI if (cli_session_kill[0]) { int i; - for (i = 0; i < MAXSESSION && cli_session_kill[i]; i++) + for (i = 1; i < MAXSESSION && cli_session_kill[i]; i++) { log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); - sessionshutdown(cli_session_kill[i], "Requested by CLI"); + sessionshutdown(cli_session_kill[i], "Requested by administrator"); cli_session_kill[i] = 0; } } @@ -1780,29 +1781,28 @@ void mainloop(void) if (cli_tunnel_kill[0]) { int i; - for (i = 0; i < MAXTUNNEL && cli_tunnel_kill[i]; i++) + for (i = 1; i < MAXTUNNEL && cli_tunnel_kill[i]; i++) { log(2, 0, cli_tunnel_kill[i], 0, "Dropping tunnel by CLI\n"); - tunnelshutdown(cli_tunnel_kill[i], "Requested by CLI"); + tunnelshutdown(cli_tunnel_kill[i], "Requested by administrator"); cli_tunnel_kill[i] = 0; } } -#endif - for (s = 0; s < MAXSESSION; s++) + for (s = 1; s < MAXSESSION; s++) { // check for expired sessions if (session[s].die && session[s].die <= when) { - STAT(session_timeout); sessionkill(s, "Expired"); continue; } // Drop sessions who have not responded within IDLE_TIMEOUT seconds - if (session[s].user[0] && (time_now - session[s].last_packet >= IDLE_TIMEOUT)) + if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT)) { sessionkill(s, "No response to LCP ECHO requests"); + STAT(session_timeout); continue; } @@ -1823,7 +1823,7 @@ void mainloop(void) continue; } } - if (accounting_dir && next_acct <= when) + if (config->accounting_dir && next_acct <= when) { // Dump accounting data next_acct = when + ACCT_TIME; @@ -1834,7 +1834,7 @@ void mainloop(void) { // Dump accounting data next_cluster_ping = when + 50; - cluster_send_message(cluster_address, bind_address, C_PING, hostname, strlen(hostname)); + cluster_send_message(config->cluster_address, config->bind_address, C_PING, hostname, strlen(hostname)); } if (best <= when) @@ -1852,38 +1852,47 @@ void initdata(void) int i; _statistics = mmap(NULL, sizeof(struct Tstats), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (_statistics <= 0) + if (_statistics == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for _statistics: %s\n", strerror(errno)); exit(1); } + config = mmap(NULL, sizeof(struct configt), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (config == MAP_FAILED) + { + log(0, 0, 0, 0, "Error doing mmap for configuration: %s\n", strerror(errno)); + exit(1); + } + memset(config, 0, sizeof(struct configt)); + time(&config->start_time); + strncpy(config->config_file, CONFIGFILE, sizeof(config->config_file) - 1); tunnel = mmap(NULL, sizeof(tunnelt) * MAXTUNNEL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (tunnel <= 0) + if (tunnel == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for tunnels: %s\n", strerror(errno)); exit(1); } session = mmap(NULL, sizeof(sessiont) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (session <= 0) + if (session == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for sessions: %s\n", strerror(errno)); exit(1); } radius = mmap(NULL, sizeof(radiust) * MAXRADIUS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (radius <= 0) + if (radius == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); exit(1); } ip_address_pool = mmap(NULL, sizeof(ippoolt) * MAXIPPOOL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (ip_address_pool <= 0) + if (ip_address_pool == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); exit(1); } #ifdef RINGBUFFER ringbuffer = mmap(NULL, sizeof(struct Tringbuffer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (ringbuffer <= 0) + if (ringbuffer == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); exit(1); @@ -1891,16 +1900,15 @@ void initdata(void) memset(ringbuffer, 0, sizeof(struct Tringbuffer)); #endif -#ifdef HAVE_LIBCLI cli_session_kill = mmap(NULL, sizeof(sessionidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (cli_session_kill <= 0) + if (cli_session_kill == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for cli session kill: %s\n", strerror(errno)); exit(1); } memset(cli_session_kill, 0, sizeof(sessionidt) * MAXSESSION); cli_tunnel_kill = mmap(NULL, sizeof(tunnelidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (cli_tunnel_kill <= 0) + if (cli_tunnel_kill == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for cli tunnel kill: %s\n", strerror(errno)); exit(1); @@ -1908,33 +1916,24 @@ void initdata(void) memset(cli_tunnel_kill, 0, sizeof(tunnelidt) * MAXSESSION); filter_buckets = mmap(NULL, sizeof(tbft) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (filter_buckets <= 0) + if (filter_buckets == MAP_FAILED) { log(0, 0, 0, 0, "Error doing mmap for filter buckets: %s\n", strerror(errno)); exit(1); } memset(filter_buckets, 0, sizeof(tbft) * MAXSESSION); -#endif - memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL); memset(session, 0, sizeof(sessiont) * MAXSESSION); memset(radius, 0, sizeof(radiust) * MAXRADIUS); memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL); - for (i = 1; i < MAXTUNNEL - 1; i++) - tunnel[i].next = i + 1; - tunnel[MAXTUNNEL - 1].next = 0; - tunnelfree = 1; for (i = 1; i < MAXSESSION - 1; i++) session[i].next = i + 1; session[MAXSESSION - 1].next = 0; sessionfree = 1; - for (i = 1; i < MAXRADIUS - 1; i++) - radius[i].next = i + 1; - radius[MAXRADIUS - 1].next = 0; - radiusfree = 1; if (!*hostname) { + char *p; // Grab my hostname unless it's been specified gethostname(hostname, sizeof(hostname)); { @@ -1942,12 +1941,10 @@ void initdata(void) if (h) myip = ntohl(*(u32 *) h->h_addr); } + + if ((p = strstr(hostname, ".optusnet.com.au"))) *p = 0; } _statistics->start_time = _statistics->last_reset = time(NULL); - - // Start the timer routine off - time(&time_now); - strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); } void initiptables(void) @@ -1957,51 +1954,78 @@ void initiptables(void) system("iptables -t mangle -F l2tpns"); } -ipt assign_ip_address() +int assign_ip_address(sessionidt s) { - int c = 0; + unsigned i; + int best = -1; + clockt best_time = time_now; + char *u = session[s].user; + char reuse = 0; + #ifdef STAT_CALLS STAT(call_assign_ip_address); #endif - ip_pool_index++; - while (1) + for (i = 0; i < ip_pool_size; i++) { - if (ip_pool_index >= ip_pool_size) + if (!ip_address_pool[i].address || ip_address_pool[i].assigned) + continue; + + if (!session[s].servicenet && ip_address_pool[i].user[0] && !strcmp(u, ip_address_pool[i].user)) { - if (++c == 2) - return 0; - ip_pool_index = 0; + best = i; + reuse = 1; + break; } - if (!ip_address_pool[ip_pool_index].assigned && ip_address_pool[ip_pool_index].address) + + if (ip_address_pool[i].last < best_time) { - ip_address_pool[ip_pool_index].assigned = 1; - log(4, ip_address_pool[ip_pool_index].address, 0, 0, "assign_ip_address(): Allocating ip address %lu from pool\n", ip_pool_index); - STAT(ip_allocated); - return ntohl(ip_address_pool[ip_pool_index].address); + best = i; + if (!(best_time = ip_address_pool[i].last)) + break; // never used, grab this one } - ip_pool_index++; } - return 0; + + if (best < 0) + { + log(0, 0, s, session[s].tunnel, "assign_ip_address(): out of addresses\n"); + return 0; + } + + session[s].ip = ntohl(ip_address_pool[best].address); + session[s].ip_pool_index = best; + ip_address_pool[best].assigned = 1; + ip_address_pool[best].last = time_now; + if (session[s].servicenet) + /* Don't track addresses of users in walled garden (note: this + means that their address isn't "sticky" even if they get + un-gardened). */ + ip_address_pool[best].user[0] = 0; + else + strncpy(ip_address_pool[best].user, u, sizeof(ip_address_pool[best].user) - 1); + + STAT(ip_allocated); + log(4, ip_address_pool[best].address, s, session[s].tunnel, + "assign_ip_address(): %s ip address %lu from pool\n", reuse ? "Reusing" : "Allocating", best); + + return 1; } -void free_ip_address(ipt address) +void free_ip_address(sessionidt s) { - int i; - ipt a; + int i = session[s].ip_pool_index; + #ifdef STAT_CALLS STAT(call_free_ip_address); #endif - a = ntohl(address); - for (i = 0; i <= ip_pool_size; i++) - { - if (ip_address_pool[i].address == a) - { - STAT(ip_freed); - ip_address_pool[i].assigned = 0; - } - } - uncache_sessionid(htonl(address)); + if (!session[s].ip) + return; // what the? + + STAT(ip_freed); + uncache_sessionid(session[s].ip); + session[s].ip = 0; + ip_address_pool[i].assigned = 0; + ip_address_pool[i].last = time_now; } // Initialize the IP address pool @@ -2037,7 +2061,7 @@ void initippool() exit(-1); } // This entry is for a specific IP only - if (src != bind_address) + if (src != config->bind_address) continue; *p = ':'; pool = p+1; @@ -2070,7 +2094,7 @@ void initippool() // Add a static route for this pool log(5, 0, 0, 0, "Adding route for address pool %s/%d\n", inet_toa(htonl(start)), 32+mask); memset(&r, 0, sizeof(r)); - r.rt_dev = tapdevice; + r.rt_dev = config->tapdevice; r.rt_dst.sa_family = AF_INET; *(u32 *) & (((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr) = htonl(start); r.rt_genmask.sa_family = AF_INET; @@ -2099,6 +2123,7 @@ void snoop_send_packet(char *packet, u16 size) if (!snoop_addr.sin_port || snoopfd <= 0 || size <= 0 || !packet) return; + log(5, 0, 0, 0, "Snooping packet at %p (%d bytes) to %s:%d\n", packet, size, inet_toa(snoop_addr.sin_addr.s_addr), htons(snoop_addr.sin_port)); if (sendto(snoopfd, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, (void *) &snoop_addr, sizeof(snoop_addr)) < 0) log(0, 0, 0, 0, "Error sending intercept packet: %s\n", strerror(errno)); STAT(packets_snooped); @@ -2116,11 +2141,11 @@ void dump_acct_info() STAT(call_dump_acct_info); #endif strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t)); - snprintf(filename, 1024, "%s/%s", accounting_dir, timestr); + snprintf(filename, 1024, "%s/%s", config->accounting_dir, timestr); for (i = 0; i < MAXSESSION; i++) { - if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden) + if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].servicenet) continue; if (!f) { @@ -2176,21 +2201,18 @@ int main(int argc, char *argv[]) time(&basetime); // start clock // scan args - while ((o = getopt(argc, argv, "vc:f:h:a:")) >= 0) + while ((o = getopt(argc, argv, "vc:h:a:")) >= 0) { switch (o) { case 'v': - debug++; + config->debug++; break; case 'c': - config_file = strdup(optarg); - break; - case 'f': - log_filename = strdup(optarg); + strncpy(config->config_file, optarg, sizeof(config->config_file) - 1); break; case 'h': - strncpy(hostname, optarg, 1000); + strncpy(hostname, optarg, 999); break; case 'a': myip = inet_addr(optarg); @@ -2198,45 +2220,51 @@ int main(int argc, char *argv[]) log(0, 0, 0, 0, "Invalid ip %s\n", optarg); exit(-1); } - bind_address = myip; + config->bind_address = myip; handle_interface = 1; break; case '?': default: - printf("Args are:\n\t-c \tConfig file\n\t-h \tForce hostname\n\t-a
\tUse specific address\n\t-f \tLog File\n\t-v\t\tDebug\n"); + printf("Args are:\n\t-c \tConfig file\n\t-h \tForce hostname\n\t-a
\tUse specific address\n\t-v\t\tDebug\n"); return (0); break; } } + // Start the timer routine off + time(&time_now); + strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + initiptables(); initplugins(); - read_config_file(); initdata(); - log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + init_cli(); + read_config_file(); + log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); /* Start up the cluster first, so that we don't have two machines with * the same IP at once. * This is still racy, but the second GARP should fix that */ - cluster_init(bind_address, 0); - cluster_send_message(cluster_address, bind_address, C_HELLO, hostname, strlen(hostname)); + cluster_init(config->bind_address, 0); + cluster_send_message(config->cluster_address, config->bind_address, C_HELLO, hostname, strlen(hostname)); inittap(); - log(1, 0, 0, 0, "Set up on interface %s\n", tapdevice); + log(1, 0, 0, 0, "Set up on interface %s\n", config->tapdevice); initudp(); initrad(); initippool(); init_rl(); if (handle_interface) { - send_garp(bind_address); + send_garp(config->bind_address); } - read_state(); -#ifdef HAVE_LIBCLI - init_cli(); -#endif + // If NOSTATEFILE exists, we will ignore any updates from the cluster master for this execution + if (!unlink(NOSTATEFILE)) + config->ignore_cluster_updates = 1; + + read_state(); signal(SIGALRM, sigalrm_handler); signal(SIGHUP, sighup_handler); @@ -2245,55 +2273,47 @@ int main(int argc, char *argv[]) signal(SIGQUIT, sigquit_handler); signal(SIGCHLD, sigchild_handler); signal(SIGSEGV, sigsegv_handler); - if (debug) - { - int n; - for (n = 0; n < numradiusservers; n++) - log(1, 0, 0, 0, "RADIUS to %s\n", inet_toa(htonl(radiusserver[n]))); - } alarm(1); // Drop privileges here - if (target_uid > 0 && geteuid() == 0) - setuid(target_uid); + if (config->target_uid > 0 && geteuid() == 0) + setuid(config->target_uid); mainloop(); - - if (l2tpsecret) free(l2tpsecret); - if (log_filename) free(log_filename); - if (snoop_destination_host) free(snoop_destination_host); - if (radiussecret) free(radiussecret); - return 0; } void sighup_handler(int junk) { - if (log_stream != stderr) + if (log_stream && log_stream != stderr) + { fclose(log_stream); + log_stream = NULL; + } - log_stream = NULL; read_config_file(); } void sigalrm_handler(int junk) { // Log current traffic stats - if (dump_speed) - { - printf("UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%lu OUT:%lu\n", - (udp_rx / 1024.0 / 1024.0 * 8), - (eth_tx / 1024.0 / 1024.0 * 8), - (eth_rx / 1024.0 / 1024.0 * 8), - (udp_tx / 1024.0 / 1024.0 * 8), - ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8), - udp_rx_pkt, eth_rx_pkt); - udp_tx = udp_rx = 0; - udp_rx_pkt = eth_rx_pkt = 0; - eth_tx = eth_rx = 0; - } + snprintf(config->bandwidth, sizeof(config->bandwidth), + "UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%lu OUT:%lu", + (udp_rx / 1024.0 / 1024.0 * 8), + (eth_tx / 1024.0 / 1024.0 * 8), + (eth_rx / 1024.0 / 1024.0 * 8), + (udp_tx / 1024.0 / 1024.0 * 8), + ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8), + udp_rx_pkt, eth_rx_pkt); + + udp_tx = udp_rx = 0; + udp_rx_pkt = eth_rx_pkt = 0; + eth_tx = eth_rx = 0; + + if (config->dump_speed) + printf("%s\n", config->bandwidth); // Update the internal time counter time(&time_now); @@ -2311,48 +2331,43 @@ void sigalrm_handler(int junk) void sigterm_handler(int junk) { log(1, 0, 0, 0, "Shutting down cleanly\n"); - if (config_save_state) + if (config->save_state) dump_state(); main_quit++; } void sigquit_handler(int junk) { + FILE *f; int i; + log(1, 0, 0, 0, "Shutting down without saving sessions\n"); - for (i = 0; i < MAXSESSION; i++) + for (i = 1; i < MAXSESSION; i++) { if (session[i].opened) sessionkill(i, "L2TPNS Closing"); } - for (i = 0; i < MAXTUNNEL; i++) + for (i = 1; i < MAXTUNNEL; i++) { - if (tunnel[i].ip) + if (tunnel[i].ip || tunnel[i].state) tunnelshutdown(i, "L2TPNS Closing"); } + + cluster_send_goodbye(); + + // Touch a file which says not to reload the state + f = fopen(NOSTATEFILE, "w"); + if (f) fclose(f); + main_quit++; } void sigchild_handler(int signal) { - int status; - int pid; - - pid = wait(&status); -#ifdef HAVE_LIBCLI - status = (WIFEXITED(status)) ? WEXITSTATUS(status) : 0; - if (pid == cli_pid) - { - if (status == 0) - log(3, 0, 0, 0, "CLI client closed connection\n"); - else - log(2, 0, 0, 0, "CLI child died with rc %d!\n", status); - } -#endif + while (waitpid(-1, NULL, WNOHANG) > 0) + ; } -void *backtrace_buffer[30] = {0}; - void sigsegv_handler(int signal) { log(0, 0, 0, 0, "----------------------------------------------\n"); @@ -2363,135 +2378,169 @@ void sigsegv_handler(int signal) void read_state() { - struct stat sb; - FILE *f; + struct stat sb; + int i; + ippoolt itmp; + FILE *f; + char magic[sizeof(DUMP_MAGIC)-1]; + u32 buf[2]; - if (!config_save_state) return; + if (!config->save_state) + return; - if (stat(STATEFILE, &sb) < 0) - return; + // Ignore saved state if NOSTATEFILE exists + if (config->ignore_cluster_updates) + { + unlink(STATEFILE); + return; + } - if (sb.st_mtime < (time(NULL) - 60)) - { - log(0, 0, 0, 0, "State file is too old to read\n"); - unlink(STATEFILE); - return; - } + if (stat(STATEFILE, &sb) < 0) + return; - if (!(f = fopen(STATEFILE, "r"))) + if (sb.st_mtime < (time(NULL) - 60)) + { + log(0, 0, 0, 0, "State file is too old to read, ignoring\n"); + unlink(STATEFILE); + return; + } + + f = fopen(STATEFILE, "r"); + unlink(STATEFILE); + + if (!f) + { + log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno)); + exit(1); + } + + if (fread(magic, sizeof(magic), 1, f) != 1 || strncmp(magic, DUMP_MAGIC, sizeof(magic))) + { + log(0, 0, 0, 0, "Bad state file magic\n"); + exit(1); + } + + log(1, 0, 0, 0, "Reading state information\n"); + if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] > MAXIPPOOL || buf[1] != sizeof(ippoolt)) + { + log(0, 0, 0, 0, "Error/mismatch reading ip pool header from state file\n"); + exit(1); + } + + if (buf[0] > ip_pool_size) + { + log(0, 0, 0, 0, "ip pool has shrunk! state = %d, current = %d\n", buf[0], ip_pool_size); + exit(1); + } + + log(2, 0, 0, 0, "Loading %u ip addresses\n", buf[0]); + for (i = 0; i < buf[0]; i++) + { + if (fread(&itmp, sizeof(itmp), 1, f) != 1) { - log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno)); - unlink(STATEFILE); - return; + log(0, 0, 0, 0, "Error reading ip %d from state file: %s\n", i, strerror(errno)); + exit(1); } - fseek(f, 0, 0); - log(1, 0, 0, 0, "Reading state information\n"); + if (itmp.address != ip_address_pool[i].address) { - u32 i, numtunnels; - if (fread(&numtunnels, sizeof(numtunnels), 1, f) <= 0) - { - log(0, 0, 0, 0, "Error reading saved state (tunnel count): %s\n", strerror(errno)); - fclose(f); - unlink(STATEFILE); - return; - } - log(2, 0, 0, 0, "Reading %lu tunnels\n", numtunnels); - fread(tunnel, sizeof(tunnelt), numtunnels, f); - tunnelfree = 0; - for (i = 0; i < numtunnels; i++) - { - tunnel[i].controlc = 0; - tunnel[i].controls = NULL; - tunnel[i].controle = NULL; - if (*tunnel[i].hostname) - { - log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname); - tunnelfree = i; - } - } - tunnelfree++; + log(0, 0, 0, 0, "Mismatched ip %d from state file: pool may only be extended\n", i); + exit(1); } + + memcpy(&ip_address_pool[i], &itmp, sizeof(itmp)); + } + + if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXTUNNEL || buf[1] != sizeof(tunnelt)) + { + log(0, 0, 0, 0, "Error/mismatch reading tunnel header from state file\n"); + exit(1); + } + + log(2, 0, 0, 0, "Loading %u tunnels\n", MAXTUNNEL); + if (fread(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL) + { + log(0, 0, 0, 0, "Error reading tunnel data from state file\n"); + exit(1); + } + + for (i = 0; i < MAXTUNNEL; i++) + { + tunnel[i].controlc = 0; + tunnel[i].controls = NULL; + tunnel[i].controle = NULL; + if (*tunnel[i].hostname) + log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname); + } + + if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXSESSION || buf[1] != sizeof(sessiont)) + { + log(0, 0, 0, 0, "Error/mismatch reading session header from state file\n"); + exit(1); + } + + log(2, 0, 0, 0, "Loading %u sessions\n", MAXSESSION); + if (fread(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION) + { + log(0, 0, 0, 0, "Error reading session data from state file\n"); + exit(1); + } + + for (i = 0; i < MAXSESSION; i++) + { + session[i].tbf = 0; + session[i].throttle = 0; + if (session[i].opened) { - u32 i, numsessions; - if (fread(&numsessions, sizeof(numsessions), 1, f) <= 0) - { - log(0, 0, 0, 0, "Error reading saved state (session count): %s\n", strerror(errno)); - fclose(f); - unlink(STATEFILE); - return; - } - log(2, 0, 0, 0, "Reading %lu sessions\n", numsessions); - if (fread(session, sizeof(sessiont), numsessions, f) < numsessions) - { - log(0, 0, 0, 0, "Error reading saved state (%d sessions): %s\n", numsessions, strerror(errno)); - fclose(f); - unlink(STATEFILE); - return; - } - for (i = 0; i < numsessions; i++) - { - session[i].tbf = 0; - session[i].throttle = 0; - if (session[i].opened) - { - log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user); - if (session[i].ip && session[i].ip != 0xFFFFFFFE) - { - int x; - sessionsetup(session[i].tunnel, i, 0); - for (x = 0; x < MAXIPPOOL && ip_address_pool[x].address; x++) - { - if (ip_address_pool[x].address == session[i].ip) - { - ip_address_pool[x].assigned = 1; - break; - } - } - } - else - { - log(2, 0, i, 0, "No IP for session\n"); - } - } - } - for (i = 0; i < numsessions && session[i].opened; i++) - sessionfree = session[i].next; + log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user); + if (session[i].ip && session[i].ip != 0xFFFFFFFE) + sessionsetup(session[i].tunnel, i, 0); } - fclose(f); - log(0, 0, 0, 0, "Loaded saved state information\n"); - unlink(STATEFILE); + } + + fclose(f); + log(0, 0, 0, 0, "Loaded saved state information\n"); } void dump_state() { - FILE *f; + FILE *f; + u32 buf[2]; - if (!config_save_state) return; + if (!config->save_state) + return; - if ((f = fopen(STATEFILE, "w"))) - { - u32 i; - log(1, 0, 0, 0, "Dumping state information\n"); + do { + if (!(f = fopen(STATEFILE, "w"))) + break; - i = MAXTUNNEL; - fwrite(&i, sizeof(i), 1, f); // Number of tunnels + log(1, 0, 0, 0, "Dumping state information\n"); - log(2, 0, 0, 0, "Dumping %lu tunnels\n", i); - fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f); + if (fwrite(DUMP_MAGIC, sizeof(DUMP_MAGIC)-1, 1, f) != 1) break; - i = MAXSESSION; - fwrite(&i, sizeof(i), 1, f); // Number of sessions - log(2, 0, 0, 0, "Dumping %lu sessions\n", i); - fwrite(session, sizeof(sessiont), MAXSESSION, f); + log(2, 0, 0, 0, "Dumping %u ip addresses\n", ip_pool_size); + buf[0] = ip_pool_size; + buf[1] = sizeof(ippoolt); + if (fwrite(buf, sizeof(buf), 1, f) != 1) break; + if (fwrite(ip_address_pool, sizeof(ippoolt), ip_pool_size, f) != ip_pool_size) break; - fclose(f); - } - else - { - log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno)); - } - return; + log(2, 0, 0, 0, "Dumping %u tunnels\n", MAXTUNNEL); + buf[0] = MAXTUNNEL; + buf[1] = sizeof(tunnelt); + if (fwrite(buf, sizeof(buf), 1, f) != 1) break; + if (fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL) break; + + log(2, 0, 0, 0, "Dumping %u sessions\n", MAXSESSION); + buf[0] = MAXSESSION; + buf[1] = sizeof(sessiont); + if (fwrite(buf, sizeof(buf), 1, f) != 1) break; + if (fwrite(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION) break; + + if (fclose(f) == 0) return; // OK + } while (0); + + log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno)); + unlink(STATEFILE); } void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response) @@ -2499,222 +2548,130 @@ void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **ch MD5_CTX ctx; *challenge_response = NULL; - if (!l2tpsecret || !*l2tpsecret) + if (!*config->l2tpsecret) { log(0, 0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n"); return; } - /* - if (challenge_length != 16) - { - log(0, 0, 0, 0, "Challenge length != 16.\n"); - return; - } - */ - log(4, 0, 0, 0, " Building challenge response for CHAP request\n"); *challenge_response = (char *)calloc(17, 1); MD5Init(&ctx); MD5Update(&ctx, &id, 1); - MD5Update(&ctx, l2tpsecret, strlen(l2tpsecret)); + MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); MD5Update(&ctx, challenge, challenge_length); MD5Final(*challenge_response, &ctx); return; } -void read_config_file() +static int facility_value(char *name) { - FILE *f; - char *buf; - - if (!config_file) return; - if (!(f = fopen(config_file, "r"))) { - fprintf(stderr, "Can't open config file %s: %s\n", config_file, strerror(errno)); - return; - } + int i; + for (i = 0; facilitynames[i].c_name; i++) + { + if (strcmp(facilitynames[i].c_name, name) == 0) + return facilitynames[i].c_val; + } + return 0; +} - if (radiussecret) - { - free(radiussecret); - radiussecret = NULL; - } +void update_config() +{ + int i; - if (l2tpsecret) - { - free(l2tpsecret); - l2tpsecret = NULL; - } + snoop_addr.sin_family = AF_INET; + snoop_addr.sin_addr.s_addr = config->snoop_destination_host; + snoop_addr.sin_port = htons(config->snoop_destination_port); - if (log_filename) + // Update logging + closelog(); + syslog_log = 0; + if (log_stream) { - free(log_filename); - log_filename = NULL; + fclose(log_stream); + log_stream = NULL; } - - if (snoop_destination_host) + if (*config->log_filename) { - free(snoop_destination_host); - snoop_destination_host = NULL; - } - - if (numradiusservers) { - int n; - for (n = 0; n < numradiusservers; n++) - radiusserver[n] = 0; - numradiusservers = 0; - } - - snoop_destination_port = 0L; - config_save_state = 0; - rl_rate = 0L; - debug = 1; - default_dns1 = default_dns2 = 0; - radius_accounting = 0; - - buf = (char *)malloc(4096); - - while (fgets(buf, 4096, f)) { - char *p, *t; - - if (*buf == '#') continue; - if ((p = strchr(buf, '\n'))) *p = 0; - p = t = strchr(buf, '='); - if (!p) continue; - *p = 0; p++; - t--; - while (*p && *p == ' ') p++; - while (*t && *t == ' ') *t-- = 0; - - if (strcmp(buf, "log file") == 0) { - if (!log_filename) - log_filename = strdup(p); - } else if (strcmp(buf, "l2tp secret") == 0) { - if (!l2tpsecret) - l2tpsecret = strdup(p); - log(0, 0, 0, 0, "L2TP Secret is \"%s\"\n", l2tpsecret); - } else if (strcmp(buf, "radius secret") == 0) { - if (!radiussecret) - radiussecret = strdup(p); - log(4, 0, 0, 0, "Radius Secret is \"%s\"\n", radiussecret); - } else if (strcmp(buf, "radius accounting") == 0) { - radius_accounting = atoi(p); - log(4, 0, 0, 0, "Radius Account is %s\n", radius_accounting ? "on" : "off"); - } else if (strcmp(buf, "throttle rate") == 0) { - rl_rate = atol(p); - if (rl_rate == 0) + if (strstr(config->log_filename, "file:") == config->log_filename) + { + if ((log_stream = fopen((char *)(config->log_filename + 5), "a"))) { - log(1, 0, 0, 0, "Disabled throttling.\n"); + fseek(log_stream, 0, SEEK_END); + setbuf(log_stream, NULL); } else { - log(1, 0, 0, 0, "Enabled throttling (rate is %lu kbits/s)\n", rl_rate); + log_stream = stderr; + setbuf(log_stream, NULL); } - } else if (strcmp(buf, "debug") == 0) { - debug = atoi(p); - log(debug, 0, 0, 0, "Set debugging level to %d\n", debug); - } else if (strcmp(buf, "accounting dir") == 0) { - accounting_dir = strdup(p); - log(debug, 0, 0, 0, "Will dump accounting information to %s\n", accounting_dir); - } else if (strcmp(buf, "dns server") == 0) { - unsigned long addr = 0; - if (inet_aton(p, (struct in_addr *)&addr) < 0) { - printf("Invalid DNS server %s\n", p); - continue; - } - if (default_dns1 == 0) - default_dns1 = addr; - else if (default_dns2 == 0) - default_dns2 = addr; - } else if (strcmp(buf, "radius server") == 0) { - struct hostent *h = gethostbyname(p); - if (h) - { - while (*h->h_addr_list) - { - ipt ip = ntohl(*(u32 *) * h->h_addr_list); - if (numradiusservers < MAXRADSERVER) - radiusserver[numradiusservers++] = ip; - else - log(0, 0, 0, 0, "Too many RADIUS IPs\n"); - h->h_addr_list++; - } - } - else - { // may be IP? - ipt ip = ntohl(inet_addr(p)); - if (ip && ip != 0xFFFFFFFF) - { - if (numradiusservers < MAXRADSERVER) - radiusserver[numradiusservers++] = ip; - else - log(0, 0, 0, 0, "Too many RADIUS IPs\n"); - } - else - log(0, 0, 0, 0, "Unknown server %s\n", p); - } - } else if (strcmp(buf, "snoop host") == 0) { - snoop_destination_host = strdup(p); - } else if (strcmp(buf, "snoop port") == 0) { - snoop_destination_port = atol(p); - } else if (strcmp(buf, "bind address") == 0) { - if (!bind_address) - { - // Already overridden on the command line - bind_address = inet_addr(p); - handle_interface = 1; - } - } else if (strcmp(buf, "dump speed") == 0) { - dump_speed = atoi(p); - } else if (strcmp(buf, "setuid") == 0) { - target_uid = atoi(p); - } else if (strcmp(buf, "cluster master") == 0) { - struct hostent *h = gethostbyname(p); - if (h) + } + else if (strstr(config->log_filename, "syslog:") == config->log_filename) + { + char *p = config->log_filename + 7; + if (*p) { - if (*h->h_addr_list) - { - cluster_address = *(u32 *) *h->h_addr_list; - } - } - else - { // may be IP? - cluster_address = inet_addr(p); - } - } else if (strcmp(buf, "save state") == 0) { - if (strcasecmp(p, "no") == 0) { - config_save_state = 0; - } else { - config_save_state = 1; + openlog("l2tpns", LOG_PID, facility_value(p)); + syslog_log = 1; } - } else if (strcmp(buf, "plugin") == 0) { - add_plugin(p); - } else { - struct param_config cp = { buf, p }; - int rc = run_plugins(PLUGIN_CONFIG, &cp); - if (rc == 0) log(0, 0, 0, 0, "Unknown config directive \"%s\"\n", buf); } } + else + { + log_stream = stderr; + setbuf(log_stream, NULL); + } + - if (snoop_destination_host) + // Update radius + config->numradiusservers = 0; + for (i = 0; i < MAXRADSERVER; i++) + if (config->radiusserver[i]) config->numradiusservers++; + + if (!config->numradiusservers) { - if (inet_aton(snoop_destination_host, &snoop_addr.sin_addr)) + log(0, 0, 0, 0, "No RADIUS servers defined!\n"); + } + + // Update plugins + for (i = 0; i < MAXPLUGINS; i++) + { + if (strcmp(config->plugins[i], config->old_plugins[i]) == 0) + continue; + if (*config->plugins[i]) { - snoop_addr.sin_port = htons(snoop_destination_port); - snoop_addr.sin_family = AF_INET; + // Plugin added + add_plugin(config->plugins[i]); } - else + else if (*config->old_plugins[i]) { - log(0, 0, 0, 0, "Can't find address for snoop host %s\n", snoop_destination_host); + // Plugin removed + remove_plugin(config->old_plugins[i]); } } + memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); + config->reload_config = 0; +} - free(buf); +void read_config_file() +{ + FILE *f; + + if (!config->config_file) return; + if (!(f = fopen(config->config_file, "r"))) { + fprintf(stderr, "Can't open config file %s: %s\n", config->config_file, strerror(errno)); + return; + } + + log(3, 0, 0, 0, "Reading config file %s\n", config->config_file); + cli_do_file(f); + log(3, 0, 0, 0, "Done reading config file\n"); fclose(f); - log(2, 0, 0, 0, "Done reading config file\n"); + update_config(); + log_stream = NULL; } int sessionsetup(tunnelidt t, sessionidt s, u8 routes) @@ -2731,26 +2688,19 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes) log(0, session[s].ip, s, t, "VERY VERY BAD! sessionsetup() called with no session[s].ip\n"); return 1; } - if (session[s].ip == 0xFFFFFFFE) - { - session[s].ip = assign_ip_address(); // Assign one from the pool; - - log(2, session[s].ip, s, t, "IP assigned is a magic token. Assign address from pool: %s\n", - inet_toa(htonl(session[s].ip))); - } // Make sure this is right session[s].tunnel = t; // zap old sessions with same IP and/or username - // Don't kill walled_garden sessions - doing so leads to a DoS + // Don't kill walled garden sessions - doing so leads to a DoS // from someone who doesn't need to know the password ip = session[s].ip; user = session[s].user; - for (i = 0; i < MAXSESSION; i++) + for (i = 1; i < MAXSESSION; i++) { if (i == s) continue; if (ip == session[i].ip) sessionkill(i, "Duplicate IP address"); - if (!session[s].walled_garden && !session[i].walled_garden && strcasecmp(user, session[i].user) == 0) + if (!session[s].servicenet && !session[i].servicenet && strcasecmp(user, session[i].user) == 0) sessionkill(i, "Duplicate session for user"); } @@ -2779,7 +2729,9 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes) run_plugins(PLUGIN_NEW_SESSION, &data); } - session[s].sid = ++last_sid; + if (!session[s].sid) + session[s].sid = ++last_sid; + cache_sessionid(htonl(session[s].ip), s); cluster_send_session(s); @@ -2951,7 +2903,8 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 }; log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received "); - dump_packet(buf, log_stream); + if (log_stream) + dump_packet(buf, log_stream); resp = calloc(1400, 1); l = new_packet(PKT_RESP_ERROR, resp); @@ -2986,3 +2939,46 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) free(resp); } +/* + * HACK + * Go through all of the tunnels and do some cleanups + */ +void tunnel_clean() +{ + int i; + + log(1, 0, 0, 0, "Cleaning tunnels array\n"); + + for (i = 1; i < MAXTUNNEL; i++) + { + if (!tunnel[i].ip + || !*tunnel[i].hostname + || (tunnel[i].state == TUNNELDIE && tunnel[i].die >= time_now)) + { + tunnelclear(i); + } + } +} + +void tunnelclear(tunnelidt t) +{ + if (!t) return; + memset(&tunnel[t], 0, sizeof(tunnel[t])); + tunnel[t].state = TUNNELFREE; +} + +tunnelidt new_tunnel() +{ + tunnelidt i; + for (i = 1; i < MAXTUNNEL; i++) + { + if (tunnel[i].state == TUNNELFREE) + { + log(4, 0, 0, i, "Assigning tunnel ID %d\n", i); + return i; + } + } + log(0, 0, 0, 0, "Can't find a free tunnel! There shouldn't be this many in use!\n"); + return 0; +} + diff --git a/l2tpns.h b/l2tpns.h index afbfe3f..ff2e9e9 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,12 +1,11 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include - #include "config.h" -#define VERSION "1.0" +#define VERSION "1.1.0" // Limits #define MAXTUNNEL 500 // could be up to 65535 @@ -15,6 +14,7 @@ #define MAXCONTROL 1000 // max length control message we ever send... #define MAXETHER (1500+18) // max packet we try sending to tap #define MAXTEL 96 // telephone number +#define MAXPLUGINS 20 // maximum number of plugins to load #define MAXRADSERVER 10 // max radius servers #define MAXROUTE 10 // max static routes per session #define MAXIPPOOL 131072 // max number of ip addresses in pool @@ -27,17 +27,17 @@ #define STATISTICS #define STAT_CALLS #define RINGBUFFER -#define UDP 17 #define TAPDEVICE "/dev/net/tun" -#define CLIUSERS ETCDIR "l2tpns.users" // CLI Users file +#define UDP 17 +#define HOMEDIR "/home/l2tpns/" // Base dir for data +#define STATEFILE "/tmp/l2tpns.dump" // State dump file +#define NOSTATEFILE "/tmp/l2tpns.no_state_reload" // If exists, state will not be reloaded #define CONFIGFILE ETCDIR "l2tpns.cfg" // Configuration file -#define IPPOOLFILE ETCDIR "l2tpns.ip_pool" // Address pool configuration -#define STATEFILE "/tmp/l2tpns.dump" // State dump file - +#define CLIUSERS ETCDIR "l2tpns.users" // CLI Users file +#define IPPOOLFILE ETCDIR "l2tpns.ip_pool" // Address pool configuration #ifndef LIBDIR #define LIBDIR "/usr/lib/l2tpns" #endif - #define ACCT_TIME 3000 // 5 minute accounting interval #define L2TPPORT 1701 // L2TP port #define RADPORT 1645 // old radius port... @@ -52,20 +52,20 @@ #define PPPCCP 0x80FD #define PPPIP 0x0021 #define PPPMP 0x003D -#define ConfigReq 1 -#define ConfigAck 2 -#define ConfigNak 3 -#define ConfigRej 4 -#define TerminateReq 5 -#define TerminateAck 6 -#define CodeRej 7 -#define ProtocolRej 8 -#define EchoReq 9 -#define EchoReply 10 -#define DiscardRequest 11 - -#undef TC_TBF -#define TC_HTB +enum +{ + ConfigReq = 1, + ConfigAck, + ConfigNak, + ConfigRej, + TerminateReq, + TerminateAck, + CodeRej, + ProtocolRej, + EchoReq, + EchoReply, + DiscardRequest +}; // Types typedef unsigned short u16; @@ -78,6 +78,9 @@ typedef u16 tunnelidt; typedef u32 clockt; typedef u8 hasht[16]; +// dump header: update number if internal format changes +#define DUMP_MAGIC "L2TPNS#" VERSION "#" + // structures typedef struct routes // route { @@ -88,7 +91,7 @@ routet; typedef struct controls // control message { - struct controls *next; // next in queue + struct controls *next; // next in queue u16 length; // length u8 buf[MAXCONTROL]; } @@ -106,34 +109,37 @@ typedef struct stbft // 336 bytes per session typedef struct sessions { - sessionidt next; // next session in linked list - sessionidt far; // far end session ID - tunnelidt tunnel; // tunnel ID - ipt ip; // IP of session set by RADIUS response - unsigned long sid; // session id for hsddb - u16 nr; // next receive - u16 ns; // next send - u32 magic; // ppp magic number - u32 cin, cout; // byte counts - u32 pin, pout; // packet counts - u32 id; // session id - clockt opened; // when started - clockt die; // being closed, when to finally free - time_t last_packet; // Last packet from the user (used for idle timeouts) - ipt dns1, dns2; // DNS servers - routet route[MAXROUTE]; // static routes - u8 radius; // which radius session is being used (0 for not waiting on authentication) - u8 flags; // various bit flags - u8 snoop; // are we snooping this session? - u8 throttle; // is this session throttled? - u8 walled_garden; // is this session stuck in the walled garden? - u16 mru; // maximum receive unit - u16 tbf; // filter bucket for throttling + sessionidt next; // next session in linked list + sessionidt far; // far end session ID + tunnelidt tunnel; // tunnel ID + ipt ip; // IP of session set by RADIUS response + int ip_pool_index; // index to IP pool + unsigned long sid; // session id for hsddb + u16 nr; // next receive + u16 ns; // next send + u32 magic; // ppp magic number + u32 cin, cout; // byte counts + u32 pin, pout; // packet counts + u32 total_cin; // This counter is never reset while a session is open + u32 total_cout; // This counter is never reset while a session is open + u32 id; // session id + clockt opened; // when started + clockt die; // being closed, when to finally free + time_t last_packet; // Last packet from the user (used for idle timeouts) + ipt dns1, dns2; // DNS servers + routet route[MAXROUTE]; // static routes + u8 radius; // which radius session is being used (0 for not waiting on authentication) + u8 flags; // various bit flags + u8 snoop; // are we snooping this session? + u8 throttle; // is this session throttled? + u8 servicenet; // is this session servicenetted? + u16 mru; // maximum receive unit + u16 tbf; // filter bucket for throttling char random_vector[MAXTEL]; int random_vector_length; - char user[129]; // user (needed in seesion for radius stop messages) - char called[MAXTEL]; // called number - char calling[MAXTEL]; // calling number + char user[129]; // user (needed in seesion for radius stop messages) + char called[MAXTEL]; // called number + char calling[MAXTEL]; // calling number unsigned long tx_connect_speed; unsigned long rx_connect_speed; } @@ -145,16 +151,17 @@ sessiont; // 168 bytes per tunnel typedef struct tunnels { - tunnelidt next; // next tunnel in linked list tunnelidt far; // far end tunnel ID ipt ip; // Ip for far end portt port; // port for far end u16 window; // Rx window u16 nr; // next receive u16 ns; // next send + int state; // current state (tunnelstate enum) clockt last; // when last control message sent (used for resend timeout) clockt retry; // when to try resenting pending control clockt die; // being closed, when to finally free + clockt lastrec; // when the last control message was received char hostname[128]; // tunnel hostname char vendor[128]; // LAC vendor u8 try; // number of retrys on a control message @@ -167,10 +174,9 @@ tunnelt; // 180 bytes per radius session typedef struct radiuss // outstanding RADIUS requests { - u8 next; // next in free list sessionidt session; // which session this applies to hasht auth; // request authenticator - clockt retry; // ehwne to try next + clockt retry; // when to try next char calling[MAXTEL]; // calling number char pass[129]; // password u8 id; // ID for PPP response @@ -184,6 +190,8 @@ typedef struct { ipt address; char assigned; // 1 if assigned, 0 if free + clockt last; // last used + char user[129]; // user (try to have ip addresses persistent) } ippoolt; @@ -202,15 +210,27 @@ struct Tringbuffer }; #endif +/* + * Possible tunnel states + * TUNNELFREE -> TUNNELOPEN -> TUNNELDIE -> TUNNELFREE + */ enum { - RADIUSNULL, // Not in use - RADIUSCHAP, // sending CHAP down PPP - RADIUSAUTH, // sending auth to RADIUS server - RADIUSIPCP, // sending IPCP to end user - RADIUSSTART, // sending start accounting to RADIUS server - RADIUSSTOP, // sending stop accounting to RADIUS server - RADIUSWAIT // waiting timeout before available, in case delayed replies + TUNNELFREE, // Not in use + TUNNELOPEN, // Active tunnel + TUNNELDIE, // Currently closing + TUNNELOPENING // Busy opening +}; + +enum +{ + RADIUSNULL, // Not in use + RADIUSCHAP, // sending CHAP down PPP + RADIUSAUTH, // sending auth to RADIUS server + RADIUSIPCP, // sending IPCP to end user + RADIUSSTART, // sending start accounting to RADIUS server + RADIUSSTOP, // sending stop accounting to RADIUS server + RADIUSWAIT // waiting timeout before available, in case delayed replies }; struct Tstats @@ -297,6 +317,49 @@ struct Tstats #define SET_STAT(x, y) #endif +struct configt +{ + int debug; // debugging level + time_t start_time; // time when l2tpns was started + char bandwidth[256]; // current bandwidth + + char config_file[128]; + int reload_config; // flag to re-read config (set by cli) + + char tapdevice[10]; // tap device name + char log_filename[128]; + char l2tpsecret[64]; + + char radiussecret[64]; + int radius_accounting; + ipt radiusserver[MAXRADSERVER]; // radius servers + u8 numradiusservers; // radius server count + + ipt default_dns1, default_dns2; + + ipt snoop_destination_host; + u16 snoop_destination_port; + + unsigned long rl_rate; + int save_state; + uint32_t cluster_address; + int ignore_cluster_updates; + char accounting_dir[128]; + ipt bind_address; + int target_uid; + int dump_speed; + char plugins[64][MAXPLUGINS]; + char old_plugins[64][MAXPLUGINS]; +}; + +struct config_descriptt +{ + char *key; + int offset; + int size; + enum { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IP } type; +}; + // arp.c void sendarp(int ifr_idx, const unsigned char* mac, ipt ip); @@ -321,6 +384,7 @@ void radiussend(u8 r, u8 state); void processrad(u8 *buf, int len); void radiusretry(u8 r); u8 radiusnew(sessionidt s); +void radiusclear(u8 r, sessionidt s); // throttle.c int throttle_session(sessionidt s, int throttle); @@ -343,7 +407,6 @@ void initudp(void); void initdata(void); void initippool(); sessionidt sessionbyip(ipt ip); -/* NB - sessionbyuser ignores walled garden'd sessions */ sessionidt sessionbyuser(char *username); void sessionshutdown(sessionidt s, char *reason); void sessionsendarp(sessionidt s); @@ -365,14 +428,14 @@ void processarp(u8 * buf, int len); void processudp(u8 * buf, int len, struct sockaddr_in *addr); void processtap(u8 * buf, int len); void processcontrol(u8 * buf, int len, struct sockaddr_in *addr); -ipt assign_ip_address(); -void free_ip_address(ipt address); +int assign_ip_address(sessionidt s); +void free_ip_address(sessionidt s); void snoop_send_packet(char *packet, u16 size); void dump_acct_info(); void mainloop(void); #define log _log #ifndef log_hex -#define log_hex(a,b,c,d) do{if (a <= debug) _log_hex(a,0,0,0,b,c,d);}while (0) +#define log_hex(a,b,c,d) do{if (a <= config->debug) _log_hex(a,0,0,0,b,c,d);}while (0) #endif void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...); void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize); @@ -380,10 +443,10 @@ void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **ch int sessionsetup(tunnelidt t, sessionidt s, u8 routes); int cluster_send_session(int s); int cluster_send_tunnel(int t); -#ifdef HAVE_LIBCLI +int cluster_send_goodbye(); void init_cli(); +void cli_do_file(FILE *fh); void cli_do(int sockfd); -#endif #ifdef RINGBUFFER void ringbuffer_dump(FILE *stream); #endif @@ -391,3 +454,9 @@ void initplugins(); int run_plugins(int plugin_type, void *data); void add_plugin(char *plugin_name); void remove_plugin(char *plugin_name); +void tunnelclear(tunnelidt t); +void host_unreachable(ipt destination, u16 id, ipt source, char *packet, int packet_len); + +extern tunnelt *tunnel; +extern sessiont *session; +#define sessionfree (session[0].next) diff --git a/ll.c b/ll.c index a4aad8b..fc18fa5 100644 --- a/ll.c +++ b/ll.c @@ -1,5 +1,5 @@ // L2TPNS Linked List Stuff -// $Id: ll.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: ll.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include diff --git a/md5.h b/md5.h index 53ecf51..cf6eee8 100644 --- a/md5.h +++ b/md5.h @@ -1,8 +1,6 @@ /* GLOBAL.H - RSAREF types and constants */ -#include "config.h" - /* PROTOTYPES should be set to one if and only if the compiler supports function argument prototyping. The following makes PROTOTYPES default to 0 if it has not already diff --git a/nsctl.c b/nsctl.c index 8a8aee0..aa6a300 100644 --- a/nsctl.c +++ b/nsctl.c @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) } for (p = 0; p < commands[i].params; p++) { - strncpy((packet + len), argv[p + 3], 1400 - len); + strncpy((packet + len), argv[p + 3], 1400 - len - 1); len += strlen(argv[p + 3]) + 1; } break; diff --git a/plugin.h b/plugin.h index 28855e1..b41b16f 100644 --- a/plugin.h +++ b/plugin.h @@ -4,16 +4,18 @@ #define PLUGIN_API_VERSION 1 #define MAX_PLUGIN_TYPES 30 -#define PLUGIN_PRE_AUTH 1 -#define PLUGIN_POST_AUTH 2 -#define PLUGIN_PACKET_RX 3 -#define PLUGIN_PACKET_TX 4 -#define PLUGIN_TIMER 5 -#define PLUGIN_CONFIG 6 -#define PLUGIN_NEW_SESSION 7 -#define PLUGIN_KILL_SESSION 8 -#define PLUGIN_CONTROL 9 -#define PLUGIN_RADIUS_RESPONSE 10 +enum +{ + PLUGIN_PRE_AUTH = 1, + PLUGIN_POST_AUTH, + PLUGIN_PACKET_RX, + PLUGIN_PACKET_TX, + PLUGIN_TIMER, + PLUGIN_NEW_SESSION, + PLUGIN_KILL_SESSION, + PLUGIN_CONTROL, + PLUGIN_RADIUS_RESPONSE +}; #define PLUGIN_RET_ERROR 0 #define PLUGIN_RET_OK 1 diff --git a/ppp.c b/ppp.c index cf9122d..bacd7b8 100644 --- a/ppp.c +++ b/ppp.c @@ -1,5 +1,5 @@ // L2TPNS PPP Stuff -// $Id: ppp.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: ppp.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include @@ -11,16 +11,15 @@ #include "plugin.h" #include "util.h" -extern char debug; extern tunnelt *tunnel; extern sessiont *session; extern radiust *radius; -extern u16 tapmac[3]; extern int tapfd; extern char hostname[1000]; extern struct Tstats *_statistics; extern unsigned long eth_tx; extern time_t time_now; +extern struct configt *config; // Process PAP messages void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) @@ -77,7 +76,7 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) p[4] = 0; // no message if (session[s].ip) { - log(3, session[s].ip, s, t, "%d Already an IP allocated: %s (%d)\n", getpid(), inet_toa(htonl(session[s].ip)), session[s].ip); + log(3, session[s].ip, s, t, "%d Already an IP allocated: %s (%d)\n", getpid(), inet_toa(htonl(session[s].ip)), session[s].ip_pool_index); } else { @@ -101,8 +100,8 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) return; } - strncpy(session[s].user, packet.username, sizeof(session[s].user)); - strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass)); + strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1); + strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass) - 1); free(packet.username); free(packet.password); @@ -180,7 +179,7 @@ void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) return; } - strncpy(session[s].user, packet.username, sizeof(session[s].user)); + strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1); memcpy(radius[r].pass, packet.password, 16); free(packet.username); @@ -408,7 +407,7 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) *p = EchoReply; // reply *(u32 *) (p + 4) = htonl(session[s].magic); // our magic number q = makeppp(b, p, l, t, s, PPPLCP); - log(3, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n"); + log(4, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n"); tunnelsend(b, l + (q - b), t); // send it } else if (*p == EchoReply) @@ -439,7 +438,7 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) if (*p == ConfigAck) { // happy with our IPCP u8 r = session[s].radius; - if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) + if ((!r || radius[r].state == RADIUSIPCP) && !session[s].servicenet) if (!r) r = radiusnew(s); if (r) @@ -552,6 +551,7 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) } session[s].cin += l; + session[s].total_cin += l; session[s].pin++; eth_tx += l; diff --git a/radius.c b/radius.c index 3f1d541..458b5f4 100644 --- a/radius.c +++ b/radius.c @@ -1,5 +1,5 @@ // L2TPNS Radius Stuff -// $Id: radius.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: radius.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include @@ -16,20 +16,13 @@ #include "plugin.h" #include "util.h" -extern char *radiussecret; extern radiust *radius; extern sessiont *session; extern tunnelt *tunnel; -extern ipt radiusserver[MAXRADSERVER]; // radius servers extern u32 sessionid; -extern u8 radiusfree; extern int radfd; -extern u8 numradiusservers; -extern char debug; -extern unsigned long default_dns1, default_dns2; extern struct Tstats *_statistics; -extern int radius_accounting; -extern uint32_t bind_address; +extern struct configt *config; const char *radius_state(int state) { @@ -51,27 +44,35 @@ void initrad(void) void radiusclear(u8 r, sessionidt s) { - radius[r].state = RADIUSNULL; if (s) session[s].radius = 0; - memset(&radius[r], 0, sizeof(radius[r])); - radius[r].next = radiusfree; - radiusfree = r; + memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL; +} + +static u8 new_radius() +{ + u8 i; + for (i = 1; i < MAXRADIUS; i++) + { + if (radius[i].state == RADIUSNULL) + return i; + } + log(0, 0, 0, 0, "Can't find a free radius session! This could be bad!\n"); + return 0; } u8 radiusnew(sessionidt s) { u8 r; - if (!radiusfree) + if (!(r = new_radius())) { log(1, 0, s, session[s].tunnel, "No free RADIUS sessions\n"); STAT(radius_overflow); return 0; }; - r = radiusfree; - session[s].radius = r; - radiusfree = radius[r].next; memset(&radius[r], 0, sizeof(radius[r])); + session[s].radius = r; radius[r].session = s; + radius[r].state = RADIUSWAIT; return r; } @@ -87,19 +88,19 @@ void radiussend(u8 r, u8 state) #ifdef STAT_CALLS STAT(call_radiussend); #endif - if (!numradiusservers) + s = radius[r].session; + if (!config->numradiusservers) { - log(0, 0, 0, 0, "No RADIUS servers\n"); + log(0, 0, s, session[s].tunnel, "No RADIUS servers\n"); return; } - if (!radiussecret) + if (!*config->radiussecret) { - log(0, 0, 0, 0, "No RADIUS secret\n"); + log(0, 0, s, session[s].tunnel, "No RADIUS secret\n"); return; } - s = radius[r].session; - if (state != RADIUSAUTH && !radius_accounting) + if (state != RADIUSAUTH && !config->radius_accounting) { // Radius accounting is turned off radiusclear(r, s); @@ -111,7 +112,7 @@ void radiussend(u8 r, u8 state) radius[r].state = state; radius[r].retry = backoff(radius[r].try++); log(4, 0, s, session[s].tunnel, "Send RADIUS %d state %s try %d\n", r, radius_state(radius[r].state), radius[r].try); - if (radius[r].try > numradiusservers * 2) + if (radius[r].try > config->numradiusservers * 2) { if (s) { @@ -177,7 +178,7 @@ void radiussend(u8 r, u8 state) { MD5_CTX ctx; MD5Init(&ctx); - MD5Update(&ctx, radiussecret, strlen(radiussecret)); + MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret)); if (p) MD5Update(&ctx, pass + p - 16, 16); else @@ -280,7 +281,7 @@ void radiussend(u8 r, u8 state) // NAS-IP-Address *p = 4; p[1] = 6; - *(u32 *)(p + 2) = bind_address; + *(u32 *)(p + 2) = config->bind_address; p += p[1]; // All AVpairs added @@ -295,14 +296,14 @@ void radiussend(u8 r, u8 state) MD5Update(&ctx, b, 4); MD5Update(&ctx, z, 16); MD5Update(&ctx, b + 20, (p - b) - 20); - MD5Update(&ctx, radiussecret, strlen(radiussecret)); + MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret)); MD5Final(hash, &ctx); memcpy(b + 4, hash, 16); memcpy(radius[r].auth, hash, 16); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - *(u32 *) & addr.sin_addr = htonl(radiusserver[(radius[r].try - 1) % numradiusservers]); + *(u32 *) & addr.sin_addr = config->radiusserver[(radius[r].try - 1) % config->numradiusservers]; addr.sin_port = htons((state == RADIUSAUTH) ? RADPORT : RADAPORT); log_hex(5, "RADIUS Send", b, (p - b)); @@ -348,7 +349,7 @@ void processrad(u8 * buf, int len) MD5Update(&ctx, buf, 4); MD5Update(&ctx, radius[r].auth, 16); MD5Update(&ctx, buf + 20, len - 20); - MD5Update(&ctx, radiussecret, strlen(radiussecret)); + MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret)); MD5Final(hash, &ctx); do { if (memcmp(hash, buf + 4, 16)) @@ -534,22 +535,22 @@ void processrad(u8 * buf, int len) // Check for Assign-IP-Address if (!session[s].ip || session[s].ip == 0xFFFFFFFE) { - session[s].ip = assign_ip_address(); + assign_ip_address(s); if (session[s].ip) log(3, 0, s, t, " No IP allocated by radius. Assigned %s from pool\n", inet_toa(htonl(session[s].ip))); else log(3, 0, s, t, " No IP allocated by radius. None available in pool\n"); } - if (!session[s].dns1 && default_dns1) + if (!session[s].dns1 && config->default_dns1) { - session[s].dns1 = htonl(default_dns1); - log(3, 0, s, t, " Sending dns1 = %s\n", inet_toa(default_dns1)); + session[s].dns1 = htonl(config->default_dns1); + log(3, 0, s, t, " Sending dns1 = %s\n", inet_toa(config->default_dns1)); } - if (!session[s].dns2 && default_dns2) + if (!session[s].dns2 && config->default_dns2) { - session[s].dns2 = htonl(default_dns2); - log(3, 0, s, t, " Sending dns2 = %s\n", inet_toa(default_dns2)); + session[s].dns2 = htonl(config->default_dns2); + log(3, 0, s, t, " Sending dns2 = %s\n", inet_toa(config->default_dns2)); } if (session[s].ip) @@ -612,3 +613,18 @@ void radiusretry(u8 r) } } +void radius_clean() +{ + int i; + + log(1, 0, 0, 0, "Cleaning radius session array\n"); + + for (i = 1; i < MAXRADIUS; i++) + { + if (radius[i].retry == 0 + || !session[radius[i].session].opened + || session[radius[i].session].die + || session[radius[i].session].tunnel == 0) + radiusclear(i, 0); + } +} diff --git a/rl.c b/rl.c index 7b2084b..49bd72c 100644 --- a/rl.c +++ b/rl.c @@ -1,5 +1,5 @@ // L2TPNS Rate Limiting Stuff -// $Id: rl.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: rl.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include @@ -11,47 +11,30 @@ #include #include "l2tpns.h" -extern char *radiussecret; extern radiust *radius; extern sessiont *session; -extern ipt radiusserver[MAXRADSERVER]; // radius servers extern u32 sessionid; -extern u8 radiusfree; extern int radfd; -extern u8 numradiusservers; -extern char debug; -extern char *tapdevice; extern tbft *filter_buckets; +extern struct configt *config; #define DEVICE "tun0" -unsigned long rl_rate = 0; int next_tbf = 1; void init_rl() { -#ifdef TC_TBF - system("tc qdisc del dev " DEVICE " root"); - system("tc qdisc add dev " DEVICE " root handle 1: cbq avpkt 10000 bandwidth 100mbit"); - system("tc filter del dev " DEVICE " protocol ip pref 1 fw"); - system("iptables -t mangle -N throttle 2>&1 > /dev/null"); - system("iptables -t mangle -F throttle"); - system("iptables -t mangle -A l2tpns -j throttle"); -#endif -#ifdef TC_HTB char *commands[] = { "tc qdisc add dev " DEVICE " root handle 1: htb default 1", "tc class add dev " DEVICE " parent 1: classid 1:1 htb rate 100mbit burst 300k", "tc filter del dev " DEVICE " protocol ip pref 1 fw", - "iptables -t mangle -N throttle 2>&1 > /dev/null", - "iptables -t mangle -F throttle", - "iptables -t mangle -A l2tpns -j throttle", + "iptables -t mangle -N throttle 2>&1 >/dev/null", + "iptables -t mangle -F throttle 2>&1 >/dev/null", + "iptables -t mangle -A l2tpns -j throttle 2>&1 >/dev/null", NULL }; int i; - if (!rl_rate) return; - log(2, 0, 0, 0, "Initializing HTB\n"); for (i = 0; commands[i] && *commands[i]; i++) { @@ -59,41 +42,21 @@ void init_rl() system(commands[i]); } log(2, 0, 0, 0, "Done initializing HTB\n"); -#endif } u16 rl_create_tbf() { u16 t; char cmd[2048]; - if (!rl_rate) return 0; + if (!config->rl_rate) return 0; if (next_tbf >= MAXSESSION) return 0; t = next_tbf++; snprintf(filter_buckets[t].handle, 9, "1:%d0", t); -#ifdef TC_TBF - log(2, 0, 0, 0, "Creating new tbf %s\n", filter_buckets[t].handle); - snprintf(cmd, 2048, "tc class add dev " DEVICE " parent 1: classid 1:%d cbq bandwidth 100Mbit rate 100Mbit " - "weight 1 prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000 bounded isolated", - t); - log(3, 0, 0, 0, "%s\n", cmd); - system(cmd); - - snprintf(cmd, 2048, "tc qdisc add dev " DEVICE " parent 1:%d handle %s tbf rate %dkbit buffer 1600 limit 3000", - t, filter_buckets[t].handle, rl_rate); - log(3, 0, 0, 0, "%s\n", cmd); - system(cmd); - - snprintf(cmd, 2048, "tc filter add dev " DEVICE " protocol ip parent 1:0 prio 1 handle %d fw flowid 1:%d", - t, t); - log(3, 0, 0, 0, "%s\n", cmd); - system(cmd); -#endif -#ifdef TC_HTB log(2, 0, 0, 0, "Creating new htb %s\n", filter_buckets[t].handle); snprintf(cmd, 2048, "tc class add dev " DEVICE " parent 1: classid %s htb rate %lukbit burst 15k", - filter_buckets[t].handle, rl_rate); + filter_buckets[t].handle, config->rl_rate); log(3, 0, 0, 0, "%s\n", cmd); system(cmd); @@ -101,7 +64,6 @@ u16 rl_create_tbf() t, filter_buckets[t].handle); log(3, 0, 0, 0, "%s\n", cmd); system(cmd); -#endif next_tbf++; return t; @@ -110,7 +72,7 @@ u16 rl_create_tbf() u16 rl_get_tbf() { int i; - if (!rl_rate) return 0; + if (!config->rl_rate) return 0; for (i = 1; i < MAXSESSION; i++) { @@ -129,30 +91,24 @@ u16 rl_get_tbf() void rl_done_tbf(u16 t) { if (!t) return; - if (!rl_rate) return; - log(2, 0, 0, 0, "Freeing up TBF %s\n", filter_buckets[t].handle); + if (!config->rl_rate) return; + log(2, 0, 0, 0, "Freeing up HTB %s\n", filter_buckets[t].handle); filter_buckets[t].in_use = 0; } void rl_destroy_tbf(u16 t) { char cmd[2048]; - if (!rl_rate) return; + if (!config->rl_rate) return; if (filter_buckets[t].in_use) { - log(0, 0, 0, 0, "Trying to destroy an in-use TBF %s\n", filter_buckets[t].handle); + log(0, 0, 0, 0, "Trying to destroy an in-use HTB %s\n", filter_buckets[t].handle); return; } -#ifdef TC_TBF - snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle); - system(cmd); -#endif -#ifdef TC_HTB snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle); system(cmd); -#endif - system("iptables -t mangle -D l2tpns -j throttle"); - system("iptables -t mangle -X throttle"); + system("iptables -t mangle -D l2tpns -j throttle 2>&1 >/dev/null"); + system("iptables -t mangle -X throttle 2>&1 >/dev/null"); memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); } diff --git a/throttle.c b/throttle.c index ba551d2..e0731f0 100644 --- a/throttle.c +++ b/throttle.c @@ -1,5 +1,5 @@ // L2TPNS Throttle Stuff -// $Id: throttle.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// $Id: throttle.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include @@ -16,22 +16,17 @@ #include "l2tpns.h" #include "util.h" -extern char *radiussecret; extern radiust *radius; extern sessiont *session; -extern ipt radiusserver[MAXRADSERVER]; // radius servers extern u32 sessionid; -extern u8 radiusfree; extern int radfd; -extern u8 numradiusservers; -extern char debug; -extern unsigned long rl_rate; extern tbft *filter_buckets; +extern struct configt *config; // Throttle or Unthrottle a session int throttle_session(sessionidt s, int throttle) { - if (!rl_rate) return 0; + if (!config->rl_rate) return 0; if (!*session[s].user) return 0; // User not logged in @@ -40,9 +35,15 @@ int throttle_session(sessionidt s, int throttle) { // Throttle them char cmd[2048] = {0}; - log(2, 0, s, session[s].tunnel, "Throttling session %d for user %s\n", s, session[s].user); if (!session[s].tbf) session[s].tbf = rl_get_tbf(); - snprintf(cmd, 2048, "iptables -t mangle -A throttle -d %s -j MARK --set-mark %d", inet_toa(ntohl(session[s].ip)), + if (!session[s].tbf) + { + log(1, 0, s, session[s].tunnel, "Error creating a filtering bucket for user %s\n", session[s].user); + return 0; + } + log(2, 0, s, session[s].tunnel, "Throttling session %d for user %s\n", s, session[s].user); + snprintf(cmd, 2048, "iptables -t mangle -A throttle -d %s -j MARK --set-mark %d", + inet_toa(ntohl(session[s].ip)), session[s].tbf); log(4, 0, s, session[s].tunnel, "Running %s\n", cmd); system(cmd);